diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 10:05:51 +0000 |
commit | 5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch) | |
tree | a94efe259b9009378be6d90eb30d2b019d95c194 /drivers/ide | |
parent | Initial commit. (diff) | |
download | linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip |
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/ide')
97 files changed, 39119 insertions, 0 deletions
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig new file mode 100644 index 000000000..19abf11c8 --- /dev/null +++ b/drivers/ide/Kconfig @@ -0,0 +1,849 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# IDE ATA ATAPI Block device driver configuration +# + +# Select HAVE_IDE if IDE is supported +config HAVE_IDE + bool + +menuconfig IDE + tristate "ATA/ATAPI/MFM/RLL support (DEPRECATED)" + depends on HAVE_IDE + depends on BLOCK + select BLK_SCSI_REQUEST + help + If you say Y here, your kernel will be able to manage ATA/(E)IDE and + ATAPI units. The most common cases are IDE hard drives and ATAPI + CD-ROM drives. + + This subsystem is currently in maintenance mode with only bug fix + changes applied. Users of ATA hardware are encouraged to migrate to + the newer ATA subsystem ("Serial ATA (prod) and Parallel ATA + (experimental) drivers") which is more actively maintained. + + To compile this driver as a module, choose M here: the + module will be called ide-core. + + For further information, please read <file:Documentation/ide/ide.rst>. + + If unsure, say N. + +if IDE + +comment "Please see Documentation/ide/ide.rst for help/info on IDE drives" + +config IDE_XFER_MODE + bool + +config IDE_TIMINGS + bool + select IDE_XFER_MODE + +config IDE_ATAPI + bool + +config IDE_LEGACY + bool + +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", uses the SCSI subsystem + and supports most modern SATA controllers. In order to use it + you may take a look at "Serial ATA (prod) and Parallel ATA + (experimental) drivers". + + 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 IDE_GD + tristate "generic ATA/ATAPI disk support" + default y + help + Support for ATA/ATAPI disks (including ATAPI floppy drives). + + To compile this driver as a module, choose M here. + The module will be called ide-gd_mod. + + If unsure, say Y. + +config IDE_GD_ATA + bool "ATA disk support" + depends on IDE_GD + default y + help + This will include support for ATA hard disks. + + If unsure, say Y. + +config IDE_GD_ATAPI + bool "ATAPI floppy support" + depends on IDE_GD + select IDE_ATAPI + help + This will include support for ATAPI floppy drives + (i.e. Iomega ZIP or MKE LS-120). + + 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>. + + If unsure, say N. + +config BLK_DEV_IDECS + tristate "PCMCIA IDE support" + depends on PCMCIA + help + Support for Compact Flash cards, outboard IDE disks, tape drives, + and CD-ROM drives connected through a PCMCIA card. + +config BLK_DEV_DELKIN + tristate "Cardbus IDE support (Delkin/ASKA/Workbit)" + depends on CARDBUS && PCI + help + Support for Delkin, ASKA, and Workbit Cardbus CompactFlash + Adapters. This may also work for similar SD and XD adapters. + +config BLK_DEV_IDECD + tristate "Include IDE/ATAPI CDROM support" + depends on BLK_DEV + select IDE_ATAPI + select CDROM + 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". + + To compile this driver as a module, choose M here: the + module will be called ide-cd. + +config BLK_DEV_IDECD_VERBOSE_ERRORS + bool "Verbose error logging for IDE/ATAPI CDROM driver" if EXPERT + depends on BLK_DEV_IDECD + default y + help + Turn this on to have the driver print out the meanings of the + ATAPI error codes. This will use up additional 8kB of kernel-space + memory, though. + +config BLK_DEV_IDETAPE + tristate "Include IDE/ATAPI TAPE support" + select IDE_ATAPI + 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/ide.rst> + files for usage information. + + To compile this driver as a module, choose M here: the + module will be called ide-tape. + +config BLK_DEV_IDEACPI + bool "IDE ACPI support" + depends on ACPI + help + Implement ACPI support for generic IDE devices. On modern + machines ACPI support is required to properly handle ACPI S3 states. + +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 recovery if needed. This is the most + basic form of media-forensics. + + If you are unsure, say N here. + +config IDE_PROC_FS + bool "legacy /proc/ide/ support" + depends on IDE && PROC_FS + default y + help + This option enables support for the various files in + /proc/ide. In Linux 2.6 this has been superseded by + files in sysfs but many legacy applications rely on this. + + If unsure say Y. + +comment "IDE chipset support/bugfixes" + +config IDE_GENERIC + tristate "generic/default IDE chipset support" + depends on ALPHA || X86 || IA64 || MIPS || ARCH_RPC + default ARM && ARCH_RPC + help + This is the generic IDE driver. This driver attaches to the + fixed legacy ports (e.g. on PCs 0x1f0/0x170, 0x1e8/0x168 and + so on). Please note that if this driver is built into the + kernel or loaded before other ATA (IDE or libata) drivers + and the controller is located at legacy ports, this driver + may grab those ports and thus can prevent the controller + specific driver from attaching. + + Also, currently, IDE generic doesn't allow IRQ sharing + meaning that the IRQs it grabs won't be available to other + controllers sharing those IRQs which usually makes drivers + for those controllers fail. Generally, it's not a good idea + to load IDE generic driver on modern systems. + + If unsure, say N. + +config BLK_DEV_PLATFORM + tristate "Platform driver for IDE interfaces" + help + This is the platform IDE driver, used mostly for Memory Mapped + IDE devices, like Compact Flashes running in True IDE mode. + + If unsure, say N. + +config BLK_DEV_CMD640 + tristate "CMD640 chipset bugfix/support" + depends on X86 + select IDE_TIMINGS + 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: "cmd640.probe_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/ide.rst>. + +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/ide.rst>. 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 + tristate "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_IDEDMA_SFF + bool + +if PCI + +comment "PCI IDE chipsets support" + +config BLK_DEV_IDEPCI + bool + +config IDEPCI_PCIBUS_ORDER + bool "Probe IDE PCI devices in the PCI bus order (DEPRECATED)" + depends on IDE=y && BLK_DEV_IDEPCI + default y + help + Probe IDE PCI devices in the order in which they appear on the + PCI bus (i.e. 00:1f.1 PCI device before 02:01.0 PCI device) + instead of the order in which IDE PCI host drivers are loaded. + + Please note that this method of assuring stable naming of + IDE devices is unreliable and use other means for achieving + it (i.e. udev). + + If in doubt, say N. + +# TODO: split it on per host driver config options (or module parameters) +config BLK_DEV_OFFBOARD + bool "Boot off-board chipsets first support (DEPRECATED)" + depends on BLK_DEV_IDEPCI && (BLK_DEV_AEC62XX || BLK_DEV_GENERIC || BLK_DEV_HPT366 || BLK_DEV_PDC202XX_NEW || BLK_DEV_PDC202XX_OLD || BLK_DEV_TC86C001) + 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. + + Note that, if you do this, the order of the hd* devices will be + rearranged which may require modification of fstab and other files. + + Please also note that this method of assuring stable naming of + IDE devices is unreliable and use other means for achieving it + (i.e. udev). + + If in doubt, say N. + +config BLK_DEV_GENERIC + tristate "Generic PCI IDE Chipset Support" + select BLK_DEV_IDEPCI + help + This option provides generic support for various PCI IDE Chipsets + which otherwise might not be supported. + +config BLK_DEV_OPTI621 + tristate "OPTi 82C621 chipset enhanced support" + select BLK_DEV_IDEPCI + help + This is a driver for the OPTi 82C621 EIDE controller. + Please read the comments at the top of <file:drivers/ide/opti621.c>. + +config BLK_DEV_RZ1000 + tristate "RZ1000 chipset bugfix/support" + depends on X86 + select BLK_DEV_IDEPCI + 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_IDEDMA_PCI + bool + select BLK_DEV_IDEPCI + select BLK_DEV_IDEDMA_SFF + +config BLK_DEV_AEC62XX + tristate "AEC62XX chipset support" + select BLK_DEV_IDEDMA_PCI + 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" + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + 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. + + Please read the comments at the top of + <file:drivers/ide/alim15x3.c>. + + If unsure, say N. + +config BLK_DEV_AMD74XX + tristate "AMD and nVidia IDE support" + depends on !ARM + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + 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 + select BLK_DEV_IDEDMA_PCI + 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" + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + 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" + select BLK_DEV_IDEDMA_PCI + 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" + depends on ALPHA + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + help + This driver adds detection and support for the CY82C693 chipset + used on Digital's PC-Alpha 164SX boards. + +config BLK_DEV_CS5520 + tristate "Cyrix CS5510/20 MediaGX chipset support (VERY EXPERIMENTAL)" + depends on X86_32 || COMPILE_TEST + select BLK_DEV_IDEDMA_PCI + help + Include support for PIO tuning and 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" + depends on X86_32 || COMPILE_TEST + select BLK_DEV_IDEDMA_PCI + 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_CS5535 + tristate "AMD CS5535 chipset support" + depends on X86_32 + select BLK_DEV_IDEDMA_PCI + help + Include support for UDMA on the NSC/AMD CS5535 companion chipset. + This will automatically be detected and configured if found. + + It is safe to say Y to this question. + +config BLK_DEV_CS5536 + tristate "CS5536 chipset support" + depends on X86_32 + select BLK_DEV_IDEDMA_PCI + help + This option enables support for the AMD CS5536 + companion chip used with the Geode LX processor family. + + If unsure, say N. + +config BLK_DEV_HPT366 + tristate "HPT36X/37X chipset support" + select BLK_DEV_IDEDMA_PCI + 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. + + 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_JMICRON + tristate "JMicron JMB36x support" + select BLK_DEV_IDEDMA_PCI + help + Basic support for the JMicron ATA controllers. For full support + use the libata drivers. + +config BLK_DEV_SC1200 + tristate "National SCx200 chipset support" + depends on X86_32 || COMPILE_TEST + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for the on-board IDE controller on the + National SCx200 series of embedded x86 "Geode" systems. + +config BLK_DEV_PIIX + tristate "Intel PIIX/ICH chipsets support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds explicit support for Intel PIIX and ICH chips. + This allows the kernel to change PIO, DMA and UDMA speeds and to + configure the chip to optimum performance. + +config BLK_DEV_IT8172 + tristate "IT8172 IDE support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for the IDE controller on the + IT8172 System Controller. + +config BLK_DEV_IT8213 + tristate "IT8213 IDE support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for the ITE 8213 IDE controller. + +config BLK_DEV_IT821X + tristate "IT821X IDE support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for the ITE 8211 IDE controller and the + IT 8212 IDE RAID controller in both RAID and pass-through mode. + +config BLK_DEV_NS87415 + tristate "NS87415 chipset support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds detection and support for the NS87415 chip + (used mainly on SPARC64 and PA-RISC machines). + + Please read the comments at the top of <file:drivers/ide/ns87415.c>. + +config BLK_DEV_PDC202XX_OLD + tristate "PROMISE PDC202{46|62|65|67} support" + select BLK_DEV_IDEDMA_PCI + 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. + + Please read the comments at the top of + <file:drivers/ide/pdc202xx_old.c>. + + If unsure, say N. + +config BLK_DEV_PDC202XX_NEW + tristate "PROMISE PDC202{68|69|70|71|75|76|77} support" + select BLK_DEV_IDEDMA_PCI + +config BLK_DEV_SVWKS + tristate "ServerWorks OSB4/CSB5/CSB6 chipsets support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds PIO/(U)DMA support for the ServerWorks OSB4/CSB5 + chipsets. + +config BLK_DEV_SIIMAGE + tristate "Silicon Image chipset support" + select BLK_DEV_IDEDMA_PCI + 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 + select BLK_DEV_IDEDMA_PCI + 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 + + Please read the comments at the top of <file:drivers/ide/sis5513.c>. + +config BLK_DEV_SL82C105 + tristate "Winbond SL82c105 support" + depends on (PPC || ARM) + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + 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_SLC90E66 + tristate "SLC90E66 chipset support" + select BLK_DEV_IDEDMA_PCI + help + This driver ensures (U)DMA support for Victory66 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. + + Please read the comments at the top of + <file:drivers/ide/slc90e66.c>. + +config BLK_DEV_TRM290 + tristate "Tekram TRM290 chipset support" + select BLK_DEV_IDEDMA_PCI + 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/trm290.c>. + +config BLK_DEV_VIA82CXXX + tristate "VIA82CXXX chipset support" + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + 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. + +config BLK_DEV_TC86C001 + tristate "Toshiba TC86C001 support" + select BLK_DEV_IDEDMA_PCI + help + This driver adds support for Toshiba TC86C001 GOKU-S chip. + +endif + +# TODO: BLK_DEV_IDEDMA_PCI -> BLK_DEV_IDEDMA_SFF +config BLK_DEV_IDE_PMAC + tristate "PowerMac on-board IDE support" + depends on PPC_PMAC + select IDE_TIMINGS + select BLK_DEV_IDEDMA_PCI + help + This driver provides support for the on-board IDE controller on + most of the recent Apple Power Macintoshes and PowerBooks. + If unsure, say Y. + +config BLK_DEV_IDE_PMAC_ATA100FIRST + bool "Probe on-board 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_IDE_TX4938 + tristate "TX4938 internal IDE support" + depends on SOC_TX4938 + select IDE_TIMINGS + +config BLK_DEV_IDE_TX4939 + tristate "TX4939 internal IDE support" + depends on SOC_TX4939 + select BLK_DEV_IDEDMA_SFF + +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 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_GAYLE + tristate "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 on-board 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). + + It also provides support for the so-called `IDE doublers' (made + by various manufacturers, e.g. Eyetech) that can be connected to + the on-board IDE interface of some Amiga models. Using such an IDE + doubler, you can connect up to four instead of two IDE devices to + the Amiga's on-board IDE interface. The feature is enabled at kernel + runtime using the "gayle.doubler" kernel boot parameter. + + 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_BUDDHA + tristate "Buddha/Catweasel/X-Surf IDE interface support" + depends on ZORRO + 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 + tristate "Falcon IDE interface support" + depends on ATARI + help + This is the IDE driver for the on-board 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 on-board IDE + interface. + +config BLK_DEV_MAC_IDE + tristate "Macintosh Quadra/Powerbook IDE interface support" + depends on MAC + help + This is the IDE driver for the on-board IDE interface on some m68k + Macintosh models, namely Quadra/Centris 630, Performa 588 and + Powerbook 150. The IDE interface on the Powerbook 190 is not + supported by this driver and requires BLK_DEV_PLATFORM or + PATA_PLATFORM. + + 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 + on-board IDE interface. + +config BLK_DEV_Q40IDE + tristate "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_PALMCHIP_BK3710 + tristate "Palmchip bk3710 IDE controller support" + depends on ARCH_DAVINCI + select IDE_TIMINGS + select BLK_DEV_IDEDMA_SFF + help + Say Y here if you want to support the onchip IDE controller on the + TI DaVinci SoC + +# no isa -> no vlb +if ISA && (ALPHA || X86 || MIPS) + +comment "Other IDE chipsets support" +comment "Note: most of these also require special kernel boot parameters" + +config BLK_DEV_4DRIVES + tristate "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 "ide-4drives.probe" kernel boot parameter if you + say Y here. + +config BLK_DEV_ALI14XX + tristate "ALI M14xx support" + select IDE_TIMINGS + select IDE_LEGACY + help + This driver is enabled at runtime using the "ali14xx.probe" 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/ide.rst> and + <file:drivers/ide/ali14xx.c> for more info. + +config BLK_DEV_DTC2278 + tristate "DTC-2278 support" + select IDE_XFER_MODE + select IDE_LEGACY + help + This driver is enabled at runtime using the "dtc2278.probe" 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/ide.rst> and + <file:drivers/ide/dtc2278.c> files for more info. + +config BLK_DEV_HT6560B + tristate "Holtek HT6560B support" + select IDE_TIMINGS + select IDE_LEGACY + help + This driver is enabled at runtime using the "ht6560b.probe" 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/ide.rst> and + <file:drivers/ide/ht6560b.c> files for more info. + +config BLK_DEV_QD65XX + tristate "QDI QD65xx support" + select IDE_TIMINGS + select IDE_LEGACY + help + This driver is enabled at runtime using the "qd65xx.probe" kernel + boot parameter. It permits faster I/O speeds to be set. See the + <file:Documentation/ide/ide.rst> and <file:drivers/ide/qd65xx.c> + for more info. + +config BLK_DEV_UMC8672 + tristate "UMC-8672 support" + select IDE_XFER_MODE + select IDE_LEGACY + help + This driver is enabled at runtime using the "umc8672.probe" 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/ide.rst> and + <file:drivers/ide/umc8672.c> for more info. + +endif + +config BLK_DEV_IDEDMA + def_bool BLK_DEV_IDEDMA_SFF || BLK_DEV_IDEDMA_ICS + select IDE_XFER_MODE + +endif # IDE diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile new file mode 100644 index 000000000..2605b3cda --- /dev/null +++ b/drivers/ide/Makefile @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# link order is important here +# + +ide-core-y += ide.o ide-ioctls.o ide-io.o ide-iops.o ide-lib.o ide-probe.o \ + ide-taskfile.o ide-pm.o ide-park.o ide-sysfs.o ide-devsets.o \ + ide-io-std.o ide-eh.o + +# core IDE code +ide-core-$(CONFIG_IDE_XFER_MODE) += ide-pio-blacklist.o ide-xfer-mode.o +ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o +ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o +ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o +ide-core-$(CONFIG_BLK_DEV_IDEDMA_SFF) += ide-dma-sff.o +ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o +ide-core-$(CONFIG_BLK_DEV_IDEACPI) += ide-acpi.o +ide-core-$(CONFIG_IDE_LEGACY) += ide-legacy.o + +obj-$(CONFIG_IDE) += ide-core.o + +obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o +obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.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_4DRIVES) += ide-4drives.o + +obj-$(CONFIG_BLK_DEV_GAYLE) += gayle.o +obj-$(CONFIG_BLK_DEV_FALCON_IDE) += falconide.o +obj-$(CONFIG_BLK_DEV_MAC_IDE) += macide.o +obj-$(CONFIG_BLK_DEV_Q40IDE) += q40ide.o +obj-$(CONFIG_BLK_DEV_BUDDHA) += buddha.o + +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_CS5535) += cs5535.o +obj-$(CONFIG_BLK_DEV_CS5536) += cs5536.o +obj-$(CONFIG_BLK_DEV_SC1200) += sc1200.o +obj-$(CONFIG_BLK_DEV_CY82C693) += cy82c693.o +obj-$(CONFIG_BLK_DEV_DELKIN) += delkin_cb.o +obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o +obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o +obj-$(CONFIG_BLK_DEV_IT8213) += it8213.o +obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o +obj-$(CONFIG_BLK_DEV_JMICRON) += jmicron.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_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_TC86C001) += tc86c001.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) += ide-pci-generic.o + +obj-$(CONFIG_IDEPCI_PCIBUS_ORDER) += ide-scan-pci.o + +obj-$(CONFIG_BLK_DEV_CMD640) += cmd640.o + +obj-$(CONFIG_BLK_DEV_IDE_PMAC) += pmac.o + +obj-$(CONFIG_IDE_GENERIC) += ide-generic.o +obj-$(CONFIG_BLK_DEV_IDEPNP) += ide-pnp.o + +ide-gd_mod-y += ide-gd.o +ide-cd_mod-y += ide-cd.o ide-cd_ioctl.o ide-cd_verbose.o + +ifeq ($(CONFIG_IDE_GD_ATA), y) + ide-gd_mod-y += ide-disk.o ide-disk_ioctl.o +ifeq ($(CONFIG_IDE_PROC_FS), y) + ide-gd_mod-y += ide-disk_proc.o +endif +endif + +ifeq ($(CONFIG_IDE_GD_ATAPI), y) + ide-gd_mod-y += ide-floppy.o ide-floppy_ioctl.o +ifeq ($(CONFIG_IDE_PROC_FS), y) + ide-gd_mod-y += ide-floppy_proc.o +endif +endif + +obj-$(CONFIG_IDE_GD) += ide-gd_mod.o +obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd_mod.o +obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o + +obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o + +obj-$(CONFIG_BLK_DEV_PLATFORM) += ide_platform.o + +obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o +obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +obj-$(CONFIG_BLK_DEV_PALMCHIP_BK3710) += palm_bk3710.o + +obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o +obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o diff --git a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c new file mode 100644 index 000000000..4c959ce41 --- /dev/null +++ b/drivers/ide/aec62xx.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com> + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "aec62xx" + +struct chipset_bus_clock_list_entry { + u8 xfer_speed; + u8 chipset_settings; + u8 ultra_settings; +}; + +static const 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 const 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 } +}; + +/* + * 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 void aec6210_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; + u16 d_conf = 0; + u8 ultra = 0, ultra_conf = 0; + u8 tmp0 = 0, tmp1 = 0, tmp2 = 0; + const u8 speed = drive->dma_mode; + 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, bus_clock); + 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, bus_clock); + tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn)))); + pci_write_config_byte(dev, 0x54, tmp2); + local_irq_restore(flags); +} + +static void aec6260_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct chipset_bus_clock_list_entry *bus_clock = host->host_priv; + u8 unit = drive->dn & 1; + u8 tmp1 = 0, tmp2 = 0; + u8 ultra = 0, drive_conf = 0, ultra_conf = 0; + const u8 speed = drive->dma_mode; + 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, bus_clock); + 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, bus_clock); + tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit)))); + pci_write_config_byte(dev, (0x44|hwif->channel), tmp2); + local_irq_restore(flags); +} + +static void aec_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + hwif->port_ops->set_dma_mode(hwif, drive); +} + +static int init_chipset_aec62xx(struct pci_dev *dev) +{ + /* These are necessary to get AEC6280 Macintosh cards to work */ + if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || + (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) { + u8 reg49h = 0, reg4ah = 0; + /* Clear reset and test bits. */ + pci_read_config_byte(dev, 0x49, ®49h); + pci_write_config_byte(dev, 0x49, reg49h & ~0x30); + /* Enable chip interrupt output. */ + pci_read_config_byte(dev, 0x4a, ®4ah); + pci_write_config_byte(dev, 0x4a, reg4ah & ~0x01); + /* Enable burst mode. */ + pci_read_config_byte(dev, 0x4a, ®4ah); + pci_write_config_byte(dev, 0x4a, reg4ah | 0x80); + } + + return 0; +} + +static u8 atp86x_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 ata66 = 0, mask = hwif->channel ? 0x02 : 0x01; + + pci_read_config_byte(dev, 0x49, &ata66); + + return (ata66 & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +static const struct ide_port_ops atp850_port_ops = { + .set_pio_mode = aec_set_pio_mode, + .set_dma_mode = aec6210_set_mode, +}; + +static const struct ide_port_ops atp86x_port_ops = { + .set_pio_mode = aec_set_pio_mode, + .set_dma_mode = aec6260_set_mode, + .cable_detect = atp86x_cable_detect, +}; + +static const struct ide_port_info aec62xx_chipsets[] = { + { /* 0: AEC6210 */ + .name = DRV_NAME, + .init_chipset = init_chipset_aec62xx, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .port_ops = &atp850_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | + IDE_HFLAG_NO_ATAPI_DMA | + IDE_HFLAG_NO_DSC | + IDE_HFLAG_OFF_BOARD, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, + }, + { /* 1: AEC6260 */ + .name = DRV_NAME, + .init_chipset = init_chipset_aec62xx, + .port_ops = &atp86x_port_ops, + .host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA | + IDE_HFLAG_OFF_BOARD, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA4, + }, + { /* 2: AEC6260R */ + .name = DRV_NAME, + .init_chipset = init_chipset_aec62xx, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .port_ops = &atp86x_port_ops, + .host_flags = IDE_HFLAG_NO_ATAPI_DMA | + IDE_HFLAG_NON_BOOTABLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA4, + }, + { /* 3: AEC6280 */ + .name = DRV_NAME, + .init_chipset = init_chipset_aec62xx, + .port_ops = &atp86x_port_ops, + .host_flags = IDE_HFLAG_NO_ATAPI_DMA | + IDE_HFLAG_OFF_BOARD, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, + { /* 4: AEC6280R */ + .name = DRV_NAME, + .init_chipset = init_chipset_aec62xx, + .enablebits = {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, + .port_ops = &atp86x_port_ops, + .host_flags = IDE_HFLAG_NO_ATAPI_DMA | + IDE_HFLAG_OFF_BOARD, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + } +}; + +/** + * 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. + * + * NOTE: since we're going to modify the 'name' field for AEC-6[26]80[R] + * chips, pass a local copy of 'struct ide_port_info' down the call chain. + */ + +static int aec62xx_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + const struct chipset_bus_clock_list_entry *bus_clock; + struct ide_port_info d; + u8 idx = id->driver_data; + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; + int err; + + if (bus_speed <= 33) + bus_clock = aec6xxx_33_base; + else + bus_clock = aec6xxx_34_base; + + err = pci_enable_device(dev); + if (err) + return err; + + d = aec62xx_chipsets[idx]; + + if (idx == 3 || idx == 4) { + unsigned long dma_base = pci_resource_start(dev, 4); + + if (inb(dma_base + 2) & 0x10) { + printk(KERN_INFO DRV_NAME " %s: AEC6880%s card detected" + "\n", pci_name(dev), (idx == 4) ? "R" : ""); + d.udma_mask = ATA_UDMA6; + } + } + + err = ide_pci_init_one(dev, &d, (void *)bus_clock); + if (err) + pci_disable_device(dev); + + return err; +} + +static void aec62xx_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_disable_device(dev); +} + +static const struct pci_device_id aec62xx_pci_tbl[] = { + { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF), 0 }, + { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860), 1 }, + { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R), 2 }, + { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP865), 3 }, + { PCI_VDEVICE(ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R), 4 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl); + +static struct pci_driver aec62xx_pci_driver = { + .name = "AEC62xx_IDE", + .id_table = aec62xx_pci_tbl, + .probe = aec62xx_init_one, + .remove = aec62xx_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init aec62xx_ide_init(void) +{ + return ide_pci_register_driver(&aec62xx_pci_driver); +} + +static void __exit aec62xx_ide_exit(void) +{ + pci_unregister_driver(&aec62xx_pci_driver); +} + +module_init(aec62xx_ide_init); +module_exit(aec62xx_ide_exit); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for ARTOP AEC62xx IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ali14xx.c b/drivers/ide/ali14xx.c new file mode 100644 index 000000000..3268931c2 --- /dev/null +++ b/drivers/ide/ali14xx.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + */ + +#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/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "ali14xx" + +/* port addresses for auto-detection */ +#define ALI_NUM_PORTS 4 +static const int ports[ALI_NUM_PORTS] __initconst = + { 0x074, 0x0f4, 0x034, 0x0e4 }; + +/* register initialization data */ +typedef struct { u8 reg, data; } RegInitializer; + +static const RegInitializer initData[] __initconst = { + {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} +}; + +/* 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); +} + +static DEFINE_SPINLOCK(ali14xx_lock); + +/* + * Set PIO mode for the specified drive. + * This function computes timing parameters + * and sets controller registers accordingly. + */ +static void ali14xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + int driveNum; + int time1, time2; + u8 param1, param2, param3, param4; + unsigned long flags; + int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; + const u8 pio = drive->pio_mode - XFER_PIO_0; + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + + /* calculate timing, according to PIO mode */ + time1 = ide_pio_cycle_time(drive, pio); + time2 = t->active; + 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 = (drive->hwif->index << 1) + (drive->dn & 1); + spin_lock_irqsave(&ali14xx_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(&ali14xx_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) +{ + const 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 const struct ide_port_ops ali14xx_port_ops = { + .set_pio_mode = ali14xx_set_pio_mode, +}; + +static const struct ide_port_info ali14xx_port_info = { + .name = DRV_NAME, + .chipset = ide_ali14xx, + .port_ops = &ali14xx_port_ops, + .host_flags = IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO4, +}; + +static int __init ali14xx_probe(void) +{ + 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; + } + + return ide_legacy_device_add(&ali14xx_port_info, 0); +} + +static bool probe_ali14xx; + +module_param_named(probe, probe_ali14xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets"); + +static int __init ali14xx_init(void) +{ + if (probe_ali14xx == 0) + goto out; + + /* auto-detect IDE controller port */ + if (findPort()) { + if (ali14xx_probe()) + return -ENODEV; + return 0; + } + printk(KERN_ERR "ali14xx: not found.\n"); +out: + return -ENODEV; +} + +module_init(ali14xx_init); + +MODULE_AUTHOR("see local file"); +MODULE_DESCRIPTION("support of ALI 14XX IDE chipsets"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c new file mode 100644 index 000000000..3265970ae --- /dev/null +++ b/drivers/ide/alim15x3.c @@ -0,0 +1,602 @@ +/* + * 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 + * ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw> + * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz + * + * (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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/dmi.h> + +#include <asm/io.h> + +#define DRV_NAME "alim15x3" + +/* + * 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; + +static void ali_fifo_control(ide_hwif_t *hwif, ide_drive_t *drive, int on) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + int pio_fifo = 0x54 + hwif->channel; + u8 fifo; + int shift = 4 * (drive->dn & 1); + + pci_read_config_byte(pdev, pio_fifo, &fifo); + fifo &= ~(0x0F << shift); + fifo |= (on << shift); + pci_write_config_byte(pdev, pio_fifo, fifo); +} + +static void ali_program_timings(ide_hwif_t *hwif, ide_drive_t *drive, + struct ide_timing *t, u8 ultra) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int port = hwif->channel ? 0x5c : 0x58; + int udmat = 0x56 + hwif->channel; + u8 unit = drive->dn & 1, udma; + int shift = 4 * unit; + + /* Set up the UDMA */ + pci_read_config_byte(dev, udmat, &udma); + udma &= ~(0x0F << shift); + udma |= ultra << shift; + pci_write_config_byte(dev, udmat, udma); + + if (t == NULL) + return; + + t->setup = clamp_val(t->setup, 1, 8) & 7; + t->act8b = clamp_val(t->act8b, 1, 8) & 7; + t->rec8b = clamp_val(t->rec8b, 1, 16) & 15; + t->active = clamp_val(t->active, 1, 8) & 7; + t->recover = clamp_val(t->recover, 1, 16) & 15; + + pci_write_config_byte(dev, port, t->setup); + pci_write_config_byte(dev, port + 1, (t->act8b << 4) | t->rec8b); + pci_write_config_byte(dev, port + unit + 2, + (t->active << 4) | t->recover); +} + +/** + * ali_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Program the controller for the given PIO mode. + */ + +static void ali_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_drive_t *pair = ide_get_pair_dev(drive); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; + unsigned long T = 1000000 / bus_speed; /* PCI clock based */ + struct ide_timing t; + + ide_timing_compute(drive, drive->pio_mode, &t, T, 1); + if (pair) { + struct ide_timing p; + + ide_timing_compute(pair, pair->pio_mode, &p, T, 1); + ide_timing_merge(&p, &t, &t, + IDE_TIMING_SETUP | IDE_TIMING_8BIT); + if (pair->dma_mode) { + ide_timing_compute(pair, pair->dma_mode, &p, T, 1); + ide_timing_merge(&p, &t, &t, + IDE_TIMING_SETUP | IDE_TIMING_8BIT); + } + } + + /* + * PIO mode => ATA FIFO on, ATAPI FIFO off + */ + ali_fifo_control(hwif, drive, (drive->media == ide_disk) ? 0x05 : 0x00); + + ali_program_timings(hwif, drive, &t, 0); +} + +/** + * ali_udma_filter - compute UDMA mask + * @drive: IDE device + * + * Return available UDMA modes. + * + * The actual rules for the ALi are: + * No UDMA on revisions <= 0x20 + * Disk only for revisions < 0xC2 + * Not WDC drives on M1543C-E (?) + */ + +static u8 ali_udma_filter(ide_drive_t *drive) +{ + if (m5229_revision > 0x20 && m5229_revision < 0xC2) { + if (drive->media != ide_disk) + return 0; + if (chip_is_1543c_e && + strstr((char *)&drive->id[ATA_ID_PROD], "WDC ")) + return 0; + } + + return drive->hwif->ultra_mask; +} + +/** + * ali_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Configure the hardware for the desired IDE transfer mode. + */ + +static void ali_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static u8 udma_timing[7] = { 0xC, 0xB, 0xA, 0x9, 0x8, 0xF, 0xD }; + struct pci_dev *dev = to_pci_dev(hwif->dev); + ide_drive_t *pair = ide_get_pair_dev(drive); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; + unsigned long T = 1000000 / bus_speed; /* PCI clock based */ + const u8 speed = drive->dma_mode; + u8 tmpbyte = 0x00; + struct ide_timing t; + + if (speed < XFER_UDMA_0) { + ide_timing_compute(drive, drive->dma_mode, &t, T, 1); + if (pair) { + struct ide_timing p; + + ide_timing_compute(pair, pair->pio_mode, &p, T, 1); + ide_timing_merge(&p, &t, &t, + IDE_TIMING_SETUP | IDE_TIMING_8BIT); + if (pair->dma_mode) { + ide_timing_compute(pair, pair->dma_mode, + &p, T, 1); + ide_timing_merge(&p, &t, &t, + IDE_TIMING_SETUP | IDE_TIMING_8BIT); + } + } + ali_program_timings(hwif, drive, &t, 0); + } else { + ali_program_timings(hwif, drive, NULL, + udma_timing[speed - XFER_UDMA_0]); + if (speed >= XFER_UDMA_3) { + pci_read_config_byte(dev, 0x4b, &tmpbyte); + tmpbyte |= 1; + pci_write_config_byte(dev, 0x4b, tmpbyte); + } + } +} + +/** + * ali_dma_check - DMA check + * @drive: target device + * @cmd: command + * + * Returns 1 if the DMA cannot be performed, zero on success. + */ + +static int ali_dma_check(ide_drive_t *drive, struct ide_cmd *cmd) +{ + if (m5229_revision < 0xC2 && drive->media != ide_disk) { + if (cmd->tf_flags & IDE_TFLAG_WRITE) + return 1; /* try PIO instead of DMA */ + } + return 0; +} + +/** + * init_chipset_ali15x3 - Initialise an ALi IDE controller + * @dev: PCI device + * + * This function initializes the ALI IDE controller and where + * appropriate also sets up the 1533 southbridge. + */ + +static int init_chipset_ali15x3(struct pci_dev *dev) +{ + unsigned long flags; + u8 tmpbyte; + struct pci_dev *north = pci_get_slot(dev->bus, PCI_DEVFN(0,0)); + + m5229_revision = dev->revision; + + isa_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + + 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); + /* + * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010 + */ + if (m5229_revision >= 0x20 && isa_dev) { + pci_read_config_byte(isa_dev, 0x5e, &tmpbyte); + chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0; + } + goto out; + } + + /* + * 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) + goto out; + + 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); + } + } + +out: + /* + * 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. + */ + if (m5229_revision >= 0x20) { + pci_read_config_byte(dev, 0x53, &tmpbyte); + + if (m5229_revision <= 0x20) + tmpbyte = (tmpbyte & (~0x02)) | 0x01; + else if (m5229_revision == 0xc7 || m5229_revision == 0xc8) + tmpbyte |= 0x03; + else + tmpbyte |= 0x01; + + pci_write_config_byte(dev, 0x53, tmpbyte); + } + local_irq_restore(flags); + pci_dev_put(north); + pci_dev_put(isa_dev); + return 0; +} + +/* + * Cable special cases + */ + +static const struct dmi_system_id cable_dmi_table[] = { + { + .ident = "HP Pavilion N5430", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"), + }, + }, + { + .ident = "Toshiba Satellite S1800-814", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_NAME, "S1800-814"), + }, + }, + { } +}; + +static int ali_cable_override(struct pci_dev *pdev) +{ + /* Fujitsu P2000 */ + if (pdev->subsystem_vendor == 0x10CF && + pdev->subsystem_device == 0x10AF) + return 1; + + /* Mitac 8317 (Winbook-A) and relatives */ + if (pdev->subsystem_vendor == 0x1071 && + pdev->subsystem_device == 0x8317) + return 1; + + /* Systems by DMI */ + if (dmi_check_system(cable_dmi_table)) + return 1; + + return 0; +} + +/** + * ali_cable_detect - cable detection + * @hwif: IDE interface + * + * This checks if the controller and the cable are capable + * of UDMA66 transfers. It doesn't check the drives. + */ + +static u8 ali_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 cbl = ATA_CBL_PATA40, tmpbyte; + + if (m5229_revision >= 0xC2) { + /* + * m5229 80-pin cable detection (from Host View) + * + * 0x4a bit0 is 0 => primary channel has 80-pin + * 0x4a bit1 is 0 => secondary channel has 80-pin + * + * Certain laptops use short but suitable cables + * and don't implement the detect logic. + */ + if (ali_cable_override(dev)) + cbl = ATA_CBL_PATA40_SHORT; + else { + pci_read_config_byte(dev, 0x4a, &tmpbyte); + if ((tmpbyte & (1 << hwif->channel)) == 0) + cbl = ATA_CBL_PATA80; + } + } + + return cbl; +} + +#ifndef CONFIG_SPARC64 +/** + * 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_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 (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; + } +} +#else +#define init_hwif_ali15x3 NULL +#endif /* CONFIG_SPARC64 */ + +/** + * init_dma_ali15x3 - set up DMA on ALi15x3 + * @hwif: IDE interface + * @d: IDE port info + * + * Set up the DMA functionality on the ALi 15x3. + */ + +static int init_dma_ali15x3(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long base = ide_pci_dma_base(hwif, d); + + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) + return -1; + + if (!hwif->channel) + outb(inb(base + 2) & 0x60, base + 2); + + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", + hwif->name, base, base + 7); + + if (ide_allocate_dma_engine(hwif)) + return -1; + + return 0; +} + +static const struct ide_port_ops ali_port_ops = { + .set_pio_mode = ali_set_pio_mode, + .set_dma_mode = ali_set_dma_mode, + .udma_filter = ali_udma_filter, + .cable_detect = ali_cable_detect, +}; + +static const struct ide_dma_ops ali_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_check = ali_dma_check, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info ali15x3_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_ali15x3, + .init_hwif = init_hwif_ali15x3, + .init_dma = init_dma_ali15x3, + .port_ops = &ali_port_ops, + .dma_ops = &sff_dma_ops, + .pio_mask = ATA_PIO5, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, +}; + +/** + * 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 alim15x3_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct ide_port_info d = ali15x3_chipset; + u8 rev = dev->revision, idx = id->driver_data; + + /* don't use LBA48 DMA on ALi devices before rev 0xC5 */ + if (rev <= 0xC4) + d.host_flags |= IDE_HFLAG_NO_LBA48_DMA; + + if (rev >= 0x20) { + if (rev == 0x20) + d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA; + + if (rev < 0xC2) + d.udma_mask = ATA_UDMA2; + else if (rev == 0xC2 || rev == 0xC3) + d.udma_mask = ATA_UDMA4; + else if (rev == 0xC4) + d.udma_mask = ATA_UDMA5; + else + d.udma_mask = ATA_UDMA6; + + d.dma_ops = &ali_dma_ops; + } else { + d.host_flags |= IDE_HFLAG_NO_DMA; + + d.mwdma_mask = d.swdma_mask = 0; + } + + if (idx == 0) + d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; + + return ide_pci_init_one(dev, &d, NULL); +} + + +static const struct pci_device_id alim15x3_pci_tbl[] = { + { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5229), 0 }, + { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5228), 1 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, alim15x3_pci_tbl); + +static struct pci_driver alim15x3_pci_driver = { + .name = "ALI15x3_IDE", + .id_table = alim15x3_pci_tbl, + .probe = alim15x3_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init ali15x3_ide_init(void) +{ + return ide_pci_register_driver(&alim15x3_pci_driver); +} + +static void __exit ali15x3_ide_exit(void) +{ + pci_unregister_driver(&alim15x3_pci_driver); +} + +module_init(ali15x3_ide_init); +module_exit(ali15x3_ide_exit); + +MODULE_AUTHOR("Michael Aubry, Andrzej Krzysztofowicz, CJ, Andre Hedrick, Alan Cox, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("PCI driver module for ALi 15x3 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c new file mode 100644 index 000000000..7340597a3 --- /dev/null +++ b/drivers/ide/amd74xx.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AMD 755/756/766/8111 and nVidia nForce/2/2s/3/3s/CK804/MCP04 + * IDE driver for Linux. + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2007-2010 Bartlomiej Zolnierkiewicz + * + * Based on the work of: + * Andre Hedrick + */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> + +#define DRV_NAME "amd74xx" + +enum { + AMD_IDE_CONFIG = 0x41, + AMD_CABLE_DETECT = 0x42, + AMD_DRIVE_TIMING = 0x48, + AMD_8BIT_TIMING = 0x4e, + AMD_ADDRESS_SETUP = 0x4c, + AMD_UDMA_TIMING = 0x50, +}; + +static unsigned int amd_80w; +static unsigned int amd_clock; + +static char *amd_dma[] = { "16", "25", "33", "44", "66", "100", "133" }; +static unsigned char amd_cyc2udma[] = { 6, 6, 5, 4, 0, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 7 }; + +static inline u8 amd_offset(struct pci_dev *dev) +{ + return (dev->vendor == PCI_VENDOR_ID_NVIDIA) ? 0x10 : 0; +} + +/* + * amd_set_speed() writes timing values to the chipset registers + */ + +static void amd_set_speed(struct pci_dev *dev, u8 dn, u8 udma_mask, + struct ide_timing *timing) +{ + u8 t = 0, offset = amd_offset(dev); + + pci_read_config_byte(dev, AMD_ADDRESS_SETUP + offset, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((clamp_val(timing->setup, 1, 4) - 1) << ((3 - dn) << 1)); + pci_write_config_byte(dev, AMD_ADDRESS_SETUP + offset, t); + + pci_write_config_byte(dev, AMD_8BIT_TIMING + offset + (1 - (dn >> 1)), + ((clamp_val(timing->act8b, 1, 16) - 1) << 4) | (clamp_val(timing->rec8b, 1, 16) - 1)); + + pci_write_config_byte(dev, AMD_DRIVE_TIMING + offset + (3 - dn), + ((clamp_val(timing->active, 1, 16) - 1) << 4) | (clamp_val(timing->recover, 1, 16) - 1)); + + switch (udma_mask) { + case ATA_UDMA2: t = timing->udma ? (0xc0 | (clamp_val(timing->udma, 2, 5) - 2)) : 0x03; break; + case ATA_UDMA4: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 2, 10)]) : 0x03; break; + case ATA_UDMA5: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 1, 10)]) : 0x03; break; + case ATA_UDMA6: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 1, 15)]) : 0x03; break; + default: return; + } + + if (timing->udma) + pci_write_config_byte(dev, AMD_UDMA_TIMING + offset + 3 - dn, t); +} + +/* + * amd_set_drive() computes timing values and configures the chipset + * to a desired transfer mode. It also can be called by upper layers. + */ + +static void amd_set_drive(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + ide_drive_t *peer = ide_get_pair_dev(drive); + struct ide_timing t, p; + int T, UT; + u8 udma_mask = hwif->ultra_mask; + const u8 speed = drive->dma_mode; + + T = 1000000000 / amd_clock; + UT = (udma_mask == ATA_UDMA2) ? T : (T / 2); + + ide_timing_compute(drive, speed, &t, T, UT); + + if (peer) { + ide_timing_compute(peer, peer->pio_mode, &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(dev, drive->dn, udma_mask, &t); +} + +/* + * amd_set_pio_mode() is a callback from upper layers for PIO-only tuning. + */ + +static void amd_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + amd_set_drive(hwif, drive); +} + +static void amd7409_cable_detect(struct pci_dev *dev) +{ + /* no host side cable detection */ + amd_80w = 0x03; +} + +static void amd7411_cable_detect(struct pci_dev *dev) +{ + int i; + u32 u = 0; + u8 t = 0, offset = amd_offset(dev); + + pci_read_config_byte(dev, AMD_CABLE_DETECT + offset, &t); + pci_read_config_dword(dev, AMD_UDMA_TIMING + offset, &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 DRV_NAME " %s: BIOS didn't set " + "cable bits correctly. Enabling workaround.\n", + pci_name(dev)); + amd_80w |= (1 << (1 - (i >> 4))); + } +} + +/* + * The initialization callback. Initialize drive independent registers. + */ + +static int init_chipset_amd74xx(struct pci_dev *dev) +{ + u8 t = 0, offset = amd_offset(dev); + +/* + * Check 80-wire cable presence. + */ + + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_COBRA_7401) + ; /* no UDMA > 2 */ + else if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_VIPER_7409) + amd7409_cable_detect(dev); + else + amd7411_cable_detect(dev); + +/* + * Take care of prefetch & postwrite. + */ + + pci_read_config_byte(dev, AMD_IDE_CONFIG + offset, &t); + /* + * Check for broken FIFO support. + */ + if (dev->vendor == PCI_VENDOR_ID_AMD && + dev->device == PCI_DEVICE_ID_AMD_VIPER_7411) + t &= 0x0f; + else + t |= 0xf0; + pci_write_config_byte(dev, AMD_IDE_CONFIG + offset, t); + + return 0; +} + +static u8 amd_cable_detect(ide_hwif_t *hwif) +{ + if ((amd_80w >> hwif->channel) & 1) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; +} + +static const struct ide_port_ops amd_port_ops = { + .set_pio_mode = amd_set_pio_mode, + .set_dma_mode = amd_set_drive, + .cable_detect = amd_cable_detect, +}; + +#define IDE_HFLAGS_AMD \ + (IDE_HFLAG_PIO_NO_BLACKLIST | \ + IDE_HFLAG_POST_SET_MODE | \ + IDE_HFLAG_IO_32BIT | \ + IDE_HFLAG_UNMASK_IRQS) + +#define DECLARE_AMD_DEV(swdma, udma) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_amd74xx, \ + .enablebits = {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, \ + .port_ops = &amd_port_ops, \ + .host_flags = IDE_HFLAGS_AMD, \ + .pio_mask = ATA_PIO5, \ + .swdma_mask = swdma, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = udma, \ + } + +#define DECLARE_NV_DEV(udma) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_amd74xx, \ + .enablebits = {{0x50,0x02,0x02}, {0x50,0x01,0x01}}, \ + .port_ops = &amd_port_ops, \ + .host_flags = IDE_HFLAGS_AMD, \ + .pio_mask = ATA_PIO5, \ + .swdma_mask = ATA_SWDMA2, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = udma, \ + } + +static const struct ide_port_info amd74xx_chipsets[] = { + /* 0: AMD7401 */ DECLARE_AMD_DEV(0x00, ATA_UDMA2), + /* 1: AMD7409 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA4), + /* 2: AMD7411/7441 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5), + /* 3: AMD8111 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA6), + + /* 4: NFORCE */ DECLARE_NV_DEV(ATA_UDMA5), + /* 5: >= NFORCE2 */ DECLARE_NV_DEV(ATA_UDMA6), + + /* 6: AMD5536 */ DECLARE_AMD_DEV(ATA_SWDMA2, ATA_UDMA5), +}; + +static int amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d; + u8 idx = id->driver_data; + + d = amd74xx_chipsets[idx]; + + /* + * Check for bad SWDMA and incorrectly wired Serenade mainboards. + */ + if (idx == 1) { + if (dev->revision <= 7) + d.swdma_mask = 0; + d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; + } else if (idx == 3) { + if (dev->subsystem_vendor == PCI_VENDOR_ID_AMD && + dev->subsystem_device == PCI_DEVICE_ID_AMD_SERENADE) + d.udma_mask = ATA_UDMA5; + } + + /* + * It seems that on some nVidia controllers using AltStatus + * register can be unreliable so default to Status register + * if the device is in Compatibility Mode. + */ + if (dev->vendor == PCI_VENDOR_ID_NVIDIA && + ide_pci_is_in_compatibility_mode(dev)) + d.host_flags |= IDE_HFLAG_BROKEN_ALTSTATUS; + + printk(KERN_INFO "%s %s: UDMA%s controller\n", + d.name, pci_name(dev), amd_dma[fls(d.udma_mask) - 1]); + + /* + * Determine the system bus clock. + */ + amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 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", + d.name, amd_clock); + amd_clock = 33333; + } + + return ide_pci_init_one(dev, &d, NULL); +} + +static const struct pci_device_id amd74xx_pci_tbl[] = { + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7409), 1 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_VIPER_7411), 2 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_OPUS_7441), 2 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_8111_IDE), 3 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_IDE), 4 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_IDE), 5 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE2S_SATA), 5 }, +#endif + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_IDE), 5 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE3S_SATA2), 5 }, +#endif + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_CK804_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP67_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE), 5 }, + { PCI_VDEVICE(NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE), 5 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), 6 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl); + +static struct pci_driver amd74xx_pci_driver = { + .name = "AMD_IDE", + .id_table = amd74xx_pci_tbl, + .probe = amd74xx_probe, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init amd74xx_ide_init(void) +{ + return ide_pci_register_driver(&amd74xx_pci_driver); +} + +static void __exit amd74xx_ide_exit(void) +{ + pci_unregister_driver(&amd74xx_pci_driver); +} + +module_init(amd74xx_ide_init); +module_exit(amd74xx_ide_exit); + +MODULE_AUTHOR("Vojtech Pavlik, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("AMD PCI IDE driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/atiixp.c b/drivers/ide/atiixp.c new file mode 100644 index 000000000..e08b0aac0 --- /dev/null +++ b/drivers/ide/atiixp.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2003 ATI Inc. <hyu@ati.com> + * Copyright (C) 2004,2007 Bartlomiej Zolnierkiewicz + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "atiixp" + +#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 + +struct atiixp_ide_timing { + u8 command_width; + u8 recover_width; +}; + +static struct atiixp_ide_timing pio_timing[] = { + { 0x05, 0x0d }, + { 0x04, 0x07 }, + { 0x03, 0x04 }, + { 0x02, 0x02 }, + { 0x02, 0x00 }, +}; + +static struct atiixp_ide_timing mdma_timing[] = { + { 0x07, 0x07 }, + { 0x02, 0x01 }, + { 0x02, 0x00 }, +}; + +static DEFINE_SPINLOCK(atiixp_lock); + +/** + * atiixp_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Set the interface PIO mode. + */ + +static void atiixp_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long flags; + int timing_shift = (drive->dn ^ 1) * 8; + u32 pio_timing_data; + u16 pio_mode_data; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + spin_lock_irqsave(&atiixp_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(&atiixp_lock, flags); +} + +/** + * atiixp_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Set a ATIIXP host controller to the desired DMA mode. This involves + * programming the right timing data into the PCI configuration space. + */ + +static void atiixp_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long flags; + int timing_shift = (drive->dn ^ 1) * 8; + u32 tmp32; + u16 tmp16; + u16 udma_ctl = 0; + const u8 speed = drive->dma_mode; + + spin_lock_irqsave(&atiixp_lock, flags); + + pci_read_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, &udma_ctl); + + 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); + + udma_ctl |= (1 << drive->dn); + } else if (speed >= XFER_MW_DMA_0) { + u8 i = speed & 0x03; + + pci_read_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, &tmp32); + tmp32 &= ~(0xff << timing_shift); + tmp32 |= (mdma_timing[i].recover_width << timing_shift) | + (mdma_timing[i].command_width << (timing_shift + 4)); + pci_write_config_dword(dev, ATIIXP_IDE_MDMA_TIMING, tmp32); + + udma_ctl &= ~(1 << drive->dn); + } + + pci_write_config_word(dev, ATIIXP_IDE_UDMA_CONTROL, udma_ctl); + + spin_unlock_irqrestore(&atiixp_lock, flags); +} + +static u8 atiixp_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + u8 udma_mode = 0, ch = hwif->channel; + + pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ch, &udma_mode); + + if ((udma_mode & 0x07) >= 0x04 || (udma_mode & 0x70) >= 0x40) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; +} + +static const struct ide_port_ops atiixp_port_ops = { + .set_pio_mode = atiixp_set_pio_mode, + .set_dma_mode = atiixp_set_dma_mode, + .cable_detect = atiixp_cable_detect, +}; + +static const struct ide_port_info atiixp_pci_info[] = { + { /* 0: IXP200/300/400/700 */ + .name = DRV_NAME, + .enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}}, + .port_ops = &atiixp_port_ops, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, + { /* 1: IXP600 */ + .name = DRV_NAME, + .enablebits = {{0x48,0x01,0x00}, {0x00,0x00,0x00}}, + .port_ops = &atiixp_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, +}; + +/** + * 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 atiixp_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &atiixp_pci_info[id->driver_data], NULL); +} + +static const struct pci_device_id atiixp_pci_tbl[] = { + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP200_IDE), 0 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP300_IDE), 0 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP400_IDE), 0 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP600_IDE), 1 }, + { PCI_VDEVICE(ATI, PCI_DEVICE_ID_ATI_IXP700_IDE), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_HUDSON2_IDE), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl); + +static struct pci_driver atiixp_pci_driver = { + .name = "ATIIXP_IDE", + .id_table = atiixp_pci_tbl, + .probe = atiixp_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init atiixp_ide_init(void) +{ + return ide_pci_register_driver(&atiixp_pci_driver); +} + +static void __exit atiixp_ide_exit(void) +{ + pci_unregister_driver(&atiixp_pci_driver); +} + +module_init(atiixp_ide_init); +module_exit(atiixp_ide_exit); + +MODULE_AUTHOR("HUI YU"); +MODULE_DESCRIPTION("PCI driver module for ATI IXP IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/buddha.c b/drivers/ide/buddha.c new file mode 100644 index 000000000..46eaf58d8 --- /dev/null +++ b/drivers/ide/buddha.c @@ -0,0 +1,238 @@ +/* + * 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/zorro.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/module.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 + +#define MAX_NUM_HWIFS 3 + + /* + * Bases of the IDE interfaces (relative to the board address) + */ + +#define BUDDHA_BASE1 0x800 +#define BUDDHA_BASE2 0xa00 +#define BUDDHA_BASE3 0xc00 + +#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_CONTROL 0x11a + + /* + * 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; + +static const char *buddha_board_name[] = { "Buddha", "Catweasel", "X-Surf" }; + + /* + * Check and acknowledge the interrupt status + */ + +static int buddha_test_irq(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports.irq_addr); + if (!(ch & 0x80)) + return 0; + return 1; +} + +static void xsurf_clear_irq(ide_drive_t *drive) +{ + /* + * X-Surf needs 0 written to IRQ register to ensure ISA bit A11 stays at 0 + */ + z_writeb(0, drive->hwif->io_ports.irq_addr); +} + +static void __init buddha_setup_ports(struct ide_hw *hw, unsigned long base, + unsigned long ctl, unsigned long irq_port) +{ + int i; + + memset(hw, 0, sizeof(*hw)); + + hw->io_ports.data_addr = base; + + for (i = 1; i < 8; i++) + hw->io_ports_array[i] = base + 2 + i * 4; + + hw->io_ports.ctl_addr = ctl; + hw->io_ports.irq_addr = irq_port; + + hw->irq = IRQ_AMIGA_PORTS; +} + +static const struct ide_port_ops buddha_port_ops = { + .test_irq = buddha_test_irq, +}; + +static const struct ide_port_ops xsurf_port_ops = { + .clear_irq = xsurf_clear_irq, + .test_irq = buddha_test_irq, +}; + +static const struct ide_port_info buddha_port_info = { + .port_ops = &buddha_port_ops, + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_generic, +}; + + /* + * Probe for a Buddha or Catweasel IDE interface + */ + +static int __init buddha_init(void) +{ + struct zorro_dev *z = NULL; + u_long buddha_board = 0; + BuddhaType type; + int buddha_num_hwifs, i; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + unsigned long board; + struct ide_hw hw[MAX_NUM_HWIFS], *hws[MAX_NUM_HWIFS]; + struct ide_port_info d = buddha_port_info; + + 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; + d.port_ops = &xsurf_port_ops; + } else + continue; + + board = z->resource.start; + + 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 = (unsigned long)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); + + printk(KERN_INFO "ide: %s IDE controller\n", + buddha_board_name[type]); + + for (i = 0; i < buddha_num_hwifs; i++) { + unsigned long base, ctl, irq_port; + + if (type != BOARD_XSURF) { + base = buddha_board + buddha_bases[i]; + ctl = base + BUDDHA_CONTROL; + irq_port = buddha_board + buddha_irqports[i]; + } else { + base = buddha_board + xsurf_bases[i]; + /* X-Surf has no CS1* (Control/AltStat) */ + ctl = 0; + irq_port = buddha_board + xsurf_irqports[i]; + } + + buddha_setup_ports(&hw[i], base, ctl, irq_port); + + hws[i] = &hw[i]; + } + + ide_host_add(&d, hws, i, NULL); + } + + return 0; +} + +module_init(buddha_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/cmd640.c b/drivers/ide/cmd640.c new file mode 100644 index 000000000..f48decb9f --- /dev/null +++ b/drivers/ide/cmd640.c @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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) + */ + +#define CMD640_PREFETCH_MASKS 1 + +/*#define CMD640_DUMP_REGS */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <asm/io.h> + +#define DRV_NAME "cmd640" + +static bool cmd640_vlb; + +/* + * 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 ARTTIM23_IDE23INTR 0x10 +#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 */ + +static DEFINE_SPINLOCK(cmd640_lock); + +/* + * 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) +{ + unsigned long flags; + u8 b; + + spin_lock_irqsave(&cmd640_lock, flags); + b = __get_cmd640_reg(reg); + spin_unlock_irqrestore(&cmd640_lock, flags); + return b; +} + +static void put_cmd640_reg(u16 reg, u8 val) +{ + unsigned long flags; + + spin_lock_irqsave(&cmd640_lock, flags); + __put_cmd640_reg(reg, val); + spin_unlock_irqrestore(&cmd640_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(&cmd640_lock, flags); + + outb_p(0x0a, 0x176); /* select drive0 */ + udelay(100); + if ((inb_p(0x176) & 0x1f) != 0x0a) { + outb_p(0x1a, 0x176); /* select drive1 */ + udelay(100); + if ((inb_p(0x176) & 0x1f) != 0x1a) { + spin_unlock_irqrestore(&cmd640_lock, flags); + return 0; /* nothing responded */ + } + } + spin_unlock_irqrestore(&cmd640_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 + +static void __set_prefetch_mode(ide_drive_t *drive, int mode) +{ + if (mode) { /* want prefetch on? */ +#if CMD640_PREFETCH_MASKS + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; + drive->dev_flags &= ~IDE_DFLAG_UNMASK; +#endif + drive->dev_flags &= ~IDE_DFLAG_NO_IO_32BIT; + } else { + drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK; + drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT; + drive->io_32bit = 0; + } +} + +#ifndef CONFIG_BLK_DEV_CMD640_ENHANCED +/* + * Check whether prefetch is on for a drive, + * and initialize the unmask flags for safe operation. + */ +static void __init check_prefetch(ide_drive_t *drive, unsigned int index) +{ + u8 b = get_cmd640_reg(prefetch_regs[index]); + + __set_prefetch_mode(drive, (b & prefetch_masks[index]) ? 0 : 1); +} +#else + +/* + * Sets prefetch mode for a drive. + */ +static void set_prefetch_mode(ide_drive_t *drive, unsigned int index, int mode) +{ + unsigned long flags; + int reg = prefetch_regs[index]; + u8 b; + + spin_lock_irqsave(&cmd640_lock, flags); + b = __get_cmd640_reg(reg); + __set_prefetch_mode(drive, mode); + if (mode) + b &= ~prefetch_masks[index]; /* enable prefetch */ + else + b |= prefetch_masks[index]; /* disable prefetch */ + __put_cmd640_reg(reg, b); + spin_unlock_irqrestore(&cmd640_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 + */ +static inline u8 pack_nibbles(u8 upper, u8 lower) +{ + return ((upper & 0x0f) << 4) | (lower & 0x0f); +} + +/* + * 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(ide_drive_t *drive, 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) { + ide_drive_t *peer = ide_get_pair_dev(drive); + unsigned int mate = index ^ 1; + + if (peer) { + 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(&cmd640_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(&cmd640_lock, flags); +} + +/* + * Set a specific pio_mode for a drive + */ +static void cmd640_set_mode(ide_drive_t *drive, unsigned int index, + u8 pio_mode, unsigned int cycle_time) +{ + struct ide_timing *t; + int setup_time, active_time, recovery_time, clock_time; + u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count; + int bus_speed; + + if (cmd640_vlb) + bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; + else + bus_speed = ide_pci_clk ? ide_pci_clk : 33; + + if (pio_mode > 5) + pio_mode = 5; + + t = ide_timing_find_mode(XFER_PIO_0 + pio_mode); + setup_time = t->setup; + active_time = t->active; + + recovery_time = cycle_time - (setup_time + active_time); + clock_time = 1000 / bus_speed; + cycle_count = DIV_ROUND_UP(cycle_time, clock_time); + + setup_count = DIV_ROUND_UP(setup_time, clock_time); + + active_count = DIV_ROUND_UP(active_time, clock_time); + if (active_count < 2) + active_count = 2; /* minimum allowed by cmd640 */ + + recovery_count = DIV_ROUND_UP(recovery_time, 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(drive, index); +} + +static void cmd640_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned int index = 0, cycle_time; + const u8 pio = drive->pio_mode - XFER_PIO_0; + u8 b; + + switch (pio) { + case 6: /* set fast-devsel off */ + case 7: /* set fast-devsel on */ + b = get_cmd640_reg(CNTRL) & ~0x27; + if (pio & 1) + b |= 0x27; + put_cmd640_reg(CNTRL, b); + printk("%s: %sabled cmd640 fast host timing (devsel)\n", + drive->name, (pio & 1) ? "en" : "dis"); + return; + case 8: /* set prefetch off */ + case 9: /* set prefetch on */ + set_prefetch_mode(drive, index, pio & 1); + printk("%s: %sabled cmd640 prefetch\n", + drive->name, (pio & 1) ? "en" : "dis"); + return; + } + + cycle_time = ide_pio_cycle_time(drive, pio); + cmd640_set_mode(drive, index, pio, cycle_time); + + printk("%s: selected cmd640 PIO mode%d (%dns)", + drive->name, pio, cycle_time); + + display_clocks(index); +} +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + +static void __init cmd640_init_dev(ide_drive_t *drive) +{ + unsigned int i = drive->hwif->channel * 2 + (drive->dn & 1); + +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + /* + * Reset timing to the slowest speed and turn off prefetch. + * This way, the drive identify code has a better chance. + */ + setup_counts[i] = 4; /* max possible */ + active_counts[i] = 16; /* max possible */ + recovery_counts[i] = 16; /* max possible */ + program_drive_counts(drive, i); + set_prefetch_mode(drive, i, 0); + printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch cleared\n", i); +#else + /* + * Set the drive unmask flags to match the prefetch setting. + */ + check_prefetch(drive, i); + printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch(%s) preserved\n", + i, (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) ? "off" : "on"); +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ +} + +static int cmd640_test_irq(ide_hwif_t *hwif) +{ + int irq_reg = hwif->channel ? ARTTIM23 : CFR; + u8 irq_mask = hwif->channel ? ARTTIM23_IDE23INTR : + CFR_IDE01INTR; + u8 irq_stat = get_cmd640_reg(irq_reg); + + return (irq_stat & irq_mask) ? 1 : 0; +} + +static const struct ide_port_ops cmd640_port_ops = { + .init_dev = cmd640_init_dev, +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + .set_pio_mode = cmd640_set_pio_mode, +#endif + .test_irq = cmd640_test_irq, +}; + +static int pci_conf1(void) +{ + unsigned long flags; + u32 tmp; + + spin_lock_irqsave(&cmd640_lock, flags); + outb(0x01, 0xCFB); + tmp = inl(0xCF8); + outl(0x80000000, 0xCF8); + if (inl(0xCF8) == 0x80000000) { + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&cmd640_lock, flags); + return 1; + } + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&cmd640_lock, flags); + return 0; +} + +static int pci_conf2(void) +{ + unsigned long flags; + + spin_lock_irqsave(&cmd640_lock, flags); + outb(0x00, 0xCFB); + outb(0x00, 0xCF8); + outb(0x00, 0xCFA); + if (inb(0xCF8) == 0x00 && inb(0xCF8) == 0x00) { + spin_unlock_irqrestore(&cmd640_lock, flags); + return 1; + } + spin_unlock_irqrestore(&cmd640_lock, flags); + return 0; +} + +static const struct ide_port_info cmd640_port_info __initconst = { + .chipset = ide_cmd640, + .host_flags = IDE_HFLAG_SERIALIZE | + IDE_HFLAG_NO_DMA | + IDE_HFLAG_ABUSE_PREFETCH | + IDE_HFLAG_ABUSE_FAST_DEVSEL, + .port_ops = &cmd640_port_ops, + .pio_mask = ATA_PIO5, +}; + +static int __init cmd640x_init_one(unsigned long base, unsigned long ctl) +{ + if (!request_region(base, 8, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + DRV_NAME, base, base + 7); + return -EBUSY; + } + + if (!request_region(ctl, 1, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", + DRV_NAME, ctl); + release_region(base, 8); + return -EBUSY; + } + + return 0; +} + +/* + * Probe for a cmd640 chipset, and initialize it if found. + */ +static int __init cmd640x_init(void) +{ + int second_port_cmd640 = 0, rc; + const char *bus_type, *port2; + u8 b, cfr; + struct ide_hw hw[2], *hws[2]; + + 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; + } + + rc = cmd640x_init_one(0x1f0, 0x3f6); + if (rc) + return rc; + + rc = cmd640x_init_one(0x170, 0x376); + if (rc) { + release_region(0x3f6, 1); + release_region(0x1f0, 8); + return rc; + } + + memset(&hw, 0, sizeof(hw)); + + ide_std_init_ports(&hw[0], 0x1f0, 0x3f6); + hw[0].irq = 14; + + ide_std_init_ports(&hw[1], 0x170, 0x376); + hw[1].irq = 15; + + printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x" + "\n", 'a' + cmd640_chip_version - 1, bus_type, cfr); + + /* + * Initialize data for primary port + */ + hws[0] = &hw[0]; + + /* + * 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); + + b = get_cmd640_reg(CNTRL); + + /* + * Try to enable the secondary interface, if not already enabled + */ + 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; + 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) + hws[1] = &hw[1]; + + printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n", + second_port_cmd640 ? "" : "not ", port2); + +#ifdef CMD640_DUMP_REGS + cmd640_dump_regs(); +#endif + + return ide_host_add(&cmd640_port_info, hws, second_port_cmd640 ? 2 : 1, + NULL); +} + +module_param_named(probe_vlb, cmd640_vlb, bool, 0); +MODULE_PARM_DESC(probe_vlb, "probe for VLB version of CMD640 chipset"); + +module_init(cmd640x_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c new file mode 100644 index 000000000..943bf944b --- /dev/null +++ b/drivers/ide/cmd64x.c @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. + * 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> + * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz + * Copyright (C) 2007,2009 MontaVista Software, Inc. <source@mvista.com> + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "cmd64x" + +/* + * CMD64x specific registers definition. + */ +#define CFR 0x50 +#define CFR_INTR_CH0 0x04 + +#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 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 UDIDETCR0 0x73 +#define DTPR0 0x74 +#define BMIDECR1 0x78 +#define BMIDECSR 0x79 +#define UDIDETCR1 0x7B +#define DTPR1 0x7C + +static void cmd64x_program_timings(ide_drive_t *drive, u8 mode) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; + const unsigned long T = 1000000 / bus_speed; + static const u8 recovery_values[] = + {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}; + static const u8 setup_values[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0}; + static const u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23}; + static const u8 drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM2, DRWTIM3}; + struct ide_timing t; + u8 arttim = 0; + + if (drive->dn >= ARRAY_SIZE(drwtim_regs)) + return; + + ide_timing_compute(drive, mode, &t, T, 0); + + /* + * In case we've got too long recovery phase, try to lengthen + * the active phase + */ + if (t.recover > 16) { + t.active += t.recover - 16; + t.recover = 16; + } + if (t.active > 16) /* shouldn't actually happen... */ + t.active = 16; + + /* + * Convert values to internal chipset representation + */ + t.recover = recovery_values[t.recover]; + t.active &= 0x0f; + + /* Program the active/recovery counts into the DRWTIM register */ + pci_write_config_byte(dev, drwtim_regs[drive->dn], + (t.active << 4) | t.recover); + + /* + * The primary channel has individual address setup timing registers + * for each drive and the hardware selects the slowest timing itself. + * The secondary channel has one common register and we have to select + * the slowest address setup timing ourselves. + */ + if (hwif->channel) { + ide_drive_t *pair = ide_get_pair_dev(drive); + + if (pair) { + struct ide_timing tp; + + ide_timing_compute(pair, pair->pio_mode, &tp, T, 0); + ide_timing_merge(&t, &tp, &t, IDE_TIMING_SETUP); + if (pair->dma_mode) { + ide_timing_compute(pair, pair->dma_mode, + &tp, T, 0); + ide_timing_merge(&tp, &t, &t, IDE_TIMING_SETUP); + } + } + } + + if (t.setup > 5) /* shouldn't actually happen... */ + t.setup = 5; + + /* + * Program the address setup clocks into the ARTTIM registers. + * Avoid clearing the secondary channel's interrupt bit. + */ + (void) pci_read_config_byte (dev, arttim_regs[drive->dn], &arttim); + if (hwif->channel) + arttim &= ~ARTTIM23_INTR_CH1; + arttim &= ~0xc0; + arttim |= setup_values[t.setup]; + (void) pci_write_config_byte(dev, arttim_regs[drive->dn], arttim); +} + +/* + * Attempts to set drive's PIO mode. + * Special cases are 8: prefetch off, 9: prefetch on (both never worked) + */ + +static void cmd64x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* + * Filter out the prefetch control values + * to prevent PIO5 from being programmed + */ + if (pio == 8 || pio == 9) + return; + + cmd64x_program_timings(drive, XFER_PIO_0 + pio); +} + +static void cmd64x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 unit = drive->dn & 0x01; + u8 regU = 0, pciU = hwif->channel ? UDIDETCR1 : UDIDETCR0; + const u8 speed = drive->dma_mode; + + pci_read_config_byte(dev, pciU, ®U); + regU &= ~(unit ? 0xCA : 0x35); + + 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: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + cmd64x_program_timings(drive, speed); + break; + } + + pci_write_config_byte(dev, pciU, regU); +} + +static void cmd648_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long base = pci_resource_start(dev, 4); + u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : + MRDMODE_INTR_CH0; + u8 mrdmode = inb(base + 1); + + /* clear the interrupt bit */ + outb((mrdmode & ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1)) | irq_mask, + base + 1); +} + +static void cmd64x_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + int irq_reg = hwif->channel ? ARTTIM23 : CFR; + u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : + CFR_INTR_CH0; + u8 irq_stat = 0; + + (void) pci_read_config_byte(dev, irq_reg, &irq_stat); + /* clear the interrupt bit */ + (void) pci_write_config_byte(dev, irq_reg, irq_stat | irq_mask); +} + +static int cmd648_test_irq(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long base = pci_resource_start(dev, 4); + u8 irq_mask = hwif->channel ? MRDMODE_INTR_CH1 : + MRDMODE_INTR_CH0; + u8 mrdmode = inb(base + 1); + + pr_debug("%s: mrdmode: 0x%02x irq_mask: 0x%02x\n", + hwif->name, mrdmode, irq_mask); + + return (mrdmode & irq_mask) ? 1 : 0; +} + +static int cmd64x_test_irq(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int irq_reg = hwif->channel ? ARTTIM23 : CFR; + u8 irq_mask = hwif->channel ? ARTTIM23_INTR_CH1 : + CFR_INTR_CH0; + u8 irq_stat = 0; + + (void) pci_read_config_byte(dev, irq_reg, &irq_stat); + + pr_debug("%s: irq_stat: 0x%02x irq_mask: 0x%02x\n", + hwif->name, irq_stat, irq_mask); + + return (irq_stat & irq_mask) ? 1 : 0; +} + +/* + * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old + * event order for DMA transfers. + */ + +static int cmd646_1_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = 0, dma_cmd = 0; + + /* get DMA status */ + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + /* read DMA command state */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + /* stop DMA */ + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); + /* clear the INTR & ERROR bits */ + outb(dma_stat | 6, hwif->dma_base + ATA_DMA_STATUS); + /* verify good DMA status */ + return (dma_stat & 7) != 4; +} + +static int init_chipset_cmd64x(struct pci_dev *dev) +{ + u8 mrdmode = 0; + + /* 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 */ + + /* + * Enable interrupts, select MEMORY READ LINE for reads. + * + * NOTE: although not mentioned in the PCI0646U specs, + * bits 0-1 are write only and won't be read back as + * set or not -- PCI0646U2 specs clarify this point. + */ + (void) pci_read_config_byte (dev, MRDMODE, &mrdmode); + mrdmode &= ~0x30; + (void) pci_write_config_byte(dev, MRDMODE, (mrdmode | 0x02)); + + return 0; +} + +static u8 cmd64x_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 bmidecsr = 0, mask = hwif->channel ? 0x02 : 0x01; + + switch (dev->device) { + case PCI_DEVICE_ID_CMD_648: + case PCI_DEVICE_ID_CMD_649: + pci_read_config_byte(dev, BMIDECSR, &bmidecsr); + return (bmidecsr & mask) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; + default: + return ATA_CBL_PATA40; + } +} + +static const struct ide_port_ops cmd64x_port_ops = { + .set_pio_mode = cmd64x_set_pio_mode, + .set_dma_mode = cmd64x_set_dma_mode, + .clear_irq = cmd64x_clear_irq, + .test_irq = cmd64x_test_irq, + .cable_detect = cmd64x_cable_detect, +}; + +static const struct ide_port_ops cmd648_port_ops = { + .set_pio_mode = cmd64x_set_pio_mode, + .set_dma_mode = cmd64x_set_dma_mode, + .clear_irq = cmd648_clear_irq, + .test_irq = cmd648_test_irq, + .cable_detect = cmd64x_cable_detect, +}; + +static const struct ide_dma_ops cmd646_rev1_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = cmd646_1_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info cmd64x_chipsets[] = { + { /* 0: CMD643 */ + .name = DRV_NAME, + .init_chipset = init_chipset_cmd64x, + .enablebits = {{0x00,0x00,0x00}, {0x51,0x08,0x08}}, + .port_ops = &cmd64x_port_ops, + .host_flags = IDE_HFLAG_CLEAR_SIMPLEX | + IDE_HFLAG_ABUSE_PREFETCH | + IDE_HFLAG_SERIALIZE, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = 0x00, /* no udma */ + }, + { /* 1: CMD646 */ + .name = DRV_NAME, + .init_chipset = init_chipset_cmd64x, + .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, + .port_ops = &cmd648_port_ops, + .host_flags = IDE_HFLAG_ABUSE_PREFETCH | + IDE_HFLAG_SERIALIZE, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, + }, + { /* 2: CMD648 */ + .name = DRV_NAME, + .init_chipset = init_chipset_cmd64x, + .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, + .port_ops = &cmd648_port_ops, + .host_flags = IDE_HFLAG_ABUSE_PREFETCH, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA4, + }, + { /* 3: CMD649 */ + .name = DRV_NAME, + .init_chipset = init_chipset_cmd64x, + .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, + .port_ops = &cmd648_port_ops, + .host_flags = IDE_HFLAG_ABUSE_PREFETCH, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + } +}; + +static int cmd64x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d; + u8 idx = id->driver_data; + + d = cmd64x_chipsets[idx]; + + if (idx == 1) { + /* + * UltraDMA only supported on PCI646U and PCI646U2, which + * correspond to revisions 0x03, 0x05 and 0x07 respectively. + * Actually, although the CMD tech support people won't + * tell me the details, the 0x03 revision cannot support + * UDMA correctly without hardware modifications, and even + * then it only works with Quantum disks due to some + * hold time assumptions in the 646U part which are fixed + * in the 646U2. + * + * So we only do UltraDMA on revision 0x05 and 0x07 chipsets. + */ + if (dev->revision < 5) { + d.udma_mask = 0x00; + /* + * The original PCI0646 didn't have the primary + * channel enable bit, it appeared starting with + * PCI0646U (i.e. revision ID 3). + */ + if (dev->revision < 3) { + d.enablebits[0].reg = 0; + d.port_ops = &cmd64x_port_ops; + if (dev->revision == 1) + d.dma_ops = &cmd646_rev1_dma_ops; + } + } + } + + return ide_pci_init_one(dev, &d, NULL); +} + +static const struct pci_device_id cmd64x_pci_tbl[] = { + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 2 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 3 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cmd64x_pci_tbl); + +static struct pci_driver cmd64x_pci_driver = { + .name = "CMD64x_IDE", + .id_table = cmd64x_pci_tbl, + .probe = cmd64x_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init cmd64x_ide_init(void) +{ + return ide_pci_register_driver(&cmd64x_pci_driver); +} + +static void __exit cmd64x_ide_exit(void) +{ + pci_unregister_driver(&cmd64x_pci_driver); +} + +module_init(cmd64x_ide_init); +module_exit(cmd64x_ide_exit); + +MODULE_AUTHOR("Eddie Dost, David Miller, Andre Hedrick, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("PCI driver module for CMD64x IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/cs5520.c b/drivers/ide/cs5520.c new file mode 100644 index 000000000..89a4ff100 --- /dev/null +++ b/drivers/ide/cs5520.c @@ -0,0 +1,168 @@ +/* + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/dma-mapping.h> + +#define DRV_NAME "cs5520" + +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 void cs5520_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + int controller = drive->dn > 1 ? 1 : 0; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* 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)); +} + +static void cs5520_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + printk(KERN_ERR "cs55x0: bad ide timing.\n"); + + drive->pio_mode = XFER_PIO_0 + 0; + cs5520_set_pio_mode(hwif, drive); +} + +static const struct ide_port_ops cs5520_port_ops = { + .set_pio_mode = cs5520_set_pio_mode, + .set_dma_mode = cs5520_set_dma_mode, +}; + +static const struct ide_port_info cyrix_chipset = { + .name = DRV_NAME, + .enablebits = { { 0x60, 0x01, 0x01 }, { 0x60, 0x02, 0x02 } }, + .port_ops = &cs5520_port_ops, + .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_CS5520, + .pio_mask = ATA_PIO4, +}; + +/* + * 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 cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + const struct ide_port_info *d = &cyrix_chipset; + struct ide_hw hw[2], *hws[] = { NULL, NULL }; + + 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_io(dev)) { + printk(KERN_WARNING "%s: Unable to enable 55x0.\n", d->name); + return -ENODEV; + } + pci_set_master(dev); + if (dma_set_mask(&dev->dev, DMA_BIT_MASK(32))) { + printk(KERN_WARNING "%s: No suitable DMA available.\n", + d->name); + return -ENODEV; + } + + /* + * Now the chipset is configured we can let the core + * do all the device setup for us + */ + + ide_pci_setup_ports(dev, d, &hw[0], &hws[0]); + hw[0].irq = 14; + hw[1].irq = 15; + + return ide_host_add(d, hws, 2, NULL); +} + +static const struct pci_device_id cs5520_pci_tbl[] = { + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5510), 0 }, + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5520), 1 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cs5520_pci_tbl); + +static struct pci_driver cs5520_pci_driver = { + .name = "Cyrix_IDE", + .id_table = cs5520_pci_tbl, + .probe = cs5520_init_one, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init cs5520_ide_init(void) +{ + return ide_pci_register_driver(&cs5520_pci_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/cs5530.c b/drivers/ide/cs5530.c new file mode 100644 index 000000000..65371599b --- /dev/null +++ b/drivers/ide/cs5530.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2000 Mark Lord <mlord@pobox.com> + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> + +#include <asm/io.h> + +#define DRV_NAME "cs5530" + +/* + * 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_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Handles setting of PIO mode for the chipset. + * + * The init_hwif_cs5530() routine guarantees that all drives + * will have valid default PIO timings set up before we get here. + */ + +static void cs5530_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long basereg = CS5530_BASEREG(hwif); + unsigned int format = (inl(basereg + 4) >> 31) & 1; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + outl(cs5530_pio_timings[format][pio], basereg + ((drive->dn & 1)<<3)); +} + +/** + * cs5530_udma_filter - UDMA filter + * @drive: drive + * + * cs5530_udma_filter() does UDMA mask filtering for the given drive + * taking into the consideration capabilities of the mate device. + * + * 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 MWDMA2 + * but instead drop to PIO. + */ + +static u8 cs5530_udma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + ide_drive_t *mate = ide_get_pair_dev(drive); + u16 *mateid; + u8 mask = hwif->ultra_mask; + + if (mate == NULL) + goto out; + mateid = mate->id; + + if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { + if ((mateid[ATA_ID_FIELD_VALID] & 4) && + (mateid[ATA_ID_UDMA_MODES] & 7)) + goto out; + if (mateid[ATA_ID_MWDMA_MODES] & 7) + mask = 0; + } +out: + return mask; +} + +static void cs5530_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long basereg; + unsigned int reg, timings = 0; + + switch (drive->dma_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; + } + basereg = CS5530_BASEREG(hwif); + reg = inl(basereg + 4); /* get drive0 config register */ + timings |= reg & 0x80000000; /* preserve PIO format bit */ + if ((drive-> dn & 1) == 0) { /* are we configuring drive0? */ + outl(timings, basereg + 4); /* write drive0 config register */ + } else { + if (timings & 0x00100000) + reg |= 0x00100000; /* enable UDMA timings for both drives */ + else + reg &= ~0x00100000; /* disable UDMA timings for both drives */ + outl(reg, basereg + 4); /* write drive0 config register */ + outl(timings, basereg + 12); /* write drive1 config register */ + } +} + +/** + * init_chipset_5530 - set up 5530 bridge + * @dev: PCI device + * + * Initialize the cs5530 bridge for reliable IDE DMA operation. + */ + +static int init_chipset_cs5530(struct pci_dev *dev) +{ + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + + if (pci_resource_start(dev, 4) == 0) + return -EFAULT; + + dev = NULL; + while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) { + switch (dev->device) { + case PCI_DEVICE_ID_CYRIX_PCI_MASTER: + master_0 = pci_dev_get(dev); + break; + case PCI_DEVICE_ID_CYRIX_5530_LEGACY: + cs5530_0 = pci_dev_get(dev); + break; + } + } + if (!master_0) { + printk(KERN_ERR DRV_NAME ": unable to locate PCI MASTER function\n"); + goto out; + } + if (!cs5530_0) { + printk(KERN_ERR DRV_NAME ": unable to locate CS5530 LEGACY function\n"); + goto out; + } + + /* + * 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_try_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); + +out: + pci_dev_put(master_0); + pci_dev_put(cs5530_0); + 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_hwif_cs5530 (ide_hwif_t *hwif) +{ + unsigned long basereg; + u32 d0_timings; + + basereg = CS5530_BASEREG(hwif); + d0_timings = inl(basereg + 0); + if (CS5530_BAD_PIO(d0_timings)) + outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 0); + if (CS5530_BAD_PIO(inl(basereg + 8))) + outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 8); +} + +static const struct ide_port_ops cs5530_port_ops = { + .set_pio_mode = cs5530_set_pio_mode, + .set_dma_mode = cs5530_set_dma_mode, + .udma_filter = cs5530_udma_filter, +}; + +static const struct ide_port_info cs5530_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_cs5530, + .init_hwif = init_hwif_cs5530, + .port_ops = &cs5530_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | + IDE_HFLAG_POST_SET_MODE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, +}; + +static int cs5530_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &cs5530_chipset, NULL); +} + +static const struct pci_device_id cs5530_pci_tbl[] = { + { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cs5530_pci_tbl); + +static struct pci_driver cs5530_pci_driver = { + .name = "CS5530 IDE", + .id_table = cs5530_pci_tbl, + .probe = cs5530_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init cs5530_ide_init(void) +{ + return ide_pci_register_driver(&cs5530_pci_driver); +} + +static void __exit cs5530_ide_exit(void) +{ + pci_unregister_driver(&cs5530_pci_driver); +} + +module_init(cs5530_ide_init); +module_exit(cs5530_ide_exit); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Cyrix/NS 5530 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/cs5535.c b/drivers/ide/cs5535.c new file mode 100644 index 000000000..70fdbe316 --- /dev/null +++ b/drivers/ide/cs5535.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2004-2005 Advanced Micro Devices, Inc. + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + * + * History: + * 09/20/2005 - Jaya Kumar <jayakumar.ide@gmail.com> + * - Reworked tuneproc, set_drive, misc mods to prep for mainline + * - Work was sponsored by CIS (M) Sdn Bhd. + * Ported to Kernel 2.6.11 on June 26, 2005 by + * Wolfgang Zuleger <wolfgang.zuleger@gmx.de> + * Alexander Kiausch <alex.kiausch@t-online.de> + * Originally developed by AMD for 2.4/2.6 + * + * Development of this chipset driver was funded + * by the nice folks at National Semiconductor/AMD. + * + * Documentation: + * CS5535 documentation available from AMD + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/ide.h> + +#define DRV_NAME "cs5535" + +#define MSR_ATAC_BASE 0x51300000 +#define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0) +#define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01) +#define ATAC_GLD_MSR_SMI (MSR_ATAC_BASE+0x02) +#define ATAC_GLD_MSR_ERROR (MSR_ATAC_BASE+0x03) +#define ATAC_GLD_MSR_PM (MSR_ATAC_BASE+0x04) +#define ATAC_GLD_MSR_DIAG (MSR_ATAC_BASE+0x05) +#define ATAC_IO_BAR (MSR_ATAC_BASE+0x08) +#define ATAC_RESET (MSR_ATAC_BASE+0x10) +#define ATAC_CH0D0_PIO (MSR_ATAC_BASE+0x20) +#define ATAC_CH0D0_DMA (MSR_ATAC_BASE+0x21) +#define ATAC_CH0D1_PIO (MSR_ATAC_BASE+0x22) +#define ATAC_CH0D1_DMA (MSR_ATAC_BASE+0x23) +#define ATAC_PCI_ABRTERR (MSR_ATAC_BASE+0x24) +#define ATAC_BM0_CMD_PRIM 0x00 +#define ATAC_BM0_STS_PRIM 0x02 +#define ATAC_BM0_PRD 0x04 +#define CS5535_CABLE_DETECT 0x48 + +/* Format I PIO settings. We separate out cmd and data for safer timings */ + +static unsigned int cs5535_pio_cmd_timings[5] = +{ 0xF7F4, 0x53F3, 0x13F1, 0x5131, 0x1131 }; +static unsigned int cs5535_pio_dta_timings[5] = +{ 0xF7F4, 0xF173, 0x8141, 0x5131, 0x1131 }; + +static unsigned int cs5535_mwdma_timings[3] = +{ 0x7F0FFFF3, 0x7F035352, 0x7f024241 }; + +static unsigned int cs5535_udma_timings[5] = +{ 0x7F7436A1, 0x7F733481, 0x7F723261, 0x7F713161, 0x7F703061 }; + +/* Macros to check if the register is the reset value - reset value is an + invalid timing and indicates the register has not been set previously */ + +#define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL) == 0x00009172 ) +#define CS5535_BAD_DMA(timings) ( (timings & 0x000FFFFF) == 0x00077771 ) + +/**** + * cs5535_set_speed - Configure the chipset to the new speed + * @drive: Drive to set up + * @speed: desired speed + * + * cs5535_set_speed() configures the chipset to a new speed. + */ +static void cs5535_set_speed(ide_drive_t *drive, const u8 speed) +{ + u32 reg = 0, dummy; + u8 unit = drive->dn & 1; + + /* Set the PIO timings */ + if (speed < XFER_SW_DMA_0) { + ide_drive_t *pair = ide_get_pair_dev(drive); + u8 cmd, pioa; + + cmd = pioa = speed - XFER_PIO_0; + + if (pair) { + u8 piob = pair->pio_mode - XFER_PIO_0; + + if (piob < cmd) + cmd = piob; + } + + /* Write the speed of the current drive */ + reg = (cs5535_pio_cmd_timings[cmd] << 16) | + cs5535_pio_dta_timings[pioa]; + wrmsr(unit ? ATAC_CH0D1_PIO : ATAC_CH0D0_PIO, reg, 0); + + /* And if nessesary - change the speed of the other drive */ + rdmsr(unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO, reg, dummy); + + if (((reg >> 16) & cs5535_pio_cmd_timings[cmd]) != + cs5535_pio_cmd_timings[cmd]) { + reg &= 0x0000FFFF; + reg |= cs5535_pio_cmd_timings[cmd] << 16; + wrmsr(unit ? ATAC_CH0D0_PIO : ATAC_CH0D1_PIO, reg, 0); + } + + /* Set bit 31 of the DMA register for PIO format 1 timings */ + rdmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, dummy); + wrmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, + reg | 0x80000000UL, 0); + } else { + rdmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, dummy); + + reg &= 0x80000000UL; /* Preserve the PIO format bit */ + + if (speed >= XFER_UDMA_0 && speed <= XFER_UDMA_4) + reg |= cs5535_udma_timings[speed - XFER_UDMA_0]; + else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) + reg |= cs5535_mwdma_timings[speed - XFER_MW_DMA_0]; + else + return; + + wrmsr(unit ? ATAC_CH0D1_DMA : ATAC_CH0D0_DMA, reg, 0); + } +} + +/** + * cs5535_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Programs the chipset for DMA mode. + */ + +static void cs5535_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + cs5535_set_speed(drive, drive->dma_mode); +} + +/** + * cs5535_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * A callback from the upper layers for PIO-only tuning. + */ + +static void cs5535_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + cs5535_set_speed(drive, drive->pio_mode); +} + +static u8 cs5535_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 bit; + + /* if a 80 wire cable was detected */ + pci_read_config_byte(dev, CS5535_CABLE_DETECT, &bit); + + return (bit & 1) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; +} + +static const struct ide_port_ops cs5535_port_ops = { + .set_pio_mode = cs5535_set_pio_mode, + .set_dma_mode = cs5535_set_dma_mode, + .cable_detect = cs5535_cable_detect, +}; + +static const struct ide_port_info cs5535_chipset = { + .name = DRV_NAME, + .port_ops = &cs5535_port_ops, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA4, +}; + +static int cs5535_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &cs5535_chipset, NULL); +} + +static const struct pci_device_id cs5535_pci_tbl[] = { + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_CS5535_IDE), 0 }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5535_IDE), }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, cs5535_pci_tbl); + +static struct pci_driver cs5535_pci_driver = { + .name = "CS5535_IDE", + .id_table = cs5535_pci_tbl, + .probe = cs5535_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init cs5535_ide_init(void) +{ + return ide_pci_register_driver(&cs5535_pci_driver); +} + +static void __exit cs5535_ide_exit(void) +{ + pci_unregister_driver(&cs5535_pci_driver); +} + +module_init(cs5535_ide_init); +module_exit(cs5535_ide_exit); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("PCI driver module for AMD/NS CS5535 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c new file mode 100644 index 000000000..8b5ca1451 --- /dev/null +++ b/drivers/ide/cs5536.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CS5536 PATA support + * (C) 2007 Martin K. Petersen <mkp@mkp.net> + * (C) 2009 Bartlomiej Zolnierkiewicz + * + * Documentation: + * Available from AMD web site. + * + * The IDE timing registers for the CS5536 live in the Geode Machine + * Specific Register file and not PCI config space. Most BIOSes + * virtualize the PCI registers so the chip looks like a standard IDE + * controller. Unfortunately not all implementations get this right. + * In particular some have problems with unaligned accesses to the + * virtualized PCI registers. This driver always does full dword + * writes to work around the issue. Also, in case of a bad BIOS this + * driver can be loaded with the "msr=1" parameter which forces using + * the Machine Specific Registers to configure the device. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <asm/msr.h> + +#define DRV_NAME "cs5536" + +enum { + MSR_IDE_CFG = 0x51300010, + PCI_IDE_CFG = 0x40, + + CFG = 0, + DTC = 2, + CAST = 3, + ETC = 4, + + IDE_CFG_CHANEN = (1 << 1), + IDE_CFG_CABLE = (1 << 17) | (1 << 16), + + IDE_D0_SHIFT = 24, + IDE_D1_SHIFT = 16, + IDE_DRV_MASK = 0xff, + + IDE_CAST_D0_SHIFT = 6, + IDE_CAST_D1_SHIFT = 4, + IDE_CAST_DRV_MASK = 0x3, + + IDE_CAST_CMD_SHIFT = 24, + IDE_CAST_CMD_MASK = 0xff, + + IDE_ETC_UDMA_MASK = 0xc0, +}; + +static int use_msr; + +static int cs5536_read(struct pci_dev *pdev, int reg, u32 *val) +{ + if (unlikely(use_msr)) { + u32 dummy; + + rdmsr(MSR_IDE_CFG + reg, *val, dummy); + return 0; + } + + return pci_read_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); +} + +static int cs5536_write(struct pci_dev *pdev, int reg, int val) +{ + if (unlikely(use_msr)) { + wrmsr(MSR_IDE_CFG + reg, val, 0); + return 0; + } + + return pci_write_config_dword(pdev, PCI_IDE_CFG + reg * 4, val); +} + +static void cs5536_program_dtc(ide_drive_t *drive, u8 tim) +{ + struct pci_dev *pdev = to_pci_dev(drive->hwif->dev); + int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT; + u32 dtc; + + cs5536_read(pdev, DTC, &dtc); + dtc &= ~(IDE_DRV_MASK << dshift); + dtc |= tim << dshift; + cs5536_write(pdev, DTC, dtc); +} + +/** + * cs5536_cable_detect - detect cable type + * @hwif: Port to detect on + * + * Perform cable detection for ATA66 capable cable. + * + * Returns a cable type. + */ + +static u8 cs5536_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + u32 cfg; + + cs5536_read(pdev, CFG, &cfg); + + if (cfg & IDE_CFG_CABLE) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; +} + +/** + * cs5536_set_pio_mode - PIO timing setup + * @hwif: ATA port + * @drive: ATA device + */ + +static void cs5536_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u8 drv_timings[5] = { + 0x98, 0x55, 0x32, 0x21, 0x20, + }; + + static const u8 addr_timings[5] = { + 0x2, 0x1, 0x0, 0x0, 0x0, + }; + + static const u8 cmd_timings[5] = { + 0x99, 0x92, 0x90, 0x22, 0x20, + }; + + struct pci_dev *pdev = to_pci_dev(hwif->dev); + ide_drive_t *pair = ide_get_pair_dev(drive); + int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT; + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + u32 cast; + const u8 pio = drive->pio_mode - XFER_PIO_0; + u8 cmd_pio = pio; + + if (pair) + cmd_pio = min_t(u8, pio, pair->pio_mode - XFER_PIO_0); + + timings &= (IDE_DRV_MASK << 8); + timings |= drv_timings[pio]; + ide_set_drivedata(drive, (void *)timings); + + cs5536_program_dtc(drive, drv_timings[pio]); + + cs5536_read(pdev, CAST, &cast); + + cast &= ~(IDE_CAST_DRV_MASK << cshift); + cast |= addr_timings[pio] << cshift; + + cast &= ~(IDE_CAST_CMD_MASK << IDE_CAST_CMD_SHIFT); + cast |= cmd_timings[cmd_pio] << IDE_CAST_CMD_SHIFT; + + cs5536_write(pdev, CAST, cast); +} + +/** + * cs5536_set_dma_mode - DMA timing setup + * @hwif: ATA port + * @drive: ATA device + */ + +static void cs5536_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u8 udma_timings[6] = { + 0xc2, 0xc1, 0xc0, 0xc4, 0xc5, 0xc6, + }; + + static const u8 mwdma_timings[3] = { + 0x67, 0x21, 0x20, + }; + + struct pci_dev *pdev = to_pci_dev(hwif->dev); + int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT; + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + u32 etc; + const u8 mode = drive->dma_mode; + + cs5536_read(pdev, ETC, &etc); + + if (mode >= XFER_UDMA_0) { + etc &= ~(IDE_DRV_MASK << dshift); + etc |= udma_timings[mode - XFER_UDMA_0] << dshift; + } else { /* MWDMA */ + etc &= ~(IDE_ETC_UDMA_MASK << dshift); + timings &= IDE_DRV_MASK; + timings |= mwdma_timings[mode - XFER_MW_DMA_0] << 8; + ide_set_drivedata(drive, (void *)timings); + } + + cs5536_write(pdev, ETC, etc); +} + +static void cs5536_dma_start(ide_drive_t *drive) +{ + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + + if (drive->current_speed < XFER_UDMA_0 && + (timings >> 8) != (timings & IDE_DRV_MASK)) + cs5536_program_dtc(drive, timings >> 8); + + ide_dma_start(drive); +} + +static int cs5536_dma_end(ide_drive_t *drive) +{ + int ret = ide_dma_end(drive); + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + + if (drive->current_speed < XFER_UDMA_0 && + (timings >> 8) != (timings & IDE_DRV_MASK)) + cs5536_program_dtc(drive, timings & IDE_DRV_MASK); + + return ret; +} + +static const struct ide_port_ops cs5536_port_ops = { + .set_pio_mode = cs5536_set_pio_mode, + .set_dma_mode = cs5536_set_dma_mode, + .cable_detect = cs5536_cable_detect, +}; + +static const struct ide_dma_ops cs5536_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = cs5536_dma_start, + .dma_end = cs5536_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info cs5536_info = { + .name = DRV_NAME, + .port_ops = &cs5536_port_ops, + .dma_ops = &cs5536_dma_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, +}; + +/** + * cs5536_init_one + * @dev: PCI device + * @id: Entry in match table + */ + +static int cs5536_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + u32 cfg; + + if (use_msr) + printk(KERN_INFO DRV_NAME ": Using MSR regs instead of PCI\n"); + + cs5536_read(dev, CFG, &cfg); + + if ((cfg & IDE_CFG_CHANEN) == 0) { + printk(KERN_ERR DRV_NAME ": disabled by BIOS\n"); + return -ENODEV; + } + + return ide_pci_init_one(dev, &cs5536_info, NULL); +} + +static const struct pci_device_id cs5536_pci_tbl[] = { + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CS5536_IDE), }, + { }, +}; + +static struct pci_driver cs5536_pci_driver = { + .name = DRV_NAME, + .id_table = cs5536_pci_tbl, + .probe = cs5536_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +module_pci_driver(cs5536_pci_driver); + +MODULE_AUTHOR("Martin K. Petersen, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("low-level driver for the CS5536 IDE controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cs5536_pci_tbl); + +module_param_named(msr, use_msr, int, 0644); +MODULE_PARM_DESC(msr, "Force using MSR to configure IDE function (Default: 0)"); diff --git a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c new file mode 100644 index 000000000..bc01660ee --- /dev/null +++ b/drivers/ide/cy82c693.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>, Integrator + * Copyright (C) 2007-2011 Bartlomiej Zolnierkiewicz + * + * CYPRESS CY82C693 chipset IDE controller + * + * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "cy82c693" + +/* + * 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_CHANNEL0 0x30 +#define CY82_INDEX_CHANNEL1 0x31 +#define CY82_INDEX_TIMEOUT 0x32 + +/* + * set DMA mode a specific channel for CY82C693 + */ + +static void cy82c693_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + const u8 mode = drive->dma_mode; + u8 single = (mode & 0x10) >> 4, index = 0, data = 0; + + index = hwif->channel ? CY82_INDEX_CHANNEL1 : CY82_INDEX_CHANNEL0; + + data = (mode & 3) | (single << 2); + + outb(index, CY82_INDEX_PORT); + outb(data, CY82_DATA_PORT); + + /* + * note: below we set the value for Bus Master IDE TimeOut Register + * I'm not absolutely 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; + outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); + outb(data, CY82_DATA_PORT); +} + +static void cy82c693_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int bus_speed = ide_pci_clk ? ide_pci_clk : 33; + const unsigned long T = 1000000 / bus_speed; + unsigned int addrCtrl; + struct ide_timing t; + u8 time_16, time_8; + + /* select primary or secondary channel */ + if (drive->dn > 1) { /* drive is on the secondary channel */ + dev = pci_get_slot(dev->bus, dev->devfn+1); + if (!dev) { + printk(KERN_ERR "%s: tune_drive: " + "Cannot find secondary interface!\n", + drive->name); + return; + } + } + + ide_timing_compute(drive, drive->pio_mode, &t, T, 1); + + time_16 = clamp_val(t.recover - 1, 0, 15) | + (clamp_val(t.active - 1, 0, 15) << 4); + time_8 = clamp_val(t.act8b - 1, 0, 15) | + (clamp_val(t.rec8b - 1, 0, 15) << 4); + + /* now let's write the clocks registers */ + if ((drive->dn & 1) == 0) { + /* + * set master drive + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + + addrCtrl &= (~0xF); + addrCtrl |= clamp_val(t.setup - 1, 0, 15); + 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, time_16); + pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, time_16); + pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, time_8); + } else { + /* + * set slave drive + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + + addrCtrl &= (~0xF0); + addrCtrl |= (clamp_val(t.setup - 1, 0, 15) << 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, time_16); + pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, time_16); + pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, time_8); + } + if (drive->dn > 1) + pci_dev_put(dev); +} + +static void init_iops_cy82c693(ide_hwif_t *hwif) +{ + static ide_hwif_t *primary; + struct pci_dev *dev = to_pci_dev(hwif->dev); + + if (PCI_FUNC(dev->devfn) == 1) + primary = hwif; + else { + hwif->mate = primary; + hwif->channel = 1; + } +} + +static const struct ide_port_ops cy82c693_port_ops = { + .set_pio_mode = cy82c693_set_pio_mode, + .set_dma_mode = cy82c693_set_dma_mode, +}; + +static const struct ide_port_info cy82c693_chipset = { + .name = DRV_NAME, + .init_iops = init_iops_cy82c693, + .port_ops = &cy82c693_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, +}; + +static int cy82c693_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + 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_get_slot(dev->bus, dev->devfn + 1); + ret = ide_pci_init_two(dev, dev2, &cy82c693_chipset, NULL); + if (ret) + pci_dev_put(dev2); + } + return ret; +} + +static void cy82c693_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); +} + +static const struct pci_device_id cy82c693_pci_tbl[] = { + { PCI_VDEVICE(CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, cy82c693_pci_tbl); + +static struct pci_driver cy82c693_pci_driver = { + .name = "Cypress_IDE", + .id_table = cy82c693_pci_tbl, + .probe = cy82c693_init_one, + .remove = cy82c693_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init cy82c693_ide_init(void) +{ + return ide_pci_register_driver(&cy82c693_pci_driver); +} + +static void __exit cy82c693_ide_exit(void) +{ + pci_unregister_driver(&cy82c693_pci_driver); +} + +module_init(cy82c693_ide_init); +module_exit(cy82c693_ide_exit); + +MODULE_AUTHOR("Andreas Krebs, Andre Hedrick, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("PCI driver module for the Cypress CY82C693 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/delkin_cb.c b/drivers/ide/delkin_cb.c new file mode 100644 index 000000000..300daabaa --- /dev/null +++ b/drivers/ide/delkin_cb.c @@ -0,0 +1,181 @@ +/* + * Created 20 Oct 2004 by Mark Lord + * + * Basic support for Delkin/ASKA/Workbit Cardbus CompactFlash adapter + * + * Modeled after the 16-bit PCMCIA driver: ide-cs.c + * + * This is slightly peculiar, in that it is a PCI driver, + * but is NOT an IDE PCI driver -- the IDE layer does not directly + * support hot insertion/removal of PCI interfaces, so this driver + * is unable to use the IDE PCI interfaces. Instead, it uses the + * same interfaces as the ide-cs (PCMCIA) driver uses. + * On the plus side, the driver is also smaller/simpler this way. + * + * 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/module.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/pci.h> + +#include <asm/io.h> + +/* + * No chip documentation has yet been found, + * so these configuration values were pulled from + * a running Win98 system using "debug". + * This gives around 3MByte/second read performance, + * which is about 2/3 of what the chip is capable of. + * + * There is also a 4KByte mmio region on the card, + * but its purpose has yet to be reverse-engineered. + */ +static const u8 setup[] = { + 0x00, 0x05, 0xbe, 0x01, 0x20, 0x8f, 0x00, 0x00, + 0xa4, 0x1f, 0xb3, 0x1b, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa4, 0x83, 0x02, 0x13, +}; + +static const struct ide_port_ops delkin_cb_port_ops = { + .quirkproc = ide_undecoded_slave, +}; + +static int delkin_cb_init_chipset(struct pci_dev *dev) +{ + unsigned long base = pci_resource_start(dev, 0); + int i; + + outb(0x02, base + 0x1e); /* set nIEN to block interrupts */ + inb(base + 0x17); /* read status to clear interrupts */ + + for (i = 0; i < sizeof(setup); ++i) { + if (setup[i]) + outb(setup[i], base + i); + } + + return 0; +} + +static const struct ide_port_info delkin_cb_port_info = { + .port_ops = &delkin_cb_port_ops, + .host_flags = IDE_HFLAG_IO_32BIT | IDE_HFLAG_UNMASK_IRQS | + IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .init_chipset = delkin_cb_init_chipset, + .chipset = ide_pci, +}; + +static int delkin_cb_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_host *host; + unsigned long base; + int rc; + struct ide_hw hw, *hws[] = { &hw }; + + rc = pci_enable_device(dev); + if (rc) { + printk(KERN_ERR "delkin_cb: pci_enable_device failed (%d)\n", rc); + return rc; + } + rc = pci_request_regions(dev, "delkin_cb"); + if (rc) { + printk(KERN_ERR "delkin_cb: pci_request_regions failed (%d)\n", rc); + pci_disable_device(dev); + return rc; + } + base = pci_resource_start(dev, 0); + + delkin_cb_init_chipset(dev); + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, base + 0x10, base + 0x1e); + hw.irq = dev->irq; + hw.dev = &dev->dev; + + rc = ide_host_add(&delkin_cb_port_info, hws, 1, &host); + if (rc) + goto out_disable; + + pci_set_drvdata(dev, host); + + return 0; + +out_disable: + pci_release_regions(dev); + pci_disable_device(dev); + return rc; +} + +static void +delkin_cb_remove (struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + + ide_host_remove(host); + + pci_release_regions(dev); + pci_disable_device(dev); +} + +#ifdef CONFIG_PM +static int delkin_cb_suspend(struct pci_dev *dev, pm_message_t state) +{ + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + return 0; +} + +static int delkin_cb_resume(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + int rc; + + pci_set_power_state(dev, PCI_D0); + + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_restore_state(dev); + pci_set_master(dev); + + if (host->init_chipset) + host->init_chipset(dev); + + return 0; +} +#else +#define delkin_cb_suspend NULL +#define delkin_cb_resume NULL +#endif + +static struct pci_device_id delkin_cb_pci_tbl[] = { + { 0x1145, 0xf021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0x1145, 0xf024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl); + +static struct pci_driver delkin_cb_pci_driver = { + .name = "Delkin-ASKA-Workbit Cardbus IDE", + .id_table = delkin_cb_pci_tbl, + .probe = delkin_cb_probe, + .remove = delkin_cb_remove, + .suspend = delkin_cb_suspend, + .resume = delkin_cb_resume, +}; + +module_pci_driver(delkin_cb_pci_driver); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("Basic support for Delkin/ASKA/Workbit Cardbus IDE"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/ide/dtc2278.c b/drivers/ide/dtc2278.c new file mode 100644 index 000000000..714e8cd0f --- /dev/null +++ b/drivers/ide/dtc2278.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1996 Linus Torvalds & author (see below) + */ + +#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/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "dtc2278" + +/* + * 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 DEFINE_SPINLOCK(dtc2278_lock); + +static void dtc2278_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long flags; + + if (drive->pio_mode >= XFER_PIO_3) { + spin_lock_irqsave(&dtc2278_lock, flags); + /* + * This enables PIO mode4 (3?) on the first interface + */ + sub22(1,0xc3); + sub22(0,0xa0); + spin_unlock_irqrestore(&dtc2278_lock, flags); + } else { + /* we don't know how to set it back again.. */ + /* Actually we do - there is a data sheet available for the + Winbond but does anyone actually care */ + } +} + +static const struct ide_port_ops dtc2278_port_ops = { + .set_pio_mode = dtc2278_set_pio_mode, +}; + +static const struct ide_port_info dtc2278_port_info __initconst = { + .name = DRV_NAME, + .chipset = ide_dtc2278, + .port_ops = &dtc2278_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | + IDE_HFLAG_NO_UNMASK_IRQS | + IDE_HFLAG_IO_32BIT | + /* disallow ->io_32bit changes */ + IDE_HFLAG_NO_IO_32BIT | + IDE_HFLAG_NO_DMA | + IDE_HFLAG_DTC2278, + .pio_mask = ATA_PIO4, +}; + +static int __init dtc2278_probe(void) +{ + unsigned long flags; + + 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); + + return ide_legacy_device_add(&dtc2278_port_info, 0); +} + +static bool probe_dtc2278; + +module_param_named(probe, probe_dtc2278, bool, 0); +MODULE_PARM_DESC(probe, "probe for DTC2278xx chipsets"); + +static int __init dtc2278_init(void) +{ + if (probe_dtc2278 == 0) + return -ENODEV; + + if (dtc2278_probe()) { + printk(KERN_ERR "dtc2278: ide interfaces already in use!\n"); + return -EBUSY; + } + return 0; +} + +module_init(dtc2278_init); + +MODULE_AUTHOR("See Local File"); +MODULE_DESCRIPTION("support of DTC-2278 VLB IDE chipsets"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/falconide.c b/drivers/ide/falconide.c new file mode 100644 index 000000000..607c44bc5 --- /dev/null +++ b/drivers/ide/falconide.c @@ -0,0 +1,199 @@ +/* + * 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/module.h> +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <asm/setup.h> +#include <asm/atarihw.h> +#include <asm/atariints.h> +#include <asm/atari_stdma.h> +#include <asm/ide.h> + +#define DRV_NAME "falconide" + + /* + * Offsets from base address + */ + +#define ATA_HD_CONTROL 0x39 + + /* + * falconide_intr_lock is used to obtain access to the IDE interrupt, + * which is shared between several drivers. + */ + +static int falconide_intr_lock; + +static void falconide_release_lock(void) +{ + if (falconide_intr_lock == 0) { + printk(KERN_ERR "%s: bug\n", __func__); + return; + } + falconide_intr_lock = 0; + stdma_release(); +} + +static void falconide_get_lock(irq_handler_t handler, void *data) +{ + if (falconide_intr_lock == 0) { + if (in_interrupt() > 0) + panic("Falcon IDE hasn't ST-DMA lock in interrupt"); + stdma_lock(handler, data); + falconide_intr_lock = 1; + } +} + +static void falconide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long data_addr = drive->hwif->io_ports.data_addr; + + if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) { + __ide_mm_insw(data_addr, buf, (len + 1) / 2); + return; + } + + raw_insw_swapw((u16 *)data_addr, buf, (len + 1) / 2); +} + +static void falconide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long data_addr = drive->hwif->io_ports.data_addr; + + if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) { + __ide_mm_outsw(data_addr, buf, (len + 1) / 2); + return; + } + + raw_outsw_swapw((u16 *)data_addr, buf, (len + 1) / 2); +} + +/* Atari has a byte-swapped IDE interface */ +static const struct ide_tp_ops falconide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = falconide_input_data, + .output_data = falconide_output_data, +}; + +static const struct ide_port_info falconide_port_info = { + .get_lock = falconide_get_lock, + .release_lock = falconide_release_lock, + .tp_ops = &falconide_tp_ops, + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE | + IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_generic, +}; + +static void __init falconide_setup_ports(struct ide_hw *hw, unsigned long base) +{ + int i; + + memset(hw, 0, sizeof(*hw)); + + hw->io_ports.data_addr = base; + + for (i = 1; i < 8; i++) + hw->io_ports_array[i] = base + 1 + i * 4; + + hw->io_ports.ctl_addr = base + ATA_HD_CONTROL; + + hw->irq = IRQ_MFP_IDE; +} + + /* + * Probe for a Falcon IDE interface + */ + +static int __init falconide_init(struct platform_device *pdev) +{ + struct resource *res; + struct ide_host *host; + struct ide_hw hw, *hws[] = { &hw }; + unsigned long base; + int rc; + + dev_info(&pdev->dev, "Atari Falcon IDE controller\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "resources busy\n"); + return -EBUSY; + } + + base = (unsigned long)res->start; + + falconide_setup_ports(&hw, base); + + host = ide_host_alloc(&falconide_port_info, hws, 1); + if (host == NULL) { + rc = -ENOMEM; + goto err; + } + + falconide_get_lock(NULL, NULL); + rc = ide_host_register(host, &falconide_port_info, hws); + falconide_release_lock(); + + if (rc) + goto err_free; + + platform_set_drvdata(pdev, host); + return 0; +err_free: + ide_host_free(host); +err: + release_mem_region(res->start, resource_size(res)); + return rc; +} + +static int falconide_remove(struct platform_device *pdev) +{ + struct ide_host *host = platform_get_drvdata(pdev); + + ide_host_remove(host); + + return 0; +} + +static struct platform_driver ide_falcon_driver = { + .remove = falconide_remove, + .driver = { + .name = "atari-falcon-ide", + }, +}; + +module_platform_driver_probe(ide_falcon_driver, falconide_init); + +MODULE_AUTHOR("Geert Uytterhoeven"); +MODULE_DESCRIPTION("low-level driver for Atari Falcon IDE"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:atari-falcon-ide"); diff --git a/drivers/ide/gayle.c b/drivers/ide/gayle.c new file mode 100644 index 000000000..901e6ebfe --- /dev/null +++ b/drivers/ide/gayle.c @@ -0,0 +1,188 @@ +/* + * 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/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/zorro.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/setup.h> +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#include <asm/amigayle.h> + + + /* + * Offsets from one of the above bases + */ + +#define GAYLE_CONTROL 0x101a + + /* + * 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 + +#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) + +static bool ide_doubler; +module_param_named(doubler, ide_doubler, bool, 0); +MODULE_PARM_DESC(doubler, "enable support for IDE doublers"); + + /* + * Check and acknowledge the interrupt status + */ + +static int gayle_test_irq(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports.irq_addr); + if (!(ch & GAYLE_IRQ_IDE)) + return 0; + return 1; +} + +static void gayle_a1200_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + + (void)z_readb(hwif->io_ports.status_addr); + z_writeb(0x7c, hwif->io_ports.irq_addr); +} + +static void __init gayle_setup_ports(struct ide_hw *hw, unsigned long base, + unsigned long ctl, unsigned long irq_port) +{ + int i; + + memset(hw, 0, sizeof(*hw)); + + hw->io_ports.data_addr = base; + + for (i = 1; i < 8; i++) + hw->io_ports_array[i] = base + 2 + i * 4; + + hw->io_ports.ctl_addr = ctl; + hw->io_ports.irq_addr = irq_port; + + hw->irq = IRQ_AMIGA_PORTS; +} + +static const struct ide_port_ops gayle_a4000_port_ops = { + .test_irq = gayle_test_irq, +}; + +static const struct ide_port_ops gayle_a1200_port_ops = { + .clear_irq = gayle_a1200_clear_irq, + .test_irq = gayle_test_irq, +}; + +static const struct ide_port_info gayle_port_info = { + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_SERIALIZE | + IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_generic, +}; + + /* + * Probe for a Gayle IDE interface (and optionally for an IDE doubler) + */ + +static int __init amiga_gayle_ide_probe(struct platform_device *pdev) +{ + struct resource *res; + struct gayle_ide_platform_data *pdata; + unsigned long base, ctrlport, irqport; + unsigned int i; + int error; + struct ide_hw hw[GAYLE_NUM_HWIFS], *hws[GAYLE_NUM_HWIFS]; + struct ide_port_info d = gayle_port_info; + struct ide_host *host; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (!request_mem_region(res->start, resource_size(res), "IDE")) + return -EBUSY; + + pdata = dev_get_platdata(&pdev->dev); + pr_info("ide: Gayle IDE controller (A%u style%s)\n", + pdata->explicit_ack ? 1200 : 4000, + ide_doubler ? ", IDE doubler" : ""); + + base = (unsigned long)ZTWO_VADDR(pdata->base); + ctrlport = 0; + irqport = (unsigned long)ZTWO_VADDR(pdata->irqport); + if (pdata->explicit_ack) + d.port_ops = &gayle_a1200_port_ops; + else + d.port_ops = &gayle_a4000_port_ops; + + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++, base += GAYLE_NEXT_PORT) { + if (GAYLE_HAS_CONTROL_REG) + ctrlport = base + GAYLE_CONTROL; + + gayle_setup_ports(&hw[i], base, ctrlport, irqport); + hws[i] = &hw[i]; + } + + error = ide_host_add(&d, hws, i, &host); + if (error) + goto out; + + platform_set_drvdata(pdev, host); + return 0; + +out: + release_mem_region(res->start, resource_size(res)); + return error; +} + +static int __exit amiga_gayle_ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + ide_host_remove(host); + release_mem_region(res->start, resource_size(res)); + return 0; +} + +static struct platform_driver amiga_gayle_ide_driver = { + .remove = __exit_p(amiga_gayle_ide_remove), + .driver = { + .name = "amiga-gayle-ide", + }, +}; + +module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:amiga-gayle-ide"); diff --git a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c new file mode 100644 index 000000000..50c9a4146 --- /dev/null +++ b/drivers/ide/hpt366.c @@ -0,0 +1,1545 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> + * Portions Copyright (C) 2001 Sun Microsystems, Inc. + * Portions Copyright (C) 2003 Red Hat Inc + * Portions Copyright (C) 2007 Bartlomiej Zolnierkiewicz + * Portions Copyright (C) 2005-2009 MontaVista Software, 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. + * + * + * HighPoint has its own drivers (open source except for the RAID part) + * available from http://www.highpoint-tech.com/USA_new/service_support.htm + * This may be useful to anyone wanting to work on this driver, however do not + * trust them too much since the code tends to become less and less meaningful + * as the time passes... :-/ + * + * 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@lxorguk.ukuu.org.uk> + * + * - fix the clock turnaround code: it was writing to the wrong ports when + * called for the secondary channel, caching the current clock mode per- + * channel caused the cached register value to get out of sync with the + * actual one, the channels weren't serialized, the turnaround shouldn't + * be done on 66 MHz PCI bus + * - disable UltraATA/100 for HPT370 by default as the 33 MHz clock being used + * does not allow for this speed anyway + * - avoid touching disabled channels (e.g. HPT371/N are single channel chips, + * their primary channel is kind of virtual, it isn't tied to any pins) + * - fix/remove bad/unused timing tables and use one set of tables for the whole + * HPT37x chip family; save space by introducing the separate transfer mode + * table in which the mode lookup is done + * - use f_CNT value saved by the HighPoint BIOS as reading it directly gives + * the wrong PCI frequency since DPLL has already been calibrated by BIOS; + * read it only from the function 0 of HPT374 chips + * - fix the hotswap code: it caused RESET- to glitch when tristating the bus, + * and for HPT36x the obsolete HDIO_TRISTATE_HWIF handler was called instead + * - pass to init_chipset() handlers a copy of the IDE PCI device structure as + * they tamper with its fields + * - pass to the init_setup handlers a copy of the ide_pci_device_t structure + * since they may tamper with its fields + * - prefix the driver startup messages with the real chip name + * - claim the extra 240 bytes of I/O space for all chips + * - optimize the UltraDMA filtering and the drive list lookup code + * - use pci_get_slot() to get to the function 1 of HPT36x/374 + * - cache offset of the channel's misc. control registers (MCRs) being used + * throughout the driver + * - only touch the relevant MCR when detecting the cable type on HPT374's + * function 1 + * - rename all the register related variables consistently + * - move all the interrupt twiddling code from the speedproc handlers into + * init_hwif_hpt366(), also grouping all the DMA related code together there + * - merge HPT36x/HPT37x speedproc handlers, fix PIO timing register mask and + * separate the UltraDMA and MWDMA masks there to avoid changing PIO timings + * when setting an UltraDMA mode + * - fix hpt3xx_tune_drive() to set the PIO mode requested, not always select + * the best possible one + * - clean up DMA timeout handling for HPT370 + * - switch to using the enumeration type to differ between the numerous chip + * variants, matching PCI device/revision ID with the chip type early, at the + * init_setup stage + * - extend the hpt_info structure to hold the DPLL and PCI clock frequencies, + * stop duplicating it for each channel by storing the pointer in the pci_dev + * structure: first, at the init_setup stage, point it to a static "template" + * with only the chip type and its specific base DPLL frequency, the highest + * UltraDMA mode, and the chip settings table pointer filled, then, at the + * init_chipset stage, allocate per-chip instance and fill it with the rest + * of the necessary information + * - get rid of the constant thresholds in the HPT37x PCI clock detection code, + * switch to calculating PCI clock frequency based on the chip's base DPLL + * frequency + * - switch to using the DPLL clock and enable UltraATA/133 mode by default on + * anything newer than HPT370/A (except HPT374 that is not capable of this + * mode according to the manual) + * - fold PCI clock detection and DPLL setup code into init_chipset_hpt366(), + * also fixing the interchanged 25/40 MHz PCI clock cases for HPT36x chips; + * unify HPT36x/37x timing setup code and the speedproc handlers by joining + * the register setting lists into the table indexed by the clock selected + * - set the correct hwif->ultra_mask for each individual chip + * - add Ultra and MW DMA mode filtering for the HPT37[24] based SATA cards + * - stop resetting HPT370's state machine before each DMA transfer as that has + * caused more harm than good + * Sergei Shtylyov, <sshtylyov@ru.mvista.com> or <source@mvista.com> + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/blkdev.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/slab.h> + +#include <linux/uaccess.h> +#include <asm/io.h> + +#define DRV_NAME "hpt366" + +/* various tuning parameters */ +#undef HPT_RESET_STATE_ENGINE +#undef HPT_DELAY_INTERRUPT + +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", + "MAXTOR STM3320620A", + 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 +}; + +static u8 xfer_speeds[] = { + XFER_UDMA_6, + XFER_UDMA_5, + XFER_UDMA_4, + XFER_UDMA_3, + XFER_UDMA_2, + XFER_UDMA_1, + XFER_UDMA_0, + + XFER_MW_DMA_2, + XFER_MW_DMA_1, + XFER_MW_DMA_0, + + XFER_PIO_4, + XFER_PIO_3, + XFER_PIO_2, + XFER_PIO_1, + XFER_PIO_0 +}; + +/* Key for bus clock timings + * 36x 37x + * bits bits + * 0:3 0:3 data_high_time. Inactive time of DIOW_/DIOR_ for PIO and MW DMA. + * cycles = value + 1 + * 4:7 4:8 data_low_time. Active time of DIOW_/DIOR_ for PIO and MW DMA. + * cycles = value + 1 + * 8:11 9:12 cmd_high_time. Inactive time of DIOW_/DIOR_ during task file + * register access. + * 12:15 13:17 cmd_low_time. Active time of DIOW_/DIOR_ during task file + * register access. + * 16:18 18:20 udma_cycle_time. Clock cycles for UDMA xfer. + * - 21 CLK frequency: 0=ATA clock, 1=dual ATA clock. + * 19:21 22:24 pre_high_time. Time to initialize the 1st cycle for PIO and + * MW DMA xfer. + * 22:24 25:27 cmd_pre_high_time. Time to initialize the 1st PIO cycle for + * task file register access. + * 28 28 UDMA enable. + * 29 29 DMA enable. + * 30 30 PIO MST enable. If set, the chip is in bus master mode during + * PIO xfer. + * 31 31 FIFO enable. + */ + +static u32 forty_base_hpt36x[] = { + /* XFER_UDMA_6 */ 0x900fd943, + /* XFER_UDMA_5 */ 0x900fd943, + /* 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 +}; + +static u32 thirty_three_base_hpt36x[] = { + /* XFER_UDMA_6 */ 0x90c9a731, + /* XFER_UDMA_5 */ 0x90c9a731, + /* 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 */ +}; + +static u32 twenty_five_base_hpt36x[] = { + /* XFER_UDMA_6 */ 0x90c98521, + /* XFER_UDMA_5 */ 0x90c98521, + /* 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 +}; + +/* + * The following are the new timing tables with PIO mode data/taskfile transfer + * overclocking fixed... + */ + +/* This table is taken from the HPT370 data manual rev. 1.02 */ +static u32 thirty_three_base_hpt37x[] = { + /* XFER_UDMA_6 */ 0x16455031, /* 0x16655031 ?? */ + /* XFER_UDMA_5 */ 0x16455031, + /* XFER_UDMA_4 */ 0x16455031, + /* XFER_UDMA_3 */ 0x166d5031, + /* XFER_UDMA_2 */ 0x16495031, + /* XFER_UDMA_1 */ 0x164d5033, + /* XFER_UDMA_0 */ 0x16515097, + + /* XFER_MW_DMA_2 */ 0x26515031, + /* XFER_MW_DMA_1 */ 0x26515033, + /* XFER_MW_DMA_0 */ 0x26515097, + + /* XFER_PIO_4 */ 0x06515021, + /* XFER_PIO_3 */ 0x06515022, + /* XFER_PIO_2 */ 0x06515033, + /* XFER_PIO_1 */ 0x06915065, + /* XFER_PIO_0 */ 0x06d1508a +}; + +static u32 fifty_base_hpt37x[] = { + /* XFER_UDMA_6 */ 0x1a861842, + /* XFER_UDMA_5 */ 0x1a861842, + /* XFER_UDMA_4 */ 0x1aae1842, + /* XFER_UDMA_3 */ 0x1a8e1842, + /* XFER_UDMA_2 */ 0x1a0e1842, + /* XFER_UDMA_1 */ 0x1a161854, + /* XFER_UDMA_0 */ 0x1a1a18ea, + + /* XFER_MW_DMA_2 */ 0x2a821842, + /* XFER_MW_DMA_1 */ 0x2a821854, + /* XFER_MW_DMA_0 */ 0x2a8218ea, + + /* XFER_PIO_4 */ 0x0a821842, + /* XFER_PIO_3 */ 0x0a821843, + /* XFER_PIO_2 */ 0x0a821855, + /* XFER_PIO_1 */ 0x0ac218a8, + /* XFER_PIO_0 */ 0x0b02190c +}; + +static u32 sixty_six_base_hpt37x[] = { + /* XFER_UDMA_6 */ 0x1c86fe62, + /* XFER_UDMA_5 */ 0x1caefe62, /* 0x1c8afe62 */ + /* XFER_UDMA_4 */ 0x1c8afe62, + /* XFER_UDMA_3 */ 0x1c8efe62, + /* XFER_UDMA_2 */ 0x1c92fe62, + /* XFER_UDMA_1 */ 0x1c9afe62, + /* XFER_UDMA_0 */ 0x1c82fe62, + + /* XFER_MW_DMA_2 */ 0x2c82fe62, + /* XFER_MW_DMA_1 */ 0x2c82fe66, + /* XFER_MW_DMA_0 */ 0x2c82ff2e, + + /* XFER_PIO_4 */ 0x0c82fe62, + /* XFER_PIO_3 */ 0x0c82fe84, + /* XFER_PIO_2 */ 0x0c82fea6, + /* XFER_PIO_1 */ 0x0d02ff26, + /* XFER_PIO_0 */ 0x0d42ff7f +}; + +#define HPT371_ALLOW_ATA133_6 1 +#define HPT302_ALLOW_ATA133_6 1 +#define HPT372_ALLOW_ATA133_6 1 +#define HPT370_ALLOW_ATA100_5 0 +#define HPT366_ALLOW_ATA66_4 1 +#define HPT366_ALLOW_ATA66_3 1 + +/* Supported ATA clock frequencies */ +enum ata_clock { + ATA_CLOCK_25MHZ, + ATA_CLOCK_33MHZ, + ATA_CLOCK_40MHZ, + ATA_CLOCK_50MHZ, + ATA_CLOCK_66MHZ, + NUM_ATA_CLOCKS +}; + +struct hpt_timings { + u32 pio_mask; + u32 dma_mask; + u32 ultra_mask; + u32 *clock_table[NUM_ATA_CLOCKS]; +}; + +/* + * Hold all the HighPoint chip information in one place. + */ + +struct hpt_info { + char *chip_name; /* Chip name */ + u8 chip_type; /* Chip type */ + u8 udma_mask; /* Allowed UltraDMA modes mask. */ + u8 dpll_clk; /* DPLL clock in MHz */ + u8 pci_clk; /* PCI clock in MHz */ + struct hpt_timings *timings; /* Chipset timing data */ + u8 clock; /* ATA clock selected */ +}; + +/* Supported HighPoint chips */ +enum { + HPT36x, + HPT370, + HPT370A, + HPT374, + HPT372, + HPT372A, + HPT302, + HPT371, + HPT372N, + HPT302N, + HPT371N +}; + +static struct hpt_timings hpt36x_timings = { + .pio_mask = 0xc1f8ffff, + .dma_mask = 0x303800ff, + .ultra_mask = 0x30070000, + .clock_table = { + [ATA_CLOCK_25MHZ] = twenty_five_base_hpt36x, + [ATA_CLOCK_33MHZ] = thirty_three_base_hpt36x, + [ATA_CLOCK_40MHZ] = forty_base_hpt36x, + [ATA_CLOCK_50MHZ] = NULL, + [ATA_CLOCK_66MHZ] = NULL + } +}; + +static struct hpt_timings hpt37x_timings = { + .pio_mask = 0xcfc3ffff, + .dma_mask = 0x31c001ff, + .ultra_mask = 0x303c0000, + .clock_table = { + [ATA_CLOCK_25MHZ] = NULL, + [ATA_CLOCK_33MHZ] = thirty_three_base_hpt37x, + [ATA_CLOCK_40MHZ] = NULL, + [ATA_CLOCK_50MHZ] = fifty_base_hpt37x, + [ATA_CLOCK_66MHZ] = sixty_six_base_hpt37x + } +}; + +static const struct hpt_info hpt36x = { + .chip_name = "HPT36x", + .chip_type = HPT36x, + .udma_mask = HPT366_ALLOW_ATA66_3 ? (HPT366_ALLOW_ATA66_4 ? ATA_UDMA4 : ATA_UDMA3) : ATA_UDMA2, + .dpll_clk = 0, /* no DPLL */ + .timings = &hpt36x_timings +}; + +static const struct hpt_info hpt370 = { + .chip_name = "HPT370", + .chip_type = HPT370, + .udma_mask = HPT370_ALLOW_ATA100_5 ? ATA_UDMA5 : ATA_UDMA4, + .dpll_clk = 48, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt370a = { + .chip_name = "HPT370A", + .chip_type = HPT370A, + .udma_mask = HPT370_ALLOW_ATA100_5 ? ATA_UDMA5 : ATA_UDMA4, + .dpll_clk = 48, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt374 = { + .chip_name = "HPT374", + .chip_type = HPT374, + .udma_mask = ATA_UDMA5, + .dpll_clk = 48, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt372 = { + .chip_name = "HPT372", + .chip_type = HPT372, + .udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 55, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt372a = { + .chip_name = "HPT372A", + .chip_type = HPT372A, + .udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 66, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt302 = { + .chip_name = "HPT302", + .chip_type = HPT302, + .udma_mask = HPT302_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 66, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt371 = { + .chip_name = "HPT371", + .chip_type = HPT371, + .udma_mask = HPT371_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 66, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt372n = { + .chip_name = "HPT372N", + .chip_type = HPT372N, + .udma_mask = HPT372_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 77, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt302n = { + .chip_name = "HPT302N", + .chip_type = HPT302N, + .udma_mask = HPT302_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 77, + .timings = &hpt37x_timings +}; + +static const struct hpt_info hpt371n = { + .chip_name = "HPT371N", + .chip_type = HPT371N, + .udma_mask = HPT371_ALLOW_ATA133_6 ? ATA_UDMA6 : ATA_UDMA5, + .dpll_clk = 77, + .timings = &hpt37x_timings +}; + +static bool check_in_drive_list(ide_drive_t *drive, const char **list) +{ + return match_string(list, -1, (char *)&drive->id[ATA_ID_PROD]) >= 0; +} + +static struct hpt_info *hpt3xx_get_info(struct device *dev) +{ + struct ide_host *host = dev_get_drvdata(dev); + struct hpt_info *info = (struct hpt_info *)host->host_priv; + + return dev == host->dev[1] ? info + 1 : info; +} + +/* + * The Marvell bridge chips used on the HighPoint SATA cards do not seem + * to support the UltraDMA modes 1, 2, and 3 as well as any MWDMA modes... + */ + +static u8 hpt3xx_udma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + u8 mask = hwif->ultra_mask; + + switch (info->chip_type) { + case HPT36x: + if (!HPT366_ALLOW_ATA66_4 || + check_in_drive_list(drive, bad_ata66_4)) + mask = ATA_UDMA3; + + if (!HPT366_ALLOW_ATA66_3 || + check_in_drive_list(drive, bad_ata66_3)) + mask = ATA_UDMA2; + break; + case HPT370: + if (!HPT370_ALLOW_ATA100_5 || + check_in_drive_list(drive, bad_ata100_5)) + mask = ATA_UDMA4; + break; + case HPT370A: + if (!HPT370_ALLOW_ATA100_5 || + check_in_drive_list(drive, bad_ata100_5)) + return ATA_UDMA4; + fallthrough; + case HPT372 : + case HPT372A: + case HPT372N: + case HPT374 : + if (ata_id_is_sata(drive->id)) + mask &= ~0x0e; + fallthrough; + default: + return mask; + } + + return check_in_drive_list(drive, bad_ata33) ? 0x00 : mask; +} + +static u8 hpt3xx_mdma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + + switch (info->chip_type) { + case HPT372 : + case HPT372A: + case HPT372N: + case HPT374 : + if (ata_id_is_sata(drive->id)) + return 0x00; + fallthrough; + default: + return 0x07; + } +} + +static u32 get_speed_setting(u8 speed, struct hpt_info *info) +{ + int i; + + /* + * Lookup the transfer mode table to get the index into + * the timing table. + * + * NOTE: For XFER_PIO_SLOW, PIO mode 0 timings will be used. + */ + for (i = 0; i < ARRAY_SIZE(xfer_speeds) - 1; i++) + if (xfer_speeds[i] == speed) + break; + + return info->timings->clock_table[info->clock][i]; +} + +static void hpt3xx_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + struct hpt_timings *t = info->timings; + u8 itr_addr = 0x40 + (drive->dn * 4); + u32 old_itr = 0; + const u8 speed = drive->dma_mode; + u32 new_itr = get_speed_setting(speed, info); + u32 itr_mask = speed < XFER_MW_DMA_0 ? t->pio_mask : + (speed < XFER_UDMA_0 ? t->dma_mask : + t->ultra_mask); + + pci_read_config_dword(dev, itr_addr, &old_itr); + new_itr = (old_itr & ~itr_mask) | (new_itr & itr_mask); + /* + * Disable on-chip PIO FIFO/buffer (and PIO MST mode as well) + * to avoid problems handling I/O errors later + */ + new_itr &= ~0xc0000000; + + pci_write_config_dword(dev, itr_addr, new_itr); +} + +static void hpt3xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + hpt3xx_set_mode(hwif, drive); +} + +static void hpt3xx_maskproc(ide_drive_t *drive, int mask) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + + if ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0) + return; + + if (info->chip_type >= HPT370) { + u8 scr1 = 0; + + pci_read_config_byte(dev, 0x5a, &scr1); + if (((scr1 & 0x10) >> 4) != mask) { + if (mask) + scr1 |= 0x10; + else + scr1 &= ~0x10; + pci_write_config_byte(dev, 0x5a, scr1); + } + } else if (mask) + disable_irq(hwif->irq); + else + enable_irq(hwif->irq); +} + +/* + * This is specific to the HPT366 UDMA chipset + * by HighPoint|Triones Technologies, Inc. + */ +static void hpt366_dma_lost_irq(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u8 mcr1 = 0, mcr3 = 0, scr1 = 0; + + pci_read_config_byte(dev, 0x50, &mcr1); + pci_read_config_byte(dev, 0x52, &mcr3); + pci_read_config_byte(dev, 0x5a, &scr1); + printk("%s: (%s) mcr1=0x%02x, mcr3=0x%02x, scr1=0x%02x\n", + drive->name, __func__, mcr1, mcr3, scr1); + if (scr1 & 0x10) + pci_write_config_byte(dev, 0x5a, scr1 & ~0x10); + ide_dma_lost_irq(drive); +} + +static void hpt370_clear_engine(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + + pci_write_config_byte(dev, hwif->select_data, 0x37); + udelay(10); +} + +static void hpt370_irq_timeout(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u16 bfifo = 0; + u8 dma_cmd; + + pci_read_config_word(dev, hwif->select_data + 2, &bfifo); + printk(KERN_DEBUG "%s: %d bytes in FIFO\n", drive->name, bfifo & 0x1ff); + + /* get DMA command mode */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + /* stop DMA */ + outb(dma_cmd & ~ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD); + hpt370_clear_engine(drive); +} + +static void hpt370_dma_start(ide_drive_t *drive) +{ +#ifdef HPT_RESET_STATE_ENGINE + hpt370_clear_engine(drive); +#endif + ide_dma_start(drive); +} + +static int hpt370_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + + if (dma_stat & ATA_DMA_ACTIVE) { + /* wait a little */ + udelay(20); + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + if (dma_stat & ATA_DMA_ACTIVE) + hpt370_irq_timeout(drive); + } + return ide_dma_end(drive); +} + +/* returns 1 if DMA IRQ issued, 0 otherwise */ +static int hpt374_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u16 bfifo = 0; + u8 dma_stat; + + pci_read_config_word(dev, hwif->select_data + 2, &bfifo); + if (bfifo & 0x1FF) { +// printk("%s: %d bytes in FIFO\n", drive->name, bfifo); + return 0; + } + + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + /* return 1 if INTR asserted */ + if (dma_stat & ATA_DMA_INTR) + return 1; + + return 0; +} + +static int hpt374_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 mcr = 0, mcr_addr = hwif->select_data; + u8 bwsr = 0, mask = hwif->channel ? 0x02 : 0x01; + + pci_read_config_byte(dev, 0x6a, &bwsr); + pci_read_config_byte(dev, mcr_addr, &mcr); + if (bwsr & mask) + pci_write_config_byte(dev, mcr_addr, mcr | 0x30); + return ide_dma_end(drive); +} + +/** + * hpt3xxn_set_clock - perform clock switching dance + * @hwif: hwif to switch + * @mode: clocking mode (0x21 for write, 0x23 otherwise) + * + * Switch the DPLL clock on the HPT3xxN devices. This is a right mess. + */ + +static void hpt3xxn_set_clock(ide_hwif_t *hwif, u8 mode) +{ + unsigned long base = hwif->extra_base; + u8 scr2 = inb(base + 0x6b); + + if ((scr2 & 0x7f) == mode) + return; + + /* Tristate the bus */ + outb(0x80, base + 0x63); + outb(0x80, base + 0x67); + + /* Switch clock and reset channels */ + outb(mode, base + 0x6b); + outb(0xc0, base + 0x69); + + /* + * Reset the state machines. + * NOTE: avoid accidentally enabling the disabled channels. + */ + outb(inb(base + 0x60) | 0x32, base + 0x60); + outb(inb(base + 0x64) | 0x32, base + 0x64); + + /* Complete reset */ + outb(0x00, base + 0x69); + + /* Reconnect channels to bus */ + outb(0x00, base + 0x63); + outb(0x00, base + 0x67); +} + +/** + * hpt3xxn_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 HPT3xxN. + * We need it because of the clock switching. + */ + +static void hpt3xxn_rw_disk(ide_drive_t *drive, struct request *rq) +{ + hpt3xxn_set_clock(drive->hwif, rq_data_dir(rq) ? 0x21 : 0x23); +} + +/** + * hpt37x_calibrate_dpll - calibrate the DPLL + * @dev: PCI device + * + * Perform a calibration cycle on the DPLL. + * Returns 1 if this succeeds + */ +static int hpt37x_calibrate_dpll(struct pci_dev *dev, u16 f_low, u16 f_high) +{ + u32 dpll = (f_high << 16) | f_low | 0x100; + u8 scr2; + int i; + + pci_write_config_dword(dev, 0x5c, dpll); + + /* Wait for oscillator ready */ + for(i = 0; i < 0x5000; ++i) { + udelay(50); + pci_read_config_byte(dev, 0x5b, &scr2); + if (scr2 & 0x80) + break; + } + /* See if it stays ready (we'll just bail out if it's not yet) */ + for(i = 0; i < 0x1000; ++i) { + pci_read_config_byte(dev, 0x5b, &scr2); + /* DPLL destabilized? */ + if(!(scr2 & 0x80)) + return 0; + } + /* Turn off tuning, we have the DPLL set */ + pci_read_config_dword (dev, 0x5c, &dpll); + pci_write_config_dword(dev, 0x5c, (dpll & ~0x100)); + return 1; +} + +static void hpt3xx_disable_fast_irq(struct pci_dev *dev, u8 mcr_addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct hpt_info *info = host->host_priv + (&dev->dev == host->dev[1]); + u8 chip_type = info->chip_type; + u8 new_mcr, old_mcr = 0; + + /* + * Disable the "fast interrupt" prediction. Don't hold off + * on interrupts. (== 0x01 despite what the docs say) + */ + pci_read_config_byte(dev, mcr_addr + 1, &old_mcr); + + if (chip_type >= HPT374) + new_mcr = old_mcr & ~0x07; + else if (chip_type >= HPT370) { + new_mcr = old_mcr; + new_mcr &= ~0x02; +#ifdef HPT_DELAY_INTERRUPT + new_mcr &= ~0x01; +#else + new_mcr |= 0x01; +#endif + } else /* HPT366 and HPT368 */ + new_mcr = old_mcr & ~0x80; + + if (new_mcr != old_mcr) + pci_write_config_byte(dev, mcr_addr + 1, new_mcr); +} + +static int init_chipset_hpt366(struct pci_dev *dev) +{ + unsigned long io_base = pci_resource_start(dev, 4); + struct hpt_info *info = hpt3xx_get_info(&dev->dev); + const char *name = DRV_NAME; + u8 pci_clk, dpll_clk = 0; /* PCI and DPLL clock in MHz */ + u8 chip_type; + enum ata_clock clock; + + chip_type = info->chip_type; + + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4)); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78); + pci_write_config_byte(dev, PCI_MIN_GNT, 0x08); + pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); + + /* + * First, try to estimate the PCI clock frequency... + */ + if (chip_type >= HPT370) { + u8 scr1 = 0; + u16 f_cnt = 0; + u32 temp = 0; + + /* Interrupt force enable. */ + pci_read_config_byte(dev, 0x5a, &scr1); + if (scr1 & 0x10) + pci_write_config_byte(dev, 0x5a, scr1 & ~0x10); + + /* + * HighPoint does this for HPT372A. + * NOTE: This register is only writeable via I/O space. + */ + if (chip_type == HPT372A) + outb(0x0e, io_base + 0x9c); + + /* + * 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); + + /* + * We'll have to read f_CNT value in order to determine + * the PCI clock frequency according to the following ratio: + * + * f_CNT = Fpci * 192 / Fdpll + * + * First try reading the register in which the HighPoint BIOS + * saves f_CNT value before reprogramming the DPLL from its + * default setting (which differs for the various chips). + * + * NOTE: This register is only accessible via I/O space; + * HPT374 BIOS only saves it for the function 0, so we have to + * always read it from there -- no need to check the result of + * pci_get_slot() for the function 0 as the whole device has + * been already "pinned" (via function 1) in init_setup_hpt374() + */ + if (chip_type == HPT374 && (PCI_FUNC(dev->devfn) & 1)) { + struct pci_dev *dev1 = pci_get_slot(dev->bus, + dev->devfn - 1); + unsigned long io_base = pci_resource_start(dev1, 4); + + temp = inl(io_base + 0x90); + pci_dev_put(dev1); + } else + temp = inl(io_base + 0x90); + + /* + * In case the signature check fails, we'll have to + * resort to reading the f_CNT register itself in hopes + * that nobody has touched the DPLL yet... + */ + if ((temp & 0xFFFFF000) != 0xABCDE000) { + int i; + + printk(KERN_WARNING "%s %s: no clock data saved by " + "BIOS\n", name, pci_name(dev)); + + /* Calculate the average value of f_CNT. */ + for (temp = i = 0; i < 128; i++) { + pci_read_config_word(dev, 0x78, &f_cnt); + temp += f_cnt & 0x1ff; + mdelay(1); + } + f_cnt = temp / 128; + } else + f_cnt = temp & 0x1ff; + + dpll_clk = info->dpll_clk; + pci_clk = (f_cnt * dpll_clk) / 192; + + /* Clamp PCI clock to bands. */ + if (pci_clk < 40) + pci_clk = 33; + else if(pci_clk < 45) + pci_clk = 40; + else if(pci_clk < 55) + pci_clk = 50; + else + pci_clk = 66; + + printk(KERN_INFO "%s %s: DPLL base: %d MHz, f_CNT: %d, " + "assuming %d MHz PCI\n", name, pci_name(dev), + dpll_clk, f_cnt, pci_clk); + } else { + u32 itr1 = 0; + + pci_read_config_dword(dev, 0x40, &itr1); + + /* Detect PCI clock by looking at cmd_high_time. */ + switch ((itr1 >> 8) & 0x0f) { + case 0x09: + pci_clk = 40; + break; + case 0x05: + pci_clk = 25; + break; + case 0x07: + default: + pci_clk = 33; + break; + } + } + + /* Let's assume we'll use PCI clock for the ATA clock... */ + switch (pci_clk) { + case 25: + clock = ATA_CLOCK_25MHZ; + break; + case 33: + default: + clock = ATA_CLOCK_33MHZ; + break; + case 40: + clock = ATA_CLOCK_40MHZ; + break; + case 50: + clock = ATA_CLOCK_50MHZ; + break; + case 66: + clock = ATA_CLOCK_66MHZ; + break; + } + + /* + * Only try the DPLL if we don't have a table for the PCI clock that + * we are running at for HPT370/A, always use it for anything newer... + * + * NOTE: Using the internal DPLL results in slow reads on 33 MHz PCI. + * We also don't like using the DPLL because this causes glitches + * on PRST-/SRST- when the state engine gets reset... + */ + if (chip_type >= HPT374 || info->timings->clock_table[clock] == NULL) { + u16 f_low, delta = pci_clk < 50 ? 2 : 4; + int adjust; + + /* + * Select 66 MHz DPLL clock only if UltraATA/133 mode is + * supported/enabled, use 50 MHz DPLL clock otherwise... + */ + if (info->udma_mask == ATA_UDMA6) { + dpll_clk = 66; + clock = ATA_CLOCK_66MHZ; + } else if (dpll_clk) { /* HPT36x chips don't have DPLL */ + dpll_clk = 50; + clock = ATA_CLOCK_50MHZ; + } + + if (info->timings->clock_table[clock] == NULL) { + printk(KERN_ERR "%s %s: unknown bus timing!\n", + name, pci_name(dev)); + return -EIO; + } + + /* Select the DPLL clock. */ + pci_write_config_byte(dev, 0x5b, 0x21); + + /* + * Adjust the DPLL based upon PCI clock, enable it, + * and wait for stabilization... + */ + f_low = (pci_clk * 48) / dpll_clk; + + for (adjust = 0; adjust < 8; adjust++) { + if(hpt37x_calibrate_dpll(dev, f_low, f_low + delta)) + break; + + /* + * See if it'll settle at a fractionally different clock + */ + if (adjust & 1) + f_low -= adjust >> 1; + else + f_low += adjust >> 1; + } + if (adjust == 8) { + printk(KERN_ERR "%s %s: DPLL did not stabilize!\n", + name, pci_name(dev)); + return -EIO; + } + + printk(KERN_INFO "%s %s: using %d MHz DPLL clock\n", + name, pci_name(dev), dpll_clk); + } else { + /* Mark the fact that we're not using the DPLL. */ + dpll_clk = 0; + + printk(KERN_INFO "%s %s: using %d MHz PCI clock\n", + name, pci_name(dev), pci_clk); + } + + /* Store the clock frequencies. */ + info->dpll_clk = dpll_clk; + info->pci_clk = pci_clk; + info->clock = clock; + + if (chip_type >= HPT370) { + u8 mcr1, mcr4; + + /* + * Reset the state engines. + * NOTE: Avoid accidentally enabling the disabled channels. + */ + pci_read_config_byte (dev, 0x50, &mcr1); + pci_read_config_byte (dev, 0x54, &mcr4); + pci_write_config_byte(dev, 0x50, (mcr1 | 0x32)); + pci_write_config_byte(dev, 0x54, (mcr4 | 0x32)); + udelay(100); + } + + /* + * On HPT371N, if ATA clock is 66 MHz we must set bit 2 in + * the MISC. register to stretch the UltraDMA Tss timing. + * NOTE: This register is only writeable via I/O space. + */ + if (chip_type == HPT371N && clock == ATA_CLOCK_66MHZ) + outb(inb(io_base + 0x9c) | 0x04, io_base + 0x9c); + + hpt3xx_disable_fast_irq(dev, 0x50); + hpt3xx_disable_fast_irq(dev, 0x54); + + return 0; +} + +static u8 hpt3xx_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + u8 chip_type = info->chip_type; + u8 scr1 = 0, ata66 = hwif->channel ? 0x01 : 0x02; + + /* + * 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 (chip_type == HPT374 && (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 + */ + u8 mcr_addr = hwif->select_data + 2; + u16 mcr; + + pci_read_config_word(dev, mcr_addr, &mcr); + pci_write_config_word(dev, mcr_addr, mcr | 0x8000); + /* Debounce, then read cable ID register */ + udelay(10); + pci_read_config_byte(dev, 0x5a, &scr1); + pci_write_config_word(dev, mcr_addr, mcr); + } else if (chip_type >= HPT370) { + /* + * HPT370/372 and 374 pcifn 0 + * - clear bit 0 of reg 0x5b to enable P/SCBLID as inputs + */ + u8 scr2 = 0; + + pci_read_config_byte(dev, 0x5b, &scr2); + pci_write_config_byte(dev, 0x5b, scr2 & ~1); + /* Debounce, then read cable ID register */ + udelay(10); + pci_read_config_byte(dev, 0x5a, &scr1); + pci_write_config_byte(dev, 0x5b, scr2); + } else + pci_read_config_byte(dev, 0x5a, &scr1); + + return (scr1 & ata66) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +static void init_hwif_hpt366(ide_hwif_t *hwif) +{ + struct hpt_info *info = hpt3xx_get_info(hwif->dev); + u8 chip_type = info->chip_type; + + /* Cache the channel's MISC. control registers' offset */ + hwif->select_data = hwif->channel ? 0x54 : 0x50; + + /* + * HPT3xxN chips have some complications: + * + * - on 33 MHz PCI we must clock switch + * - on 66 MHz PCI we must NOT use the PCI clock + */ + if (chip_type >= HPT372N && info->dpll_clk && info->pci_clk < 66) { + /* + * Clock is shared between the channels, + * so we'll have to serialize them... :-( + */ + hwif->host->host_flags |= IDE_HFLAG_SERIALIZE; + hwif->rw_disk = &hpt3xxn_rw_disk; + } +} + +static int init_dma_hpt366(ide_hwif_t *hwif, + const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long flags, base = ide_pci_dma_base(hwif, d); + u8 dma_old, dma_new, masterdma = 0, slavedma = 0; + + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) + return -1; + + dma_old = inb(base + 2); + + local_irq_save(flags); + + dma_new = dma_old; + pci_read_config_byte(dev, hwif->channel ? 0x4b : 0x43, &masterdma); + pci_read_config_byte(dev, hwif->channel ? 0x4f : 0x47, &slavedma); + + if (masterdma & 0x30) dma_new |= 0x20; + if ( slavedma & 0x30) dma_new |= 0x40; + if (dma_new != dma_old) + outb(dma_new, base + 2); + + local_irq_restore(flags); + + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", + hwif->name, base, base + 7); + + hwif->extra_base = base + (hwif->channel ? 8 : 16); + + if (ide_allocate_dma_engine(hwif)) + return -1; + + return 0; +} + +static void hpt374_init(struct pci_dev *dev, struct pci_dev *dev2) +{ + if (dev2->irq != dev->irq) { + /* FIXME: we need a core pci_set_interrupt() */ + dev2->irq = dev->irq; + printk(KERN_INFO DRV_NAME " %s: PCI config space interrupt " + "fixed\n", pci_name(dev2)); + } +} + +static void hpt371_init(struct pci_dev *dev) +{ + u8 mcr1 = 0; + + /* + * HPT371 chips physically have only one channel, the secondary one, + * but the primary channel registers do exist! Go figure... + * So, we manually disable the non-existing channel here + * (if the BIOS hasn't done this already). + */ + pci_read_config_byte(dev, 0x50, &mcr1); + if (mcr1 & 0x04) + pci_write_config_byte(dev, 0x50, mcr1 & ~0x04); +} + +static int hpt36x_init(struct pci_dev *dev, struct pci_dev *dev2) +{ + u8 mcr1 = 0, pin1 = 0, pin2 = 0; + + /* + * Now we'll have to force both channels enabled if + * at least one of them has been enabled by BIOS... + */ + pci_read_config_byte(dev, 0x50, &mcr1); + if (mcr1 & 0x30) + pci_write_config_byte(dev, 0x50, mcr1 | 0x30); + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2); + + if (pin1 != pin2 && dev->irq == dev2->irq) { + printk(KERN_INFO DRV_NAME " %s: onboard version of chipset, " + "pin1=%d pin2=%d\n", pci_name(dev), pin1, pin2); + return 1; + } + + return 0; +} + +#define IDE_HFLAGS_HPT3XX \ + (IDE_HFLAG_NO_ATAPI_DMA | \ + IDE_HFLAG_OFF_BOARD) + +static const struct ide_port_ops hpt3xx_port_ops = { + .set_pio_mode = hpt3xx_set_pio_mode, + .set_dma_mode = hpt3xx_set_mode, + .maskproc = hpt3xx_maskproc, + .mdma_filter = hpt3xx_mdma_filter, + .udma_filter = hpt3xx_udma_filter, + .cable_detect = hpt3xx_cable_detect, +}; + +static const struct ide_dma_ops hpt37x_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = hpt374_dma_end, + .dma_test_irq = hpt374_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_dma_ops hpt370_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = hpt370_dma_start, + .dma_end = hpt370_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_clear = hpt370_irq_timeout, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_dma_ops hpt36x_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = hpt366_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info hpt366_chipsets[] = { + { /* 0: HPT36x */ + .name = DRV_NAME, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + /* + * HPT36x chips have one channel per function and have + * both channel enable bits located differently and visible + * to both functions -- really stupid design decision... :-( + * Bit 4 is for the primary channel, bit 5 for the secondary. + */ + .enablebits = {{0x50,0x10,0x10}, {0x54,0x04,0x04}}, + .port_ops = &hpt3xx_port_ops, + .dma_ops = &hpt36x_dma_ops, + .host_flags = IDE_HFLAGS_HPT3XX | IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + }, + { /* 1: HPT3xx */ + .name = DRV_NAME, + .init_chipset = init_chipset_hpt366, + .init_hwif = init_hwif_hpt366, + .init_dma = init_dma_hpt366, + .enablebits = {{0x50,0x04,0x04}, {0x54,0x04,0x04}}, + .port_ops = &hpt3xx_port_ops, + .dma_ops = &hpt37x_dma_ops, + .host_flags = IDE_HFLAGS_HPT3XX, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + } +}; + +/** + * 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 hpt366_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + const struct hpt_info *info = NULL; + struct hpt_info *dyn_info; + struct pci_dev *dev2 = NULL; + struct ide_port_info d; + u8 idx = id->driver_data; + u8 rev = dev->revision; + int ret; + + if ((idx == 0 || idx == 4) && (PCI_FUNC(dev->devfn) & 1)) + return -ENODEV; + + switch (idx) { + case 0: + if (rev < 3) + info = &hpt36x; + else { + switch (min_t(u8, rev, 6)) { + case 3: info = &hpt370; break; + case 4: info = &hpt370a; break; + case 5: info = &hpt372; break; + case 6: info = &hpt372n; break; + } + idx++; + } + break; + case 1: + info = (rev > 1) ? &hpt372n : &hpt372a; + break; + case 2: + info = (rev > 1) ? &hpt302n : &hpt302; + break; + case 3: + hpt371_init(dev); + info = (rev > 1) ? &hpt371n : &hpt371; + break; + case 4: + info = &hpt374; + break; + case 5: + info = &hpt372n; + break; + } + + printk(KERN_INFO DRV_NAME ": %s chipset detected\n", info->chip_name); + + d = hpt366_chipsets[min_t(u8, idx, 1)]; + + d.udma_mask = info->udma_mask; + + /* fixup ->dma_ops for HPT370/HPT370A */ + if (info == &hpt370 || info == &hpt370a) + d.dma_ops = &hpt370_dma_ops; + + if (info == &hpt36x || info == &hpt374) + dev2 = pci_get_slot(dev->bus, dev->devfn + 1); + + dyn_info = kcalloc(dev2 ? 2 : 1, sizeof(*dyn_info), GFP_KERNEL); + if (dyn_info == NULL) { + printk(KERN_ERR "%s %s: out of memory!\n", + d.name, pci_name(dev)); + pci_dev_put(dev2); + return -ENOMEM; + } + + /* + * Copy everything from a static "template" structure + * to just allocated per-chip hpt_info structure. + */ + memcpy(dyn_info, info, sizeof(*dyn_info)); + + if (dev2) { + memcpy(dyn_info + 1, info, sizeof(*dyn_info)); + + if (info == &hpt374) + hpt374_init(dev, dev2); + else { + if (hpt36x_init(dev, dev2)) + d.host_flags &= ~IDE_HFLAG_NON_BOOTABLE; + } + + ret = ide_pci_init_two(dev, dev2, &d, dyn_info); + if (ret < 0) { + pci_dev_put(dev2); + kfree(dyn_info); + } + return ret; + } + + ret = ide_pci_init_one(dev, &d, dyn_info); + if (ret < 0) + kfree(dyn_info); + + return ret; +} + +static void hpt366_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct ide_info *info = host->host_priv; + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); + kfree(info); +} + +static const struct pci_device_id hpt366_pci_tbl[] = { + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), 0 }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), 1 }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), 2 }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), 3 }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT374), 4 }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), 5 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, hpt366_pci_tbl); + +static struct pci_driver hpt366_pci_driver = { + .name = "HPT366_IDE", + .id_table = hpt366_pci_tbl, + .probe = hpt366_init_one, + .remove = hpt366_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init hpt366_ide_init(void) +{ + return ide_pci_register_driver(&hpt366_pci_driver); +} + +static void __exit hpt366_ide_exit(void) +{ + pci_unregister_driver(&hpt366_pci_driver); +} + +module_init(hpt366_ide_init); +module_exit(hpt366_ide_exit); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for Highpoint HPT366 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c new file mode 100644 index 000000000..743bc3693 --- /dev/null +++ b/drivers/ide/ht6560b.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1995-2000 Linus Torvalds & author (see below) + */ + +/* + * 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 <j.e.van.grootheest@caiway.nl> + * + */ + +#define DRV_NAME "ht6560b" +#define HT6560B_VERSION "v0.08" + +#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/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 + +static inline u8 HT_CONFIG(ide_drive_t *drive) +{ + return ((unsigned long)ide_get_drivedata(drive) & 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 Select register 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. + */ +static inline u8 HT_TIMING(ide_drive_t *drive) +{ + return (unsigned long)ide_get_drivedata(drive) & 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_dev_select(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + 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); + + /* + * Need to enforce prefetch sometimes because otherwise + * it'll hang (hard). + */ + if (drive->media != ide_disk || + (drive->dev_flags & IDE_DFLAG_PRESENT) == 0) + select |= HT_PREFETCH_MODE; + + if (select != current_select || timing != current_timing) { + current_select = select; + current_timing = timing; + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + outb(select, HT_CONFIG_PORT); + /* + * Set timing for this drive: + */ + outb(timing, hwif->io_ports.device_addr); + (void)inb(hwif->io_ports.status_addr); +#ifdef DEBUG + printk("ht6560b: %s: select=%#x timing=%#x\n", + drive->name, select, timing); +#endif + } + local_irq_restore(flags); + + outb(drive->select | ATA_DEVICE_OBS, hwif->io_ports.device_addr); +} + +/* + * 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); /* Select register */ + (void)inb(0x1f7); /* Status register */ + + printk("ht6560b " HT6560B_VERSION + ": chipset detected and initialized" +#ifdef DEBUG + " with debug enabled" +#endif + "\n" + ); + return 1; +} + +static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio) +{ + int active_time, recovery_time; + int active_cycles, recovery_cycles; + int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50; + + if (pio) { + unsigned int cycle_time; + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + + cycle_time = ide_pio_cycle_time(drive, pio); + + /* + * Just like opti621.c we try to calculate the + * actual cycle time for recovery and activity + * according system bus speed. + */ + active_time = t->active; + recovery_time = cycle_time - active_time - t->setup; + /* + * 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 */ + } +} + +static DEFINE_SPINLOCK(ht6560b_lock); + +/* + * Enable/Disable so called prefetch mode + */ +static void ht_set_prefetch(ide_drive_t *drive, u8 state) +{ + unsigned long flags, config; + int t = HT_PREFETCH_MODE << 8; + + spin_lock_irqsave(&ht6560b_lock, flags); + + config = (unsigned long)ide_get_drivedata(drive); + + /* + * Prefetch mode and unmask irq seems to conflict + */ + if (state) { + config |= t; /* enable prefetch mode */ + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; + drive->dev_flags &= ~IDE_DFLAG_UNMASK; + } else { + config &= ~t; /* disable prefetch mode */ + drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK; + } + + ide_set_drivedata(drive, (void *)config); + + spin_unlock_irqrestore(&ht6560b_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis")); +#endif +} + +static void ht6560b_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long flags, config; + const u8 pio = drive->pio_mode - XFER_PIO_0; + 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(&ht6560b_lock, flags); + config = (unsigned long)ide_get_drivedata(drive); + config &= 0xff00; + config |= timing; + ide_set_drivedata(drive, (void *)config); + spin_unlock_irqrestore(&ht6560b_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing); +#endif +} + +static void __init ht6560b_init_dev(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + /* Setting default configurations for drives. */ + unsigned long t = (HT_CONFIG_DEFAULT << 8) | HT_TIMING_DEFAULT; + + if (hwif->channel) + t |= (HT_SECONDARY_IF << 8); + + ide_set_drivedata(drive, (void *)t); +} + +static bool probe_ht6560b; + +module_param_named(probe, probe_ht6560b, bool, 0); +MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); + +static const struct ide_tp_ops ht6560b_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ht6560b_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_port_ops ht6560b_port_ops = { + .init_dev = ht6560b_init_dev, + .set_pio_mode = ht6560b_set_pio_mode, +}; + +static const struct ide_port_info ht6560b_port_info __initconst = { + .name = DRV_NAME, + .chipset = ide_ht6560b, + .tp_ops = &ht6560b_tp_ops, + .port_ops = &ht6560b_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | /* is this needed? */ + IDE_HFLAG_NO_DMA | + IDE_HFLAG_ABUSE_PREFETCH, + .pio_mask = ATA_PIO4, +}; + +static int __init ht6560b_init(void) +{ + if (probe_ht6560b == 0) + return -ENODEV; + + if (!request_region(HT_CONFIG_PORT, 1, DRV_NAME)) { + printk(KERN_NOTICE "%s: HT_CONFIG_PORT not found\n", + __func__); + return -ENODEV; + } + + if (!try_to_init_ht6560b()) { + printk(KERN_NOTICE "%s: HBA not found\n", __func__); + goto release_region; + } + + return ide_legacy_device_add(&ht6560b_port_info, 0); + +release_region: + release_region(HT_CONFIG_PORT, 1); + return -ENODEV; +} + +module_init(ht6560b_init); + +MODULE_AUTHOR("See Local File"); +MODULE_DESCRIPTION("HT-6560B EIDE-controller support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c new file mode 100644 index 000000000..329c7e4bc --- /dev/null +++ b/drivers/ide/icside.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 1996-2004 Russell King. + * + * Please note that this platform does not support 32-bit IDE IO. + */ + +#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/ide.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/scatterlist.h> +#include <linux/io.h> + +#include <asm/dma.h> +#include <asm/ecard.h> + +#define DRV_NAME "icside" + +#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 sel; + unsigned int type; + struct ide_host *host; +}; + +#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 = drive->hwif; + struct expansion_card *ec = ECARD_DEV(hwif->dev); + struct icside_state *state = ecard_get_drvdata(ec); + 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); +} + +static const struct ide_port_ops icside_v6_no_dma_port_ops = { + .maskproc = icside_maskproc, +}; + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS +/* + * SG-DMA support. + * + * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers. + * There is only one DMA controller per card, which means that only + * one drive can be accessed at one time. NOTE! We do not enforce that + * here, but we rely on the main IDE driver spotting that both + * interfaces use the same IRQ, which should guarantee this. + */ + +/* + * 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 void icside_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long cycle_time = 0; + int use_dma_info = 0; + const u8 xfer_mode = drive->dma_mode; + + 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[ATA_ID_EIDE_DMA_TIME] > cycle_time) + cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME]; + + ide_set_drivedata(drive, (void *)cycle_time); + + printk(KERN_INFO "%s: %s selected (peak %luMB/s)\n", + drive->name, ide_xfer_verbose(xfer_mode), + 2000 / (cycle_time ? cycle_time : (unsigned long) -1)); +} + +static const struct ide_port_ops icside_v6_port_ops = { + .set_dma_mode = icside_set_dma_mode, + .maskproc = icside_maskproc, +}; + +static void icside_dma_host_set(ide_drive_t *drive, int on) +{ +} + +static int icside_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct expansion_card *ec = ECARD_DEV(hwif->dev); + + disable_dma(ec->dma); + + return get_dma_residue(ec->dma) != 0; +} + +static void icside_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct expansion_card *ec = ECARD_DEV(hwif->dev); + + /* We can not enable DMA on both channels simultaneously. */ + BUG_ON(dma_channel_active(ec->dma)); + enable_dma(ec->dma); +} + +static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + struct expansion_card *ec = ECARD_DEV(hwif->dev); + struct icside_state *state = ecard_get_drvdata(ec); + unsigned int dma_mode; + + if (cmd->tf_flags & IDE_TFLAG_WRITE) + dma_mode = DMA_MODE_WRITE; + else + dma_mode = DMA_MODE_READ; + + /* + * We can not enable DMA on both channels. + */ + BUG_ON(dma_channel_active(ec->dma)); + + /* + * Ensure that we have the right interrupt routed. + */ + icside_maskproc(drive, 0); + + /* + * Route the DMA signals to the correct interface. + */ + writeb(state->sel | hwif->channel, state->ioc_base); + + /* + * Select the correct timing for this drive. + */ + set_dma_speed(ec->dma, (unsigned long)ide_get_drivedata(drive)); + + /* + * Tell the DMA engine about the SG table and + * data direction. + */ + set_dma_sg(ec->dma, hwif->sg_table, cmd->sg_nents); + set_dma_mode(ec->dma, dma_mode); + + return 0; +} + +static int icside_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct expansion_card *ec = ECARD_DEV(hwif->dev); + struct icside_state *state = ecard_get_drvdata(ec); + + return readb(state->irq_port + + (hwif->channel ? + ICS_ARCIN_V6_INTRSTAT_2 : + ICS_ARCIN_V6_INTRSTAT_1)) & 1; +} + +static int icside_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + + return 0; +} + +static const struct ide_dma_ops icside_v6_dma_ops = { + .dma_host_set = icside_dma_host_set, + .dma_setup = icside_dma_setup, + .dma_start = icside_dma_start, + .dma_end = icside_dma_end, + .dma_test_irq = icside_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, +}; +#endif + +static int icside_dma_off_init(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + return -EOPNOTSUPP; +} + +static void icside_setup_ports(struct ide_hw *hw, void __iomem *base, + struct cardinfo *info, struct expansion_card *ec) +{ + unsigned long port = (unsigned long)base + info->dataoffset; + + hw->io_ports.data_addr = port; + hw->io_ports.error_addr = port + (1 << info->stepping); + hw->io_ports.nsect_addr = port + (2 << info->stepping); + hw->io_ports.lbal_addr = port + (3 << info->stepping); + hw->io_ports.lbam_addr = port + (4 << info->stepping); + hw->io_ports.lbah_addr = port + (5 << info->stepping); + hw->io_ports.device_addr = port + (6 << info->stepping); + hw->io_ports.status_addr = port + (7 << info->stepping); + hw->io_ports.ctl_addr = (unsigned long)base + info->ctrloffset; + + hw->irq = ec->irq; + hw->dev = &ec->dev; +} + +static const struct ide_port_info icside_v5_port_info = { + .host_flags = IDE_HFLAG_NO_DMA, + .chipset = ide_acorn, +}; + +static int icside_register_v5(struct icside_state *state, + struct expansion_card *ec) +{ + void __iomem *base; + struct ide_host *host; + struct ide_hw hw, *hws[] = { &hw }; + int ret; + + base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); + if (!base) + return -ENOMEM; + + state->irq_port = base; + + ec->irqaddr = base + ICS_ARCIN_V5_INTRSTAT; + ec->irqmask = 1; + + ecard_setirq(ec, &icside_ops_arcin_v5, state); + + /* + * Be on the safe side - disable interrupts + */ + icside_irqdisable_arcin_v5(ec, 0); + + icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec); + + host = ide_host_alloc(&icside_v5_port_info, hws, 1); + if (host == NULL) + return -ENODEV; + + state->host = host; + + ecard_set_drvdata(ec, state); + + ret = ide_host_register(host, &icside_v5_port_info, hws); + if (ret) + goto err_free; + + return 0; +err_free: + ide_host_free(host); + ecard_set_drvdata(ec, NULL); + return ret; +} + +static const struct ide_port_info icside_v6_port_info = { + .init_dma = icside_dma_off_init, + .port_ops = &icside_v6_no_dma_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_MMIO, + .mwdma_mask = ATA_MWDMA2, + .swdma_mask = ATA_SWDMA2, + .chipset = ide_acorn, +}; + +static int icside_register_v6(struct icside_state *state, + struct expansion_card *ec) +{ + void __iomem *ioc_base, *easi_base; + struct ide_host *host; + unsigned int sel = 0; + int ret; + struct ide_hw hw[2], *hws[] = { &hw[0], &hw[1] }; + struct ide_port_info d = icside_v6_port_info; + + ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); + if (!ioc_base) { + ret = -ENOMEM; + goto out; + } + + easi_base = ioc_base; + + if (ecard_resource_flags(ec, ECARD_RES_EASI)) { + easi_base = ecardm_iomap(ec, ECARD_RES_EASI, 0, 0); + if (!easi_base) { + ret = -ENOMEM; + goto out; + } + + /* + * Enable access to the EASI region. + */ + sel = 1 << 5; + } + + writeb(sel, ioc_base); + + ecard_setirq(ec, &icside_ops_arcin_v6, state); + + state->irq_port = easi_base; + state->ioc_base = ioc_base; + state->sel = sel; + + /* + * Be on the safe side - disable interrupts + */ + icside_irqdisable_arcin_v6(ec, 0); + + icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec); + icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec); + + host = ide_host_alloc(&d, hws, 2); + if (host == NULL) + return -ENODEV; + + state->host = host; + + ecard_set_drvdata(ec, state); + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS + if (ec->dma != NO_DMA && !request_dma(ec->dma, DRV_NAME)) { + d.init_dma = icside_dma_init; + d.port_ops = &icside_v6_port_ops; + d.dma_ops = &icside_v6_dma_ops; + } +#endif + + ret = ide_host_register(host, &d, hws); + if (ret) + goto err_free; + + return 0; +err_free: + ide_host_free(host); + if (d.dma_ops) + free_dma(ec->dma); + ecard_set_drvdata(ec, NULL); +out: + return ret; +} + +static int 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 = kzalloc(sizeof(struct icside_state), GFP_KERNEL); + if (!state) { + ret = -ENOMEM; + goto release; + } + + state->type = ICS_TYPE_NOTYPE; + + idmem = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0); + 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; + ecardm_iounmap(ec, 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) + goto out; + + kfree(state); + release: + ecard_release_resources(ec); + out: + return ret; +} + +static void 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); + + 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 = icside_remove, + .shutdown = icside_shutdown, + .id_table = icside_ids, + .drv = { + .name = "icside", + }, +}; + +static int __init icside_init(void) +{ + return ecard_register_driver(&icside_driver); +} + +static void __exit icside_exit(void) +{ + ecard_remove_driver(&icside_driver); +} + +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ICS IDE driver"); + +module_init(icside_init); +module_exit(icside_exit); diff --git a/drivers/ide/ide-4drives.c b/drivers/ide/ide-4drives.c new file mode 100644 index 000000000..06c6215e0 --- /dev/null +++ b/drivers/ide/ide-4drives.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ide.h> + +#define DRV_NAME "ide-4drives" + +static bool probe_4drives; + +module_param_named(probe, probe_4drives, bool, 0); +MODULE_PARM_DESC(probe, "probe for generic IDE chipset with 4 drives/port"); + +static void ide_4drives_init_dev(ide_drive_t *drive) +{ + if (drive->hwif->channel) + drive->select ^= 0x20; +} + +static const struct ide_port_ops ide_4drives_port_ops = { + .init_dev = ide_4drives_init_dev, +}; + +static const struct ide_port_info ide_4drives_port_info = { + .port_ops = &ide_4drives_port_ops, + .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA | + IDE_HFLAG_4DRIVES, + .chipset = ide_4drives, +}; + +static int __init ide_4drives_init(void) +{ + unsigned long base = 0x1f0, ctl = 0x3f6; + struct ide_hw hw, *hws[] = { &hw, &hw }; + + if (probe_4drives == 0) + return -ENODEV; + + if (!request_region(base, 8, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + DRV_NAME, base, base + 7); + return -EBUSY; + } + + if (!request_region(ctl, 1, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", + DRV_NAME, ctl); + release_region(base, 8); + return -EBUSY; + } + + memset(&hw, 0, sizeof(hw)); + + ide_std_init_ports(&hw, base, ctl); + hw.irq = 14; + + return ide_host_add(&ide_4drives_port_info, hws, 2, NULL); +} + +module_init(ide_4drives_init); + +MODULE_AUTHOR("Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("generic IDE chipset with 4 drives/port support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c new file mode 100644 index 000000000..05e18d658 --- /dev/null +++ b/drivers/ide/ide-acpi.c @@ -0,0 +1,622 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Provides ACPI support for IDE drives. + * + * Copyright (C) 2005 Intel Corp. + * Copyright (C) 2005 Randy Dunlap + * Copyright (C) 2006 SUSE Linux Products GmbH + * Copyright (C) 2006 Hannes Reinecke + */ + +#include <linux/acpi.h> +#include <linux/ata.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/ide.h> +#include <linux/pci.h> +#include <linux/dmi.h> +#include <linux/module.h> + +#define REGS_PER_GTF 7 + +struct GTM_buffer { + u32 PIO_speed0; + u32 DMA_speed0; + u32 PIO_speed1; + u32 DMA_speed1; + u32 GTM_flags; +}; + +struct ide_acpi_drive_link { + acpi_handle obj_handle; + u8 idbuff[512]; +}; + +struct ide_acpi_hwif_link { + ide_hwif_t *hwif; + acpi_handle obj_handle; + struct GTM_buffer gtm; + struct ide_acpi_drive_link master; + struct ide_acpi_drive_link slave; +}; + +#undef DEBUGGING +/* note: adds function name and KERN_DEBUG */ +#ifdef DEBUGGING +#define DEBPRINT(fmt, args...) \ + printk(KERN_DEBUG "%s: " fmt, __func__, ## args) +#else +#define DEBPRINT(fmt, args...) do {} while (0) +#endif /* DEBUGGING */ + +static bool ide_noacpi; +module_param_named(noacpi, ide_noacpi, bool, 0); +MODULE_PARM_DESC(noacpi, "disable IDE ACPI support"); + +static bool ide_acpigtf; +module_param_named(acpigtf, ide_acpigtf, bool, 0); +MODULE_PARM_DESC(acpigtf, "enable IDE ACPI _GTF support"); + +static bool ide_acpionboot; +module_param_named(acpionboot, ide_acpionboot, bool, 0); +MODULE_PARM_DESC(acpionboot, "call IDE ACPI methods on boot"); + +static bool ide_noacpi_psx; +static int no_acpi_psx(const struct dmi_system_id *id) +{ + ide_noacpi_psx = true; + printk(KERN_NOTICE"%s detected - disable ACPI _PSx.\n", id->ident); + return 0; +} + +static const struct dmi_system_id ide_acpi_dmi_table[] = { + /* Bug 9673. */ + /* We should check if this is because ACPI NVS isn't save/restored. */ + { + .callback = no_acpi_psx, + .ident = "HP nx9005", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies Ltd."), + DMI_MATCH(DMI_BIOS_VERSION, "KAM1.60") + }, + }, + + { } /* terminate list */ +}; + +int ide_acpi_init(void) +{ + dmi_check_system(ide_acpi_dmi_table); + return 0; +} + +bool ide_port_acpi(ide_hwif_t *hwif) +{ + return ide_noacpi == 0 && hwif->acpidata; +} + +static acpi_handle acpi_get_child(acpi_handle handle, u64 addr) +{ + struct acpi_device *adev; + + if (!handle || acpi_bus_get_device(handle, &adev)) + return NULL; + + adev = acpi_find_child_device(adev, addr, false); + return adev ? adev->handle : NULL; +} + +/** + * ide_get_dev_handle - finds acpi_handle and PCI device.function + * @dev: device to locate + * @handle: returned acpi_handle for @dev + * @pcidevfn: return PCI device.func for @dev + * + * Returns the ACPI object handle to the corresponding PCI device. + * + * Returns 0 on success, <0 on error. + */ +static int ide_get_dev_handle(struct device *dev, acpi_handle *handle, + u64 *pcidevfn) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned int bus, devnum, func; + u64 addr; + acpi_handle dev_handle; + acpi_status status; + struct acpi_device_info *dinfo = NULL; + int ret = -ENODEV; + + bus = pdev->bus->number; + devnum = PCI_SLOT(pdev->devfn); + func = PCI_FUNC(pdev->devfn); + /* ACPI _ADR encoding for PCI bus: */ + addr = (u64)(devnum << 16 | func); + + DEBPRINT("ENTER: pci %02x:%02x.%01x\n", bus, devnum, func); + + dev_handle = ACPI_HANDLE(dev); + if (!dev_handle) { + DEBPRINT("no acpi handle for device\n"); + goto err; + } + + status = acpi_get_object_info(dev_handle, &dinfo); + if (ACPI_FAILURE(status)) { + DEBPRINT("get_object_info for device failed\n"); + goto err; + } + if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && + dinfo->address == addr) { + *pcidevfn = addr; + *handle = dev_handle; + } else { + DEBPRINT("get_object_info for device has wrong " + " address: %llu, should be %u\n", + dinfo ? (unsigned long long)dinfo->address : -1ULL, + (unsigned int)addr); + goto err; + } + + DEBPRINT("for dev=0x%x.%x, addr=0x%llx, *handle=0x%p\n", + devnum, func, (unsigned long long)addr, *handle); + ret = 0; +err: + kfree(dinfo); + return ret; +} + +/** + * ide_acpi_hwif_get_handle - Get ACPI object handle for a given hwif + * @hwif: device to locate + * + * Retrieves the object handle for a given hwif. + * + * Returns handle on success, 0 on error. + */ +static acpi_handle ide_acpi_hwif_get_handle(ide_hwif_t *hwif) +{ + struct device *dev = hwif->gendev.parent; + acpi_handle dev_handle; + u64 pcidevfn; + acpi_handle chan_handle; + int err; + + DEBPRINT("ENTER: device %s\n", hwif->name); + + if (!dev) { + DEBPRINT("no PCI device for %s\n", hwif->name); + return NULL; + } + + err = ide_get_dev_handle(dev, &dev_handle, &pcidevfn); + if (err < 0) { + DEBPRINT("ide_get_dev_handle failed (%d)\n", err); + return NULL; + } + + /* get child objects of dev_handle == channel objects, + * + _their_ children == drive objects */ + /* channel is hwif->channel */ + chan_handle = acpi_get_child(dev_handle, hwif->channel); + DEBPRINT("chan adr=%d: handle=0x%p\n", + hwif->channel, chan_handle); + + return chan_handle; +} + +/** + * do_drive_get_GTF - get the drive bootup default taskfile settings + * @drive: the drive for which the taskfile settings should be retrieved + * @gtf_length: number of bytes of _GTF data returned at @gtf_address + * @gtf_address: buffer containing _GTF taskfile arrays + * + * The _GTF method has no input parameters. + * It returns a variable number of register set values (registers + * hex 1F1..1F7, taskfiles). + * The <variable number> is not known in advance, so have ACPI-CA + * allocate the buffer as needed and return it, then free it later. + * + * The returned @gtf_length and @gtf_address are only valid if the + * function return value is 0. + */ +static int do_drive_get_GTF(ide_drive_t *drive, + unsigned int *gtf_length, unsigned long *gtf_address, + unsigned long *obj_loc) +{ + acpi_status status; + struct acpi_buffer output; + union acpi_object *out_obj; + int err = -ENODEV; + + *gtf_length = 0; + *gtf_address = 0UL; + *obj_loc = 0UL; + + if (!drive->acpidata->obj_handle) { + DEBPRINT("No ACPI object found for %s\n", drive->name); + goto out; + } + + /* Setting up output buffer */ + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ + + /* _GTF has no input parameters */ + err = -EIO; + status = acpi_evaluate_object(drive->acpidata->obj_handle, "_GTF", + NULL, &output); + if (ACPI_FAILURE(status)) { + printk(KERN_DEBUG + "%s: Run _GTF error: status = 0x%x\n", + __func__, status); + goto out; + } + + if (!output.length || !output.pointer) { + DEBPRINT("Run _GTF: " + "length or ptr is NULL (0x%llx, 0x%p)\n", + (unsigned long long)output.length, + output.pointer); + goto out; + } + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + DEBPRINT("Run _GTF: error: " + "expected object type of ACPI_TYPE_BUFFER, " + "got 0x%x\n", out_obj->type); + err = -ENOENT; + kfree(output.pointer); + goto out; + } + + if (!out_obj->buffer.length || !out_obj->buffer.pointer || + out_obj->buffer.length % REGS_PER_GTF) { + printk(KERN_ERR + "%s: unexpected GTF length (%d) or addr (0x%p)\n", + __func__, out_obj->buffer.length, + out_obj->buffer.pointer); + err = -ENOENT; + kfree(output.pointer); + goto out; + } + + *gtf_length = out_obj->buffer.length; + *gtf_address = (unsigned long)out_obj->buffer.pointer; + *obj_loc = (unsigned long)out_obj; + DEBPRINT("returning gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n", + *gtf_length, *gtf_address, *obj_loc); + err = 0; +out: + return err; +} + +/** + * do_drive_set_taskfiles - write the drive taskfile settings from _GTF + * @drive: the drive to which the taskfile command should be sent + * @gtf_length: total number of bytes of _GTF taskfiles + * @gtf_address: location of _GTF taskfile arrays + * + * Write {gtf_address, length gtf_length} in groups of + * REGS_PER_GTF bytes. + */ +static int do_drive_set_taskfiles(ide_drive_t *drive, + unsigned int gtf_length, + unsigned long gtf_address) +{ + int rc = 0, err; + int gtf_count = gtf_length / REGS_PER_GTF; + int ix; + + DEBPRINT("total GTF bytes=%u (0x%x), gtf_count=%d, addr=0x%lx\n", + gtf_length, gtf_length, gtf_count, gtf_address); + + /* send all taskfile registers (0x1f1-0x1f7) *in*that*order* */ + for (ix = 0; ix < gtf_count; ix++) { + u8 *gtf = (u8 *)(gtf_address + ix * REGS_PER_GTF); + struct ide_cmd cmd; + + DEBPRINT("(0x1f1-1f7): " + "hex: %02x %02x %02x %02x %02x %02x %02x\n", + gtf[0], gtf[1], gtf[2], + gtf[3], gtf[4], gtf[5], gtf[6]); + + if (!ide_acpigtf) { + DEBPRINT("_GTF execution disabled\n"); + continue; + } + + /* convert GTF to taskfile */ + memset(&cmd, 0, sizeof(cmd)); + memcpy(&cmd.tf.feature, gtf, REGS_PER_GTF); + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + err = ide_no_data_taskfile(drive, &cmd); + if (err) { + printk(KERN_ERR "%s: ide_no_data_taskfile failed: %u\n", + __func__, err); + rc = err; + } + } + + return rc; +} + +/** + * ide_acpi_exec_tfs - get then write drive taskfile settings + * @drive: the drive for which the taskfile settings should be + * written. + * + * According to the ACPI spec this should be called after _STM + * has been evaluated for the interface. Some ACPI vendors interpret + * that as a hard requirement and modify the taskfile according + * to the Identify Drive information passed down with _STM. + * So one should really make sure to call this only after _STM has + * been executed. + */ +int ide_acpi_exec_tfs(ide_drive_t *drive) +{ + int ret; + unsigned int gtf_length; + unsigned long gtf_address; + unsigned long obj_loc; + + DEBPRINT("call get_GTF, drive=%s port=%d\n", drive->name, drive->dn); + + ret = do_drive_get_GTF(drive, >f_length, >f_address, &obj_loc); + if (ret < 0) { + DEBPRINT("get_GTF error (%d)\n", ret); + return ret; + } + + DEBPRINT("call set_taskfiles, drive=%s\n", drive->name); + + ret = do_drive_set_taskfiles(drive, gtf_length, gtf_address); + kfree((void *)obj_loc); + if (ret < 0) { + DEBPRINT("set_taskfiles error (%d)\n", ret); + } + + DEBPRINT("ret=%d\n", ret); + + return ret; +} + +/** + * ide_acpi_get_timing - get the channel (controller) timings + * @hwif: target IDE interface (channel) + * + * This function executes the _GTM ACPI method for the target channel. + * + */ +void ide_acpi_get_timing(ide_hwif_t *hwif) +{ + acpi_status status; + struct acpi_buffer output; + union acpi_object *out_obj; + + /* Setting up output buffer for _GTM */ + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ + + /* _GTM has no input parameters */ + status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_GTM", + NULL, &output); + + DEBPRINT("_GTM status: %d, outptr: 0x%p, outlen: 0x%llx\n", + status, output.pointer, + (unsigned long long)output.length); + + if (ACPI_FAILURE(status)) { + DEBPRINT("Run _GTM error: status = 0x%x\n", status); + return; + } + + if (!output.length || !output.pointer) { + DEBPRINT("Run _GTM: length or ptr is NULL (0x%llx, 0x%p)\n", + (unsigned long long)output.length, + output.pointer); + kfree(output.pointer); + return; + } + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + DEBPRINT("Run _GTM: error: " + "expected object type of ACPI_TYPE_BUFFER, " + "got 0x%x\n", out_obj->type); + kfree(output.pointer); + return; + } + + if (!out_obj->buffer.length || !out_obj->buffer.pointer || + out_obj->buffer.length != sizeof(struct GTM_buffer)) { + printk(KERN_ERR + "%s: unexpected _GTM length (0x%x)[should be 0x%zx] or " + "addr (0x%p)\n", + __func__, out_obj->buffer.length, + sizeof(struct GTM_buffer), out_obj->buffer.pointer); + kfree(output.pointer); + return; + } + + memcpy(&hwif->acpidata->gtm, out_obj->buffer.pointer, + sizeof(struct GTM_buffer)); + + DEBPRINT("_GTM info: ptr: 0x%p, len: 0x%x, exp.len: 0x%zx\n", + out_obj->buffer.pointer, out_obj->buffer.length, + sizeof(struct GTM_buffer)); + + DEBPRINT("_GTM fields: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + hwif->acpidata->gtm.PIO_speed0, + hwif->acpidata->gtm.DMA_speed0, + hwif->acpidata->gtm.PIO_speed1, + hwif->acpidata->gtm.DMA_speed1, + hwif->acpidata->gtm.GTM_flags); + + kfree(output.pointer); +} + +/** + * ide_acpi_push_timing - set the channel (controller) timings + * @hwif: target IDE interface (channel) + * + * This function executes the _STM ACPI method for the target channel. + * + * _STM requires Identify Drive data, which has to passed as an argument. + * Unfortunately drive->id is a mangled version which we can't readily + * use; hence we'll get the information afresh. + */ +void ide_acpi_push_timing(ide_hwif_t *hwif) +{ + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[3]; + struct ide_acpi_drive_link *master = &hwif->acpidata->master; + struct ide_acpi_drive_link *slave = &hwif->acpidata->slave; + + /* Give the GTM buffer + drive Identify data to the channel via the + * _STM method: */ + /* setup input parameters buffer for _STM */ + input.count = 3; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = sizeof(struct GTM_buffer); + in_params[0].buffer.pointer = (u8 *)&hwif->acpidata->gtm; + in_params[1].type = ACPI_TYPE_BUFFER; + in_params[1].buffer.length = ATA_ID_WORDS * 2; + in_params[1].buffer.pointer = (u8 *)&master->idbuff; + in_params[2].type = ACPI_TYPE_BUFFER; + in_params[2].buffer.length = ATA_ID_WORDS * 2; + in_params[2].buffer.pointer = (u8 *)&slave->idbuff; + /* Output buffer: _STM has no output */ + + status = acpi_evaluate_object(hwif->acpidata->obj_handle, "_STM", + &input, NULL); + + if (ACPI_FAILURE(status)) { + DEBPRINT("Run _STM error: status = 0x%x\n", status); + } + DEBPRINT("_STM status: %d\n", status); +} + +/** + * ide_acpi_set_state - set the channel power state + * @hwif: target IDE interface + * @on: state, on/off + * + * This function executes the _PS0/_PS3 ACPI method to set the power state. + * ACPI spec requires _PS0 when IDE power on and _PS3 when power off + */ +void ide_acpi_set_state(ide_hwif_t *hwif, int on) +{ + ide_drive_t *drive; + int i; + + if (ide_noacpi_psx) + return; + + DEBPRINT("ENTER:\n"); + + /* channel first and then drives for power on and verse versa for power off */ + if (on) + acpi_bus_set_power(hwif->acpidata->obj_handle, ACPI_STATE_D0); + + ide_port_for_each_present_dev(i, drive, hwif) { + if (drive->acpidata->obj_handle) + acpi_bus_set_power(drive->acpidata->obj_handle, + on ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD); + } + + if (!on) + acpi_bus_set_power(hwif->acpidata->obj_handle, + ACPI_STATE_D3_COLD); +} + +/** + * ide_acpi_init_port - initialize the ACPI link for an IDE interface + * @hwif: target IDE interface (channel) + * + * The ACPI spec is not quite clear when the drive identify buffer + * should be obtained. Calling IDENTIFY DEVICE during shutdown + * is not the best of ideas as the drive might already being put to + * sleep. And obviously we can't call it during resume. + * So we get the information during startup; but this means that + * any changes during run-time will be lost after resume. + */ +void ide_acpi_init_port(ide_hwif_t *hwif) +{ + hwif->acpidata = kzalloc(sizeof(struct ide_acpi_hwif_link), GFP_KERNEL); + if (!hwif->acpidata) + return; + + hwif->acpidata->obj_handle = ide_acpi_hwif_get_handle(hwif); + if (!hwif->acpidata->obj_handle) { + DEBPRINT("no ACPI object for %s found\n", hwif->name); + kfree(hwif->acpidata); + hwif->acpidata = NULL; + } +} + +void ide_acpi_port_init_devices(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i, err; + + if (hwif->acpidata == NULL) + return; + + /* + * The ACPI spec mandates that we send information + * for both drives, regardless whether they are connected + * or not. + */ + hwif->devices[0]->acpidata = &hwif->acpidata->master; + hwif->devices[1]->acpidata = &hwif->acpidata->slave; + + /* get _ADR info for each device */ + ide_port_for_each_present_dev(i, drive, hwif) { + acpi_handle dev_handle; + + DEBPRINT("ENTER: %s at channel#: %d port#: %d\n", + drive->name, hwif->channel, drive->dn & 1); + + /* TBD: could also check ACPI object VALID bits */ + dev_handle = acpi_get_child(hwif->acpidata->obj_handle, + drive->dn & 1); + + DEBPRINT("drive %s handle 0x%p\n", drive->name, dev_handle); + + drive->acpidata->obj_handle = dev_handle; + } + + /* send IDENTIFY for each device */ + ide_port_for_each_present_dev(i, drive, hwif) { + err = taskfile_lib_get_identify(drive, drive->acpidata->idbuff); + if (err) + DEBPRINT("identify device %s failed (%d)\n", + drive->name, err); + } + + if (ide_noacpi || ide_acpionboot == 0) { + DEBPRINT("ACPI methods disabled on boot\n"); + return; + } + + /* ACPI _PS0 before _STM */ + ide_acpi_set_state(hwif, 1); + /* + * ACPI requires us to call _STM on startup + */ + ide_acpi_get_timing(hwif); + ide_acpi_push_timing(hwif); + + ide_port_for_each_present_dev(i, drive, hwif) { + ide_acpi_exec_tfs(drive); + } +} diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c new file mode 100644 index 000000000..013ad33fb --- /dev/null +++ b/drivers/ide/ide-atapi.c @@ -0,0 +1,756 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ATAPI support. + */ + +#include <linux/kernel.h> +#include <linux/cdrom.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/ide.h> +#include <linux/scatterlist.h> +#include <linux/gfp.h> + +#include <scsi/scsi.h> + +#define DRV_NAME "ide-atapi" +#define PFX DRV_NAME ": " + +#ifdef DEBUG +#define debug_log(fmt, args...) \ + printk(KERN_INFO "ide: " fmt, ## args) +#else +#define debug_log(fmt, args...) do {} while (0) +#endif + +#define ATAPI_MIN_CDB_BYTES 12 + +static inline int dev_is_idecd(ide_drive_t *drive) +{ + return drive->media == ide_cdrom || drive->media == ide_optical; +} + +/* + * Check whether we can support a device, + * based on the ATAPI IDENTIFY command results. + */ +int ide_check_atapi_device(ide_drive_t *drive, const char *s) +{ + u16 *id = drive->id; + u8 gcw[2], protocol, device_type, removable, drq_type, packet_size; + + *((u16 *)&gcw) = id[ATA_ID_CONFIG]; + + protocol = (gcw[1] & 0xC0) >> 6; + device_type = gcw[1] & 0x1F; + removable = (gcw[0] & 0x80) >> 7; + drq_type = (gcw[0] & 0x60) >> 5; + packet_size = gcw[0] & 0x03; + +#ifdef CONFIG_PPC + /* kludge for Apple PowerBook internal zip */ + if (drive->media == ide_floppy && device_type == 5 && + !strstr((char *)&id[ATA_ID_PROD], "CD-ROM") && + strstr((char *)&id[ATA_ID_PROD], "ZIP")) + device_type = 0; +#endif + + if (protocol != 2) + printk(KERN_ERR "%s: %s: protocol (0x%02x) is not ATAPI\n", + s, drive->name, protocol); + else if ((drive->media == ide_floppy && device_type != 0) || + (drive->media == ide_tape && device_type != 1)) + printk(KERN_ERR "%s: %s: invalid device type (0x%02x)\n", + s, drive->name, device_type); + else if (removable == 0) + printk(KERN_ERR "%s: %s: the removable flag is not set\n", + s, drive->name); + else if (drive->media == ide_floppy && drq_type == 3) + printk(KERN_ERR "%s: %s: sorry, DRQ type (0x%02x) not " + "supported\n", s, drive->name, drq_type); + else if (packet_size != 0) + printk(KERN_ERR "%s: %s: packet size (0x%02x) is not 12 " + "bytes\n", s, drive->name, packet_size); + else + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(ide_check_atapi_device); + +void ide_init_pc(struct ide_atapi_pc *pc) +{ + memset(pc, 0, sizeof(*pc)); +} +EXPORT_SYMBOL_GPL(ide_init_pc); + +/* + * Add a special packet command request to the tail of the request queue, + * and wait for it to be serviced. + */ +int ide_queue_pc_tail(ide_drive_t *drive, struct gendisk *disk, + struct ide_atapi_pc *pc, void *buf, unsigned int bufflen) +{ + struct request *rq; + int error; + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_MISC; + ide_req(rq)->special = pc; + + if (buf && bufflen) { + error = blk_rq_map_kern(drive->queue, rq, buf, bufflen, + GFP_NOIO); + if (error) + goto put_req; + } + + memcpy(scsi_req(rq)->cmd, pc->c, 12); + if (drive->media == ide_tape) + scsi_req(rq)->cmd[13] = REQ_IDETAPE_PC1; + blk_execute_rq(drive->queue, disk, rq, 0); + error = scsi_req(rq)->result ? -EIO : 0; +put_req: + blk_put_request(rq); + return error; +} +EXPORT_SYMBOL_GPL(ide_queue_pc_tail); + +int ide_do_test_unit_ready(ide_drive_t *drive, struct gendisk *disk) +{ + struct ide_atapi_pc pc; + + ide_init_pc(&pc); + pc.c[0] = TEST_UNIT_READY; + + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); +} +EXPORT_SYMBOL_GPL(ide_do_test_unit_ready); + +int ide_do_start_stop(ide_drive_t *drive, struct gendisk *disk, int start) +{ + struct ide_atapi_pc pc; + + ide_init_pc(&pc); + pc.c[0] = START_STOP; + pc.c[4] = start; + + if (drive->media == ide_tape) + pc.flags |= PC_FLAG_WAIT_FOR_DSC; + + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); +} +EXPORT_SYMBOL_GPL(ide_do_start_stop); + +int ide_set_media_lock(ide_drive_t *drive, struct gendisk *disk, int on) +{ + struct ide_atapi_pc pc; + + if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) == 0) + return 0; + + ide_init_pc(&pc); + pc.c[0] = ALLOW_MEDIUM_REMOVAL; + pc.c[4] = on; + + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); +} +EXPORT_SYMBOL_GPL(ide_set_media_lock); + +void ide_create_request_sense_cmd(ide_drive_t *drive, struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = REQUEST_SENSE; + if (drive->media == ide_floppy) { + pc->c[4] = 255; + pc->req_xfer = 18; + } else { + pc->c[4] = 20; + pc->req_xfer = 20; + } +} +EXPORT_SYMBOL_GPL(ide_create_request_sense_cmd); + +void ide_prep_sense(ide_drive_t *drive, struct request *rq) +{ + struct request_sense *sense = &drive->sense_data; + struct request *sense_rq; + struct scsi_request *req; + unsigned int cmd_len, sense_len; + int err; + + switch (drive->media) { + case ide_floppy: + cmd_len = 255; + sense_len = 18; + break; + case ide_tape: + cmd_len = 20; + sense_len = 20; + break; + default: + cmd_len = 18; + sense_len = 18; + } + + BUG_ON(sense_len > sizeof(*sense)); + + if (ata_sense_request(rq) || drive->sense_rq_armed) + return; + + sense_rq = drive->sense_rq; + if (!sense_rq) { + sense_rq = blk_mq_alloc_request(drive->queue, REQ_OP_DRV_IN, + BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT); + drive->sense_rq = sense_rq; + } + req = scsi_req(sense_rq); + + memset(sense, 0, sizeof(*sense)); + + scsi_req_init(req); + + err = blk_rq_map_kern(drive->queue, sense_rq, sense, sense_len, + GFP_NOIO); + if (unlikely(err)) { + if (printk_ratelimit()) + printk(KERN_WARNING PFX "%s: failed to map sense " + "buffer\n", drive->name); + blk_mq_free_request(sense_rq); + drive->sense_rq = NULL; + return; + } + + sense_rq->rq_disk = rq->rq_disk; + sense_rq->cmd_flags = REQ_OP_DRV_IN; + ide_req(sense_rq)->type = ATA_PRIV_SENSE; + + req->cmd[0] = GPCMD_REQUEST_SENSE; + req->cmd[4] = cmd_len; + if (drive->media == ide_tape) + req->cmd[13] = REQ_IDETAPE_PC1; + + drive->sense_rq_armed = true; +} +EXPORT_SYMBOL_GPL(ide_prep_sense); + +int ide_queue_sense_rq(ide_drive_t *drive, void *special) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *sense_rq; + unsigned long flags; + + spin_lock_irqsave(&hwif->lock, flags); + + /* deferred failure from ide_prep_sense() */ + if (!drive->sense_rq_armed) { + printk(KERN_WARNING PFX "%s: error queuing a sense request\n", + drive->name); + spin_unlock_irqrestore(&hwif->lock, flags); + return -ENOMEM; + } + + sense_rq = drive->sense_rq; + ide_req(sense_rq)->special = special; + drive->sense_rq_armed = false; + + drive->hwif->rq = NULL; + + ide_insert_request_head(drive, sense_rq); + spin_unlock_irqrestore(&hwif->lock, flags); + return 0; +} +EXPORT_SYMBOL_GPL(ide_queue_sense_rq); + +/* + * Called when an error was detected during the last packet command. + * We queue a request sense packet command at the head of the request + * queue. + */ +void ide_retry_pc(ide_drive_t *drive) +{ + struct request *failed_rq = drive->hwif->rq; + struct request *sense_rq = drive->sense_rq; + struct ide_atapi_pc *pc = &drive->request_sense_pc; + + (void)ide_read_error(drive); + + /* init pc from sense_rq */ + ide_init_pc(pc); + memcpy(pc->c, scsi_req(sense_rq)->cmd, 12); + + if (drive->media == ide_tape) + drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC; + + /* + * Push back the failed request and put request sense on top + * of it. The failed command will be retried after sense data + * is acquired. + */ + drive->hwif->rq = NULL; + ide_requeue_and_plug(drive, failed_rq); + if (ide_queue_sense_rq(drive, pc)) + ide_complete_rq(drive, BLK_STS_IOERR, blk_rq_bytes(failed_rq)); +} +EXPORT_SYMBOL_GPL(ide_retry_pc); + +int ide_cd_expiry(ide_drive_t *drive) +{ + struct request *rq = drive->hwif->rq; + unsigned long wait = 0; + + debug_log("%s: scsi_req(rq)->cmd[0]: 0x%x\n", __func__, scsi_req(rq)->cmd[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 (scsi_req(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->rq_flags & RQF_QUIET)) + printk(KERN_INFO PFX "cmd 0x%x timed out\n", + scsi_req(rq)->cmd[0]); + wait = 0; + break; + } + return wait; +} +EXPORT_SYMBOL_GPL(ide_cd_expiry); + +int ide_cd_get_xferlen(struct request *rq) +{ + switch (req_op(rq)) { + default: + return 32768; + case REQ_OP_SCSI_IN: + case REQ_OP_SCSI_OUT: + return blk_rq_bytes(rq); + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + switch (ide_req(rq)->type) { + case ATA_PRIV_PC: + case ATA_PRIV_SENSE: + return blk_rq_bytes(rq); + default: + return 0; + } + } +} +EXPORT_SYMBOL_GPL(ide_cd_get_xferlen); + +void ide_read_bcount_and_ireason(ide_drive_t *drive, u16 *bcount, u8 *ireason) +{ + struct ide_taskfile tf; + + drive->hwif->tp_ops->tf_read(drive, &tf, IDE_VALID_NSECT | + IDE_VALID_LBAM | IDE_VALID_LBAH); + + *bcount = (tf.lbah << 8) | tf.lbam; + *ireason = tf.nsect & 3; +} +EXPORT_SYMBOL_GPL(ide_read_bcount_and_ireason); + +/* + * Check the contents of the interrupt reason register and attempt to recover if + * there are problems. + * + * Returns: + * - 0 if everything's ok + * - 1 if the request has to be terminated. + */ +int ide_check_ireason(ide_drive_t *drive, struct request *rq, int len, + int ireason, int rw) +{ + ide_hwif_t *hwif = drive->hwif; + + debug_log("ireason: 0x%x, rw: 0x%x\n", ireason, rw); + + if (ireason == (!rw << 1)) + return 0; + else if (ireason == (rw << 1)) { + printk(KERN_ERR PFX "%s: %s: wrong transfer direction!\n", + drive->name, __func__); + + if (dev_is_idecd(drive)) + ide_pad_transfer(drive, rw, len); + } else if (!rw && ireason == ATAPI_COD) { + if (dev_is_idecd(drive)) { + /* + * Some drives (ASUS) seem to tell us that status info + * is available. Just get it and ignore. + */ + (void)hwif->tp_ops->read_status(hwif); + return 0; + } + } else { + if (ireason & ATAPI_COD) + printk(KERN_ERR PFX "%s: CoD != 0 in %s\n", drive->name, + __func__); + + /* drive wants a command packet, or invalid ireason... */ + printk(KERN_ERR PFX "%s: %s: bad interrupt reason 0x%02x\n", + drive->name, __func__, ireason); + } + + if (dev_is_idecd(drive) && ata_pc_request(rq)) + rq->rq_flags |= RQF_FAILED; + + return 1; +} +EXPORT_SYMBOL_GPL(ide_check_ireason); + +/* + * This 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. + */ +static ide_startstop_t ide_pc_intr(ide_drive_t *drive) +{ + struct ide_atapi_pc *pc = drive->pc; + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; + struct request *rq = hwif->rq; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + unsigned int timeout, done; + u16 bcount; + u8 stat, ireason, dsc = 0; + u8 write = !!(pc->flags & PC_FLAG_WRITING); + + debug_log("Enter %s - interrupt handler\n", __func__); + + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; + + /* Clear the interrupt */ + stat = tp_ops->read_status(hwif); + + if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { + int rc; + + drive->waiting_for_dma = 0; + rc = hwif->dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + + if (rc || (drive->media == ide_tape && (stat & ATA_ERR))) { + if (drive->media == ide_floppy) + printk(KERN_ERR PFX "%s: DMA %s error\n", + drive->name, rq_data_dir(pc->rq) + ? "write" : "read"); + pc->flags |= PC_FLAG_DMA_ERROR; + } else + scsi_req(rq)->resid_len = 0; + debug_log("%s: DMA finished\n", drive->name); + } + + /* No more interrupts */ + if ((stat & ATA_DRQ) == 0) { + int uptodate; + blk_status_t error; + + debug_log("Packet command completed, %d bytes transferred\n", + blk_rq_bytes(rq)); + + pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; + + local_irq_enable_in_hardirq(); + + if (drive->media == ide_tape && + (stat & ATA_ERR) && scsi_req(rq)->cmd[0] == REQUEST_SENSE) + stat &= ~ATA_ERR; + + if ((stat & ATA_ERR) || (pc->flags & PC_FLAG_DMA_ERROR)) { + /* Error detected */ + debug_log("%s: I/O error\n", drive->name); + + if (drive->media != ide_tape) + scsi_req(pc->rq)->result++; + + if (scsi_req(rq)->cmd[0] == REQUEST_SENSE) { + printk(KERN_ERR PFX "%s: I/O error in request " + "sense command\n", drive->name); + return ide_do_reset(drive); + } + + debug_log("[cmd %x]: check condition\n", scsi_req(rq)->cmd[0]); + + /* Retry operation */ + ide_retry_pc(drive); + + /* queued, but not started */ + return ide_stopped; + } + pc->error = 0; + + if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) && (stat & ATA_DSC) == 0) + dsc = 1; + + /* + * ->pc_callback() might change rq->data_len for + * residual count, cache total length. + */ + done = blk_rq_bytes(rq); + + /* Command finished - Call the callback function */ + uptodate = drive->pc_callback(drive, dsc); + + if (uptodate == 0) + drive->failed_pc = NULL; + + if (ata_misc_request(rq)) { + scsi_req(rq)->result = 0; + error = BLK_STS_OK; + } else { + + if (blk_rq_is_passthrough(rq) && uptodate <= 0) { + if (scsi_req(rq)->result == 0) + scsi_req(rq)->result = -EIO; + } + + error = uptodate ? BLK_STS_OK : BLK_STS_IOERR; + } + + ide_complete_rq(drive, error, blk_rq_bytes(rq)); + return ide_stopped; + } + + if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) { + pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS; + printk(KERN_ERR PFX "%s: The device wants to issue more " + "interrupts in DMA mode\n", drive->name); + ide_dma_off(drive); + return ide_do_reset(drive); + } + + /* Get the number of bytes to transfer on this interrupt. */ + ide_read_bcount_and_ireason(drive, &bcount, &ireason); + + if (ide_check_ireason(drive, rq, bcount, ireason, write)) + return ide_do_reset(drive); + + done = min_t(unsigned int, bcount, cmd->nleft); + ide_pio_bytes(drive, cmd, write, done); + + /* Update transferred byte count */ + scsi_req(rq)->resid_len -= done; + + bcount -= done; + + if (bcount) + ide_pad_transfer(drive, write, bcount); + + debug_log("[cmd %x] transferred %d bytes, padded %d bytes, resid: %u\n", + scsi_req(rq)->cmd[0], done, bcount, scsi_req(rq)->resid_len); + + /* And set the interrupt handler again */ + ide_set_handler(drive, ide_pc_intr, timeout); + return ide_started; +} + +static void ide_init_packet_cmd(struct ide_cmd *cmd, u8 valid_tf, + u16 bcount, u8 dma) +{ + cmd->protocol = dma ? ATAPI_PROT_DMA : ATAPI_PROT_PIO; + cmd->valid.out.tf = IDE_VALID_LBAH | IDE_VALID_LBAM | + IDE_VALID_FEATURE | valid_tf; + cmd->tf.command = ATA_CMD_PACKET; + cmd->tf.feature = dma; /* Use PIO/DMA */ + cmd->tf.lbam = bcount & 0xff; + cmd->tf.lbah = (bcount >> 8) & 0xff; +} + +static u8 ide_read_ireason(ide_drive_t *drive) +{ + struct ide_taskfile tf; + + drive->hwif->tp_ops->tf_read(drive, &tf, IDE_VALID_NSECT); + + return tf.nsect & 3; +} + +static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason) +{ + int retries = 100; + + while (retries-- && ((ireason & ATAPI_COD) == 0 || + (ireason & ATAPI_IO))) { + printk(KERN_ERR PFX "%s: (IO,CoD != (0,1) while issuing " + "a packet command, retrying\n", drive->name); + udelay(100); + ireason = ide_read_ireason(drive); + if (retries == 0) { + printk(KERN_ERR PFX "%s: (IO,CoD != (0,1) while issuing" + " a packet command, ignoring\n", + drive->name); + ireason |= ATAPI_COD; + ireason &= ~ATAPI_IO; + } + } + + return ireason; +} + +static int ide_delayed_transfer_pc(ide_drive_t *drive) +{ + /* Send the actual packet */ + drive->hwif->tp_ops->output_data(drive, NULL, drive->pc->c, 12); + + /* Timeout for the packet command */ + return WAIT_FLOPPY_CMD; +} + +static ide_startstop_t ide_transfer_pc(ide_drive_t *drive) +{ + struct ide_atapi_pc *pc; + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->rq; + ide_expiry_t *expiry; + unsigned int timeout; + int cmd_len; + ide_startstop_t startstop; + u8 ireason; + + if (ide_wait_stat(&startstop, drive, ATA_DRQ, ATA_BUSY, WAIT_READY)) { + printk(KERN_ERR PFX "%s: Strange, packet command initiated yet " + "DRQ isn't asserted\n", drive->name); + return startstop; + } + + if (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT) { + if (drive->dma) + drive->waiting_for_dma = 1; + } + + if (dev_is_idecd(drive)) { + /* ATAPI commands get padded out to 12 bytes minimum */ + cmd_len = COMMAND_SIZE(scsi_req(rq)->cmd[0]); + if (cmd_len < ATAPI_MIN_CDB_BYTES) + cmd_len = ATAPI_MIN_CDB_BYTES; + + timeout = rq->timeout; + expiry = ide_cd_expiry; + } else { + pc = drive->pc; + + cmd_len = ATAPI_MIN_CDB_BYTES; + + /* + * If necessary schedule the packet transfer to occur 'timeout' + * milliseconds later in ide_delayed_transfer_pc() after the + * device says it's ready for a packet. + */ + if (drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) { + timeout = drive->pc_delay; + expiry = &ide_delayed_transfer_pc; + } else { + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; + expiry = NULL; + } + + ireason = ide_read_ireason(drive); + if (drive->media == ide_tape) + ireason = ide_wait_ireason(drive, ireason); + + if ((ireason & ATAPI_COD) == 0 || (ireason & ATAPI_IO)) { + printk(KERN_ERR PFX "%s: (IO,CoD) != (0,1) while " + "issuing a packet command\n", drive->name); + + return ide_do_reset(drive); + } + } + + hwif->expiry = expiry; + + /* Set the interrupt routine */ + ide_set_handler(drive, + (dev_is_idecd(drive) ? drive->irq_handler + : ide_pc_intr), + timeout); + + /* Send the actual packet */ + if ((drive->atapi_flags & IDE_AFLAG_ZIP_DRIVE) == 0) + hwif->tp_ops->output_data(drive, NULL, scsi_req(rq)->cmd, cmd_len); + + /* Begin DMA, if necessary */ + if (dev_is_idecd(drive)) { + if (drive->dma) + hwif->dma_ops->dma_start(drive); + } else { + if (pc->flags & PC_FLAG_DMA_OK) { + pc->flags |= PC_FLAG_DMA_IN_PROGRESS; + hwif->dma_ops->dma_start(drive); + } + } + + return ide_started; +} + +ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_cmd *cmd) +{ + struct ide_atapi_pc *pc; + ide_hwif_t *hwif = drive->hwif; + ide_expiry_t *expiry = NULL; + struct request *rq = hwif->rq; + unsigned int timeout, bytes; + u16 bcount; + u8 valid_tf; + u8 drq_int = !!(drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT); + + if (dev_is_idecd(drive)) { + valid_tf = IDE_VALID_NSECT | IDE_VALID_LBAL; + bcount = ide_cd_get_xferlen(rq); + expiry = ide_cd_expiry; + timeout = ATAPI_WAIT_PC; + + if (drive->dma) + drive->dma = !ide_dma_prepare(drive, cmd); + } else { + pc = drive->pc; + + valid_tf = IDE_VALID_DEVICE; + bytes = blk_rq_bytes(rq); + bcount = ((drive->media == ide_tape) ? bytes + : min_t(unsigned int, + bytes, 63 * 1024)); + + /* We haven't transferred any data yet */ + scsi_req(rq)->resid_len = bcount; + + if (pc->flags & PC_FLAG_DMA_ERROR) { + pc->flags &= ~PC_FLAG_DMA_ERROR; + ide_dma_off(drive); + } + + if (pc->flags & PC_FLAG_DMA_OK) + drive->dma = !ide_dma_prepare(drive, cmd); + + if (!drive->dma) + pc->flags &= ~PC_FLAG_DMA_OK; + + timeout = (drive->media == ide_floppy) ? WAIT_FLOPPY_CMD + : WAIT_TAPE_CMD; + } + + ide_init_packet_cmd(cmd, valid_tf, bcount, drive->dma); + + (void)do_rw_taskfile(drive, cmd); + + if (drq_int) { + if (drive->dma) + drive->waiting_for_dma = 0; + hwif->expiry = expiry; + } + + ide_execute_command(drive, cmd, ide_transfer_pc, timeout); + + return drq_int ? ide_started : ide_transfer_pc(drive); +} +EXPORT_SYMBOL_GPL(ide_issue_pc); diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c new file mode 100644 index 000000000..25d2d88e8 --- /dev/null +++ b/drivers/ide/ide-cd.c @@ -0,0 +1,1858 @@ +/* + * ATAPI CD-ROM driver. + * + * Copyright (C) 1994-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> + * Copyright (C) 2005, 2007-2009 Bartlomiej Zolnierkiewicz + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * See Documentation/cdrom/ide-cd.rst for usage information. + * + * Suggestions are welcome. Patches that work are more welcome though. ;-) + * + * Documentation: + * Mt. Fuji (SFF8090 version 4) and ATAPI (SFF-8020i rev 2.6) standards. + * + * For historical changelog please see: + * Documentation/ide/ChangeLog.ide-cd.1994-2004 + */ + +#define DRV_NAME "ide-cd" +#define PFX DRV_NAME ": " + +#define IDECD_VERSION "5.00" + +#include <linux/compat.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched/task_stack.h> +#include <linux/delay.h> +#include <linux/timer.h> +#include <linux/seq_file.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 <linux/mutex.h> +#include <linux/bcd.h> + +/* For SCSI -> ATAPI command conversion */ +#include <scsi/scsi.h> + +#include <linux/io.h> +#include <asm/byteorder.h> +#include <linux/uaccess.h> +#include <asm/unaligned.h> + +#include "ide-cd.h" + +static DEFINE_MUTEX(ide_cd_mutex); +static DEFINE_MUTEX(idecd_ref_mutex); + +static void ide_cd_release(struct device *); + +static struct cdrom_info *ide_cd_get(struct gendisk *disk) +{ + struct cdrom_info *cd = NULL; + + mutex_lock(&idecd_ref_mutex); + cd = ide_drv_g(disk, cdrom_info); + if (cd) { + if (ide_device_get(cd->drive)) + cd = NULL; + else + get_device(&cd->dev); + + } + mutex_unlock(&idecd_ref_mutex); + return cd; +} + +static void ide_cd_put(struct cdrom_info *cd) +{ + ide_drive_t *drive = cd->drive; + + mutex_lock(&idecd_ref_mutex); + put_device(&cd->dev); + ide_device_put(drive); + mutex_unlock(&idecd_ref_mutex); +} + +/* + * 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) +{ + drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED; + drive->atapi_flags &= ~IDE_AFLAG_TOC_VALID; +} + +static int cdrom_log_sense(ide_drive_t *drive, struct request *rq) +{ + struct request_sense *sense = &drive->sense_data; + int log = 0; + + if (!sense || !rq || (rq->rq_flags & RQF_QUIET)) + return 0; + + ide_debug_log(IDE_DBG_SENSE, "sense_key: 0x%x", sense->sense_key); + + 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 (scsi_req(rq)->cmd[0] == GPCMD_START_STOP_UNIT && sense->asc == 0x24) + break; + log = 1; + 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 = &drive->sense_data; + struct cdrom_info *info = drive->driver_data; + unsigned long sector; + unsigned long bio_sectors; + + ide_debug_log(IDE_DBG_SENSE, "error_code: 0x%x, sense_key: 0x%x", + sense->error_code, sense->sense_key); + + if (failed_command) + ide_debug_log(IDE_DBG_SENSE, "failed cmd: 0x%x", + failed_command->cmd[0]); + + if (!cdrom_log_sense(drive, failed_command)) + 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 && scsi_req(failed_command)->cmd[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + + /* current error */ + if (sense->error_code == 0x70) { + switch (sense->sense_key) { + case MEDIUM_ERROR: + case VOLUME_OVERFLOW: + case ILLEGAL_REQUEST: + if (!sense->valid) + break; + if (failed_command == NULL || + blk_rq_is_passthrough(failed_command)) + break; + sector = (sense->information[0] << 24) | + (sense->information[1] << 16) | + (sense->information[2] << 8) | + (sense->information[3]); + + if (queue_logical_block_size(drive->queue) == 2048) + /* device sector size is 2K */ + sector <<= 2; + + bio_sectors = max(bio_sectors(failed_command->bio), 4U); + sector &= ~(bio_sectors - 1); + + /* + * The SCSI specification allows for the value + * returned by READ CAPACITY to be up to 75 2K + * sectors past the last readable block. + * Therefore, if we hit a medium error within the + * last 75 2K sectors, we decrease the saved size + * value. + */ + if (sector < get_capacity(info->disk) && + drive->probed_capacity - sector < 4 * 75) + set_capacity(info->disk, sector); + } + } + + ide_cd_log_error(drive->name, failed_command, sense); +} + +static void ide_cd_complete_failed_rq(ide_drive_t *drive, struct request *rq) +{ + /* + * For ATA_PRIV_SENSE, "ide_req(rq)->special" points to the original + * failed request. Also, the sense data should be read + * directly from rq which might be different from the original + * sense buffer if it got copied during mapping. + */ + struct request *failed = ide_req(rq)->special; + void *sense = bio_data(rq->bio); + + if (failed) { + /* + * Sense is always read into drive->sense_data, copy back to the + * original request. + */ + memcpy(scsi_req(failed)->sense, sense, 18); + scsi_req(failed)->sense_len = scsi_req(rq)->sense_len; + cdrom_analyze_sense_data(drive, failed); + + if (ide_end_rq(drive, failed, BLK_STS_IOERR, blk_rq_bytes(failed))) + BUG(); + } else + cdrom_analyze_sense_data(drive, NULL); +} + + +/* + * Allow the drive 5 seconds to recover; some devices will return NOT_READY + * while flushing data from cache. + * + * returns: 0 failed (write timeout expired) + * 1 success + */ +static int ide_cd_breathe(ide_drive_t *drive, struct request *rq) +{ + + struct cdrom_info *info = drive->driver_data; + + if (!scsi_req(rq)->result) + info->write_timeout = jiffies + ATAPI_WAIT_WRITE_BUSY; + + scsi_req(rq)->result = 1; + + if (time_after(jiffies, info->write_timeout)) + return 0; + else { + /* + * take a breather + */ + blk_mq_requeue_request(rq, false); + blk_mq_delay_kick_requeue_list(drive->queue, 1); + return 1; + } +} + +static void ide_cd_free_sense(ide_drive_t *drive) +{ + if (!drive->sense_rq) + return; + + blk_mq_free_request(drive->sense_rq); + drive->sense_rq = NULL; + drive->sense_rq_armed = false; +} + +/** + * Returns: + * 0: if the request should be continued. + * 1: if the request will be going through error recovery. + * 2: if the request should be ended. + */ +static int cdrom_decode_status(ide_drive_t *drive, u8 stat) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->rq; + int err, sense_key, do_end_request = 0; + + /* get the IDE error register */ + err = ide_read_error(drive); + sense_key = err >> 4; + + ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, rq->cmd_type: 0x%x, err: 0x%x, " + "stat 0x%x", + rq->cmd[0], rq->cmd_type, err, stat); + + if (ata_sense_request(rq)) { + /* + * 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->rq_flags |= RQF_FAILED; + return 2; + } + + /* if we have an error, pass CHECK_CONDITION as the SCSI status byte */ + if (blk_rq_is_scsi(rq) && !scsi_req(rq)->result) + scsi_req(rq)->result = SAM_STAT_CHECK_CONDITION; + + if (blk_noretry_request(rq)) + do_end_request = 1; + + switch (sense_key) { + case NOT_READY: + if (req_op(rq) == REQ_OP_WRITE) { + if (ide_cd_breathe(drive, rq)) + return 1; + } else { + cdrom_saw_media_change(drive); + + if (!blk_rq_is_passthrough(rq) && + !(rq->rq_flags & RQF_QUIET)) + printk(KERN_ERR PFX "%s: tray open\n", + drive->name); + } + do_end_request = 1; + break; + case UNIT_ATTENTION: + cdrom_saw_media_change(drive); + + if (blk_rq_is_passthrough(rq)) + return 0; + + /* + * Arrange to retry the request but be sure to give up if we've + * retried too many times. + */ + if (++scsi_req(rq)->result > ERROR_MAX) + do_end_request = 1; + break; + case ILLEGAL_REQUEST: + /* + * Don't print error message for this condition -- SFF8090i + * indicates that 5/24/00 is the correct response to a request + * to close the tray if the drive doesn't have that capability. + * + * cdrom_log_sense() knows this! + */ + if (scsi_req(rq)->cmd[0] == GPCMD_START_STOP_UNIT) + break; + fallthrough; + case DATA_PROTECT: + /* + * No point in retrying after an illegal request or data + * protect error. + */ + if (!(rq->rq_flags & RQF_QUIET)) + ide_dump_status(drive, "command error", stat); + do_end_request = 1; + break; + case MEDIUM_ERROR: + /* + * No point in re-trying a zillion times on a bad sector. + * If we got here the error is not correctable. + */ + if (!(rq->rq_flags & RQF_QUIET)) + ide_dump_status(drive, "media error " + "(bad sector)", stat); + do_end_request = 1; + break; + case BLANK_CHECK: + /* disk appears blank? */ + if (!(rq->rq_flags & RQF_QUIET)) + ide_dump_status(drive, "media error (blank)", + stat); + do_end_request = 1; + break; + default: + if (blk_rq_is_passthrough(rq)) + break; + if (err & ~ATA_ABORTED) { + /* go to the default handler for other errors */ + ide_error(drive, "cdrom_decode_status", stat); + return 1; + } else if (++scsi_req(rq)->result > ERROR_MAX) + /* we've racked up too many retries, abort */ + do_end_request = 1; + } + + if (blk_rq_is_passthrough(rq)) { + rq->rq_flags |= RQF_FAILED; + do_end_request = 1; + } + + /* + * End a request through request sense analysis when we have sense data. + * We need this in order to perform end of media processing. + */ + if (do_end_request) + goto end_request; + + /* if we got a CHECK_CONDITION status, queue a request sense command */ + if (stat & ATA_ERR) + return ide_queue_sense_rq(drive, NULL) ? 2 : 1; + return 1; + +end_request: + if (stat & ATA_ERR) { + hwif->rq = NULL; + return ide_queue_sense_rq(drive, rq) ? 2 : 1; + } else + return 2; +} + +static void ide_cd_request_sense_fixup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + struct request *rq = cmd->rq; + + ide_debug_log(IDE_DBG_FUNC, "rq->cmd[0]: 0x%x", rq->cmd[0]); + + /* + * Some of the trailing request sense fields are optional, + * and some drives don't send them. Sigh. + */ + if (scsi_req(rq)->cmd[0] == GPCMD_REQUEST_SENSE && + cmd->nleft > 0 && cmd->nleft <= 5) + cmd->nleft = 0; +} + +int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd, + int write, void *buffer, unsigned *bufflen, + struct scsi_sense_hdr *sshdr, int timeout, + req_flags_t rq_flags) +{ + struct cdrom_info *info = drive->driver_data; + struct scsi_sense_hdr local_sshdr; + int retries = 10; + bool failed; + + ide_debug_log(IDE_DBG_PC, "cmd[0]: 0x%x, write: 0x%x, timeout: %d, " + "rq_flags: 0x%x", + cmd[0], write, timeout, rq_flags); + + if (!sshdr) + sshdr = &local_sshdr; + + /* start of retry loop */ + do { + struct request *rq; + int error; + bool delay = false; + + rq = blk_get_request(drive->queue, + write ? REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + memcpy(scsi_req(rq)->cmd, cmd, BLK_MAX_CDB); + ide_req(rq)->type = ATA_PRIV_PC; + rq->rq_flags |= rq_flags; + rq->timeout = timeout; + if (buffer) { + error = blk_rq_map_kern(drive->queue, rq, buffer, + *bufflen, GFP_NOIO); + if (error) { + blk_put_request(rq); + return error; + } + } + + blk_execute_rq(drive->queue, info->disk, rq, 0); + error = scsi_req(rq)->result ? -EIO : 0; + + if (buffer) + *bufflen = scsi_req(rq)->resid_len; + scsi_normalize_sense(scsi_req(rq)->sense, + scsi_req(rq)->sense_len, sshdr); + + /* + * FIXME: we should probably abort/retry or something in case of + * failure. + */ + failed = (rq->rq_flags & RQF_FAILED) != 0; + if (failed) { + /* + * The request failed. Retry if it was due to a unit + * attention status (usually means media was changed). + */ + if (sshdr->sense_key == UNIT_ATTENTION) + cdrom_saw_media_change(drive); + else if (sshdr->sense_key == NOT_READY && + sshdr->asc == 4 && sshdr->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. + */ + delay = true; + } else { + /* otherwise, don't retry */ + retries = 0; + } + --retries; + } + blk_put_request(rq); + if (delay) + ssleep(2); + } while (failed && retries >= 0); + + /* return an error if the command failed */ + return failed ? -EIO : 0; +} + +/* + * returns true if rq has been completed + */ +static bool ide_cd_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd) +{ + unsigned int nr_bytes = cmd->nbytes - cmd->nleft; + + if (cmd->tf_flags & IDE_TFLAG_WRITE) + nr_bytes -= cmd->last_xfer_len; + + if (nr_bytes > 0) { + ide_complete_rq(drive, BLK_STS_OK, nr_bytes); + return true; + } + + return false; +} + +/* standard prep_rq that builds 10 byte cmds */ +static bool ide_cdrom_prep_fs(struct request_queue *q, struct request *rq) +{ + int hard_sect = queue_logical_block_size(q); + long block = (long)blk_rq_pos(rq) / (hard_sect >> 9); + unsigned long blocks = blk_rq_sectors(rq) / (hard_sect >> 9); + struct scsi_request *req = scsi_req(rq); + + if (rq_data_dir(rq) == READ) + req->cmd[0] = GPCMD_READ_10; + else + req->cmd[0] = GPCMD_WRITE_10; + + /* + * fill in lba + */ + req->cmd[2] = (block >> 24) & 0xff; + req->cmd[3] = (block >> 16) & 0xff; + req->cmd[4] = (block >> 8) & 0xff; + req->cmd[5] = block & 0xff; + + /* + * and transfer length + */ + req->cmd[7] = (blocks >> 8) & 0xff; + req->cmd[8] = blocks & 0xff; + req->cmd_len = 10; + return true; +} + +/* + * Most of the SCSI commands are supported directly by ATAPI devices. + * This transform handles the few exceptions. + */ +static bool ide_cdrom_prep_pc(struct request *rq) +{ + u8 *c = scsi_req(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); + scsi_req(rq)->cmd_len = 10; + return true; + } + + /* + * 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) { + scsi_req(rq)->result = ILLEGAL_REQUEST; + return false; + } + + return true; +} + +static bool ide_cdrom_prep_rq(ide_drive_t *drive, struct request *rq) +{ + if (!blk_rq_is_passthrough(rq)) { + scsi_req_init(scsi_req(rq)); + + return ide_cdrom_prep_fs(drive->queue, rq); + } else if (blk_rq_is_scsi(rq)) + return ide_cdrom_prep_pc(rq); + + return true; +} + +static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; + struct request *rq = hwif->rq; + ide_expiry_t *expiry = NULL; + int dma_error = 0, dma, thislen, uptodate = 0; + int write = (rq_data_dir(rq) == WRITE) ? 1 : 0, rc = 0; + int sense = ata_sense_request(rq); + unsigned int timeout; + u16 len; + u8 ireason, stat; + + ide_debug_log(IDE_DBG_PC, "cmd: 0x%x, write: 0x%x", rq->cmd[0], write); + + /* check for errors */ + dma = drive->dma; + if (dma) { + drive->dma = 0; + drive->waiting_for_dma = 0; + dma_error = hwif->dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + if (dma_error) { + printk(KERN_ERR PFX "%s: DMA %s error\n", drive->name, + write ? "write" : "read"); + ide_dma_off(drive); + } + } + + /* check status */ + stat = hwif->tp_ops->read_status(hwif); + + if (!OK_STAT(stat, 0, BAD_R_STAT)) { + rc = cdrom_decode_status(drive, stat); + if (rc) { + if (rc == 2) + goto out_end; + return ide_stopped; + } + } + + /* using dma, transfer is complete now */ + if (dma) { + if (dma_error) + return ide_error(drive, "dma error", stat); + uptodate = 1; + goto out_end; + } + + ide_read_bcount_and_ireason(drive, &len, &ireason); + + thislen = !blk_rq_is_passthrough(rq) ? len : cmd->nleft; + if (thislen > len) + thislen = len; + + ide_debug_log(IDE_DBG_PC, "DRQ: stat: 0x%x, thislen: %d", + stat, thislen); + + /* If DRQ is clear, the command has completed. */ + if ((stat & ATA_DRQ) == 0) { + switch (req_op(rq)) { + default: + /* + * If we're not done reading/writing, complain. + * Otherwise, complete the command normally. + */ + uptodate = 1; + if (cmd->nleft > 0) { + printk(KERN_ERR PFX "%s: %s: data underrun " + "(%u bytes)\n", drive->name, __func__, + cmd->nleft); + if (!write) + rq->rq_flags |= RQF_FAILED; + uptodate = 0; + } + goto out_end; + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + ide_cd_request_sense_fixup(drive, cmd); + + uptodate = cmd->nleft ? 0 : 1; + + /* + * suck out the remaining bytes from the drive in an + * attempt to complete the data xfer. (see BZ#13399) + */ + if (!(stat & ATA_ERR) && !uptodate && thislen) { + ide_pio_bytes(drive, cmd, write, thislen); + uptodate = cmd->nleft ? 0 : 1; + } + + if (!uptodate) + rq->rq_flags |= RQF_FAILED; + goto out_end; + case REQ_OP_SCSI_IN: + case REQ_OP_SCSI_OUT: + goto out_end; + } + } + + rc = ide_check_ireason(drive, rq, len, ireason, write); + if (rc) + goto out_end; + + cmd->last_xfer_len = 0; + + ide_debug_log(IDE_DBG_PC, "data transfer, rq->cmd_type: 0x%x, " + "ireason: 0x%x", + rq->cmd_type, ireason); + + /* transfer data */ + while (thislen > 0) { + int blen = min_t(int, thislen, cmd->nleft); + + if (cmd->nleft == 0) + break; + + ide_pio_bytes(drive, cmd, write, blen); + cmd->last_xfer_len += blen; + + thislen -= blen; + len -= blen; + + if (sense && write == 0) + scsi_req(rq)->sense_len += blen; + } + + /* pad, if necessary */ + if (len > 0) { + if (blk_rq_is_passthrough(rq) || write == 0) + ide_pad_transfer(drive, write, len); + else { + printk(KERN_ERR PFX "%s: confused, missing data\n", + drive->name); + blk_dump_rq_flags(rq, "cdrom_newpc_intr"); + } + } + + switch (req_op(rq)) { + case REQ_OP_SCSI_IN: + case REQ_OP_SCSI_OUT: + timeout = rq->timeout; + break; + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + expiry = ide_cd_expiry; + fallthrough; + default: + timeout = ATAPI_WAIT_PC; + break; + } + + hwif->expiry = expiry; + ide_set_handler(drive, cdrom_newpc_intr, timeout); + return ide_started; + +out_end: + if (blk_rq_is_scsi(rq) && rc == 0) { + scsi_req(rq)->resid_len = 0; + blk_mq_end_request(rq, BLK_STS_OK); + hwif->rq = NULL; + } else { + if (sense && uptodate) + ide_cd_complete_failed_rq(drive, rq); + + if (!blk_rq_is_passthrough(rq)) { + if (cmd->nleft == 0) + uptodate = 1; + } else { + if (uptodate <= 0 && scsi_req(rq)->result == 0) + scsi_req(rq)->result = -EIO; + } + + if (uptodate == 0 && rq->bio) + if (ide_cd_error_cmd(drive, cmd)) + return ide_stopped; + + /* make sure it's fully ended */ + if (blk_rq_is_passthrough(rq)) { + scsi_req(rq)->resid_len -= cmd->nbytes - cmd->nleft; + if (uptodate == 0 && (cmd->tf_flags & IDE_TFLAG_WRITE)) + scsi_req(rq)->resid_len += cmd->last_xfer_len; + } + + ide_complete_rq(drive, uptodate ? BLK_STS_OK : BLK_STS_IOERR, blk_rq_bytes(rq)); + + if (sense && rc == 2) + ide_error(drive, "request sense failure", stat); + } + + ide_cd_free_sense(drive); + return ide_stopped; +} + +static ide_startstop_t cdrom_start_rw(ide_drive_t *drive, struct request *rq) +{ + struct cdrom_info *cd = drive->driver_data; + struct request_queue *q = drive->queue; + int write = rq_data_dir(rq) == WRITE; + unsigned short sectors_per_frame = + queue_logical_block_size(q) >> SECTOR_SHIFT; + + ide_debug_log(IDE_DBG_RQ, "rq->cmd[0]: 0x%x, rq->cmd_flags: 0x%x, " + "secs_per_frame: %u", + rq->cmd[0], rq->cmd_flags, sectors_per_frame); + + if (write) { + /* disk has become write protected */ + if (get_disk_ro(cd->disk)) + return ide_stopped; + } else { + /* + * We may be retrying this request after an error. Fix up any + * weirdness which might be present in the request packet. + */ + ide_cdrom_prep_rq(drive, rq); + } + + /* fs requests *must* be hardware frame aligned */ + if ((blk_rq_sectors(rq) & (sectors_per_frame - 1)) || + (blk_rq_pos(rq) & (sectors_per_frame - 1))) + return ide_stopped; + + /* use DMA, if possible */ + drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); + + if (write) + cd->devinfo.media_written = 1; + + rq->timeout = ATAPI_WAIT_PC; + + return ide_started; +} + +static void cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) +{ + + ide_debug_log(IDE_DBG_PC, "rq->cmd[0]: 0x%x, rq->cmd_type: 0x%x", + rq->cmd[0], rq->cmd_type); + + if (blk_rq_is_scsi(rq)) + rq->rq_flags |= RQF_QUIET; + else + rq->rq_flags &= ~RQF_FAILED; + + drive->dma = 0; + + /* sg request */ + if (rq->bio) { + struct request_queue *q = drive->queue; + char *buf = bio_data(rq->bio); + unsigned int alignment; + + drive->dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); + + /* + * check if dma is safe + * + * NOTE! The "len" and "addr" checks should possibly have + * separate masks. + */ + alignment = queue_dma_alignment(q) | q->dma_pad_mask; + if ((unsigned long)buf & alignment + || blk_rq_bytes(rq) & q->dma_pad_mask + || object_is_on_stack(buf)) + drive->dma = 0; + } +} + +static ide_startstop_t ide_cd_do_request(ide_drive_t *drive, struct request *rq, + sector_t block) +{ + struct ide_cmd cmd; + int uptodate = 0; + unsigned int nsectors; + + ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, block: %llu", + rq->cmd[0], (unsigned long long)block); + + if (drive->debug_mask & IDE_DBG_RQ) + blk_dump_rq_flags(rq, "ide_cd_do_request"); + + switch (req_op(rq)) { + default: + if (cdrom_start_rw(drive, rq) == ide_stopped) + goto out_end; + break; + case REQ_OP_SCSI_IN: + case REQ_OP_SCSI_OUT: + handle_pc: + if (!rq->timeout) + rq->timeout = ATAPI_WAIT_PC; + cdrom_do_block_pc(drive, rq); + break; + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + switch (ide_req(rq)->type) { + case ATA_PRIV_MISC: + /* right now this can only be a reset... */ + uptodate = 1; + goto out_end; + case ATA_PRIV_SENSE: + case ATA_PRIV_PC: + goto handle_pc; + default: + BUG(); + } + } + + /* prepare sense request for this command */ + ide_prep_sense(drive, rq); + + memset(&cmd, 0, sizeof(cmd)); + + if (rq_data_dir(rq)) + cmd.tf_flags |= IDE_TFLAG_WRITE; + + cmd.rq = rq; + + if (!blk_rq_is_passthrough(rq) || blk_rq_bytes(rq)) { + ide_init_sg_cmd(&cmd, blk_rq_bytes(rq)); + ide_map_sg(drive, &cmd); + } + + return ide_issue_pc(drive, &cmd); +out_end: + nsectors = blk_rq_sectors(rq); + + if (nsectors == 0) + nsectors = 1; + + ide_complete_rq(drive, uptodate ? BLK_STS_OK : BLK_STS_IOERR, nsectors << 9); + + 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. + */ +static void msf_from_bcd(struct atapi_msf *msf) +{ + msf->minute = bcd2bin(msf->minute); + msf->second = bcd2bin(msf->second); + msf->frame = bcd2bin(msf->frame); +} + +int cdrom_check_status(ide_drive_t *drive, struct scsi_sense_hdr *sshdr) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi; + unsigned char cmd[BLK_MAX_CDB]; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (!info) + return -EIO; + + cdi = &info->devinfo; + + memset(cmd, 0, BLK_MAX_CDB); + cmd[0] = GPCMD_TEST_UNIT_READY; + + /* + * Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to switch CDs + * instead of supporting the LOAD_UNLOAD opcode. + */ + cmd[7] = cdi->sanyo_slot % 3; + + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, sshdr, 0, RQF_QUIET); +} + +static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, + unsigned long *sectors_per_frame) +{ + struct { + __be32 lba; + __be32 blocklen; + } capbuf; + + int stat; + unsigned char cmd[BLK_MAX_CDB]; + unsigned len = sizeof(capbuf); + u32 blocklen; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + memset(cmd, 0, BLK_MAX_CDB); + cmd[0] = GPCMD_READ_CDVD_CAPACITY; + + stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, NULL, 0, + RQF_QUIET); + if (stat) + return stat; + + /* + * Sanity check the given block size, in so far as making + * sure the sectors_per_frame we give to the caller won't + * end up being bogus. + */ + blocklen = be32_to_cpu(capbuf.blocklen); + blocklen = (blocklen >> SECTOR_SHIFT) << SECTOR_SHIFT; + switch (blocklen) { + case 512: + case 1024: + case 2048: + case 4096: + break; + default: + printk_once(KERN_ERR PFX "%s: weird block size %u; " + "setting default block size to 2048\n", + drive->name, blocklen); + blocklen = 2048; + break; + } + + *capacity = 1 + be32_to_cpu(capbuf.lba); + *sectors_per_frame = blocklen >> SECTOR_SHIFT; + + ide_debug_log(IDE_DBG_PROBE, "cap: %lu, sectors_per_frame: %lu", + *capacity, *sectors_per_frame); + + return 0; +} + +static int ide_cdrom_read_tocentry(ide_drive_t *drive, int trackno, + int msf_flag, int format, char *buf, int buflen) +{ + unsigned char cmd[BLK_MAX_CDB]; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_READ_TOC_PMA_ATIP; + cmd[6] = trackno; + cmd[7] = (buflen >> 8); + cmd[8] = (buflen & 0xff); + cmd[9] = (format << 6); + + if (msf_flag) + cmd[1] = 2; + + return ide_cd_queue_pc(drive, cmd, 0, buf, &buflen, NULL, 0, RQF_QUIET); +} + +/* Try to read the entire TOC for the disk into our internal buffer. */ +int ide_cd_read_toc(ide_drive_t *drive) +{ + 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; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (toc == NULL) { + /* try to allocate space */ + toc = kmalloc(sizeof(struct atapi_toc), GFP_KERNEL); + if (toc == NULL) { + printk(KERN_ERR PFX "%s: No cdrom TOC buffer!\n", + drive->name); + return -ENOMEM; + } + info->toc = toc; + } + + /* + * Check to see if the existing data is still valid. If it is, + * just return. + */ + (void) cdrom_check_status(drive, NULL); + + if (drive->atapi_flags & IDE_AFLAG_TOC_VALID) + return 0; + + /* try to get the total cdrom capacity and sector size */ + stat = cdrom_read_capacity(drive, &toc->capacity, §ors_per_frame); + if (stat) + toc->capacity = 0x1fffff; + + set_capacity(info->disk, toc->capacity * sectors_per_frame); + /* save a private copy of the TOC capacity for error handling */ + drive->probed_capacity = toc->capacity * sectors_per_frame; + + blk_queue_logical_block_size(drive->queue, + sectors_per_frame << SECTOR_SHIFT); + + /* first read just the header, so we know how long the TOC is */ + stat = ide_cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr, + sizeof(struct atapi_toc_header)); + if (stat) + return stat; + + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { + toc->hdr.first_track = bcd2bin(toc->hdr.first_track); + toc->hdr.last_track = bcd2bin(toc->hdr.last_track); + } + + 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 = ide_cdrom_read_tocentry(drive, toc->hdr.first_track, 1, 0, + (char *)&toc->hdr, + sizeof(struct atapi_toc_header) + + (ntracks + 1) * + sizeof(struct atapi_toc_entry)); + + if (stat && toc->hdr.first_track > 1) { + /* + * Cds with CDI tracks only don't have any TOC entries, despite + * of this the returned values are + * first_track == last_track = number of CDI tracks + 1, + * so that this case is indistinguishable from the same layout + * plus an additional audio track. If we get an error for the + * regular case, we assume a CDI without additional audio + * tracks. In this case the readable TOC is empty (CDI tracks + * are not included) and only holds the Leadout entry. + * + * Heiko Eißfeldt. + */ + ntracks = 0; + stat = ide_cdrom_read_tocentry(drive, CDROM_LEADOUT, 1, 0, + (char *)&toc->hdr, + sizeof(struct atapi_toc_header) + + (ntracks + 1) * + sizeof(struct atapi_toc_entry)); + if (stat) + return stat; + + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { + toc->hdr.first_track = (u8)bin2bcd(CDROM_LEADOUT); + toc->hdr.last_track = (u8)bin2bcd(CDROM_LEADOUT); + } else { + toc->hdr.first_track = CDROM_LEADOUT; + toc->hdr.last_track = CDROM_LEADOUT; + } + } + + if (stat) + return stat; + + toc->hdr.toc_length = be16_to_cpu(toc->hdr.toc_length); + + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) { + toc->hdr.first_track = bcd2bin(toc->hdr.first_track); + toc->hdr.last_track = bcd2bin(toc->hdr.last_track); + } + + for (i = 0; i <= ntracks; i++) { + if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { + if (drive->atapi_flags & IDE_AFLAG_TOCTRACKS_AS_BCD) + toc->ent[i].track = bcd2bin(toc->ent[i].track); + msf_from_bcd(&toc->ent[i].addr.msf); + } + 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); + } + + if (toc->hdr.first_track != CDROM_LEADOUT) { + /* read the multisession information */ + stat = ide_cdrom_read_tocentry(drive, 0, 0, 1, (char *)&ms_tmp, + sizeof(ms_tmp)); + if (stat) + return stat; + + toc->last_session_lba = be32_to_cpu(ms_tmp.ent.addr.lba); + } else { + ms_tmp.hdr.last_track = CDROM_LEADOUT; + ms_tmp.hdr.first_track = ms_tmp.hdr.last_track; + toc->last_session_lba = msf_to_lba(0, 2, 0); /* 0m 2s 0f */ + } + + if (drive->atapi_flags & IDE_AFLAG_TOCADDR_AS_BCD) { + /* re-read multisession information using MSF format */ + stat = ide_cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, + sizeof(ms_tmp)); + 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); + } + + 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); + drive->probed_capacity = toc->capacity * sectors_per_frame; + } + + /* Remember that we've read this stuff. */ + drive->atapi_flags |= IDE_AFLAG_TOC_VALID; + + return 0; +} + +int ide_cdrom_get_capabilities(ide_drive_t *drive, u8 *buf) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct packet_command cgc; + int stat, attempts = 3, size = ATAPI_CAPABILITIES_PAGE_SIZE; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if ((drive->atapi_flags & IDE_AFLAG_FULL_CAPS_PAGE) == 0) + size -= ATAPI_CAPABILITIES_PAGE_PAD_SIZE; + + init_cdrom_command(&cgc, buf, 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; +} + +void ide_cdrom_update_speed(ide_drive_t *drive, u8 *buf) +{ + struct cdrom_info *cd = drive->driver_data; + u16 curspeed, maxspeed; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (drive->atapi_flags & IDE_AFLAG_LE_SPEED_FIELDS) { + curspeed = le16_to_cpup((__le16 *)&buf[8 + 14]); + maxspeed = le16_to_cpup((__le16 *)&buf[8 + 8]); + } else { + curspeed = be16_to_cpup((__be16 *)&buf[8 + 14]); + maxspeed = be16_to_cpup((__be16 *)&buf[8 + 8]); + } + + ide_debug_log(IDE_DBG_PROBE, "curspeed: %u, maxspeed: %u", + curspeed, maxspeed); + + cd->current_speed = DIV_ROUND_CLOSEST(curspeed, 176); + cd->max_speed = DIV_ROUND_CLOSEST(maxspeed, 176); +} + +#define IDE_CD_CAPABILITIES \ + (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_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) + +static const struct cdrom_device_ops ide_cdrom_dops = { + .open = ide_cdrom_open_real, + .release = ide_cdrom_release_real, + .drive_status = ide_cdrom_drive_status, + .check_events = ide_cdrom_check_events_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, + .capability = IDE_CD_CAPABILITIES, + .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; + + ide_debug_log(IDE_DBG_PROBE, "nslots: %d", nslots); + + devinfo->ops = &ide_cdrom_dops; + devinfo->speed = info->current_speed; + devinfo->capacity = nslots; + devinfo->handle = drive; + strcpy(devinfo->name, drive->name); + + if (drive->atapi_flags & IDE_AFLAG_NO_SPEED_SELECT) + devinfo->mask |= CDC_SELECT_SPEED; + + return register_cdrom(info->disk, devinfo); +} + +static int ide_cdrom_probe_capabilities(ide_drive_t *drive) +{ + struct cdrom_info *cd = drive->driver_data; + struct cdrom_device_info *cdi = &cd->devinfo; + u8 buf[ATAPI_CAPABILITIES_PAGE_SIZE]; + mechtype_t mechtype; + int nslots = 1; + + ide_debug_log(IDE_DBG_PROBE, "media: 0x%x, atapi_flags: 0x%lx", + drive->media, drive->atapi_flags); + + cdi->mask = (CDC_CD_R | CDC_CD_RW | CDC_DVD | CDC_DVD_R | + CDC_DVD_RAM | CDC_SELECT_DISC | CDC_PLAY_AUDIO | + CDC_MO_DRIVE | CDC_RAM); + + if (drive->media == ide_optical) { + cdi->mask &= ~(CDC_MO_DRIVE | CDC_RAM); + printk(KERN_ERR PFX "%s: ATAPI magneto-optical drive\n", + drive->name); + return nslots; + } + + if (drive->atapi_flags & IDE_AFLAG_PRE_ATAPI12) { + drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; + cdi->mask &= ~CDC_PLAY_AUDIO; + 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 = drive; + cdi->ops = &ide_cdrom_dops; + + if (ide_cdrom_get_capabilities(drive, buf)) + return 0; + + if ((buf[8 + 6] & 0x01) == 0) + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; + if (buf[8 + 6] & 0x08) + drive->atapi_flags &= ~IDE_AFLAG_NO_EJECT; + if (buf[8 + 3] & 0x01) + cdi->mask &= ~CDC_CD_R; + if (buf[8 + 3] & 0x02) + cdi->mask &= ~(CDC_CD_RW | CDC_RAM); + if (buf[8 + 2] & 0x38) + cdi->mask &= ~CDC_DVD; + if (buf[8 + 3] & 0x20) + cdi->mask &= ~(CDC_DVD_RAM | CDC_RAM); + if (buf[8 + 3] & 0x10) + cdi->mask &= ~CDC_DVD_R; + if ((buf[8 + 4] & 0x01) || (drive->atapi_flags & IDE_AFLAG_PLAY_AUDIO_OK)) + cdi->mask &= ~CDC_PLAY_AUDIO; + + mechtype = buf[8 + 6] >> 5; + if (mechtype == mechtype_caddy || + mechtype == mechtype_popup || + (drive->atapi_flags & IDE_AFLAG_NO_AUTOCLOSE)) + cdi->mask |= CDC_CLOSE_TRAY; + + if (cdi->sanyo_slot > 0) { + cdi->mask &= ~CDC_SELECT_DISC; + nslots = 3; + } else if (mechtype == mechtype_individual_changer || + mechtype == mechtype_cartridge_changer) { + nslots = cdrom_number_of_slots(cdi); + if (nslots > 1) + cdi->mask &= ~CDC_SELECT_DISC; + } + + ide_cdrom_update_speed(drive, buf); + + printk(KERN_INFO PFX "%s: ATAPI", drive->name); + + /* don't print speed if the drive reported 0 */ + if (cd->max_speed) + printk(KERN_CONT " %dX", cd->max_speed); + + printk(KERN_CONT " %s", (cdi->mask & CDC_DVD) ? "CD-ROM" : "DVD-ROM"); + + if ((cdi->mask & CDC_DVD_R) == 0 || (cdi->mask & CDC_DVD_RAM) == 0) + printk(KERN_CONT " DVD%s%s", + (cdi->mask & CDC_DVD_R) ? "" : "-R", + (cdi->mask & CDC_DVD_RAM) ? "" : "/RAM"); + + if ((cdi->mask & CDC_CD_R) == 0 || (cdi->mask & CDC_CD_RW) == 0) + printk(KERN_CONT " CD%s%s", + (cdi->mask & CDC_CD_R) ? "" : "-R", + (cdi->mask & CDC_CD_RW) ? "" : "/RW"); + + if ((cdi->mask & CDC_SELECT_DISC) == 0) + printk(KERN_CONT " changer w/%d slots", nslots); + else + printk(KERN_CONT " drive"); + + printk(KERN_CONT ", %dkB Cache\n", + be16_to_cpup((__be16 *)&buf[8 + 12])); + + return nslots; +} + +struct cd_list_entry { + const char *id_model; + const char *id_firmware; + unsigned int cd_flags; +}; + +#ifdef CONFIG_IDE_PROC_FS +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)) + return 0; + + return capacity * sectors_per_frame; +} + +static int idecd_capacity_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = m->private; + + seq_printf(m, "%llu\n", (long long)ide_cdrom_capacity(drive)); + return 0; +} + +static ide_proc_entry_t idecd_proc[] = { + { "capacity", S_IFREG|S_IRUGO, idecd_capacity_proc_show }, + {} +}; + +static ide_proc_entry_t *ide_cd_proc_entries(ide_drive_t *drive) +{ + return idecd_proc; +} + +static const struct ide_proc_devset *ide_cd_proc_devsets(ide_drive_t *drive) +{ + return NULL; +} +#endif + +static const struct cd_list_entry ide_cd_quirks_list[] = { + /* SCR-3231 doesn't support the SET_CD_SPEED command. */ + { "SAMSUNG CD-ROM SCR-3231", NULL, IDE_AFLAG_NO_SPEED_SELECT }, + /* Old NEC260 (not R) was released before ATAPI 1.2 spec. */ + { "NEC CD-ROM DRIVE:260", "1.01", IDE_AFLAG_TOCADDR_AS_BCD | + IDE_AFLAG_PRE_ATAPI12, }, + /* Vertos 300, some versions of this drive like to talk BCD. */ + { "V003S0DS", NULL, IDE_AFLAG_VERTOS_300_SSD, }, + /* Vertos 600 ESD. */ + { "V006E0DS", NULL, IDE_AFLAG_VERTOS_600_ESD, }, + /* + * Sanyo 3 CD changer uses a non-standard command for CD changing + * (by default standard ATAPI support for CD changers is used). + */ + { "CD-ROM CDR-C3 G", NULL, IDE_AFLAG_SANYO_3CD }, + { "CD-ROM CDR-C3G", NULL, IDE_AFLAG_SANYO_3CD }, + { "CD-ROM CDR_C36", NULL, IDE_AFLAG_SANYO_3CD }, + /* Stingray 8X CD-ROM. */ + { "STINGRAY 8422 IDE 8X CD-ROM 7-27-95", NULL, IDE_AFLAG_PRE_ATAPI12 }, + /* + * ACER 50X CD-ROM and WPI 32X CD-ROM require the full spec length + * mode sense page capabilities size, but older drives break. + */ + { "ATAPI CD ROM DRIVE 50X MAX", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, + { "WPI CDS-32X", NULL, IDE_AFLAG_FULL_CAPS_PAGE }, + /* ACER/AOpen 24X CD-ROM has the speed fields byte-swapped. */ + { "", "241N", IDE_AFLAG_LE_SPEED_FIELDS }, + /* + * Some drives used by Apple don't advertise audio play + * but they do support reading TOC & audio datas. + */ + { "MATSHITADVD-ROM SR-8187", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8186", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8176", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "MATSHITADVD-ROM SR-8174", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "Optiarc DVD RW AD-5200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "Optiarc DVD RW AD-7200A", NULL, IDE_AFLAG_PLAY_AUDIO_OK }, + { "Optiarc DVD RW AD-7543A", NULL, IDE_AFLAG_NO_AUTOCLOSE }, + { "TEAC CD-ROM CD-224E", NULL, IDE_AFLAG_NO_AUTOCLOSE }, + { NULL, NULL, 0 } +}; + +static unsigned int ide_cd_flags(u16 *id) +{ + const struct cd_list_entry *cle = ide_cd_quirks_list; + + while (cle->id_model) { + if (strcmp(cle->id_model, (char *)&id[ATA_ID_PROD]) == 0 && + (cle->id_firmware == NULL || + strstr((char *)&id[ATA_ID_FW_REV], cle->id_firmware))) + return cle->cd_flags; + cle++; + } + + return 0; +} + +static int ide_cdrom_setup(ide_drive_t *drive) +{ + struct cdrom_info *cd = drive->driver_data; + struct cdrom_device_info *cdi = &cd->devinfo; + struct request_queue *q = drive->queue; + u16 *id = drive->id; + char *fw_rev = (char *)&id[ATA_ID_FW_REV]; + int nslots; + + ide_debug_log(IDE_DBG_PROBE, "enter"); + + drive->prep_rq = ide_cdrom_prep_rq; + blk_queue_dma_alignment(q, 31); + blk_queue_update_dma_pad(q, 15); + + drive->dev_flags |= IDE_DFLAG_MEDIA_CHANGED; + drive->atapi_flags = IDE_AFLAG_NO_EJECT | ide_cd_flags(id); + + if ((drive->atapi_flags & IDE_AFLAG_VERTOS_300_SSD) && + fw_rev[4] == '1' && fw_rev[6] <= '2') + drive->atapi_flags |= (IDE_AFLAG_TOCTRACKS_AS_BCD | + IDE_AFLAG_TOCADDR_AS_BCD); + else if ((drive->atapi_flags & IDE_AFLAG_VERTOS_600_ESD) && + fw_rev[4] == '1' && fw_rev[6] <= '2') + drive->atapi_flags |= IDE_AFLAG_TOCTRACKS_AS_BCD; + else if (drive->atapi_flags & IDE_AFLAG_SANYO_3CD) + /* 3 => use CD in slot 0 */ + cdi->sanyo_slot = 3; + + nslots = ide_cdrom_probe_capabilities(drive); + + blk_queue_logical_block_size(q, CD_FRAMESIZE); + + if (ide_cdrom_register(drive, nslots)) { + printk(KERN_ERR PFX "%s: %s failed to register device with the" + " cdrom driver.\n", drive->name, __func__); + cd->devinfo.handle = NULL; + return 1; + } + + ide_proc_register_driver(drive, cd->driver); + return 0; +} + +static void ide_cd_remove(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + ide_proc_unregister_driver(drive, info->driver); + device_del(&info->dev); + del_gendisk(info->disk); + + mutex_lock(&idecd_ref_mutex); + put_device(&info->dev); + mutex_unlock(&idecd_ref_mutex); +} + +static void ide_cd_release(struct device *dev) +{ + struct cdrom_info *info = to_ide_drv(dev, cdrom_info); + struct cdrom_device_info *devinfo = &info->devinfo; + ide_drive_t *drive = info->drive; + struct gendisk *g = info->disk; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + kfree(info->toc); + if (devinfo->handle == drive) + unregister_cdrom(devinfo); + drive->driver_data = NULL; + drive->prep_rq = NULL; + g->private_data = NULL; + put_disk(g); + kfree(info); +} + +static int ide_cd_probe(ide_drive_t *); + +static struct ide_driver ide_cdrom_driver = { + .gen_driver = { + .owner = THIS_MODULE, + .name = "ide-cdrom", + .bus = &ide_bus_type, + }, + .probe = ide_cd_probe, + .remove = ide_cd_remove, + .version = IDECD_VERSION, + .do_request = ide_cd_do_request, +#ifdef CONFIG_IDE_PROC_FS + .proc_entries = ide_cd_proc_entries, + .proc_devsets = ide_cd_proc_devsets, +#endif +}; + +static int idecd_open(struct block_device *bdev, fmode_t mode) +{ + struct cdrom_info *info; + int rc = -ENXIO; + + if (bdev_check_media_change(bdev)) { + info = ide_drv_g(bdev->bd_disk, cdrom_info); + + ide_cd_read_toc(info->drive); + } + + mutex_lock(&ide_cd_mutex); + info = ide_cd_get(bdev->bd_disk); + if (!info) + goto out; + + rc = cdrom_open(&info->devinfo, bdev, mode); + if (rc < 0) + ide_cd_put(info); +out: + mutex_unlock(&ide_cd_mutex); + return rc; +} + +static void idecd_release(struct gendisk *disk, fmode_t mode) +{ + struct cdrom_info *info = ide_drv_g(disk, cdrom_info); + + mutex_lock(&ide_cd_mutex); + cdrom_release(&info->devinfo, mode); + + ide_cd_put(info); + mutex_unlock(&ide_cd_mutex); +} + +static int idecd_set_spindown(struct cdrom_device_info *cdi, unsigned long arg) +{ + struct packet_command cgc; + char buffer[16]; + int stat; + char spindown; + + if (copy_from_user(&spindown, (void __user *)arg, sizeof(char))) + return -EFAULT; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN); + + stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0); + if (stat) + return stat; + + buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f); + return cdrom_mode_select(cdi, &cgc); +} + +static int idecd_get_spindown(struct cdrom_device_info *cdi, unsigned long arg) +{ + struct packet_command cgc; + char buffer[16]; + int stat; + char spindown; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN); + + stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0); + if (stat) + return stat; + + spindown = buffer[11] & 0x0f; + if (copy_to_user((void __user *)arg, &spindown, sizeof(char))) + return -EFAULT; + return 0; +} + +static int idecd_locked_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info); + int err; + + switch (cmd) { + case CDROMSETSPINDOWN: + return idecd_set_spindown(&info->devinfo, arg); + case CDROMGETSPINDOWN: + return idecd_get_spindown(&info->devinfo, arg); + default: + break; + } + + err = generic_ide_ioctl(info->drive, bdev, cmd, arg); + if (err == -EINVAL) + err = cdrom_ioctl(&info->devinfo, bdev, mode, cmd, arg); + + return err; +} + +static int idecd_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + int ret; + + mutex_lock(&ide_cd_mutex); + ret = idecd_locked_ioctl(bdev, mode, cmd, arg); + mutex_unlock(&ide_cd_mutex); + + return ret; +} + +static int idecd_locked_compat_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct cdrom_info *info = ide_drv_g(bdev->bd_disk, cdrom_info); + void __user *argp = compat_ptr(arg); + int err; + + switch (cmd) { + case CDROMSETSPINDOWN: + return idecd_set_spindown(&info->devinfo, (unsigned long)argp); + case CDROMGETSPINDOWN: + return idecd_get_spindown(&info->devinfo, (unsigned long)argp); + default: + break; + } + + err = generic_ide_ioctl(info->drive, bdev, cmd, arg); + if (err == -EINVAL) + err = cdrom_ioctl(&info->devinfo, bdev, mode, cmd, + (unsigned long)argp); + + return err; +} + +static int idecd_compat_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + int ret; + + mutex_lock(&ide_cd_mutex); + ret = idecd_locked_compat_ioctl(bdev, mode, cmd, arg); + mutex_unlock(&ide_cd_mutex); + + return ret; +} + +static unsigned int idecd_check_events(struct gendisk *disk, + unsigned int clearing) +{ + struct cdrom_info *info = ide_drv_g(disk, cdrom_info); + return cdrom_check_events(&info->devinfo, clearing); +} + +static const struct block_device_operations idecd_ops = { + .owner = THIS_MODULE, + .open = idecd_open, + .release = idecd_release, + .ioctl = idecd_ioctl, + .compat_ioctl = IS_ENABLED(CONFIG_COMPAT) ? + idecd_compat_ioctl : NULL, + .check_events = idecd_check_events, +}; + +/* module options */ +static unsigned long debug_mask; +module_param(debug_mask, ulong, 0644); + +MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); + +static int ide_cd_probe(ide_drive_t *drive) +{ + struct cdrom_info *info; + struct gendisk *g; + + ide_debug_log(IDE_DBG_PROBE, "driver_req: %s, media: 0x%x", + drive->driver_req, drive->media); + + if (!strstr("ide-cdrom", drive->driver_req)) + goto failed; + + if (drive->media != ide_cdrom && drive->media != ide_optical) + goto failed; + + drive->debug_mask = debug_mask; + drive->irq_handler = cdrom_newpc_intr; + + info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL); + if (info == NULL) { + printk(KERN_ERR PFX "%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); + + info->dev.parent = &drive->gendev; + info->dev.release = ide_cd_release; + dev_set_name(&info->dev, "%s", dev_name(&drive->gendev)); + + if (device_register(&info->dev)) + goto out_free_disk; + + info->drive = drive; + info->driver = &ide_cdrom_driver; + info->disk = g; + + g->private_data = &info->driver; + + drive->driver_data = info; + + g->minors = 1; + g->flags = GENHD_FL_CD | GENHD_FL_REMOVABLE; + if (ide_cdrom_setup(drive)) { + put_device(&info->dev); + goto failed; + } + + ide_cd_read_toc(drive); + g->fops = &idecd_ops; + g->flags |= GENHD_FL_REMOVABLE | GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE; + g->events = DISK_EVENT_MEDIA_CHANGE; + device_add_disk(&drive->gendev, g, NULL); + return 0; + +out_free_disk: + put_disk(g); +out_free_cd: + kfree(info); +failed: + return -ENODEV; +} + +static void __exit ide_cdrom_exit(void) +{ + driver_unregister(&ide_cdrom_driver.gen_driver); +} + +static int __init ide_cdrom_init(void) +{ + printk(KERN_INFO DRV_NAME " driver " IDECD_VERSION "\n"); + return driver_register(&ide_cdrom_driver.gen_driver); +} + +MODULE_ALIAS("ide:*m-cdrom*"); +MODULE_ALIAS("ide-cd"); +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 000000000..a69dc7f61 --- /dev/null +++ b/drivers/ide/ide-cd.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * 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> + +#define IDECD_DEBUG_LOG 0 + +#if IDECD_DEBUG_LOG +#define ide_debug_log(lvl, fmt, args...) __ide_debug_log(lvl, fmt, ## args) +#else +#define ide_debug_log(lvl, fmt, args...) do {} while (0) +#endif + +#define ATAPI_WAIT_WRITE_BUSY (10 * HZ) + +/************************************************************************/ + +#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_SHIFT) +#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) + +/* Capabilities Page size including 8 bytes of Mode Page Header */ +#define ATAPI_CAPABILITIES_PAGE_SIZE (8 + 20) +#define ATAPI_CAPABILITIES_PAGE_PAD_SIZE 4 + +/* Structure of a MSF cdrom address. */ +struct atapi_msf { + u8 reserved; + u8 minute; + u8 second; + u8 frame; +}; + +/* Space to hold the disk TOC. */ +#define MAX_TRACKS 99 +struct atapi_toc_header { + unsigned short toc_length; + u8 first_track; + u8 last_track; +}; + +struct atapi_toc_entry { + u8 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 + u8 track; + u8 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. */ +}; + +/* Extra per-device info for cdrom drives. */ +struct cdrom_info { + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct device dev; + + /* Buffer for table of contents. NULL if we haven't allocated + a TOC buffer for this device yet. */ + + struct atapi_toc *toc; + + u8 max_speed; /* Max speed of the drive. */ + u8 current_speed; /* Current speed of the drive. */ + + /* Per-device info needed by cdrom.c generic driver. */ + struct cdrom_device_info devinfo; + + unsigned long write_timeout; +}; + +/* ide-cd_verbose.c */ +void ide_cd_log_error(const char *, struct request *, struct request_sense *); + +/* ide-cd.c functions used by ide-cd_ioctl.c */ +int ide_cd_queue_pc(ide_drive_t *, const unsigned char *, int, void *, + unsigned *, struct scsi_sense_hdr *, int, req_flags_t); +int ide_cd_read_toc(ide_drive_t *); +int ide_cdrom_get_capabilities(ide_drive_t *, u8 *); +void ide_cdrom_update_speed(ide_drive_t *, u8 *); +int cdrom_check_status(ide_drive_t *, struct scsi_sense_hdr *); + +/* ide-cd_ioctl.c */ +int ide_cdrom_open_real(struct cdrom_device_info *, int); +void ide_cdrom_release_real(struct cdrom_device_info *); +int ide_cdrom_drive_status(struct cdrom_device_info *, int); +unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *, + unsigned int clearing, int slot_nr); +int ide_cdrom_tray_move(struct cdrom_device_info *, int); +int ide_cdrom_lock_door(struct cdrom_device_info *, int); +int ide_cdrom_select_speed(struct cdrom_device_info *, int); +int ide_cdrom_get_last_session(struct cdrom_device_info *, + struct cdrom_multisession *); +int ide_cdrom_get_mcn(struct cdrom_device_info *, struct cdrom_mcn *); +int ide_cdrom_reset(struct cdrom_device_info *cdi); +int ide_cdrom_audio_ioctl(struct cdrom_device_info *, unsigned int, void *); +int ide_cdrom_packet(struct cdrom_device_info *, struct packet_command *); + +#endif /* _IDE_CD_H */ diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c new file mode 100644 index 000000000..46f2df288 --- /dev/null +++ b/drivers/ide/ide-cd_ioctl.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * cdrom.c IOCTLs handling for ide-cd driver. + * + * Copyright (C) 1994-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> + */ + +#include <linux/kernel.h> +#include <linux/cdrom.h> +#include <linux/gfp.h> +#include <linux/ide.h> +#include <scsi/scsi.h> + +#include "ide-cd.h" + +/**************************************************************************** + * Other driver requests (open, close, check media change). + */ +int ide_cdrom_open_real(struct cdrom_device_info *cdi, int purpose) +{ + return 0; +} + +/* + * Close down the device. Invalidate all cached blocks. + */ +void ide_cdrom_release_real(struct cdrom_device_info *cdi) +{ + ide_drive_t *drive = cdi->handle; + + if (!cdi->use_count) + drive->atapi_flags &= ~IDE_AFLAG_TOC_VALID; +} + +/* + * 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 + */ +int ide_cdrom_drive_status(struct cdrom_device_info *cdi, int slot_nr) +{ + ide_drive_t *drive = cdi->handle; + struct media_event_desc med; + struct scsi_sense_hdr sshdr; + int stat; + + if (slot_nr != CDSL_CURRENT) + return -EINVAL; + + stat = cdrom_check_status(drive, &sshdr); + if (!stat || sshdr.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 (sshdr.sense_key == NOT_READY && sshdr.asc == 0x04 + && sshdr.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 (sshdr.sense_key == NOT_READY) { + if (sshdr.asc == 0x3a && sshdr.ascq == 1) + return CDS_NO_DISC; + else + return CDS_TRAY_OPEN; + } + return CDS_DRIVE_NOT_READY; +} + +/* + * ide-cd always generates media changed event if media is missing, which + * makes it impossible to use for proper event reporting, so + * DISK_EVENT_FLAG_UEVENT is cleared in disk->event_flags + * and the following function is used only to trigger + * revalidation and never propagated to userland. + */ +unsigned int ide_cdrom_check_events_real(struct cdrom_device_info *cdi, + unsigned int clearing, int slot_nr) +{ + ide_drive_t *drive = cdi->handle; + int retval; + + if (slot_nr == CDSL_CURRENT) { + (void) cdrom_check_status(drive, NULL); + retval = (drive->dev_flags & IDE_DFLAG_MEDIA_CHANGED) ? 1 : 0; + drive->dev_flags &= ~IDE_DFLAG_MEDIA_CHANGED; + return retval ? DISK_EVENT_MEDIA_CHANGE : 0; + } else { + return 0; + } +} + +/* 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 cdrom_info *cd = drive->driver_data; + struct cdrom_device_info *cdi = &cd->devinfo; + char loej = 0x02; + unsigned char cmd[BLK_MAX_CDB]; + + if ((drive->atapi_flags & IDE_AFLAG_NO_EJECT) && !ejectflag) + return -EDRIVE_CANT_DO_THIS; + + /* reload fails on some drives, if the tray is locked */ + if ((drive->atapi_flags & IDE_AFLAG_DOOR_LOCKED) && ejectflag) + return 0; + + /* only tell drive to close tray if open, if it can do that */ + if (ejectflag && (cdi->mask & CDC_CLOSE_TRAY)) + loej = 0; + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_START_STOP_UNIT; + cmd[4] = loej | (ejectflag != 0); + + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, NULL, 0, 0); +} + +/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */ +static +int ide_cd_lockdoor(ide_drive_t *drive, int lockflag) +{ + struct scsi_sense_hdr sshdr; + int stat; + + /* If the drive cannot lock the door, just pretend. */ + if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) == 0) { + stat = 0; + } else { + unsigned char cmd[BLK_MAX_CDB]; + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; + cmd[4] = lockflag ? 1 : 0; + + stat = ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, + &sshdr, 0, 0); + } + + /* If we got an illegal field error, the drive + probably cannot lock the door. */ + if (stat != 0 && + sshdr.sense_key == ILLEGAL_REQUEST && + (sshdr.asc == 0x24 || sshdr.asc == 0x20)) { + printk(KERN_ERR "%s: door locking not supported\n", + drive->name); + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; + stat = 0; + } + + /* no medium, that's alright. */ + if (stat != 0 && sshdr.sense_key == NOT_READY && sshdr.asc == 0x3a) + stat = 0; + + if (stat == 0) { + if (lockflag) + drive->atapi_flags |= IDE_AFLAG_DOOR_LOCKED; + else + drive->atapi_flags &= ~IDE_AFLAG_DOOR_LOCKED; + } + + return stat; +} + +int ide_cdrom_tray_move(struct cdrom_device_info *cdi, int position) +{ + ide_drive_t *drive = cdi->handle; + + if (position) { + int stat = ide_cd_lockdoor(drive, 0); + + if (stat) + return stat; + } + + return cdrom_eject(drive, !position); +} + +int ide_cdrom_lock_door(struct cdrom_device_info *cdi, int lock) +{ + ide_drive_t *drive = cdi->handle; + + return ide_cd_lockdoor(drive, lock); +} + +/* + * ATAPI devices are free to select the speed you request or any slower + * rate. :-( Requesting too fast a speed will _not_ produce an error. + */ +int ide_cdrom_select_speed(struct cdrom_device_info *cdi, int speed) +{ + ide_drive_t *drive = cdi->handle; + struct cdrom_info *cd = drive->driver_data; + u8 buf[ATAPI_CAPABILITIES_PAGE_SIZE]; + int stat; + unsigned char cmd[BLK_MAX_CDB]; + + if (speed == 0) + speed = 0xffff; /* set to max */ + else + speed *= 177; /* Nx to kbytes/s */ + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_SET_SPEED; + /* Read Drive speed in kbytes/second MSB/LSB */ + cmd[2] = (speed >> 8) & 0xff; + cmd[3] = speed & 0xff; + if ((cdi->mask & (CDC_CD_R | CDC_CD_RW | CDC_DVD_R)) != + (CDC_CD_R | CDC_CD_RW | CDC_DVD_R)) { + /* Write Drive speed in kbytes/second MSB/LSB */ + cmd[4] = (speed >> 8) & 0xff; + cmd[5] = speed & 0xff; + } + + stat = ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, NULL, 0, 0); + + if (!ide_cdrom_get_capabilities(drive, buf)) { + ide_cdrom_update_speed(drive, buf); + cdi->speed = cd->current_speed; + } + + return 0; +} + +int ide_cdrom_get_last_session(struct cdrom_device_info *cdi, + struct cdrom_multisession *ms_info) +{ + struct atapi_toc *toc; + ide_drive_t *drive = cdi->handle; + struct cdrom_info *info = drive->driver_data; + int ret; + + if ((drive->atapi_flags & IDE_AFLAG_TOC_VALID) == 0 || !info->toc) { + ret = ide_cd_read_toc(drive); + if (ret) + return ret; + } + + toc = info->toc; + ms_info->addr.lba = toc->last_session_lba; + ms_info->xa_flag = toc->xa_flag; + + return 0; +} + +int ide_cdrom_get_mcn(struct cdrom_device_info *cdi, + struct cdrom_mcn *mcn_info) +{ + ide_drive_t *drive = cdi->handle; + int stat, mcnlen; + char buf[24]; + unsigned char cmd[BLK_MAX_CDB]; + unsigned len = sizeof(buf); + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_READ_SUBCHANNEL; + cmd[1] = 2; /* MSF addressing */ + cmd[2] = 0x40; /* request subQ data */ + cmd[3] = 2; /* format */ + cmd[8] = len; + + stat = ide_cd_queue_pc(drive, cmd, 0, buf, &len, NULL, 0, 0); + if (stat) + return stat; + + mcnlen = sizeof(mcn_info->medium_catalog_number) - 1; + memcpy(mcn_info->medium_catalog_number, buf + 9, mcnlen); + mcn_info->medium_catalog_number[mcnlen] = '\0'; + + return 0; +} + +int ide_cdrom_reset(struct cdrom_device_info *cdi) +{ + ide_drive_t *drive = cdi->handle; + struct cdrom_info *cd = drive->driver_data; + struct request *rq; + int ret; + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_MISC; + rq->rq_flags = RQF_QUIET; + blk_execute_rq(drive->queue, cd->disk, rq, 0); + ret = scsi_req(rq)->result ? -EIO : 0; + blk_put_request(rq); + /* + * A reset will unlock the door. If it was previously locked, + * lock it again. + */ + if (drive->atapi_flags & IDE_AFLAG_DOOR_LOCKED) + (void)ide_cd_lockdoor(drive, 1); + + return ret; +} + +static int ide_cd_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 ((drive->atapi_flags & IDE_AFLAG_TOC_VALID) == 0) + 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; +} + +static int ide_cd_fake_play_trkind(ide_drive_t *drive, void *arg) +{ + struct cdrom_ti *ti = arg; + struct atapi_toc_entry *first_toc, *last_toc; + unsigned long lba_start, lba_end; + int stat; + unsigned char cmd[BLK_MAX_CDB]; + + stat = ide_cd_get_toc_entry(drive, ti->cdti_trk0, &first_toc); + if (stat) + return stat; + + stat = ide_cd_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; + + memset(cmd, 0, BLK_MAX_CDB); + + cmd[0] = GPCMD_PLAY_AUDIO_MSF; + lba_to_msf(lba_start, &cmd[3], &cmd[4], &cmd[5]); + lba_to_msf(lba_end - 1, &cmd[6], &cmd[7], &cmd[8]); + + return ide_cd_queue_pc(drive, cmd, 0, NULL, NULL, NULL, 0, 0); +} + +static int ide_cd_read_tochdr(ide_drive_t *drive, void *arg) +{ + struct cdrom_info *cd = drive->driver_data; + struct cdrom_tochdr *tochdr = arg; + struct atapi_toc *toc; + int stat; + + /* Make sure our saved TOC is valid. */ + stat = ide_cd_read_toc(drive); + if (stat) + return stat; + + toc = cd->toc; + tochdr->cdth_trk0 = toc->hdr.first_track; + tochdr->cdth_trk1 = toc->hdr.last_track; + + return 0; +} + +static int ide_cd_read_tocentry(ide_drive_t *drive, void *arg) +{ + struct cdrom_tocentry *tocentry = arg; + struct atapi_toc_entry *toce; + int stat; + + stat = ide_cd_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; +} + +int ide_cdrom_audio_ioctl(struct cdrom_device_info *cdi, + unsigned int cmd, void *arg) +{ + ide_drive_t *drive = cdi->handle; + + switch (cmd) { + /* + * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since + * atapi doesn't support it + */ + case CDROMPLAYTRKIND: + return ide_cd_fake_play_trkind(drive, arg); + case CDROMREADTOCHDR: + return ide_cd_read_tochdr(drive, arg); + case CDROMREADTOCENTRY: + return ide_cd_read_tocentry(drive, arg); + default: + return -EINVAL; + } +} + +/* the generic packet interface to cdrom.c */ +int ide_cdrom_packet(struct cdrom_device_info *cdi, + struct packet_command *cgc) +{ + ide_drive_t *drive = cdi->handle; + req_flags_t flags = 0; + unsigned len = cgc->buflen; + + 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. */ + + if (cgc->sshdr) + memset(cgc->sshdr, 0, sizeof(*cgc->sshdr)); + + if (cgc->quiet) + flags |= RQF_QUIET; + + cgc->stat = ide_cd_queue_pc(drive, cgc->cmd, + cgc->data_direction == CGC_DATA_WRITE, + cgc->buffer, &len, + cgc->sshdr, cgc->timeout, flags); + if (!cgc->stat) + cgc->buflen -= len; + return cgc->stat; +} diff --git a/drivers/ide/ide-cd_verbose.c b/drivers/ide/ide-cd_verbose.c new file mode 100644 index 000000000..5ecd5b2f0 --- /dev/null +++ b/drivers/ide/ide-cd_verbose.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Verbose error logging for ATAPI CD/DVD devices. + * + * Copyright (C) 1994-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> + */ + +#include <linux/kernel.h> +#include <linux/blkdev.h> +#include <linux/cdrom.h> +#include <linux/ide.h> +#include <scsi/scsi.h> +#include "ide-cd.h" + +#ifndef CONFIG_BLK_DEV_IDECD_VERBOSE_ERRORS +void ide_cd_log_error(const char *name, struct request *failed_command, + struct request_sense *sense) +{ + /* 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", + name, sense->error_code, sense->sense_key, + sense->asc, sense->ascq); +} +#else +/* 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" }, +}; + +void ide_cd_log_error(const char *name, struct request *failed_command, + struct request_sense *sense) +{ + int i; + const char *s = "bad sense key!"; + char buf[80]; + + printk(KERN_ERR "ATAPI device %s:\n", name); + if (sense->error_code == 0x70) + printk(KERN_CONT " Error: "); + else if (sense->error_code == 0x71) + printk(" Deferred Error: "); + else if (sense->error_code == 0x7f) + printk(KERN_CONT " Vendor-specific Error: "); + else + printk(KERN_CONT " Unknown Error Type: "); + + if (sense->sense_key < ARRAY_SIZE(sense_key_texts)) + s = sense_key_texts[sense->sense_key]; + + printk(KERN_CONT "%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 = ARRAY_SIZE(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 = ARRAY_SIZE(packet_command_texts); + s = NULL; + + while (hi > lo) { + mid = (lo + hi) / 2; + if (packet_command_texts[mid].packet_command == + scsi_req(failed_command)->cmd[0]) { + s = packet_command_texts[mid].text; + break; + } + if (packet_command_texts[mid].packet_command > + scsi_req(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 < BLK_MAX_CDB; i++) + printk(KERN_CONT "%02x ", scsi_req(failed_command)->cmd[i]); + printk(KERN_CONT "\"\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(KERN_CONT " bit %d", sense->sks[0] & 0x07); + + printk(KERN_CONT "\n"); + } +} +#endif diff --git a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c new file mode 100644 index 000000000..f1e922e24 --- /dev/null +++ b/drivers/ide/ide-cs.c @@ -0,0 +1,364 @@ +/*====================================================================== + + A driver for PCMCIA IDE/ATA disk cards + + 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/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/ide.h> +#include <linux/major.h> +#include <linux/delay.h> +#include <asm/io.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> +#include <pcmcia/cisreg.h> +#include <pcmcia/ciscode.h> + +#define DRV_NAME "ide-cs" + +/*====================================================================*/ + +/* Module parameters */ + +MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); +MODULE_DESCRIPTION("PCMCIA ATA/IDE card driver"); +MODULE_LICENSE("Dual MPL/GPL"); + +/*====================================================================*/ + +typedef struct ide_info_t { + struct pcmcia_device *p_dev; + struct ide_host *host; + int ndev; +} ide_info_t; + +static void ide_release(struct pcmcia_device *); +static int ide_config(struct pcmcia_device *); + +static void ide_detach(struct pcmcia_device *p_dev); + +static int ide_probe(struct pcmcia_device *link) +{ + ide_info_t *info; + + dev_dbg(&link->dev, "ide_attach()\n"); + + /* Create new ide device */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->p_dev = link; + link->priv = info; + + link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO | + CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC; + + return ide_config(link); +} /* ide_attach */ + +static void ide_detach(struct pcmcia_device *link) +{ + ide_info_t *info = link->priv; + + dev_dbg(&link->dev, "ide_detach(0x%p)\n", link); + + ide_release(link); + + kfree(info); +} /* ide_detach */ + +static const struct ide_port_ops idecs_port_ops = { + .quirkproc = ide_undecoded_slave, +}; + +static const struct ide_port_info idecs_port_info = { + .port_ops = &idecs_port_ops, + .host_flags = IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_pci, +}; + +static struct ide_host *idecs_register(unsigned long io, unsigned long ctl, + unsigned long irq, struct pcmcia_device *handle) +{ + struct ide_host *host; + ide_hwif_t *hwif; + int i, rc; + struct ide_hw hw, *hws[] = { &hw }; + + if (!request_region(io, 8, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + DRV_NAME, io, io + 7); + return NULL; + } + + if (!request_region(ctl, 1, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", + DRV_NAME, ctl); + release_region(io, 8); + return NULL; + } + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, io, ctl); + hw.irq = irq; + hw.dev = &handle->dev; + + rc = ide_host_add(&idecs_port_info, hws, 1, &host); + if (rc) + goto out_release; + + hwif = host->ports[0]; + + if (hwif->present) + return host; + + /* retry registration in case device is still spinning up */ + for (i = 0; i < 10; i++) { + msleep(100); + ide_port_scan(hwif); + if (hwif->present) + return host; + } + + return host; + +out_release: + release_region(ctl, 1); + release_region(io, 8); + return NULL; +} + +static int pcmcia_check_one_config(struct pcmcia_device *pdev, void *priv_data) +{ + int *is_kme = priv_data; + + if ((pdev->resource[0]->flags & IO_DATA_PATH_WIDTH) + != IO_DATA_PATH_WIDTH_8) { + pdev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO; + } + pdev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH; + pdev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8; + + if (pdev->resource[1]->end) { + pdev->resource[0]->end = 8; + pdev->resource[1]->end = (*is_kme) ? 2 : 1; + } else { + if (pdev->resource[0]->end < 16) + return -ENODEV; + } + + return pcmcia_request_io(pdev); +} + +static int ide_config(struct pcmcia_device *link) +{ + ide_info_t *info = link->priv; + int ret = 0, is_kme = 0; + unsigned long io_base, ctl_base; + struct ide_host *host; + + dev_dbg(&link->dev, "ide_config(0x%p)\n", link); + + is_kme = ((link->manf_id == MANFID_KME) && + ((link->card_id == PRODID_KME_KXLC005_A) || + (link->card_id == PRODID_KME_KXLC005_B))); + + if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) { + link->config_flags &= ~CONF_AUTO_CHECK_VCC; + if (pcmcia_loop_config(link, pcmcia_check_one_config, &is_kme)) + goto failed; /* No suitable config found */ + } + io_base = link->resource[0]->start; + if (link->resource[1]->end) + ctl_base = link->resource[1]->start; + else + ctl_base = link->resource[0]->start + 0x0e; + + if (!link->irq) + goto failed; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + /* disable drive interrupts during IDE probe */ + outb(0x02, ctl_base); + + /* special setup for KXLC005 card */ + if (is_kme) + outb(0x81, ctl_base+1); + + host = idecs_register(io_base, ctl_base, link->irq, link); + if (host == NULL && resource_size(link->resource[0]) == 0x20) { + outb(0x02, ctl_base + 0x10); + host = idecs_register(io_base + 0x10, ctl_base + 0x10, + link->irq, link); + } + + if (host == NULL) + goto failed; + + info->ndev = 1; + info->host = host; + dev_info(&link->dev, "ide-cs: hd%c: Vpp = %d.%d\n", + 'a' + host->ports[0]->index * 2, + link->vpp / 10, link->vpp % 10); + + return 0; + +failed: + ide_release(link); + return -ENODEV; +} /* ide_config */ + +static void ide_release(struct pcmcia_device *link) +{ + ide_info_t *info = link->priv; + struct ide_host *host = info->host; + + dev_dbg(&link->dev, "ide_release(0x%p)\n", link); + + if (info->ndev) { + ide_hwif_t *hwif = host->ports[0]; + unsigned long data_addr, ctl_addr; + + data_addr = hwif->io_ports.data_addr; + ctl_addr = hwif->io_ports.ctl_addr; + + ide_host_remove(host); + info->ndev = 0; + + release_region(ctl_addr, 1); + release_region(data_addr, 8); + } + + pcmcia_disable_device(link); +} /* ide_release */ + + +static const struct pcmcia_device_id ide_ids[] = { + PCMCIA_DEVICE_FUNC_ID(4), + PCMCIA_DEVICE_MANF_CARD(0x0000, 0x0000), /* Corsair */ + PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */ + PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000), /* I-O Data CFA */ + PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */ + PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704), + PCMCIA_DEVICE_MANF_CARD(0x0032, 0x2904), + PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */ + PCMCIA_DEVICE_MANF_CARD(0x004f, 0x0000), /* Kingston */ + PCMCIA_DEVICE_MANF_CARD(0x0097, 0x1620), /* TI emulated */ + PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */ + PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d), + PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */ + PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000), /* Hitachi */ + PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001), + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100), /* Viking CFA */ + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar, Viking CFA */ + PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0), + PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74), + PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "CD-ROM", 0x78f308dc, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("ARGOSY", "PnPIDE", 0x78f308dc, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("CNF ", "CD-ROM", 0x46d7db81, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("CNF CD-M", "CD-ROM", 0x7d93b852, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("Creative Technology Ltd.", "PCMCIA CD-ROM Interface Card", 0xff8c8a45, 0xfe8020c4), + PCMCIA_DEVICE_PROD_ID12("Digital Equipment Corporation.", "Digital Mobile Media CD-ROM", 0x17692a66, 0xef1dcbde), + PCMCIA_DEVICE_PROD_ID12("EXP", "CD+GAME", 0x6f58c983, 0x63c13aaf), + PCMCIA_DEVICE_PROD_ID12("EXP ", "CD-ROM", 0x0a5c52fd, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("EXP ", "PnPIDE", 0x0a5c52fd, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("FREECOM", "PCCARD-IDE", 0x5714cbf7, 0x48e0ab8e), + PCMCIA_DEVICE_PROD_ID12("HITACHI", "FLASH", 0xf4f43949, 0x9eb86aae), + PCMCIA_DEVICE_PROD_ID12("HITACHI", "microdrive", 0xf4f43949, 0xa6d76178), + PCMCIA_DEVICE_PROD_ID12("Hyperstone", "Model1", 0x3d5b9ef5, 0xca6ab420), + PCMCIA_DEVICE_PROD_ID12("IBM", "microdrive", 0xb569a6e5, 0xa6d76178), + PCMCIA_DEVICE_PROD_ID12("IBM", "IBM17JSSFP20", 0xb569a6e5, 0xf2508753), + PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 1GB", 0x2e6d1829, 0x55d5bffb), + PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF CARD 4GB", 0x2e6d1829, 0x531e7d10), + PCMCIA_DEVICE_PROD_ID12("KINGSTON", "CF8GB", 0x2e6d1829, 0xacbe682e), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "CBIDE2 ", 0x547e66dc, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149), + PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674), + PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b), + PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF300", 0x7ed2ad87, 0x7e9e78ee), + PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c), + PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591), + PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728), + PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1), + PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883), + PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d), + PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6), + PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003), + PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF45", 0x709b1bf1, 0xf68b6f32), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS1GCF80", 0x709b1bf1, 0x2a54d4b1), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS2GCF120", 0x709b1bf1, 0x969aa4f2), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF133", 0x709b1bf1, 0x7558f133), + PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS8GCF133", 0x709b1bf1, 0xb2f89b47), + PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852), + PCMCIA_DEVICE_PROD_ID12("WEIDA", "TWTTI", 0xcc7cf69c, 0x212bb918), + PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), + PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), + PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_PROD_ID2("Flash Card", 0x5a362506), + PCMCIA_DEVICE_NULL, +}; +MODULE_DEVICE_TABLE(pcmcia, ide_ids); + +static struct pcmcia_driver ide_cs_driver = { + .owner = THIS_MODULE, + .name = "ide-cs", + .probe = ide_probe, + .remove = ide_detach, + .id_table = ide_ids, +}; + +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); +} + +late_initcall(init_ide_cs); +module_exit(exit_ide_cs); diff --git a/drivers/ide/ide-devsets.c b/drivers/ide/ide-devsets.c new file mode 100644 index 000000000..f2f93ed40 --- /dev/null +++ b/drivers/ide/ide-devsets.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/ide.h> + +DEFINE_MUTEX(ide_setting_mtx); + +ide_devset_get(io_32bit, io_32bit); + +static int set_io_32bit(ide_drive_t *drive, int arg) +{ + if (drive->dev_flags & IDE_DFLAG_NO_IO_32BIT) + return -EPERM; + + if (arg < 0 || arg > 1 + (SUPPORT_VLB_SYNC << 1)) + return -EINVAL; + + drive->io_32bit = arg; + + return 0; +} + +ide_devset_get_flag(ksettings, IDE_DFLAG_KEEP_SETTINGS); + +static int set_ksettings(ide_drive_t *drive, int arg) +{ + if (arg < 0 || arg > 1) + return -EINVAL; + + if (arg) + drive->dev_flags |= IDE_DFLAG_KEEP_SETTINGS; + else + drive->dev_flags &= ~IDE_DFLAG_KEEP_SETTINGS; + + return 0; +} + +ide_devset_get_flag(using_dma, IDE_DFLAG_USING_DMA); + +static int set_using_dma(ide_drive_t *drive, int arg) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + int err = -EPERM; + + if (arg < 0 || arg > 1) + return -EINVAL; + + if (ata_id_has_dma(drive->id) == 0) + goto out; + + if (drive->hwif->dma_ops == NULL) + goto out; + + err = 0; + + if (arg) { + if (ide_set_dma(drive)) + err = -EIO; + } else + ide_dma_off(drive); + +out: + return err; +#else + if (arg < 0 || arg > 1) + return -EINVAL; + + return -EPERM; +#endif +} + +/* + * handle HDIO_SET_PIO_MODE ioctl abusers here, eventually it will go away + */ +static int set_pio_mode_abuse(ide_hwif_t *hwif, u8 req_pio) +{ + switch (req_pio) { + case 202: + case 201: + case 200: + case 102: + case 101: + case 100: + return (hwif->host_flags & IDE_HFLAG_ABUSE_DMA_MODES) ? 1 : 0; + case 9: + case 8: + return (hwif->host_flags & IDE_HFLAG_ABUSE_PREFETCH) ? 1 : 0; + case 7: + case 6: + return (hwif->host_flags & IDE_HFLAG_ABUSE_FAST_DEVSEL) ? 1 : 0; + default: + return 0; + } +} + +static int set_pio_mode(ide_drive_t *drive, int arg) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + + if (arg < 0 || arg > 255) + return -EINVAL; + + if (port_ops == NULL || port_ops->set_pio_mode == NULL || + (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)) + return -ENOSYS; + + if (set_pio_mode_abuse(drive->hwif, arg)) { + drive->pio_mode = arg + XFER_PIO_0; + + if (arg == 8 || arg == 9) { + unsigned long flags; + + /* take lock for IDE_DFLAG_[NO_]UNMASK/[NO_]IO_32BIT */ + spin_lock_irqsave(&hwif->lock, flags); + port_ops->set_pio_mode(hwif, drive); + spin_unlock_irqrestore(&hwif->lock, flags); + } else + port_ops->set_pio_mode(hwif, drive); + } else { + int keep_dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); + + ide_set_pio(drive, arg); + + if (hwif->host_flags & IDE_HFLAG_SET_PIO_MODE_KEEP_DMA) { + if (keep_dma) + ide_dma_on(drive); + } + } + + return 0; +} + +ide_devset_get_flag(unmaskirq, IDE_DFLAG_UNMASK); + +static int set_unmaskirq(ide_drive_t *drive, int arg) +{ + if (drive->dev_flags & IDE_DFLAG_NO_UNMASK) + return -EPERM; + + if (arg < 0 || arg > 1) + return -EINVAL; + + if (arg) + drive->dev_flags |= IDE_DFLAG_UNMASK; + else + drive->dev_flags &= ~IDE_DFLAG_UNMASK; + + return 0; +} + +ide_ext_devset_rw_sync(io_32bit, io_32bit); +ide_ext_devset_rw_sync(keepsettings, ksettings); +ide_ext_devset_rw_sync(unmaskirq, unmaskirq); +ide_ext_devset_rw_sync(using_dma, using_dma); +__IDE_DEVSET(pio_mode, DS_SYNC, NULL, set_pio_mode); + +int ide_devset_execute(ide_drive_t *drive, const struct ide_devset *setting, + int arg) +{ + struct request_queue *q = drive->queue; + struct request *rq; + int ret = 0; + + if (!(setting->flags & DS_SYNC)) + return setting->set(drive, arg); + + rq = blk_get_request(q, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_MISC; + scsi_req(rq)->cmd_len = 5; + scsi_req(rq)->cmd[0] = REQ_DEVSET_EXEC; + *(int *)&scsi_req(rq)->cmd[1] = arg; + ide_req(rq)->special = setting->set; + + blk_execute_rq(q, NULL, rq, 0); + ret = scsi_req(rq)->result; + blk_put_request(rq); + + return ret; +} + +ide_startstop_t ide_do_devset(ide_drive_t *drive, struct request *rq) +{ + int err, (*setfunc)(ide_drive_t *, int) = ide_req(rq)->special; + + err = setfunc(drive, *(int *)&scsi_req(rq)->cmd[1]); + if (err) + scsi_req(rq)->result = err; + ide_complete_rq(drive, 0, blk_rq_bytes(rq)); + return ide_stopped; +} diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c new file mode 100644 index 000000000..34b944108 --- /dev/null +++ b/drivers/ide/ide-disk.c @@ -0,0 +1,795 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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 + * Copyright (C) 2003-2005, 2007 Bartlomiej Zolnierkiewicz + */ + +/* + * 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. + */ + +#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/mutex.h> +#include <linux/leds.h> +#include <linux/ide.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <linux/uaccess.h> +#include <asm/io.h> +#include <asm/div64.h> + +#include "ide-disk.h" + +static const u8 ide_rw_cmds[] = { + ATA_CMD_READ_MULTI, + ATA_CMD_WRITE_MULTI, + ATA_CMD_READ_MULTI_EXT, + ATA_CMD_WRITE_MULTI_EXT, + ATA_CMD_PIO_READ, + ATA_CMD_PIO_WRITE, + ATA_CMD_PIO_READ_EXT, + ATA_CMD_PIO_WRITE_EXT, + ATA_CMD_READ, + ATA_CMD_WRITE, + ATA_CMD_READ_EXT, + ATA_CMD_WRITE_EXT, +}; + +static void ide_tf_set_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 dma) +{ + u8 index, lba48, write; + + lba48 = (cmd->tf_flags & IDE_TFLAG_LBA48) ? 2 : 0; + write = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 1 : 0; + + if (dma) { + cmd->protocol = ATA_PROT_DMA; + index = 8; + } else { + cmd->protocol = ATA_PROT_PIO; + if (drive->mult_count) { + cmd->tf_flags |= IDE_TFLAG_MULTI_PIO; + index = 0; + } else + index = 4; + } + + cmd->tf.command = ide_rw_cmds[index + lba48 + write]; +} + +/* + * __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 = drive->hwif; + u16 nsectors = (u16)blk_rq_sectors(rq); + u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48); + u8 dma = !!(drive->dev_flags & IDE_DFLAG_USING_DMA); + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + ide_startstop_t rc; + + if ((hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && lba48 && dma) { + if (block + blk_rq_sectors(rq) > 1ULL << 28) + dma = 0; + else + lba48 = 0; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + if (drive->dev_flags & IDE_DFLAG_LBA) { + if (lba48) { + pr_debug("%s: LBA=0x%012llx\n", drive->name, + (unsigned long long)block); + + tf->nsect = nsectors & 0xff; + tf->lbal = (u8) block; + tf->lbam = (u8)(block >> 8); + tf->lbah = (u8)(block >> 16); + tf->device = ATA_LBA; + + tf = &cmd.hob; + tf->nsect = (nsectors >> 8) & 0xff; + tf->lbal = (u8)(block >> 24); + if (sizeof(block) != 4) { + tf->lbam = (u8)((u64)block >> 32); + tf->lbah = (u8)((u64)block >> 40); + } + + cmd.valid.out.hob = IDE_VALID_OUT_HOB; + cmd.valid.in.hob = IDE_VALID_IN_HOB; + cmd.tf_flags |= IDE_TFLAG_LBA48; + } else { + tf->nsect = nsectors & 0xff; + tf->lbal = block; + tf->lbam = block >>= 8; + tf->lbah = block >>= 8; + tf->device = ((block >> 8) & 0xf) | ATA_LBA; + } + } else { + unsigned int sect, head, cyl, track; + + track = (int)block / drive->sect; + sect = (int)block % drive->sect + 1; + head = track % drive->head; + cyl = track / drive->head; + + pr_debug("%s: CHS=%u/%u/%u\n", drive->name, cyl, head, sect); + + tf->nsect = nsectors & 0xff; + tf->lbal = sect; + tf->lbam = cyl; + tf->lbah = cyl >> 8; + tf->device = head; + } + + cmd.tf_flags |= IDE_TFLAG_FS; + + if (rq_data_dir(rq)) + cmd.tf_flags |= IDE_TFLAG_WRITE; + + ide_tf_set_cmd(drive, &cmd, dma); + cmd.rq = rq; + + if (dma == 0) { + ide_init_sg_cmd(&cmd, nsectors << 9); + ide_map_sg(drive, &cmd); + } + + rc = do_rw_taskfile(drive, &cmd); + + if (rc == ide_stopped && dma) { + /* fallback to PIO */ + cmd.tf_flags |= IDE_TFLAG_DMA_PIO_FALLBACK; + ide_tf_set_cmd(drive, &cmd, 0); + ide_init_sg_cmd(&cmd, nsectors << 9); + rc = do_rw_taskfile(drive, &cmd); + } + + return rc; +} + +/* + * 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 = drive->hwif; + + BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED); + BUG_ON(blk_rq_is_passthrough(rq)); + + ledtrig_disk_activity(rq_data_dir(rq) == WRITE); + + pr_debug("%s: %sing: block=%llu, sectors=%u\n", + drive->name, rq_data_dir(rq) == READ ? "read" : "writ", + (unsigned long long)block, blk_rq_sectors(rq)); + + 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 u64 idedisk_read_native_max_address(ide_drive_t *drive, int lba48) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + u64 addr = 0; + + memset(&cmd, 0, sizeof(cmd)); + if (lba48) + tf->command = ATA_CMD_READ_NATIVE_MAX_EXT; + else + tf->command = ATA_CMD_READ_NATIVE_MAX; + tf->device = ATA_LBA; + + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + if (lba48) { + cmd.valid.out.hob = IDE_VALID_OUT_HOB; + cmd.valid.in.hob = IDE_VALID_IN_HOB; + cmd.tf_flags = IDE_TFLAG_LBA48; + } + + ide_no_data_taskfile(drive, &cmd); + + /* if OK, compute maximum address value */ + if (!(tf->status & ATA_ERR)) + addr = ide_get_lba_addr(&cmd, lba48) + 1; + + return addr; +} + +/* + * Sets maximum virtual LBA address of the drive. + * Returns new maximum virtual LBA address (> 0) or 0 on failure. + */ +static u64 idedisk_set_max_address(ide_drive_t *drive, u64 addr_req, int lba48) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + u64 addr_set = 0; + + addr_req--; + + memset(&cmd, 0, sizeof(cmd)); + tf->lbal = (addr_req >> 0) & 0xff; + tf->lbam = (addr_req >>= 8) & 0xff; + tf->lbah = (addr_req >>= 8) & 0xff; + if (lba48) { + cmd.hob.lbal = (addr_req >>= 8) & 0xff; + cmd.hob.lbam = (addr_req >>= 8) & 0xff; + cmd.hob.lbah = (addr_req >>= 8) & 0xff; + tf->command = ATA_CMD_SET_MAX_EXT; + } else { + tf->device = (addr_req >>= 8) & 0x0f; + tf->command = ATA_CMD_SET_MAX; + } + tf->device |= ATA_LBA; + + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + if (lba48) { + cmd.valid.out.hob = IDE_VALID_OUT_HOB; + cmd.valid.in.hob = IDE_VALID_IN_HOB; + cmd.tf_flags = IDE_TFLAG_LBA48; + } + + ide_no_data_taskfile(drive, &cmd); + + /* if OK, compute maximum address value */ + if (!(tf->status & ATA_ERR)) + addr_set = ide_get_lba_addr(&cmd, lba48) + 1; + + 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; +} + +/* + * Some disks report total number of sectors instead of + * maximum sector address. We list them here. + */ +static const struct drive_list_entry hpa_list[] = { + { "ST340823A", NULL }, + { "ST320413A", NULL }, + { "ST310211A", NULL }, + { NULL, NULL } +}; + +static u64 ide_disk_hpa_get_native_capacity(ide_drive_t *drive, int lba48) +{ + u64 capacity, set_max; + + capacity = drive->capacity64; + set_max = idedisk_read_native_max_address(drive, lba48); + + if (ide_in_drive_list(drive->id, hpa_list)) { + /* + * Since we are inclusive wrt to firmware revisions do this + * extra check and apply the workaround only when needed. + */ + if (set_max == capacity + 1) + set_max--; + } + + return set_max; +} + +static u64 ide_disk_hpa_set_capacity(ide_drive_t *drive, u64 set_max, int lba48) +{ + set_max = idedisk_set_max_address(drive, set_max, lba48); + if (set_max) + drive->capacity64 = set_max; + + return set_max; +} + +static void idedisk_check_hpa(ide_drive_t *drive) +{ + u64 capacity, set_max; + int lba48 = ata_id_lba48_enabled(drive->id); + + capacity = drive->capacity64; + set_max = ide_disk_hpa_get_native_capacity(drive, lba48); + + if (set_max <= capacity) + return; + + drive->probed_capacity = set_max; + + 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 ((drive->dev_flags & IDE_DFLAG_NOHPA) == 0) + return; + + set_max = ide_disk_hpa_set_capacity(drive, set_max, lba48); + if (set_max) + printk(KERN_INFO "%s: Host Protected Area disabled.\n", + drive->name); +} + +static int ide_disk_get_capacity(ide_drive_t *drive) +{ + u16 *id = drive->id; + int lba; + + if (ata_id_lba48_enabled(id)) { + /* drive speaks 48-bit LBA */ + lba = 1; + drive->capacity64 = ata_id_u64(id, ATA_ID_LBA_CAPACITY_2); + } else if (ata_id_has_lba(id) && ata_id_is_lba_capacity_ok(id)) { + /* drive speaks 28-bit LBA */ + lba = 1; + drive->capacity64 = ata_id_u32(id, ATA_ID_LBA_CAPACITY); + } else { + /* drive speaks boring old 28-bit CHS */ + lba = 0; + drive->capacity64 = drive->cyl * drive->head * drive->sect; + } + + drive->probed_capacity = drive->capacity64; + + if (lba) { + drive->dev_flags |= IDE_DFLAG_LBA; + + /* + * If this device supports the Host Protected Area feature set, + * then we may need to change our opinion about its capacity. + */ + if (ata_id_hpa_enabled(id)) + idedisk_check_hpa(drive); + } + + /* limit drive capacity to 137GB if LBA48 cannot be used */ + if ((drive->dev_flags & IDE_DFLAG_LBA48) == 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->probed_capacity = drive->capacity64 = 1ULL << 28; + } + + if ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA) && + (drive->dev_flags & IDE_DFLAG_LBA48)) { + 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->dev_flags &= ~IDE_DFLAG_LBA48; + } + + return 0; +} + +static void ide_disk_unlock_native_capacity(ide_drive_t *drive) +{ + u16 *id = drive->id; + int lba48 = ata_id_lba48_enabled(id); + + if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 || + ata_id_hpa_enabled(id) == 0) + return; + + /* + * according to the spec the SET MAX ADDRESS command shall be + * immediately preceded by a READ NATIVE MAX ADDRESS command + */ + if (!ide_disk_hpa_get_native_capacity(drive, lba48)) + return; + + if (ide_disk_hpa_set_capacity(drive, drive->probed_capacity, lba48)) + drive->dev_flags |= IDE_DFLAG_NOHPA; /* disable HPA on resume */ +} + +static bool idedisk_prep_rq(ide_drive_t *drive, struct request *rq) +{ + struct ide_cmd *cmd; + + if (req_op(rq) != REQ_OP_FLUSH) + return true; + + if (ide_req(rq)->special) { + cmd = ide_req(rq)->special; + memset(cmd, 0, sizeof(*cmd)); + } else { + cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); + } + + /* FIXME: map struct ide_taskfile on rq->cmd[] */ + BUG_ON(cmd == NULL); + + if (ata_id_flush_ext_enabled(drive->id) && + (drive->capacity64 >= (1UL << 28))) + cmd->tf.command = ATA_CMD_FLUSH_EXT; + else + cmd->tf.command = ATA_CMD_FLUSH; + cmd->valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd->tf_flags = IDE_TFLAG_DYN; + cmd->protocol = ATA_PROT_NODATA; + rq->cmd_flags &= ~REQ_OP_MASK; + rq->cmd_flags |= REQ_OP_DRV_OUT; + ide_req(rq)->type = ATA_PRIV_TASKFILE; + ide_req(rq)->special = cmd; + cmd->rq = rq; + + return true; +} + +ide_devset_get(multcount, mult_count); + +/* + * 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 (arg < 0 || arg > (drive->id[ATA_ID_MAX_MULTSECT] & 0xff)) + return -EINVAL; + + if (drive->special_flags & IDE_SFLAG_SET_MULTMODE) + return -EBUSY; + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_TASKFILE; + + drive->mult_req = arg; + drive->special_flags |= IDE_SFLAG_SET_MULTMODE; + blk_execute_rq(drive->queue, NULL, rq, 0); + blk_put_request(rq); + + return (drive->mult_count == arg) ? 0 : -EIO; +} + +ide_devset_get_flag(nowerr, IDE_DFLAG_NOWERR); + +static int set_nowerr(ide_drive_t *drive, int arg) +{ + if (arg < 0 || arg > 1) + return -EINVAL; + + if (arg) + drive->dev_flags |= IDE_DFLAG_NOWERR; + else + drive->dev_flags &= ~IDE_DFLAG_NOWERR; + + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; + + return 0; +} + +static int ide_do_setfeature(ide_drive_t *drive, u8 feature, u8 nsect) +{ + struct ide_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tf.feature = feature; + cmd.tf.nsect = nsect; + cmd.tf.command = ATA_CMD_SET_FEATURES; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + return ide_no_data_taskfile(drive, &cmd); +} + +static void update_flush(ide_drive_t *drive) +{ + u16 *id = drive->id; + bool wc = false; + + if (drive->dev_flags & IDE_DFLAG_WCACHE) { + unsigned long long capacity; + int barrier; + /* + * 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. + */ + capacity = ide_gd_capacity(drive); + barrier = ata_id_flush_enabled(id) && + (drive->dev_flags & IDE_DFLAG_NOFLUSH) == 0 && + ((drive->dev_flags & IDE_DFLAG_LBA48) == 0 || + capacity <= (1ULL << 28) || + ata_id_flush_ext_enabled(id)); + + printk(KERN_INFO "%s: cache flushes %ssupported\n", + drive->name, barrier ? "" : "not "); + + if (barrier) { + wc = true; + drive->prep_rq = idedisk_prep_rq; + } + } + + blk_queue_write_cache(drive->queue, wc, false); +} + +ide_devset_get_flag(wcache, IDE_DFLAG_WCACHE); + +static int set_wcache(ide_drive_t *drive, int arg) +{ + int err = 1; + + if (arg < 0 || arg > 1) + return -EINVAL; + + if (ata_id_flush_enabled(drive->id)) { + err = ide_do_setfeature(drive, + arg ? SETFEATURES_WC_ON : SETFEATURES_WC_OFF, 0); + if (err == 0) { + if (arg) + drive->dev_flags |= IDE_DFLAG_WCACHE; + else + drive->dev_flags &= ~IDE_DFLAG_WCACHE; + } + } + + update_flush(drive); + + return err; +} + +static int do_idedisk_flushcache(ide_drive_t *drive) +{ + struct ide_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + if (ata_id_flush_ext_enabled(drive->id)) + cmd.tf.command = ATA_CMD_FLUSH_EXT; + else + cmd.tf.command = ATA_CMD_FLUSH; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + return ide_no_data_taskfile(drive, &cmd); +} + +ide_devset_get(acoustic, acoustic); + +static int set_acoustic(ide_drive_t *drive, int arg) +{ + if (arg < 0 || arg > 254) + return -EINVAL; + + ide_do_setfeature(drive, + arg ? SETFEATURES_AAM_ON : SETFEATURES_AAM_OFF, arg); + + drive->acoustic = arg; + + return 0; +} + +ide_devset_get_flag(addressing, IDE_DFLAG_LBA48); + +/* + * drive->addressing: + * 0: 28-bit + * 1: 48-bit + * 2: 48-bit capable doing 28-bit + */ +static int set_addressing(ide_drive_t *drive, int arg) +{ + if (arg < 0 || arg > 2) + return -EINVAL; + + if (arg && ((drive->hwif->host_flags & IDE_HFLAG_NO_LBA48) || + ata_id_lba48_enabled(drive->id) == 0)) + return -EIO; + + if (arg == 2) + arg = 0; + + if (arg) + drive->dev_flags |= IDE_DFLAG_LBA48; + else + drive->dev_flags &= ~IDE_DFLAG_LBA48; + + return 0; +} + +ide_ext_devset_rw(acoustic, acoustic); +ide_ext_devset_rw(address, addressing); +ide_ext_devset_rw(multcount, multcount); +ide_ext_devset_rw(wcache, wcache); + +ide_ext_devset_rw_sync(nowerr, nowerr); + +static int ide_disk_check(ide_drive_t *drive, const char *s) +{ + return 1; +} + +static void ide_disk_setup(ide_drive_t *drive) +{ + struct ide_disk_obj *idkp = drive->driver_data; + struct request_queue *q = drive->queue; + ide_hwif_t *hwif = drive->hwif; + u16 *id = drive->id; + char *m = (char *)&id[ATA_ID_PROD]; + unsigned long long capacity; + + ide_proc_register_driver(drive, idkp->driver); + + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) + return; + + if (drive->dev_flags & IDE_DFLAG_REMOVABLE) { + /* + * Removable disks (eg. SYQUEST); ignore 'WD' drives + */ + if (m[0] != 'W' || m[1] != 'D') + drive->dev_flags |= IDE_DFLAG_DOORLOCKING; + } + + (void)set_addressing(drive, 1); + + if (drive->dev_flags & IDE_DFLAG_LBA48) { + int max_s = 2048; + + if (max_s > hwif->rqsize) + max_s = hwif->rqsize; + + blk_queue_max_hw_sectors(q, max_s); + } + + printk(KERN_INFO "%s: max request size: %dKiB\n", drive->name, + queue_max_sectors(q) / 2); + + if (ata_id_is_ssd(id)) { + blk_queue_flag_set(QUEUE_FLAG_NONROT, q); + blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, q); + } + + /* calculate drive capacity, and select LBA if possible */ + ide_disk_get_capacity(drive); + + /* + * if possible, give fdisk access to more of the drive, + * by correcting bios_cyls: + */ + capacity = ide_gd_capacity(drive); + + if ((drive->dev_flags & IDE_DFLAG_FORCED_GEOM) == 0) { + if (ata_id_lba48_enabled(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[ATA_ID_BUF_SIZE]) + printk(KERN_CONT " w/%dKiB Cache", id[ATA_ID_BUF_SIZE] / 2); + + printk(KERN_CONT ", CHS=%d/%d/%d\n", + drive->bios_cyl, drive->bios_head, drive->bios_sect); + + /* write cache enabled? */ + if ((id[ATA_ID_CSFO] & 1) || ata_id_wcache_enabled(id)) + drive->dev_flags |= IDE_DFLAG_WCACHE; + + set_wcache(drive, 1); + + if ((drive->dev_flags & IDE_DFLAG_LBA) == 0 && + (drive->head == 0 || drive->head > 16)) + printk(KERN_ERR "%s: invalid geometry: %d physical heads?\n", + drive->name, drive->head); +} + +static void ide_disk_flush(ide_drive_t *drive) +{ + if (ata_id_flush_enabled(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) + return; + + if (do_idedisk_flushcache(drive)) + printk(KERN_INFO "%s: wcache flush failed!\n", drive->name); +} + +static int ide_disk_init_media(ide_drive_t *drive, struct gendisk *disk) +{ + return 0; +} + +static int ide_disk_set_doorlock(ide_drive_t *drive, struct gendisk *disk, + int on) +{ + struct ide_cmd cmd; + int ret; + + if ((drive->dev_flags & IDE_DFLAG_DOORLOCKING) == 0) + return 0; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tf.command = on ? ATA_CMD_MEDIA_LOCK : ATA_CMD_MEDIA_UNLOCK; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + ret = ide_no_data_taskfile(drive, &cmd); + + if (ret) + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; + + return ret; +} + +const struct ide_disk_ops ide_ata_disk_ops = { + .check = ide_disk_check, + .unlock_native_capacity = ide_disk_unlock_native_capacity, + .get_capacity = ide_disk_get_capacity, + .setup = ide_disk_setup, + .flush = ide_disk_flush, + .init_media = ide_disk_init_media, + .set_doorlock = ide_disk_set_doorlock, + .do_request = ide_do_rw_disk, + .ioctl = ide_disk_ioctl, + .compat_ioctl = ide_disk_ioctl, +}; diff --git a/drivers/ide/ide-disk.h b/drivers/ide/ide-disk.h new file mode 100644 index 000000000..0e8cc18bf --- /dev/null +++ b/drivers/ide/ide-disk.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __IDE_DISK_H +#define __IDE_DISK_H + +#include "ide-gd.h" + +#ifdef CONFIG_IDE_GD_ATA +/* ide-disk.c */ +extern const struct ide_disk_ops ide_ata_disk_ops; +ide_decl_devset(address); +ide_decl_devset(multcount); +ide_decl_devset(nowerr); +ide_decl_devset(wcache); +ide_decl_devset(acoustic); + +/* ide-disk_ioctl.c */ +int ide_disk_ioctl(ide_drive_t *, struct block_device *, fmode_t, unsigned int, + unsigned long); + +#ifdef CONFIG_IDE_PROC_FS +/* ide-disk_proc.c */ +extern ide_proc_entry_t ide_disk_proc[]; +extern const struct ide_proc_devset ide_disk_settings[]; +#endif +#else +#define ide_disk_proc NULL +#define ide_disk_settings NULL +#endif + +#endif /* __IDE_DISK_H */ diff --git a/drivers/ide/ide-disk_ioctl.c b/drivers/ide/ide-disk_ioctl.c new file mode 100644 index 000000000..2c45616cf --- /dev/null +++ b/drivers/ide/ide-disk_ioctl.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/ide.h> +#include <linux/hdreg.h> +#include <linux/mutex.h> + +#include "ide-disk.h" + +static DEFINE_MUTEX(ide_disk_ioctl_mutex); +static const struct ide_ioctl_devset ide_disk_ioctl_settings[] = { +{ HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, &ide_devset_address }, +{ HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, &ide_devset_multcount }, +{ HDIO_GET_NOWERR, HDIO_SET_NOWERR, &ide_devset_nowerr }, +{ HDIO_GET_WCACHE, HDIO_SET_WCACHE, &ide_devset_wcache }, +{ HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, &ide_devset_acoustic }, +{ 0 } +}; + +int ide_disk_ioctl(ide_drive_t *drive, struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + int err; + + mutex_lock(&ide_disk_ioctl_mutex); + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_disk_ioctl_settings); + if (err != -EOPNOTSUPP) + goto out; + + err = generic_ide_ioctl(drive, bdev, cmd, arg); +out: + mutex_unlock(&ide_disk_ioctl_mutex); + return err; +} diff --git a/drivers/ide/ide-disk_proc.c b/drivers/ide/ide-disk_proc.c new file mode 100644 index 000000000..95d239b2f --- /dev/null +++ b/drivers/ide/ide-disk_proc.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/ide.h> +#include <linux/slab.h> +#include <linux/export.h> +#include <linux/seq_file.h> + +#include "ide-disk.h" + +static int smart_enable(ide_drive_t *drive) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + + memset(&cmd, 0, sizeof(cmd)); + tf->feature = ATA_SMART_ENABLE; + tf->lbam = ATA_SMART_LBAM_PASS; + tf->lbah = ATA_SMART_LBAH_PASS; + tf->command = ATA_CMD_SMART; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + return ide_no_data_taskfile(drive, &cmd); +} + +static int get_smart_data(ide_drive_t *drive, u8 *buf, u8 sub_cmd) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + + memset(&cmd, 0, sizeof(cmd)); + tf->feature = sub_cmd; + tf->nsect = 0x01; + tf->lbam = ATA_SMART_LBAM_PASS; + tf->lbah = ATA_SMART_LBAH_PASS; + tf->command = ATA_CMD_SMART; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + cmd.protocol = ATA_PROT_PIO; + + return ide_raw_taskfile(drive, &cmd, buf, 1); +} + +static int idedisk_cache_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *) m->private; + + if (drive->dev_flags & IDE_DFLAG_ID_READ) + seq_printf(m, "%i\n", drive->id[ATA_ID_BUF_SIZE] / 2); + else + seq_printf(m, "(none)\n"); + return 0; +} + +static int idedisk_capacity_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t*drive = (ide_drive_t *)m->private; + + seq_printf(m, "%llu\n", (long long)ide_gd_capacity(drive)); + return 0; +} + +static int __idedisk_proc_show(struct seq_file *m, ide_drive_t *drive, u8 sub_cmd) +{ + u8 *buf; + + buf = kmalloc(SECTOR_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + (void)smart_enable(drive); + + if (get_smart_data(drive, buf, sub_cmd) == 0) { + __le16 *val = (__le16 *)buf; + int i; + + for (i = 0; i < SECTOR_SIZE / 2; i++) { + seq_printf(m, "%04x%c", le16_to_cpu(val[i]), + (i % 8) == 7 ? '\n' : ' '); + } + } + kfree(buf); + return 0; +} + +static int idedisk_sv_proc_show(struct seq_file *m, void *v) +{ + return __idedisk_proc_show(m, m->private, ATA_SMART_READ_VALUES); +} + +static int idedisk_st_proc_show(struct seq_file *m, void *v) +{ + return __idedisk_proc_show(m, m->private, ATA_SMART_READ_THRESHOLDS); +} + +ide_proc_entry_t ide_disk_proc[] = { + { "cache", S_IFREG|S_IRUGO, idedisk_cache_proc_show }, + { "capacity", S_IFREG|S_IRUGO, idedisk_capacity_proc_show }, + { "geometry", S_IFREG|S_IRUGO, ide_geometry_proc_show }, + { "smart_values", S_IFREG|S_IRUSR, idedisk_sv_proc_show }, + { "smart_thresholds", S_IFREG|S_IRUSR, idedisk_st_proc_show }, + {} +}; + +ide_devset_rw_field(bios_cyl, bios_cyl); +ide_devset_rw_field(bios_head, bios_head); +ide_devset_rw_field(bios_sect, bios_sect); +ide_devset_rw_field(failures, failures); +ide_devset_rw_field(lun, lun); +ide_devset_rw_field(max_failures, max_failures); + +const struct ide_proc_devset ide_disk_settings[] = { + IDE_PROC_DEVSET(acoustic, 0, 254), + IDE_PROC_DEVSET(address, 0, 2), + IDE_PROC_DEVSET(bios_cyl, 0, 65535), + IDE_PROC_DEVSET(bios_head, 0, 255), + IDE_PROC_DEVSET(bios_sect, 0, 63), + IDE_PROC_DEVSET(failures, 0, 65535), + IDE_PROC_DEVSET(lun, 0, 7), + IDE_PROC_DEVSET(max_failures, 0, 65535), + IDE_PROC_DEVSET(multcount, 0, 16), + IDE_PROC_DEVSET(nowerr, 0, 1), + IDE_PROC_DEVSET(wcache, 0, 1), + { NULL }, +}; diff --git a/drivers/ide/ide-dma-sff.c b/drivers/ide/ide-dma-sff.c new file mode 100644 index 000000000..b7c2c0bd1 --- /dev/null +++ b/drivers/ide/ide-dma-sff.c @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> + +/** + * 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. + */ + +int config_drive_for_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u16 *id = drive->id; + + if (drive->media != ide_disk) { + if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA) + return 0; + } + + /* + * Enable DMA on any drive that has + * UltraDMA (mode 0/1/2/3/4/5/6) enabled + */ + if ((id[ATA_ID_FIELD_VALID] & 4) && + ((id[ATA_ID_UDMA_MODES] >> 8) & 0x7f)) + return 1; + + /* + * Enable DMA on any drive that has mode2 DMA + * (multi or single) enabled + */ + if ((id[ATA_ID_MWDMA_MODES] & 0x404) == 0x404 || + (id[ATA_ID_SWDMA_MODES] & 0x404) == 0x404) + return 1; + + /* Consult the list of known "good" drives */ + if (ide_dma_good_drive(drive)) + return 1; + + return 0; +} + +u8 ide_dma_sff_read_status(ide_hwif_t *hwif) +{ + unsigned long addr = hwif->dma_base + ATA_DMA_STATUS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)addr); + else + return inb(addr); +} +EXPORT_SYMBOL_GPL(ide_dma_sff_read_status); + +static void ide_dma_sff_write_status(ide_hwif_t *hwif, u8 val) +{ + unsigned long addr = hwif->dma_base + ATA_DMA_STATUS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(val, (void __iomem *)addr); + else + outb(val, addr); +} + +/** + * ide_dma_host_set - Enable/disable DMA on a host + * @drive: drive to control + * + * Enable/disable DMA on an IDE controller following generic + * bus-mastering IDE controller behaviour. + */ + +void ide_dma_host_set(ide_drive_t *drive, int on) +{ + ide_hwif_t *hwif = drive->hwif; + u8 unit = drive->dn & 1; + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + + if (on) + dma_stat |= (1 << (5 + unit)); + else + dma_stat &= ~(1 << (5 + unit)); + + ide_dma_sff_write_status(hwif, dma_stat); +} +EXPORT_SYMBOL_GPL(ide_dma_host_set); + +/** + * 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. + * + * Most chipsets correctly interpret a length of 0x0000 as 64KB, + * but at least one (e.g. CS5530) misinterprets it as zero (!). + * So we break the 64KB entry into two 32KB entries instead. + * + * 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 ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + __le32 *table = (__le32 *)hwif->dmatable_cpu; + unsigned int count = 0; + int i; + struct scatterlist *sg; + u8 is_trm290 = !!(hwif->host_flags & IDE_HFLAG_TRM290); + + for_each_sg(hwif->sg_table, sg, cmd->sg_nents, i) { + u32 cur_addr, cur_len, xcount, bcount; + + 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) + goto use_pio_instead; + + 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; + else if (xcount == 0x0000) { + if (count++ >= PRD_ENTRIES) + 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; + } + } + + if (count) { + if (!is_trm290) + *--table |= cpu_to_le32(0x80000000); + return count; + } + +use_pio_instead: + printk(KERN_ERR "%s: %s\n", drive->name, + count ? "DMA table too small" : "empty DMA table?"); + + return 0; /* revert to PIO for this request */ +} +EXPORT_SYMBOL_GPL(ide_build_dmatable); + +/** + * ide_dma_setup - begin a DMA phase + * @drive: target device + * @cmd: command + * + * 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, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR; + u8 dma_stat; + + /* fall back to pio! */ + if (ide_build_dmatable(drive, cmd) == 0) { + ide_map_sg(drive, cmd); + return 1; + } + + /* PRD table */ + if (mmio) + writel(hwif->dmatable_dma, + (void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS)); + else + outl(hwif->dmatable_dma, hwif->dma_base + ATA_DMA_TABLE_OFS); + + /* specify r/w */ + if (mmio) + writeb(rw, (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + else + outb(rw, hwif->dma_base + ATA_DMA_CMD); + + /* read DMA status for INTR & ERROR flags */ + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + + /* clear INTR & ERROR flags */ + ide_dma_sff_write_status(hwif, dma_stat | ATA_DMA_ERR | ATA_DMA_INTR); + + return 0; +} +EXPORT_SYMBOL_GPL(ide_dma_setup); + +/** + * ide_dma_sff_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. + */ + +int ide_dma_sff_timer_expiry(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + + printk(KERN_WARNING "%s: %s: DMA status (0x%02x)\n", + drive->name, __func__, dma_stat); + + if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ + return WAIT_CMD; + + hwif->expiry = NULL; /* one free ride for now */ + + if (dma_stat & ATA_DMA_ERR) /* ERROR */ + return -1; + + if (dma_stat & ATA_DMA_ACTIVE) /* DMAing */ + return WAIT_CMD; + + if (dma_stat & ATA_DMA_INTR) /* Got an Interrupt */ + return WAIT_CMD; + + return 0; /* Status is unknown -- reset the bus */ +} +EXPORT_SYMBOL_GPL(ide_dma_sff_timer_expiry); + +void ide_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_cmd; + + /* 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. + */ + if (hwif->host_flags & IDE_HFLAG_MMIO) { + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + writeb(dma_cmd | ATA_DMA_START, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD); + } +} +EXPORT_SYMBOL_GPL(ide_dma_start); + +/* returns 1 on error, 0 otherwise */ +int ide_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = 0, dma_cmd = 0; + + /* stop DMA */ + if (hwif->host_flags & IDE_HFLAG_MMIO) { + dma_cmd = readb((void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + writeb(dma_cmd & ~ATA_DMA_START, + (void __iomem *)(hwif->dma_base + ATA_DMA_CMD)); + } else { + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd & ~ATA_DMA_START, hwif->dma_base + ATA_DMA_CMD); + } + + /* get DMA status */ + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + + /* clear INTR & ERROR bits */ + ide_dma_sff_write_status(hwif, dma_stat | ATA_DMA_ERR | ATA_DMA_INTR); + +#define CHECK_DMA_MASK (ATA_DMA_ACTIVE | ATA_DMA_ERR | ATA_DMA_INTR) + + /* verify good DMA status */ + if ((dma_stat & CHECK_DMA_MASK) != ATA_DMA_INTR) + return 0x10 | dma_stat; + return 0; +} +EXPORT_SYMBOL_GPL(ide_dma_end); + +/* returns 1 if dma irq issued, 0 otherwise */ +int ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + + return (dma_stat & ATA_DMA_INTR) ? 1 : 0; +} +EXPORT_SYMBOL_GPL(ide_dma_test_irq); + +const struct ide_dma_ops sff_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; +EXPORT_SYMBOL_GPL(sff_dma_ops); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c new file mode 100644 index 000000000..6f344654e --- /dev/null +++ b/drivers/ide/ide-dma.c @@ -0,0 +1,551 @@ +/* + * IDE DMA support (including IDE PCI BM-DMA). + * + * Copyright (C) 1995-1998 Mark Lord + * Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2004, 2007 Bartlomiej Zolnierkiewicz + * + * May be copied or modified under the terms of the GNU General Public License + * + * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies). + */ + +/* + * Special Thanks to Mark for his Six years of work. + */ + +/* + * 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! + */ + +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> +#include <linux/scatterlist.h> +#include <linux/dma-mapping.h> + +static const struct drive_list_entry drive_whitelist[] = { + { "Micropolis 2112A" , NULL }, + { "CONNER CTMA 4000" , NULL }, + { "CONNER CTT8000-A" , NULL }, + { "ST34342A" , NULL }, + { NULL , NULL } +}; + +static const struct drive_list_entry drive_blacklist[] = { + { "WDC AC11000H" , NULL }, + { "WDC AC22100H" , NULL }, + { "WDC AC32500H" , NULL }, + { "WDC AC33100H" , NULL }, + { "WDC AC31600H" , NULL }, + { "WDC AC32100H" , "24.09P07" }, + { "WDC AC23200L" , "21.10N21" }, + { "Compaq CRD-8241B" , NULL }, + { "CRD-8400B" , NULL }, + { "CRD-8480B", NULL }, + { "CRD-8482B", NULL }, + { "CRD-84" , NULL }, + { "SanDisk SDP3B" , NULL }, + { "SanDisk SDP3B-64" , NULL }, + { "SANYO CD-ROM CRD" , NULL }, + { "HITACHI CDR-8" , NULL }, + { "HITACHI CDR-8335" , NULL }, + { "HITACHI CDR-8435" , NULL }, + { "Toshiba CD-ROM XM-6202B" , NULL }, + { "TOSHIBA CD-ROM XM-1702BC", NULL }, + { "CD-532E-A" , NULL }, + { "E-IDE CD-ROM CR-840", NULL }, + { "CD-ROM Drive/F5A", NULL }, + { "WPI CDD-820", NULL }, + { "SAMSUNG CD-ROM SC-148C", NULL }, + { "SAMSUNG CD-ROM SC", NULL }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", NULL }, + { "_NEC DV5800A", NULL }, + { "SAMSUNG CD-ROM SN-124", "N001" }, + { "Seagate STT20000A", NULL }, + { "CD-ROM CDR_U200", "1.09" }, + { NULL , NULL } + +}; + +/** + * 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) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; + u8 stat = 0, dma_stat = 0; + + drive->waiting_for_dma = 0; + dma_stat = hwif->dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + stat = hwif->tp_ops->read_status(hwif); + + if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | ATA_DRQ)) { + if (!dma_stat) { + if ((cmd->tf_flags & IDE_TFLAG_FS) == 0) + ide_finish_cmd(drive, cmd, stat); + else + ide_complete_rq(drive, BLK_STS_OK, + blk_rq_sectors(cmd->rq) << 9); + return ide_stopped; + } + printk(KERN_ERR "%s: %s: bad DMA status (0x%02x)\n", + drive->name, __func__, dma_stat); + } + return ide_error(drive, "dma_intr", stat); +} + +int ide_dma_good_drive(ide_drive_t *drive) +{ + return ide_in_drive_list(drive->id, drive_whitelist); +} + +/** + * ide_dma_map_sg - map IDE scatter gather for DMA I/O + * @drive: the drive to map the DMA table for + * @cmd: command + * + * Perform the DMA mapping magic necessary to access the source or + * target buffers of a request via DMA. The lower layers of the + * kernel provide the necessary cache management so that we can + * operate in a portable fashion. + */ + +static int ide_dma_map_sg(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + struct scatterlist *sg = hwif->sg_table; + int i; + + if (cmd->tf_flags & IDE_TFLAG_WRITE) + cmd->sg_dma_direction = DMA_TO_DEVICE; + else + cmd->sg_dma_direction = DMA_FROM_DEVICE; + + i = dma_map_sg(hwif->dev, sg, cmd->sg_nents, cmd->sg_dma_direction); + if (i) { + cmd->orig_sg_nents = cmd->sg_nents; + cmd->sg_nents = i; + } + + return i; +} + +/** + * ide_dma_unmap_sg - 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_dma_unmap_sg(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + + dma_unmap_sg(hwif->dev, hwif->sg_table, cmd->orig_sg_nents, + cmd->sg_dma_direction); +} +EXPORT_SYMBOL_GPL(ide_dma_unmap_sg); + +/** + * ide_dma_off_quietly - Generic DMA kill + * @drive: drive to control + * + * Turn off the current DMA on this IDE controller. + */ + +void ide_dma_off_quietly(ide_drive_t *drive) +{ + drive->dev_flags &= ~IDE_DFLAG_USING_DMA; + + drive->hwif->dma_ops->dma_host_set(drive, 0); +} +EXPORT_SYMBOL(ide_dma_off_quietly); + +/** + * 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. + */ + +void ide_dma_off(ide_drive_t *drive) +{ + printk(KERN_INFO "%s: DMA disabled\n", drive->name); + ide_dma_off_quietly(drive); +} +EXPORT_SYMBOL(ide_dma_off); + +/** + * ide_dma_on - Enable DMA on a device + * @drive: drive to enable DMA on + * + * Enable IDE DMA for a device on this IDE controller. + */ + +void ide_dma_on(ide_drive_t *drive) +{ + drive->dev_flags |= IDE_DFLAG_USING_DMA; + + drive->hwif->dma_ops->dma_host_set(drive, 1); +} + +int __ide_dma_bad_drive(ide_drive_t *drive) +{ + u16 *id = drive->id; + + int blacklist = ide_in_drive_list(id, drive_blacklist); + if (blacklist) { + printk(KERN_WARNING "%s: Disabling (U)DMA for %s (blacklisted)\n", + drive->name, (char *)&id[ATA_ID_PROD]); + return blacklist; + } + return 0; +} +EXPORT_SYMBOL(__ide_dma_bad_drive); + +static const u8 xfer_mode_bases[] = { + XFER_UDMA_0, + XFER_MW_DMA_0, + XFER_SW_DMA_0, +}; + +static unsigned int ide_get_mode_mask(ide_drive_t *drive, u8 base, u8 req_mode) +{ + u16 *id = drive->id; + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + unsigned int mask = 0; + + switch (base) { + case XFER_UDMA_0: + if ((id[ATA_ID_FIELD_VALID] & 4) == 0) + break; + mask = id[ATA_ID_UDMA_MODES]; + if (port_ops && port_ops->udma_filter) + mask &= port_ops->udma_filter(drive); + else + mask &= hwif->ultra_mask; + + /* + * avoid false cable warning from eighty_ninty_three() + */ + if (req_mode > XFER_UDMA_2) { + if ((mask & 0x78) && (eighty_ninty_three(drive) == 0)) + mask &= 0x07; + } + break; + case XFER_MW_DMA_0: + mask = id[ATA_ID_MWDMA_MODES]; + + /* Also look for the CF specific MWDMA modes... */ + if (ata_id_is_cfa(id) && (id[ATA_ID_CFA_MODES] & 0x38)) { + u8 mode = ((id[ATA_ID_CFA_MODES] & 0x38) >> 3) - 1; + + mask |= ((2 << mode) - 1) << 3; + } + + if (port_ops && port_ops->mdma_filter) + mask &= port_ops->mdma_filter(drive); + else + mask &= hwif->mwdma_mask; + break; + case XFER_SW_DMA_0: + mask = id[ATA_ID_SWDMA_MODES]; + if (!(mask & ATA_SWDMA2) && (id[ATA_ID_OLD_DMA_MODES] >> 8)) { + u8 mode = id[ATA_ID_OLD_DMA_MODES] >> 8; + + /* + * if the mode is valid convert it to the mask + * (the maximum allowed mode is XFER_SW_DMA_2) + */ + if (mode <= 2) + mask = (2 << mode) - 1; + } + mask &= hwif->swdma_mask; + break; + default: + BUG(); + break; + } + + return mask; +} + +/** + * ide_find_dma_mode - compute DMA speed + * @drive: IDE device + * @req_mode: requested mode + * + * Checks the drive/host capabilities and finds the speed to use for + * the DMA transfer. The speed is then limited by the requested mode. + * + * Returns 0 if the drive/host combination is incapable of DMA transfers + * or if the requested mode is not a DMA mode. + */ + +u8 ide_find_dma_mode(ide_drive_t *drive, u8 req_mode) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned int mask; + int x, i; + u8 mode = 0; + + if (drive->media != ide_disk) { + if (hwif->host_flags & IDE_HFLAG_NO_ATAPI_DMA) + return 0; + } + + for (i = 0; i < ARRAY_SIZE(xfer_mode_bases); i++) { + if (req_mode < xfer_mode_bases[i]) + continue; + mask = ide_get_mode_mask(drive, xfer_mode_bases[i], req_mode); + x = fls(mask) - 1; + if (x >= 0) { + mode = xfer_mode_bases[i] + x; + break; + } + } + + if (hwif->chipset == ide_acorn && mode == 0) { + /* + * is this correct? + */ + if (ide_dma_good_drive(drive) && + drive->id[ATA_ID_EIDE_DMA_TIME] < 150) + mode = XFER_MW_DMA_1; + } + + mode = min(mode, req_mode); + + printk(KERN_INFO "%s: %s mode selected\n", drive->name, + mode ? ide_xfer_verbose(mode) : "no DMA"); + + return mode; +} + +static int ide_tune_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 speed; + + if (ata_id_has_dma(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_NODMA)) + return 0; + + /* consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + return 0; + + if (hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA) + return config_drive_for_dma(drive); + + speed = ide_max_dma_mode(drive); + + if (!speed) + return 0; + + if (ide_set_dma_mode(drive, speed)) + return 0; + + return 1; +} + +static int ide_dma_check(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + + if (ide_tune_dma(drive)) + return 0; + + /* TODO: always do PIO fallback */ + if (hwif->host_flags & IDE_HFLAG_TRUST_BIOS_FOR_DMA) + return -1; + + ide_set_max_pio(drive); + + return -1; +} + +int ide_set_dma(ide_drive_t *drive) +{ + int rc; + + /* + * Force DMAing for the beginning of the check. + * Some chipsets appear to do interesting + * things, if not checked and cleared. + * PARANOIA!!! + */ + ide_dma_off_quietly(drive); + + rc = ide_dma_check(drive); + if (rc) + return rc; + + ide_dma_on(drive); + + return 0; +} + +void ide_check_dma_crc(ide_drive_t *drive) +{ + u8 mode; + + ide_dma_off_quietly(drive); + drive->crc_count = 0; + mode = drive->current_speed; + /* + * Don't try non Ultra-DMA modes without iCRC's. Force the + * device to PIO and make the user enable SWDMA/MWDMA modes. + */ + if (mode > XFER_UDMA_0 && mode <= XFER_UDMA_7) + mode--; + else + mode = XFER_PIO_4; + ide_set_xfer_rate(drive, mode); + if (drive->current_speed >= XFER_SW_DMA_0) + ide_dma_on(drive); +} + +void ide_dma_lost_irq(ide_drive_t *drive) +{ + printk(KERN_ERR "%s: DMA interrupt recovery\n", drive->name); +} +EXPORT_SYMBOL_GPL(ide_dma_lost_irq); + +/* + * un-busy the port etc, and clear any pending DMA status. we want to + * retry the current request in pio mode instead of risking tossing it + * all away + */ +ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_dma_ops *dma_ops = hwif->dma_ops; + struct ide_cmd *cmd = &hwif->cmd; + ide_startstop_t ret = ide_stopped; + + /* + * end current dma transaction + */ + + if (error < 0) { + printk(KERN_WARNING "%s: DMA timeout error\n", drive->name); + drive->waiting_for_dma = 0; + (void)dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + ret = ide_error(drive, "dma timeout error", + hwif->tp_ops->read_status(hwif)); + } else { + printk(KERN_WARNING "%s: DMA timeout retry\n", drive->name); + if (dma_ops->dma_clear) + dma_ops->dma_clear(drive); + printk(KERN_ERR "%s: timeout waiting for DMA\n", drive->name); + if (dma_ops->dma_test_irq(drive) == 0) { + ide_dump_status(drive, "DMA timeout", + hwif->tp_ops->read_status(hwif)); + drive->waiting_for_dma = 0; + (void)dma_ops->dma_end(drive); + ide_dma_unmap_sg(drive, cmd); + } + } + + /* + * 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->dev_flags |= IDE_DFLAG_DMA_PIO_RETRY; + drive->retry_pio++; + ide_dma_off_quietly(drive); + + /* + * make sure request is sane + */ + if (hwif->rq) + scsi_req(hwif->rq)->result = 0; + return ret; +} + +void ide_release_dma_engine(ide_hwif_t *hwif) +{ + if (hwif->dmatable_cpu) { + int prd_size = hwif->prd_max_nents * hwif->prd_ent_size; + + dma_free_coherent(hwif->dev, prd_size, + hwif->dmatable_cpu, hwif->dmatable_dma); + hwif->dmatable_cpu = NULL; + } +} +EXPORT_SYMBOL_GPL(ide_release_dma_engine); + +int ide_allocate_dma_engine(ide_hwif_t *hwif) +{ + int prd_size; + + if (hwif->prd_max_nents == 0) + hwif->prd_max_nents = PRD_ENTRIES; + if (hwif->prd_ent_size == 0) + hwif->prd_ent_size = PRD_BYTES; + + prd_size = hwif->prd_max_nents * hwif->prd_ent_size; + + hwif->dmatable_cpu = dma_alloc_coherent(hwif->dev, prd_size, + &hwif->dmatable_dma, + GFP_ATOMIC); + if (hwif->dmatable_cpu == NULL) { + printk(KERN_ERR "%s: unable to allocate PRD table\n", + hwif->name); + return -ENOMEM; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_allocate_dma_engine); + +int ide_dma_prepare(ide_drive_t *drive, struct ide_cmd *cmd) +{ + const struct ide_dma_ops *dma_ops = drive->hwif->dma_ops; + + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0 || + (dma_ops->dma_check && dma_ops->dma_check(drive, cmd))) + goto out; + ide_map_sg(drive, cmd); + if (ide_dma_map_sg(drive, cmd) == 0) + goto out_map; + if (dma_ops->dma_setup(drive, cmd)) + goto out_dma_unmap; + drive->waiting_for_dma = 1; + return 0; +out_dma_unmap: + ide_dma_unmap_sg(drive, cmd); +out_map: + ide_map_sg(drive, cmd); +out: + return 1; +} diff --git a/drivers/ide/ide-eh.c b/drivers/ide/ide-eh.c new file mode 100644 index 000000000..2f378213e --- /dev/null +++ b/drivers/ide/ide-eh.c @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> +#include <linux/delay.h> + +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 & ATA_BUSY) || + ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) { + /* other bits are useless when BUSY */ + scsi_req(rq)->result |= ERROR_RESET; + } else if (stat & ATA_ERR) { + /* err has different meaning on cdrom and tape */ + if (err == ATA_ABORTED) { + if ((drive->dev_flags & IDE_DFLAG_LBA) && + /* some newer drives don't support ATA_CMD_INIT_DEV_PARAMS */ + hwif->tp_ops->read_status(hwif) == ATA_CMD_INIT_DEV_PARAMS) + return ide_stopped; + } else if ((err & BAD_CRC) == BAD_CRC) { + /* UDMA crc error, just retry the operation */ + drive->crc_count++; + } else if (err & (ATA_BBK | ATA_UNC)) { + /* retries won't help these */ + scsi_req(rq)->result = ERROR_MAX; + } else if (err & ATA_TRK0NF) { + /* help it find track zero */ + scsi_req(rq)->result |= ERROR_RECAL; + } + } + + if ((stat & ATA_DRQ) && rq_data_dir(rq) == READ && + (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) { + int nsect = drive->mult_count ? drive->mult_count : 1; + + ide_pad_transfer(drive, READ, nsect * SECTOR_SIZE); + } + + if (scsi_req(rq)->result >= ERROR_MAX || blk_noretry_request(rq)) { + ide_kill_rq(drive, rq); + return ide_stopped; + } + + if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ)) + scsi_req(rq)->result |= ERROR_RESET; + + if ((scsi_req(rq)->result & ERROR_RESET) == ERROR_RESET) { + ++scsi_req(rq)->result; + return ide_do_reset(drive); + } + + if ((scsi_req(rq)->result & ERROR_RECAL) == ERROR_RECAL) + drive->special_flags |= IDE_SFLAG_RECALIBRATE; + + ++scsi_req(rq)->result; + + 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 & ATA_BUSY) || + ((stat & ATA_DF) && (drive->dev_flags & IDE_DFLAG_NOWERR) == 0)) { + /* other bits are useless when BUSY */ + scsi_req(rq)->result |= ERROR_RESET; + } else { + /* add decoding error stuff */ + } + + if (hwif->tp_ops->read_status(hwif) & (ATA_BUSY | ATA_DRQ)) + /* force an abort */ + hwif->tp_ops->exec_command(hwif, ATA_CMD_IDLEIMMEDIATE); + + if (scsi_req(rq)->result >= ERROR_MAX) { + ide_kill_rq(drive, rq); + } else { + if ((scsi_req(rq)->result & ERROR_RESET) == ERROR_RESET) { + ++scsi_req(rq)->result; + return ide_do_reset(drive); + } + ++scsi_req(rq)->result; + } + + return ide_stopped; +} + +static 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); +} + +/** + * 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); + + rq = drive->hwif->rq; + if (rq == NULL) + return ide_stopped; + + /* retry only "normal" I/O: */ + if (blk_rq_is_passthrough(rq)) { + if (ata_taskfile_request(rq)) { + struct ide_cmd *cmd = ide_req(rq)->special; + + if (cmd) + ide_complete_cmd(drive, cmd, stat, err); + } else if (ata_pm_request(rq)) { + scsi_req(rq)->result = 1; + ide_complete_pm_rq(drive, rq); + return ide_stopped; + } + scsi_req(rq)->result = err; + ide_complete_rq(drive, err ? BLK_STS_IOERR : BLK_STS_OK, blk_rq_bytes(rq)); + return ide_stopped; + } + + return __ide_error(drive, rq, stat, err); +} +EXPORT_SYMBOL_GPL(ide_error); + +static inline void ide_complete_drive_reset(ide_drive_t *drive, blk_status_t err) +{ + struct request *rq = drive->hwif->rq; + + if (rq && ata_misc_request(rq) && + scsi_req(rq)->cmd[0] == REQ_DRIVE_RESET) { + if (err <= 0 && scsi_req(rq)->result == 0) + scsi_req(rq)->result = -EIO; + ide_complete_rq(drive, err, blk_rq_bytes(rq)); + } +} + +/* 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_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + u8 stat; + + tp_ops->dev_select(drive); + udelay(10); + stat = tp_ops->read_status(hwif); + + if (OK_STAT(stat, 0, ATA_BUSY)) + printk(KERN_INFO "%s: ATAPI reset complete\n", drive->name); + else { + if (time_before(jiffies, hwif->poll_timeout)) { + ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20); + /* continue polling */ + return ide_started; + } + /* end of polling */ + hwif->polling = 0; + printk(KERN_ERR "%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 */ + hwif->polling = 0; + ide_complete_drive_reset(drive, BLK_STS_OK); + return ide_stopped; +} + +static void ide_reset_report_error(ide_hwif_t *hwif, u8 err) +{ + static const char *err_master_vals[] = + { NULL, "passed", "formatter device error", + "sector buffer error", "ECC circuitry error", + "controlling MPU error" }; + + u8 err_master = err & 0x7f; + + printk(KERN_ERR "%s: reset: master: ", hwif->name); + if (err_master && err_master < 6) + printk(KERN_CONT "%s", err_master_vals[err_master]); + else + printk(KERN_CONT "error (0x%02x?)", err); + if (err & 0x80) + printk(KERN_CONT "; slave: failed"); + printk(KERN_CONT "\n"); +} + +/* + * 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_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + u8 tmp; + blk_status_t err = BLK_STS_OK; + + if (port_ops && port_ops->reset_poll) { + err = port_ops->reset_poll(drive); + if (err) { + printk(KERN_ERR "%s: host reset_poll failure for %s.\n", + hwif->name, drive->name); + goto out; + } + } + + tmp = hwif->tp_ops->read_status(hwif); + + if (!OK_STAT(tmp, 0, ATA_BUSY)) { + if (time_before(jiffies, hwif->poll_timeout)) { + ide_set_handler(drive, &reset_pollfunc, HZ/20); + /* continue polling */ + return ide_started; + } + printk(KERN_ERR "%s: reset timed-out, status=0x%02x\n", + hwif->name, tmp); + drive->failures++; + err = BLK_STS_IOERR; + } else { + tmp = ide_read_error(drive); + + if (tmp == 1) { + printk(KERN_INFO "%s: reset: success\n", hwif->name); + drive->failures = 0; + } else { + ide_reset_report_error(hwif, tmp); + drive->failures++; + err = BLK_STS_IOERR; + } + } +out: + hwif->polling = 0; /* done polling */ + ide_complete_drive_reset(drive, err); + return ide_stopped; +} + +static void ide_disk_pre_reset(ide_drive_t *drive) +{ + int legacy = (drive->id[ATA_ID_CFS_ENABLE_2] & 0x0400) ? 0 : 1; + + drive->special_flags = + legacy ? (IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE) : 0; + + drive->mult_count = 0; + drive->dev_flags &= ~IDE_DFLAG_PARKED; + + if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0 && + (drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) + drive->mult_req = 0; + + if (drive->mult_req != drive->mult_count) + drive->special_flags |= IDE_SFLAG_SET_MULTMODE; +} + +static void pre_reset(ide_drive_t *drive) +{ + const struct ide_port_ops *port_ops = drive->hwif->port_ops; + + if (drive->media == ide_disk) + ide_disk_pre_reset(drive); + else + drive->dev_flags |= IDE_DFLAG_POST_RESET; + + if (drive->dev_flags & IDE_DFLAG_USING_DMA) { + if (drive->crc_count) + ide_check_dma_crc(drive); + else + ide_dma_off(drive); + } + + if ((drive->dev_flags & IDE_DFLAG_KEEP_SETTINGS) == 0) { + if ((drive->dev_flags & IDE_DFLAG_USING_DMA) == 0) { + drive->dev_flags &= ~IDE_DFLAG_UNMASK; + drive->io_32bit = 0; + } + return; + } + + if (port_ops && port_ops->pre_reset) + port_ops->pre_reset(drive); + + if (drive->current_speed != 0xff) + drive->desired_speed = drive->current_speed; + drive->current_speed = 0xff; +} + +/* + * 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) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + const struct ide_port_ops *port_ops; + ide_drive_t *tdrive; + unsigned long flags, timeout; + int i; + DEFINE_WAIT(wait); + + spin_lock_irqsave(&hwif->lock, flags); + + /* We must not reset with running handlers */ + BUG_ON(hwif->handler != NULL); + + /* For an ATAPI device, first try an ATAPI SRST. */ + if (drive->media != ide_disk && !do_not_try_atapi) { + pre_reset(drive); + tp_ops->dev_select(drive); + udelay(20); + tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); + ndelay(400); + hwif->poll_timeout = jiffies + WAIT_WORSTCASE; + hwif->polling = 1; + __ide_set_handler(drive, &atapi_reset_pollfunc, HZ/20); + spin_unlock_irqrestore(&hwif->lock, flags); + return ide_started; + } + + /* We must not disturb devices in the IDE_DFLAG_PARKED state. */ + do { + unsigned long now; + + prepare_to_wait(&ide_park_wq, &wait, TASK_UNINTERRUPTIBLE); + timeout = jiffies; + ide_port_for_each_present_dev(i, tdrive, hwif) { + if ((tdrive->dev_flags & IDE_DFLAG_PARKED) && + time_after(tdrive->sleep, timeout)) + timeout = tdrive->sleep; + } + + now = jiffies; + if (time_before_eq(timeout, now)) + break; + + spin_unlock_irqrestore(&hwif->lock, flags); + timeout = schedule_timeout_uninterruptible(timeout - now); + spin_lock_irqsave(&hwif->lock, flags); + } while (timeout); + finish_wait(&ide_park_wq, &wait); + + /* + * First, reset any device state data we were maintaining + * for any of the drives on this interface. + */ + ide_port_for_each_dev(i, tdrive, hwif) + pre_reset(tdrive); + + if (io_ports->ctl_addr == 0) { + spin_unlock_irqrestore(&hwif->lock, flags); + ide_complete_drive_reset(drive, BLK_STS_IOERR); + 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 */ + tp_ops->write_devctl(hwif, ATA_SRST | ATA_NIEN | ATA_DEVCTL_OBS); + /* more than enough time */ + udelay(10); + /* clear SRST, leave nIEN (unless device is on the quirk list) */ + tp_ops->write_devctl(hwif, + ((drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) ? 0 : ATA_NIEN) | + ATA_DEVCTL_OBS); + /* more than enough time */ + udelay(10); + hwif->poll_timeout = jiffies + WAIT_WORSTCASE; + hwif->polling = 1; + __ide_set_handler(drive, &reset_pollfunc, HZ/20); + + /* + * 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 + */ + port_ops = hwif->port_ops; + if (port_ops && port_ops->resetproc) + port_ops->resetproc(drive); + + spin_unlock_irqrestore(&hwif->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); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c new file mode 100644 index 000000000..f5a2870aa --- /dev/null +++ b/drivers/ide/ide-floppy.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IDE ATAPI floppy driver. + * + * Copyright (C) 1996-1999 Gadi Oxman <gadio@netvision.net.il> + * Copyright (C) 2000-2002 Paul Bristow <paul@paulbristow.net> + * Copyright (C) 2005 Bartlomiej Zolnierkiewicz + * + * This driver supports the following IDE floppy drives: + * + * LS-120/240 SuperDisk + * Iomega Zip 100/250 + * Iomega PC Card Clik!/PocketZip + * + * For a historical changelog see + * Documentation/ide/ChangeLog.ide-floppy.1996-2002 + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/compat.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/cdrom.h> +#include <linux/ide.h> +#include <linux/hdreg.h> +#include <linux/bitops.h> +#include <linux/mutex.h> +#include <linux/scatterlist.h> + +#include <scsi/scsi_ioctl.h> + +#include <asm/byteorder.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <asm/unaligned.h> + +#include "ide-floppy.h" + +/* + * 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 + +/* format capacities descriptor codes */ +#define CAPACITY_INVALID 0x00 +#define CAPACITY_UNFORMATTED 0x01 +#define CAPACITY_CURRENT 0x02 +#define CAPACITY_NO_CARTRIDGE 0x03 + +/* + * The following delay solves a problem with ATAPI Zip 100 drive where BSY bit + * was apparently being deasserted before the unit was ready to receive data. + */ +#define IDEFLOPPY_PC_DELAY (HZ/20) /* default delay for ZIP 100 (50ms) */ + +static int ide_floppy_callback(ide_drive_t *drive, int dsc) +{ + struct ide_disk_obj *floppy = drive->driver_data; + struct ide_atapi_pc *pc = drive->pc; + struct request *rq = pc->rq; + int uptodate = pc->error ? 0 : 1; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (drive->failed_pc == pc) + drive->failed_pc = NULL; + + if (pc->c[0] == GPCMD_READ_10 || pc->c[0] == GPCMD_WRITE_10 || + blk_rq_is_scsi(rq)) + uptodate = 1; /* FIXME */ + else if (pc->c[0] == GPCMD_REQUEST_SENSE) { + + u8 *buf = bio_data(rq->bio); + + if (!pc->error) { + floppy->sense_key = buf[2] & 0x0F; + floppy->asc = buf[12]; + floppy->ascq = buf[13]; + floppy->progress_indication = buf[15] & 0x80 ? + (u16)get_unaligned((u16 *)&buf[16]) : 0x10000; + + if (drive->failed_pc) + ide_debug_log(IDE_DBG_PC, "pc = %x", + drive->failed_pc->c[0]); + + ide_debug_log(IDE_DBG_SENSE, "sense key = %x, asc = %x," + "ascq = %x", floppy->sense_key, + floppy->asc, floppy->ascq); + } else + printk(KERN_ERR PFX "Error in REQUEST SENSE itself - " + "Aborting request!\n"); + } + + if (ata_misc_request(rq)) + scsi_req(rq)->result = uptodate ? 0 : IDE_DRV_ERROR_GENERAL; + + return uptodate; +} + +static void ide_floppy_report_error(struct ide_disk_obj *floppy, + struct ide_atapi_pc *pc) +{ + /* suppress error messages resulting from Medium not present */ + if (floppy->sense_key == 0x02 && + floppy->asc == 0x3a && + floppy->ascq == 0x00) + return; + + printk(KERN_ERR PFX "%s: I/O error, pc = %2x, key = %2x, " + "asc = %2x, ascq = %2x\n", + floppy->drive->name, pc->c[0], floppy->sense_key, + floppy->asc, floppy->ascq); + +} + +static ide_startstop_t ide_floppy_issue_pc(ide_drive_t *drive, + struct ide_cmd *cmd, + struct ide_atapi_pc *pc) +{ + struct ide_disk_obj *floppy = drive->driver_data; + + if (drive->failed_pc == NULL && + pc->c[0] != GPCMD_REQUEST_SENSE) + drive->failed_pc = pc; + + /* Set the current packet command */ + drive->pc = pc; + + if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES) { + unsigned int done = blk_rq_bytes(drive->hwif->rq); + + if (!(pc->flags & PC_FLAG_SUPPRESS_ERROR)) + ide_floppy_report_error(floppy, pc); + + /* Giving up */ + pc->error = IDE_DRV_ERROR_GENERAL; + + drive->failed_pc = NULL; + drive->pc_callback(drive, 0); + ide_complete_rq(drive, BLK_STS_IOERR, done); + return ide_stopped; + } + + ide_debug_log(IDE_DBG_FUNC, "retry #%d", pc->retries); + + pc->retries++; + + return ide_issue_pc(drive, cmd); +} + +void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = GPCMD_READ_FORMAT_CAPACITIES; + pc->c[7] = 255; + pc->c[8] = 255; + pc->req_xfer = 255; +} + +/* A mode sense command is used to "sense" floppy parameters. */ +void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) +{ + u16 length = 8; /* sizeof(Mode Parameter Header) = 8 Bytes */ + + ide_init_pc(pc); + pc->c[0] = GPCMD_MODE_SENSE_10; + pc->c[1] = 0; + pc->c[2] = page_code; + + switch (page_code) { + case IDEFLOPPY_CAPABILITIES_PAGE: + length += 12; + break; + case IDEFLOPPY_FLEXIBLE_DISK_PAGE: + length += 32; + break; + default: + printk(KERN_ERR PFX "unsupported page code in %s\n", __func__); + } + put_unaligned(cpu_to_be16(length), (u16 *) &pc->c[7]); + pc->req_xfer = length; +} + +static void idefloppy_create_rw_cmd(ide_drive_t *drive, + struct ide_atapi_pc *pc, struct request *rq, + unsigned long sector) +{ + struct ide_disk_obj *floppy = drive->driver_data; + int block = sector / floppy->bs_factor; + int blocks = blk_rq_sectors(rq) / floppy->bs_factor; + int cmd = rq_data_dir(rq); + + ide_debug_log(IDE_DBG_FUNC, "block: %d, blocks: %d", block, blocks); + + ide_init_pc(pc); + pc->c[0] = cmd == READ ? GPCMD_READ_10 : GPCMD_WRITE_10; + put_unaligned(cpu_to_be16(blocks), (unsigned short *)&pc->c[7]); + put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[2]); + + memcpy(scsi_req(rq)->cmd, pc->c, 12); + + pc->rq = rq; + if (cmd == WRITE) + pc->flags |= PC_FLAG_WRITING; + + pc->flags |= PC_FLAG_DMA_OK; +} + +static void idefloppy_blockpc_cmd(struct ide_disk_obj *floppy, + struct ide_atapi_pc *pc, struct request *rq) +{ + ide_init_pc(pc); + memcpy(pc->c, scsi_req(rq)->cmd, sizeof(pc->c)); + pc->rq = rq; + if (blk_rq_bytes(rq)) { + pc->flags |= PC_FLAG_DMA_OK; + if (rq_data_dir(rq) == WRITE) + pc->flags |= PC_FLAG_WRITING; + } +} + +static ide_startstop_t ide_floppy_do_request(ide_drive_t *drive, + struct request *rq, sector_t block) +{ + struct ide_disk_obj *floppy = drive->driver_data; + struct ide_cmd cmd; + struct ide_atapi_pc *pc; + + ide_debug_log(IDE_DBG_FUNC, "enter, cmd: 0x%x\n", rq->cmd[0]); + + if (drive->debug_mask & IDE_DBG_RQ) + blk_dump_rq_flags(rq, (rq->rq_disk + ? rq->rq_disk->disk_name + : "dev?")); + + if (scsi_req(rq)->result >= ERROR_MAX) { + if (drive->failed_pc) { + ide_floppy_report_error(floppy, drive->failed_pc); + drive->failed_pc = NULL; + } else + printk(KERN_ERR PFX "%s: I/O error\n", drive->name); + + if (ata_misc_request(rq)) { + scsi_req(rq)->result = 0; + ide_complete_rq(drive, BLK_STS_OK, blk_rq_bytes(rq)); + return ide_stopped; + } else + goto out_end; + } + + switch (req_op(rq)) { + default: + if (((long)blk_rq_pos(rq) % floppy->bs_factor) || + (blk_rq_sectors(rq) % floppy->bs_factor)) { + printk(KERN_ERR PFX "%s: unsupported r/w rq size\n", + drive->name); + goto out_end; + } + pc = &floppy->queued_pc; + idefloppy_create_rw_cmd(drive, pc, rq, (unsigned long)block); + break; + case REQ_OP_SCSI_IN: + case REQ_OP_SCSI_OUT: + pc = &floppy->queued_pc; + idefloppy_blockpc_cmd(floppy, pc, rq); + break; + case REQ_OP_DRV_IN: + case REQ_OP_DRV_OUT: + switch (ide_req(rq)->type) { + case ATA_PRIV_MISC: + case ATA_PRIV_SENSE: + pc = (struct ide_atapi_pc *)ide_req(rq)->special; + break; + default: + BUG(); + } + } + + ide_prep_sense(drive, rq); + + memset(&cmd, 0, sizeof(cmd)); + + if (rq_data_dir(rq)) + cmd.tf_flags |= IDE_TFLAG_WRITE; + + cmd.rq = rq; + + if (!blk_rq_is_passthrough(rq) || blk_rq_bytes(rq)) { + ide_init_sg_cmd(&cmd, blk_rq_bytes(rq)); + ide_map_sg(drive, &cmd); + } + + pc->rq = rq; + + return ide_floppy_issue_pc(drive, &cmd, pc); +out_end: + drive->failed_pc = NULL; + if (blk_rq_is_passthrough(rq) && scsi_req(rq)->result == 0) + scsi_req(rq)->result = -EIO; + ide_complete_rq(drive, BLK_STS_IOERR, blk_rq_bytes(rq)); + return ide_stopped; +} + +/* + * Look at the flexible disk page parameters. We ignore the CHS capacity + * parameters and use the LBA parameters instead. + */ +static int ide_floppy_get_flexible_disk_page(ide_drive_t *drive, + struct ide_atapi_pc *pc) +{ + struct ide_disk_obj *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; + u8 *page, buf[40]; + int capacity, lba_capacity; + u16 transfer_rate, sector_size, cyls, rpm; + u8 heads, sectors; + + ide_floppy_create_mode_sense_cmd(pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE); + + if (ide_queue_pc_tail(drive, disk, pc, buf, pc->req_xfer)) { + printk(KERN_ERR PFX "Can't get flexible disk page params\n"); + return 1; + } + + if (buf[3] & 0x80) + drive->dev_flags |= IDE_DFLAG_WP; + else + drive->dev_flags &= ~IDE_DFLAG_WP; + + set_disk_ro(disk, !!(drive->dev_flags & IDE_DFLAG_WP)); + + page = &buf[8]; + + transfer_rate = be16_to_cpup((__be16 *)&buf[8 + 2]); + sector_size = be16_to_cpup((__be16 *)&buf[8 + 6]); + cyls = be16_to_cpup((__be16 *)&buf[8 + 8]); + rpm = be16_to_cpup((__be16 *)&buf[8 + 28]); + heads = buf[8 + 4]; + sectors = buf[8 + 5]; + + capacity = cyls * heads * sectors * sector_size; + + if (memcmp(page, &floppy->flexible_disk_page, 32)) + printk(KERN_INFO PFX "%s: %dkB, %d/%d/%d CHS, %d kBps, " + "%d sector size, %d rpm\n", + drive->name, capacity / 1024, cyls, heads, + sectors, transfer_rate / 8, sector_size, rpm); + + memcpy(&floppy->flexible_disk_page, page, 32); + drive->bios_cyl = cyls; + drive->bios_head = heads; + drive->bios_sect = sectors; + lba_capacity = floppy->blocks * floppy->block_size; + + if (capacity < lba_capacity) { + printk(KERN_NOTICE PFX "%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; + drive->capacity64 = floppy->blocks * floppy->bs_factor; + } + + return 0; +} + +/* + * Determine if a media is present in the floppy drive, and if so, its LBA + * capacity. + */ +static int ide_floppy_get_capacity(ide_drive_t *drive) +{ + struct ide_disk_obj *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; + struct ide_atapi_pc pc; + u8 *cap_desc; + u8 pc_buf[256], header_len, desc_cnt; + int i, rc = 1, blocks, length; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + drive->bios_cyl = 0; + drive->bios_head = drive->bios_sect = 0; + floppy->blocks = 0; + floppy->bs_factor = 1; + drive->capacity64 = 0; + + ide_floppy_create_read_capacity_cmd(&pc); + if (ide_queue_pc_tail(drive, disk, &pc, pc_buf, pc.req_xfer)) { + printk(KERN_ERR PFX "Can't get floppy parameters\n"); + return 1; + } + header_len = pc_buf[3]; + cap_desc = &pc_buf[4]; + desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */ + + for (i = 0; i < desc_cnt; i++) { + unsigned int desc_start = 4 + i*8; + + blocks = be32_to_cpup((__be32 *)&pc_buf[desc_start]); + length = be16_to_cpup((__be16 *)&pc_buf[desc_start + 6]); + + ide_debug_log(IDE_DBG_PROBE, "Descriptor %d: %dkB, %d blocks, " + "%d sector size", + i, blocks * length / 1024, + blocks, length); + + if (i) + continue; + /* + * the code below is valid only for the 1st descriptor, ie i=0 + */ + + switch (pc_buf[desc_start + 4] & 0x03) { + /* Clik! drive returns this instead of CAPACITY_CURRENT */ + case CAPACITY_UNFORMATTED: + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) + /* + * If it is not a clik drive, break out + * (maintains previous driver behaviour) + */ + break; + fallthrough; + case CAPACITY_CURRENT: + /* Normal Zip/LS-120 disks */ + if (memcmp(cap_desc, &floppy->cap_desc, 8)) + printk(KERN_INFO PFX "%s: %dkB, %d blocks, %d " + "sector size\n", + drive->name, blocks * length / 1024, + blocks, length); + memcpy(&floppy->cap_desc, cap_desc, 8); + + if (!length || length % 512) { + printk(KERN_NOTICE PFX "%s: %d bytes block size" + " not supported\n", drive->name, length); + } else { + floppy->blocks = blocks; + floppy->block_size = length; + floppy->bs_factor = length / 512; + if (floppy->bs_factor != 1) + printk(KERN_NOTICE PFX "%s: Warning: " + "non 512 bytes block size not " + "fully supported\n", + drive->name); + drive->capacity64 = + floppy->blocks * floppy->bs_factor; + 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 PFX "%s: No disk in drive\n", + drive->name); + break; + case CAPACITY_INVALID: + printk(KERN_ERR PFX "%s: Invalid capacity for disk " + "in drive\n", drive->name); + break; + } + ide_debug_log(IDE_DBG_PROBE, "Descriptor 0 Code: %d", + pc_buf[desc_start + 4] & 0x03); + } + + /* Clik! disk does not support get_flexible_disk_page */ + if (!(drive->atapi_flags & IDE_AFLAG_CLIK_DRIVE)) + (void) ide_floppy_get_flexible_disk_page(drive, &pc); + + return rc; +} + +static void ide_floppy_setup(ide_drive_t *drive) +{ + struct ide_disk_obj *floppy = drive->driver_data; + u16 *id = drive->id; + + drive->pc_callback = ide_floppy_callback; + + /* + * 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 (strstarts((char *)&id[ATA_ID_PROD], "IOMEGA ZIP 100 ATAPI")) { + drive->atapi_flags |= IDE_AFLAG_ZIP_DRIVE; + /* This value will be visible in the /proc/ide/hdx/settings */ + drive->pc_delay = IDEFLOPPY_PC_DELAY; + blk_queue_max_hw_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 (strstarts((char *)&id[ATA_ID_PROD], "IOMEGA Clik!")) { + blk_queue_max_hw_sectors(drive->queue, 64); + drive->atapi_flags |= IDE_AFLAG_CLIK_DRIVE; + /* IOMEGA Clik! drives do not support lock/unlock commands */ + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; + } + + (void) ide_floppy_get_capacity(drive); + + ide_proc_register_driver(drive, floppy->driver); +} + +static void ide_floppy_flush(ide_drive_t *drive) +{ +} + +static int ide_floppy_init_media(ide_drive_t *drive, struct gendisk *disk) +{ + int ret = 0; + + if (ide_do_test_unit_ready(drive, disk)) + ide_do_start_stop(drive, disk, 1); + + ret = ide_floppy_get_capacity(drive); + + set_capacity(disk, ide_gd_capacity(drive)); + + return ret; +} + +const struct ide_disk_ops ide_atapi_disk_ops = { + .check = ide_check_atapi_device, + .get_capacity = ide_floppy_get_capacity, + .setup = ide_floppy_setup, + .flush = ide_floppy_flush, + .init_media = ide_floppy_init_media, + .set_doorlock = ide_set_media_lock, + .do_request = ide_floppy_do_request, + .ioctl = ide_floppy_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ide_floppy_compat_ioctl, +#endif +}; diff --git a/drivers/ide/ide-floppy.h b/drivers/ide/ide-floppy.h new file mode 100644 index 000000000..8505a5f58 --- /dev/null +++ b/drivers/ide/ide-floppy.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __IDE_FLOPPY_H +#define __IDE_FLOPPY_H + +#include "ide-gd.h" + +#ifdef CONFIG_IDE_GD_ATAPI +/* + * Pages of the SELECT SENSE / MODE SENSE packet commands. + * See SFF-8070i spec. + */ +#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b +#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05 + +/* 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 + +/* ide-floppy.c */ +extern const struct ide_disk_ops ide_atapi_disk_ops; +void ide_floppy_create_mode_sense_cmd(struct ide_atapi_pc *, u8); +void ide_floppy_create_read_capacity_cmd(struct ide_atapi_pc *); + +/* ide-floppy_ioctl.c */ +int ide_floppy_ioctl(ide_drive_t *, struct block_device *, fmode_t, + unsigned int, unsigned long); +int ide_floppy_compat_ioctl(ide_drive_t *, struct block_device *, fmode_t, + unsigned int, unsigned long); + +#ifdef CONFIG_IDE_PROC_FS +/* ide-floppy_proc.c */ +extern ide_proc_entry_t ide_floppy_proc[]; +extern const struct ide_proc_devset ide_floppy_settings[]; +#endif +#else +#define ide_floppy_proc NULL +#define ide_floppy_settings NULL +#endif + +#endif /*__IDE_FLOPPY_H */ diff --git a/drivers/ide/ide-floppy_ioctl.c b/drivers/ide/ide-floppy_ioctl.c new file mode 100644 index 000000000..39a790ac6 --- /dev/null +++ b/drivers/ide/ide-floppy_ioctl.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ide-floppy IOCTLs handling. + */ + +#include <linux/kernel.h> +#include <linux/ide.h> +#include <linux/compat.h> +#include <linux/cdrom.h> +#include <linux/mutex.h> + +#include <asm/unaligned.h> + +#include <scsi/scsi_ioctl.h> + +#include "ide-floppy.h" + +/* + * Obtain the list of formattable capacities. + * Very similar to ide_floppy_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 DEFINE_MUTEX(ide_floppy_ioctl_mutex); +static int ide_floppy_get_format_capacities(ide_drive_t *drive, + struct ide_atapi_pc *pc, + int __user *arg) +{ + struct ide_disk_obj *floppy = drive->driver_data; + int i, blocks, length, u_array_size, u_index; + int __user *argp; + u8 pc_buf[256], header_len, desc_cnt; + + if (get_user(u_array_size, arg)) + return -EFAULT; + + if (u_array_size <= 0) + return -EINVAL; + + ide_floppy_create_read_capacity_cmd(pc); + + if (ide_queue_pc_tail(drive, floppy->disk, pc, pc_buf, pc->req_xfer)) { + printk(KERN_ERR "ide-floppy: Can't get floppy parameters\n"); + return -EIO; + } + + header_len = pc_buf[3]; + desc_cnt = header_len / 8; /* capacity descriptor of 8 bytes */ + + 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 = 1; i < desc_cnt; i++) { + unsigned int desc_start = 4 + i*8; + + if (u_index >= u_array_size) + break; /* User-supplied buffer too small */ + + blocks = be32_to_cpup((__be32 *)&pc_buf[desc_start]); + length = be16_to_cpup((__be16 *)&pc_buf[desc_start + 6]); + + 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; +} + +static void ide_floppy_create_format_unit_cmd(struct ide_atapi_pc *pc, + u8 *buf, int b, int l, + int flags) +{ + ide_init_pc(pc); + pc->c[0] = GPCMD_FORMAT_UNIT; + pc->c[1] = 0x17; + + memset(buf, 0, 12); + buf[1] = 0xA2; + /* Default format list header, u8 1: FOV/DCRT/IMM bits set */ + + if (flags & 1) /* Verify bit on... */ + buf[1] ^= 0x20; /* ... turn off DCRT bit */ + buf[3] = 8; + + put_unaligned(cpu_to_be32(b), (unsigned int *)(&buf[4])); + put_unaligned(cpu_to_be32(l), (unsigned int *)(&buf[8])); + pc->req_xfer = 12; + pc->flags |= PC_FLAG_WRITING; +} + +static int ide_floppy_get_sfrp_bit(ide_drive_t *drive, struct ide_atapi_pc *pc) +{ + struct ide_disk_obj *floppy = drive->driver_data; + u8 buf[20]; + + drive->atapi_flags &= ~IDE_AFLAG_SRFP; + + ide_floppy_create_mode_sense_cmd(pc, IDEFLOPPY_CAPABILITIES_PAGE); + pc->flags |= PC_FLAG_SUPPRESS_ERROR; + + if (ide_queue_pc_tail(drive, floppy->disk, pc, buf, pc->req_xfer)) + return 1; + + if (buf[8 + 2] & 0x40) + drive->atapi_flags |= IDE_AFLAG_SRFP; + + return 0; +} + +static int ide_floppy_format_unit(ide_drive_t *drive, struct ide_atapi_pc *pc, + int __user *arg) +{ + struct ide_disk_obj *floppy = drive->driver_data; + u8 buf[12]; + int blocks, length, flags, err = 0; + + if (floppy->openers > 1) { + /* Don't format if someone is using the disk */ + drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS; + return -EBUSY; + } + + drive->dev_flags |= IDE_DFLAG_FORMAT_IN_PROGRESS; + + /* + * 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. + */ + if (get_user(blocks, arg) || + get_user(length, arg+1) || + get_user(flags, arg+2)) { + err = -EFAULT; + goto out; + } + + ide_floppy_get_sfrp_bit(drive, pc); + ide_floppy_create_format_unit_cmd(pc, buf, blocks, length, flags); + + if (ide_queue_pc_tail(drive, floppy->disk, pc, buf, pc->req_xfer)) + err = -EIO; + +out: + if (err) + drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS; + return err; +} + +/* + * Get ATAPI_FORMAT_UNIT progress indication. + * + * Userland gives a pointer to an int. The int is set to a progress + * 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 ide_floppy_get_format_progress(ide_drive_t *drive, + struct ide_atapi_pc *pc, + int __user *arg) +{ + struct ide_disk_obj *floppy = drive->driver_data; + u8 sense_buf[18]; + int progress_indication = 0x10000; + + if (drive->atapi_flags & IDE_AFLAG_SRFP) { + ide_create_request_sense_cmd(drive, pc); + if (ide_queue_pc_tail(drive, floppy->disk, pc, sense_buf, + pc->req_xfer)) + 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 { + ide_hwif_t *hwif = drive->hwif; + unsigned long flags; + u8 stat; + + local_irq_save(flags); + stat = hwif->tp_ops->read_status(hwif); + local_irq_restore(flags); + + progress_indication = ((stat & ATA_DSC) == 0) ? 0 : 0x10000; + } + + if (put_user(progress_indication, arg)) + return -EFAULT; + + return 0; +} + +static int ide_floppy_lockdoor(ide_drive_t *drive, struct ide_atapi_pc *pc, + unsigned long arg, unsigned int cmd) +{ + struct ide_disk_obj *floppy = drive->driver_data; + struct gendisk *disk = floppy->disk; + int prevent = (arg && cmd != CDROMEJECT) ? 1 : 0; + + if (floppy->openers > 1) + return -EBUSY; + + ide_set_media_lock(drive, disk, prevent); + + if (cmd == CDROMEJECT) + ide_do_start_stop(drive, disk, 2); + + return 0; +} + +static int ide_floppy_format_ioctl(ide_drive_t *drive, struct ide_atapi_pc *pc, + fmode_t mode, unsigned int cmd, + void __user *argp) +{ + switch (cmd) { + case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: + return 0; + case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: + return ide_floppy_get_format_capacities(drive, pc, argp); + case IDEFLOPPY_IOCTL_FORMAT_START: + if (!(mode & FMODE_WRITE)) + return -EPERM; + return ide_floppy_format_unit(drive, pc, (int __user *)argp); + case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS: + return ide_floppy_get_format_progress(drive, pc, argp); + default: + return -ENOTTY; + } +} + +int ide_floppy_ioctl(ide_drive_t *drive, struct block_device *bdev, + fmode_t mode, unsigned int cmd, unsigned long arg) +{ + struct ide_atapi_pc pc; + void __user *argp = (void __user *)arg; + int err; + + mutex_lock(&ide_floppy_ioctl_mutex); + if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR) { + err = ide_floppy_lockdoor(drive, &pc, arg, cmd); + goto out; + } + + err = ide_floppy_format_ioctl(drive, &pc, mode, cmd, argp); + if (err != -ENOTTY) + goto out; + + /* + * skip SCSI_IOCTL_SEND_COMMAND (deprecated) + * and CDROM_SEND_PACKET (legacy) ioctls + */ + if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND) + err = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); + + if (err == -ENOTTY) + err = generic_ide_ioctl(drive, bdev, cmd, arg); + +out: + mutex_unlock(&ide_floppy_ioctl_mutex); + return err; +} + +#ifdef CONFIG_COMPAT +int ide_floppy_compat_ioctl(ide_drive_t *drive, struct block_device *bdev, + fmode_t mode, unsigned int cmd, unsigned long arg) +{ + struct ide_atapi_pc pc; + void __user *argp = compat_ptr(arg); + int err; + + mutex_lock(&ide_floppy_ioctl_mutex); + if (cmd == CDROMEJECT || cmd == CDROM_LOCKDOOR) { + err = ide_floppy_lockdoor(drive, &pc, arg, cmd); + goto out; + } + + err = ide_floppy_format_ioctl(drive, &pc, mode, cmd, argp); + if (err != -ENOTTY) + goto out; + + /* + * skip SCSI_IOCTL_SEND_COMMAND (deprecated) + * and CDROM_SEND_PACKET (legacy) ioctls + */ + if (cmd != CDROM_SEND_PACKET && cmd != SCSI_IOCTL_SEND_COMMAND) + err = scsi_cmd_blk_ioctl(bdev, mode, cmd, argp); + + if (err == -ENOTTY) + err = generic_ide_ioctl(drive, bdev, cmd, arg); + +out: + mutex_unlock(&ide_floppy_ioctl_mutex); + return err; +} +#endif diff --git a/drivers/ide/ide-floppy_proc.c b/drivers/ide/ide-floppy_proc.c new file mode 100644 index 000000000..7f697ddb5 --- /dev/null +++ b/drivers/ide/ide-floppy_proc.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> +#include <linux/seq_file.h> + +#include "ide-floppy.h" + +static int idefloppy_capacity_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t*drive = (ide_drive_t *)m->private; + + seq_printf(m, "%llu\n", (long long)ide_gd_capacity(drive)); + return 0; +} + +ide_proc_entry_t ide_floppy_proc[] = { + { "capacity", S_IFREG|S_IRUGO, idefloppy_capacity_proc_show }, + { "geometry", S_IFREG|S_IRUGO, ide_geometry_proc_show }, + {} +}; + +ide_devset_rw_field(bios_cyl, bios_cyl); +ide_devset_rw_field(bios_head, bios_head); +ide_devset_rw_field(bios_sect, bios_sect); +ide_devset_rw_field(ticks, pc_delay); + +const struct ide_proc_devset ide_floppy_settings[] = { + IDE_PROC_DEVSET(bios_cyl, 0, 1023), + IDE_PROC_DEVSET(bios_head, 0, 255), + IDE_PROC_DEVSET(bios_sect, 0, 63), + IDE_PROC_DEVSET(ticks, 0, 255), + { NULL }, +}; diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c new file mode 100644 index 000000000..e2b6c8258 --- /dev/null +++ b/drivers/ide/ide-gd.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/genhd.h> +#include <linux/mutex.h> +#include <linux/ide.h> +#include <linux/hdreg.h> +#include <linux/dmi.h> +#include <linux/slab.h> + +#if !defined(CONFIG_DEBUG_BLOCK_EXT_DEVT) +#define IDE_DISK_MINORS (1 << PARTN_BITS) +#else +#define IDE_DISK_MINORS 0 +#endif + +#include "ide-disk.h" +#include "ide-floppy.h" + +#define IDE_GD_VERSION "1.18" + +/* module parameters */ +static DEFINE_MUTEX(ide_gd_mutex); +static unsigned long debug_mask; +module_param(debug_mask, ulong, 0644); + +static DEFINE_MUTEX(ide_disk_ref_mutex); + +static void ide_disk_release(struct device *); + +static struct ide_disk_obj *ide_disk_get(struct gendisk *disk) +{ + struct ide_disk_obj *idkp = NULL; + + mutex_lock(&ide_disk_ref_mutex); + idkp = ide_drv_g(disk, ide_disk_obj); + if (idkp) { + if (ide_device_get(idkp->drive)) + idkp = NULL; + else + get_device(&idkp->dev); + } + mutex_unlock(&ide_disk_ref_mutex); + return idkp; +} + +static void ide_disk_put(struct ide_disk_obj *idkp) +{ + ide_drive_t *drive = idkp->drive; + + mutex_lock(&ide_disk_ref_mutex); + put_device(&idkp->dev); + ide_device_put(drive); + mutex_unlock(&ide_disk_ref_mutex); +} + +sector_t ide_gd_capacity(ide_drive_t *drive) +{ + return drive->capacity64; +} + +static int ide_gd_probe(ide_drive_t *); + +static void ide_gd_remove(ide_drive_t *drive) +{ + struct ide_disk_obj *idkp = drive->driver_data; + struct gendisk *g = idkp->disk; + + ide_proc_unregister_driver(drive, idkp->driver); + device_del(&idkp->dev); + del_gendisk(g); + drive->disk_ops->flush(drive); + + mutex_lock(&ide_disk_ref_mutex); + put_device(&idkp->dev); + mutex_unlock(&ide_disk_ref_mutex); +} + +static void ide_disk_release(struct device *dev) +{ + struct ide_disk_obj *idkp = to_ide_drv(dev, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + struct gendisk *g = idkp->disk; + + drive->disk_ops = NULL; + drive->driver_data = NULL; + g->private_data = NULL; + put_disk(g); + kfree(idkp); +} + +/* + * On HPA drives the capacity needs to be + * reinitialized on resume otherwise the disk + * can not be used and a hard reset is required + */ +static void ide_gd_resume(ide_drive_t *drive) +{ + if (ata_id_hpa_enabled(drive->id)) + (void)drive->disk_ops->get_capacity(drive); +} + +static const struct dmi_system_id ide_coldreboot_table[] = { + { + /* Acer TravelMate 66x cuts power during reboot */ + .ident = "Acer TravelMate 660", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"), + }, + }, + + { } /* terminate list */ +}; + +static void ide_gd_shutdown(ide_drive_t *drive) +{ +#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 && + !dmi_check_system(ide_coldreboot_table)) { +#endif + drive->disk_ops->flush(drive); + return; + } + + printk(KERN_INFO "Shutdown: %s\n", drive->name); + + drive->gendev.bus->suspend(&drive->gendev, PMSG_SUSPEND); +} + +#ifdef CONFIG_IDE_PROC_FS +static ide_proc_entry_t *ide_disk_proc_entries(ide_drive_t *drive) +{ + return (drive->media == ide_disk) ? ide_disk_proc : ide_floppy_proc; +} + +static const struct ide_proc_devset *ide_disk_proc_devsets(ide_drive_t *drive) +{ + return (drive->media == ide_disk) ? ide_disk_settings + : ide_floppy_settings; +} +#endif + +static ide_startstop_t ide_gd_do_request(ide_drive_t *drive, + struct request *rq, sector_t sector) +{ + return drive->disk_ops->do_request(drive, rq, sector); +} + +static struct ide_driver ide_gd_driver = { + .gen_driver = { + .owner = THIS_MODULE, + .name = "ide-gd", + .bus = &ide_bus_type, + }, + .probe = ide_gd_probe, + .remove = ide_gd_remove, + .resume = ide_gd_resume, + .shutdown = ide_gd_shutdown, + .version = IDE_GD_VERSION, + .do_request = ide_gd_do_request, +#ifdef CONFIG_IDE_PROC_FS + .proc_entries = ide_disk_proc_entries, + .proc_devsets = ide_disk_proc_devsets, +#endif +}; + +static int ide_gd_open(struct block_device *bdev, fmode_t mode) +{ + struct gendisk *disk = bdev->bd_disk; + struct ide_disk_obj *idkp; + ide_drive_t *drive; + int ret = 0; + + idkp = ide_disk_get(disk); + if (idkp == NULL) + return -ENXIO; + + drive = idkp->drive; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + idkp->openers++; + + if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) { + drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS; + /* Just in case */ + + ret = drive->disk_ops->init_media(drive, disk); + + /* + * 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 + */ + if (ret && (mode & FMODE_NDELAY) == 0) { + ret = -EIO; + goto out_put_idkp; + } + + if ((drive->dev_flags & IDE_DFLAG_WP) && (mode & FMODE_WRITE)) { + ret = -EROFS; + goto out_put_idkp; + } + + /* + * Ignore the return code from door_lock, + * since the open() has already succeeded, + * and the door_lock is irrelevant at this point. + */ + drive->disk_ops->set_doorlock(drive, disk, 1); + if (__invalidate_device(bdev, true)) + pr_warn("VFS: busy inodes on changed media %s\n", + bdev->bd_disk->disk_name); + drive->disk_ops->get_capacity(drive); + set_capacity(disk, ide_gd_capacity(drive)); + set_bit(GD_NEED_PART_SCAN, &disk->state); + } else if (drive->dev_flags & IDE_DFLAG_FORMAT_IN_PROGRESS) { + ret = -EBUSY; + goto out_put_idkp; + } + return 0; + +out_put_idkp: + idkp->openers--; + ide_disk_put(idkp); + return ret; +} + +static int ide_gd_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + mutex_lock(&ide_gd_mutex); + ret = ide_gd_open(bdev, mode); + mutex_unlock(&ide_gd_mutex); + + return ret; +} + + +static void ide_gd_release(struct gendisk *disk, fmode_t mode) +{ + struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + mutex_lock(&ide_gd_mutex); + if (idkp->openers == 1) + drive->disk_ops->flush(drive); + + if ((drive->dev_flags & IDE_DFLAG_REMOVABLE) && idkp->openers == 1) { + drive->disk_ops->set_doorlock(drive, disk, 0); + drive->dev_flags &= ~IDE_DFLAG_FORMAT_IN_PROGRESS; + } + + idkp->openers--; + + ide_disk_put(idkp); + mutex_unlock(&ide_gd_mutex); +} + +static int ide_gd_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + struct ide_disk_obj *idkp = ide_drv_g(bdev->bd_disk, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + + geo->heads = drive->bios_head; + geo->sectors = drive->bios_sect; + geo->cylinders = (u16)drive->bios_cyl; /* truncate */ + return 0; +} + +static void ide_gd_unlock_native_capacity(struct gendisk *disk) +{ + struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + const struct ide_disk_ops *disk_ops = drive->disk_ops; + + if (disk_ops->unlock_native_capacity) + disk_ops->unlock_native_capacity(drive); +} + +static int ide_gd_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct ide_disk_obj *idkp = ide_drv_g(bdev->bd_disk, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + + return drive->disk_ops->ioctl(drive, bdev, mode, cmd, arg); +} + +#ifdef CONFIG_COMPAT +static int ide_gd_compat_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct ide_disk_obj *idkp = ide_drv_g(bdev->bd_disk, ide_disk_obj); + ide_drive_t *drive = idkp->drive; + + if (!drive->disk_ops->compat_ioctl) + return -ENOIOCTLCMD; + + return drive->disk_ops->compat_ioctl(drive, bdev, mode, cmd, arg); +} +#endif + +static const struct block_device_operations ide_gd_ops = { + .owner = THIS_MODULE, + .open = ide_gd_unlocked_open, + .release = ide_gd_release, + .ioctl = ide_gd_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ide_gd_compat_ioctl, +#endif + .getgeo = ide_gd_getgeo, + .unlock_native_capacity = ide_gd_unlock_native_capacity, +}; + +static int ide_gd_probe(ide_drive_t *drive) +{ + const struct ide_disk_ops *disk_ops = NULL; + struct ide_disk_obj *idkp; + struct gendisk *g; + + /* strstr("foo", "") is non-NULL */ + if (!strstr("ide-gd", drive->driver_req)) + goto failed; + +#ifdef CONFIG_IDE_GD_ATA + if (drive->media == ide_disk) + disk_ops = &ide_ata_disk_ops; +#endif +#ifdef CONFIG_IDE_GD_ATAPI + if (drive->media == ide_floppy) + disk_ops = &ide_atapi_disk_ops; +#endif + if (disk_ops == NULL) + goto failed; + + if (disk_ops->check(drive, DRV_NAME) == 0) { + printk(KERN_ERR PFX "%s: not supported by this driver\n", + drive->name); + goto failed; + } + + idkp = kzalloc(sizeof(*idkp), GFP_KERNEL); + if (!idkp) { + printk(KERN_ERR PFX "%s: can't allocate a disk structure\n", + drive->name); + goto failed; + } + + g = alloc_disk_node(IDE_DISK_MINORS, hwif_to_node(drive->hwif)); + if (!g) + goto out_free_idkp; + + ide_init_disk(g, drive); + + idkp->dev.parent = &drive->gendev; + idkp->dev.release = ide_disk_release; + dev_set_name(&idkp->dev, "%s", dev_name(&drive->gendev)); + + if (device_register(&idkp->dev)) + goto out_free_disk; + + idkp->drive = drive; + idkp->driver = &ide_gd_driver; + idkp->disk = g; + + g->private_data = &idkp->driver; + + drive->driver_data = idkp; + drive->debug_mask = debug_mask; + drive->disk_ops = disk_ops; + + disk_ops->setup(drive); + + set_capacity(g, ide_gd_capacity(drive)); + + g->minors = IDE_DISK_MINORS; + g->flags |= GENHD_FL_EXT_DEVT; + if (drive->dev_flags & IDE_DFLAG_REMOVABLE) + g->flags = GENHD_FL_REMOVABLE; + g->fops = &ide_gd_ops; + g->events = DISK_EVENT_MEDIA_CHANGE; + device_add_disk(&drive->gendev, g, NULL); + return 0; + +out_free_disk: + put_disk(g); +out_free_idkp: + kfree(idkp); +failed: + return -ENODEV; +} + +static int __init ide_gd_init(void) +{ + printk(KERN_INFO DRV_NAME " driver " IDE_GD_VERSION "\n"); + return driver_register(&ide_gd_driver.gen_driver); +} + +static void __exit ide_gd_exit(void) +{ + driver_unregister(&ide_gd_driver.gen_driver); +} + +MODULE_ALIAS("ide:*m-disk*"); +MODULE_ALIAS("ide-disk"); +MODULE_ALIAS("ide:*m-floppy*"); +MODULE_ALIAS("ide-floppy"); +module_init(ide_gd_init); +module_exit(ide_gd_exit); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("generic ATA/ATAPI disk driver"); diff --git a/drivers/ide/ide-gd.h b/drivers/ide/ide-gd.h new file mode 100644 index 000000000..af3fe1880 --- /dev/null +++ b/drivers/ide/ide-gd.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __IDE_GD_H +#define __IDE_GD_H + +#define DRV_NAME "ide-gd" +#define PFX DRV_NAME ": " + +/* define to see debug info */ +#define IDE_GD_DEBUG_LOG 0 + +#if IDE_GD_DEBUG_LOG +#define ide_debug_log(lvl, fmt, args...) __ide_debug_log(lvl, fmt, ## args) +#else +#define ide_debug_log(lvl, fmt, args...) do {} while (0) +#endif + +struct ide_disk_obj { + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct device dev; + unsigned int openers; /* protected by BKL for now */ + + /* used for blk_{fs,pc}_request() requests */ + struct ide_atapi_pc queued_pc; + + /* Last error information */ + u8 sense_key, asc, ascq; + + int progress_indication; + + /* Device information */ + /* Current format */ + int blocks, block_size, bs_factor; + /* Last format capacity descriptor */ + u8 cap_desc[8]; + /* Copy of the flexible disk page */ + u8 flexible_disk_page[32]; +}; + +sector_t ide_gd_capacity(ide_drive_t *); + +#endif /* __IDE_GD_H */ diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c new file mode 100644 index 000000000..80c0d69b8 --- /dev/null +++ b/drivers/ide/ide-generic.c @@ -0,0 +1,139 @@ +/* + * generic/default IDE host driver + * + * Copyright (C) 2004, 2008-2009 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> +#include <linux/pci_ids.h> + +/* FIXME: convert arm to use ide_platform host driver */ +#ifdef CONFIG_ARM +#include <asm/irq.h> +#endif + +#define DRV_NAME "ide_generic" + +static int probe_mask; +module_param(probe_mask, int, 0); +MODULE_PARM_DESC(probe_mask, "probe mask for legacy ISA IDE ports"); + +static const struct ide_port_info ide_generic_port_info = { + .host_flags = IDE_HFLAG_NO_DMA, + .chipset = ide_generic, +}; + +#ifdef CONFIG_ARM +static const u16 legacy_bases[] = { 0x1f0 }; +static const int legacy_irqs[] = { IRQ_HARDDISK }; +#elif defined(CONFIG_ALPHA) +static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168 }; +static const int legacy_irqs[] = { 14, 15, 11, 10 }; +#else +static const u16 legacy_bases[] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 }; +static const int legacy_irqs[] = { 14, 15, 11, 10, 8, 12 }; +#endif + +static void ide_generic_check_pci_legacy_iobases(int *primary, int *secondary) +{ +#ifdef CONFIG_PCI + struct pci_dev *p = NULL; + u16 val; + + for_each_pci_dev(p) { + if (pci_resource_start(p, 0) == 0x1f0) + *primary = 1; + if (pci_resource_start(p, 2) == 0x170) + *secondary = 1; + + /* Cyrix CS55{1,2}0 pre SFF MWDMA ATA on the bridge */ + if (p->vendor == PCI_VENDOR_ID_CYRIX && + (p->device == PCI_DEVICE_ID_CYRIX_5510 || + p->device == PCI_DEVICE_ID_CYRIX_5520)) + *primary = *secondary = 1; + + /* Intel MPIIX - PIO ATA on non PCI side of bridge */ + if (p->vendor == PCI_VENDOR_ID_INTEL && + p->device == PCI_DEVICE_ID_INTEL_82371MX) { + pci_read_config_word(p, 0x6C, &val); + if (val & 0x8000) { + /* ATA port enabled */ + if (val & 0x4000) + *secondary = 1; + else + *primary = 1; + } + } + } +#endif +} + +static int __init ide_generic_init(void) +{ + struct ide_hw hw, *hws[] = { &hw }; + unsigned long io_addr; + int i, rc = 0, primary = 0, secondary = 0; + + ide_generic_check_pci_legacy_iobases(&primary, &secondary); + + if (!probe_mask) { + printk(KERN_INFO DRV_NAME ": please use \"probe_mask=0x3f\" " + "module parameter for probing all legacy ISA IDE ports\n"); + + if (primary == 0) + probe_mask |= 0x1; + + if (secondary == 0) + probe_mask |= 0x2; + } else + printk(KERN_INFO DRV_NAME ": enforcing probing of I/O ports " + "upon user request\n"); + + for (i = 0; i < ARRAY_SIZE(legacy_bases); i++) { + io_addr = legacy_bases[i]; + + if ((probe_mask & (1 << i)) && io_addr) { + if (!request_region(io_addr, 8, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX " + "not free.\n", + DRV_NAME, io_addr, io_addr + 7); + rc = -EBUSY; + continue; + } + + if (!request_region(io_addr + 0x206, 1, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX " + "not free.\n", + DRV_NAME, io_addr + 0x206); + release_region(io_addr, 8); + rc = -EBUSY; + continue; + } + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, io_addr, io_addr + 0x206); +#ifdef CONFIG_IA64 + hw.irq = isa_irq_to_vector(legacy_irqs[i]); +#else + hw.irq = legacy_irqs[i]; +#endif + rc = ide_host_add(&ide_generic_port_info, hws, 1, NULL); + if (rc) { + release_region(io_addr + 0x206, 1); + release_region(io_addr, 8); + } + } + } + + return rc; +} + +module_init(ide_generic_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-io-std.c b/drivers/ide/ide-io-std.c new file mode 100644 index 000000000..94bdcf1ea --- /dev/null +++ b/drivers/ide/ide-io-std.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> + +#if defined(CONFIG_ARM) || defined(CONFIG_M68K) || defined(CONFIG_MIPS) || \ + defined(CONFIG_PARISC) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) +#include <asm/ide.h> +#else +#include <asm-generic/ide_iops.h> +#endif + +/* + * Conventional PIO operations for ATA devices + */ + +static u8 ide_inb(unsigned long port) +{ + return (u8) inb(port); +} + +static void ide_outb(u8 val, unsigned long port) +{ + outb(val, port); +} + +/* + * MMIO operations, typically used for SATA controllers + */ + +static u8 ide_mm_inb(unsigned long port) +{ + return (u8) readb((void __iomem *) port); +} + +static void ide_mm_outb(u8 value, unsigned long port) +{ + writeb(value, (void __iomem *) port); +} + +void ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + else + outb(cmd, hwif->io_ports.command_addr); +} +EXPORT_SYMBOL_GPL(ide_exec_command); + +u8 ide_read_status(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.status_addr); + else + return inb(hwif->io_ports.status_addr); +} +EXPORT_SYMBOL_GPL(ide_read_status); + +u8 ide_read_altstatus(ide_hwif_t *hwif) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + return readb((void __iomem *)hwif->io_ports.ctl_addr); + else + return inb(hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_read_altstatus); + +void ide_write_devctl(ide_hwif_t *hwif, u8 ctl) +{ + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + else + outb(ctl, hwif->io_ports.ctl_addr); +} +EXPORT_SYMBOL_GPL(ide_write_devctl); + +void ide_dev_select(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 select = drive->select | ATA_DEVICE_OBS; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + writeb(select, (void __iomem *)hwif->io_ports.device_addr); + else + outb(select, hwif->io_ports.device_addr); +} +EXPORT_SYMBOL_GPL(ide_dev_select); + +void ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + void (*tf_outb)(u8 addr, unsigned long port); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (mmio) + tf_outb = ide_mm_outb; + else + tf_outb = ide_outb; + + if (valid & IDE_VALID_FEATURE) + tf_outb(tf->feature, io_ports->feature_addr); + if (valid & IDE_VALID_NSECT) + tf_outb(tf->nsect, io_ports->nsect_addr); + if (valid & IDE_VALID_LBAL) + tf_outb(tf->lbal, io_ports->lbal_addr); + if (valid & IDE_VALID_LBAM) + tf_outb(tf->lbam, io_ports->lbam_addr); + if (valid & IDE_VALID_LBAH) + tf_outb(tf->lbah, io_ports->lbah_addr); + if (valid & IDE_VALID_DEVICE) + tf_outb(tf->device, io_ports->device_addr); +} +EXPORT_SYMBOL_GPL(ide_tf_load); + +void ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + u8 (*tf_inb)(unsigned long port); + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (mmio) + tf_inb = ide_mm_inb; + else + tf_inb = ide_inb; + + if (valid & IDE_VALID_ERROR) + tf->error = tf_inb(io_ports->feature_addr); + if (valid & IDE_VALID_NSECT) + tf->nsect = tf_inb(io_ports->nsect_addr); + if (valid & IDE_VALID_LBAL) + tf->lbal = tf_inb(io_ports->lbal_addr); + if (valid & IDE_VALID_LBAM) + tf->lbam = tf_inb(io_ports->lbam_addr); + if (valid & IDE_VALID_LBAH) + tf->lbah = tf_inb(io_ports->lbah_addr); + if (valid & IDE_VALID_DEVICE) + tf->device = tf_inb(io_ports->device_addr); +} +EXPORT_SYMBOL_GPL(ide_tf_read); + +/* + * 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(unsigned long port) +{ + (void)inb(port); + (void)inb(port); + (void)inb(port); +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + * + * These routines will round up any request for an odd number of bytes, + * so if an odd len is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +void ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; + u8 io_32bit = drive->io_32bit; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (io_32bit) { + unsigned long flags; + + if ((io_32bit & 2) && !mmio) { + local_irq_save(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + + words >>= 1; + if (mmio) + __ide_mm_insl((void __iomem *)data_addr, buf, words); + else + insl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) + local_irq_restore(flags); + + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; + } + + if (mmio) + __ide_mm_insw((void __iomem *)data_addr, buf, words); + else + insw(data_addr, buf, words); +} +EXPORT_SYMBOL_GPL(ide_input_data); + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +void ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + unsigned long data_addr = io_ports->data_addr; + unsigned int words = (len + 1) >> 1; + u8 io_32bit = drive->io_32bit; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + + if (io_32bit) { + unsigned long flags; + + if ((io_32bit & 2) && !mmio) { + local_irq_save(flags); + ata_vlb_sync(io_ports->nsect_addr); + } + + words >>= 1; + if (mmio) + __ide_mm_outsl((void __iomem *)data_addr, buf, words); + else + outsl(data_addr, buf, words); + + if ((io_32bit & 2) && !mmio) + local_irq_restore(flags); + + if (((len + 1) & 3) < 2) + return; + + buf += len & ~3; + words = 1; + } + + if (mmio) + __ide_mm_outsw((void __iomem *)data_addr, buf, words); + else + outsw(data_addr, buf, words); +} +EXPORT_SYMBOL_GPL(ide_output_data); + +const struct ide_tp_ops default_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c new file mode 100644 index 000000000..4867b67b6 --- /dev/null +++ b/drivers/ide/ide-io.c @@ -0,0 +1,904 @@ +/* + * 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/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 <linux/bitops.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <linux/uaccess.h> +#include <asm/io.h> + +int ide_end_rq(ide_drive_t *drive, struct request *rq, blk_status_t error, + unsigned int nr_bytes) +{ + /* + * 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->dev_flags & IDE_DFLAG_DMA_PIO_RETRY) && + drive->retry_pio <= 3) { + drive->dev_flags &= ~IDE_DFLAG_DMA_PIO_RETRY; + ide_dma_on(drive); + } + + if (!blk_update_request(rq, error, nr_bytes)) { + if (rq == drive->sense_rq) { + drive->sense_rq = NULL; + drive->sense_rq_active = false; + } + + __blk_mq_end_request(rq, error); + return 0; + } + + return 1; +} +EXPORT_SYMBOL_GPL(ide_end_rq); + +void ide_complete_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat, u8 err) +{ + const struct ide_tp_ops *tp_ops = drive->hwif->tp_ops; + struct ide_taskfile *tf = &cmd->tf; + struct request *rq = cmd->rq; + u8 tf_cmd = tf->command; + + tf->error = err; + tf->status = stat; + + if (cmd->ftf_flags & IDE_FTFLAG_IN_DATA) { + u8 data[2]; + + tp_ops->input_data(drive, cmd, data, 2); + + cmd->tf.data = data[0]; + cmd->hob.data = data[1]; + } + + ide_tf_readback(drive, cmd); + + if ((cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) && + tf_cmd == ATA_CMD_IDLEIMMEDIATE) { + if (tf->lbal != 0xc4) { + printk(KERN_ERR "%s: head unload failed!\n", + drive->name); + ide_tf_dump(drive->name, cmd); + } else + drive->dev_flags |= IDE_DFLAG_PARKED; + } + + if (rq && ata_taskfile_request(rq)) { + struct ide_cmd *orig_cmd = ide_req(rq)->special; + + if (cmd->tf_flags & IDE_TFLAG_DYN) + kfree(orig_cmd); + else if (cmd != orig_cmd) + memcpy(orig_cmd, cmd, sizeof(*cmd)); + } +} + +int ide_complete_rq(ide_drive_t *drive, blk_status_t error, unsigned int nr_bytes) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->rq; + int rc; + + /* + * if failfast is set on a request, override number of sectors + * and complete the whole request right now + */ + if (blk_noretry_request(rq) && error) + nr_bytes = blk_rq_sectors(rq) << 9; + + rc = ide_end_rq(drive, rq, error, nr_bytes); + if (rc == 0) + hwif->rq = NULL; + + return rc; +} +EXPORT_SYMBOL(ide_complete_rq); + +void ide_kill_rq(ide_drive_t *drive, struct request *rq) +{ + u8 drv_req = ata_misc_request(rq) && rq->rq_disk; + u8 media = drive->media; + + drive->failed_pc = NULL; + + if ((media == ide_floppy || media == ide_tape) && drv_req) { + scsi_req(rq)->result = 0; + } else { + if (media == ide_tape) + scsi_req(rq)->result = IDE_DRV_ERROR_GENERAL; + else if (blk_rq_is_passthrough(rq) && scsi_req(rq)->result == 0) + scsi_req(rq)->result = -EIO; + } + + ide_complete_rq(drive, BLK_STS_IOERR, blk_rq_bytes(rq)); +} + +static void ide_tf_set_specify_cmd(ide_drive_t *drive, struct ide_taskfile *tf) +{ + tf->nsect = drive->sect; + tf->lbal = drive->sect; + tf->lbam = drive->cyl; + tf->lbah = drive->cyl >> 8; + tf->device = (drive->head - 1) | drive->select; + tf->command = ATA_CMD_INIT_DEV_PARAMS; +} + +static void ide_tf_set_restore_cmd(ide_drive_t *drive, struct ide_taskfile *tf) +{ + tf->nsect = drive->sect; + tf->command = ATA_CMD_RESTORE; +} + +static void ide_tf_set_setmult_cmd(ide_drive_t *drive, struct ide_taskfile *tf) +{ + tf->nsect = drive->mult_req; + tf->command = ATA_CMD_SET_MULTI; +} + +/** + * do_special - issue some special commands + * @drive: drive the command is for + * + * do_special() is used to issue ATA_CMD_INIT_DEV_PARAMS, + * ATA_CMD_RESTORE and ATA_CMD_SET_MULTI commands to a drive. + */ + +static ide_startstop_t do_special(ide_drive_t *drive) +{ + struct ide_cmd cmd; + +#ifdef DEBUG + printk(KERN_DEBUG "%s: %s: 0x%02x\n", drive->name, __func__, + drive->special_flags); +#endif + if (drive->media != ide_disk) { + drive->special_flags = 0; + drive->mult_req = 0; + return ide_stopped; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.protocol = ATA_PROT_NODATA; + + if (drive->special_flags & IDE_SFLAG_SET_GEOMETRY) { + drive->special_flags &= ~IDE_SFLAG_SET_GEOMETRY; + ide_tf_set_specify_cmd(drive, &cmd.tf); + } else if (drive->special_flags & IDE_SFLAG_RECALIBRATE) { + drive->special_flags &= ~IDE_SFLAG_RECALIBRATE; + ide_tf_set_restore_cmd(drive, &cmd.tf); + } else if (drive->special_flags & IDE_SFLAG_SET_MULTMODE) { + drive->special_flags &= ~IDE_SFLAG_SET_MULTMODE; + ide_tf_set_setmult_cmd(drive, &cmd.tf); + } else + BUG(); + + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + cmd.tf_flags = IDE_TFLAG_CUSTOM_HANDLER; + + do_rw_taskfile(drive, &cmd); + + return ide_started; +} + +void ide_map_sg(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + struct scatterlist *sg = hwif->sg_table, *last_sg = NULL; + struct request *rq = cmd->rq; + + cmd->sg_nents = __blk_rq_map_sg(drive->queue, rq, sg, &last_sg); + if (blk_rq_bytes(rq) && (blk_rq_bytes(rq) & rq->q->dma_pad_mask)) + last_sg->length += + (rq->q->dma_pad_mask & ~blk_rq_bytes(rq)) + 1; +} +EXPORT_SYMBOL_GPL(ide_map_sg); + +void ide_init_sg_cmd(struct ide_cmd *cmd, unsigned int nr_bytes) +{ + cmd->nbytes = cmd->nleft = nr_bytes; + cmd->cursg_ofs = 0; + cmd->cursg = NULL; +} +EXPORT_SYMBOL_GPL(ide_init_sg_cmd); + +/** + * execute_drive_command - issue special drive command + * @drive: the drive to issue the 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) +{ + struct ide_cmd *cmd = ide_req(rq)->special; + + if (cmd) { + if (cmd->protocol == ATA_PROT_PIO) { + ide_init_sg_cmd(cmd, blk_rq_sectors(rq) << 9); + ide_map_sg(drive, cmd); + } + + return do_rw_taskfile(drive, cmd); + } + + /* + * 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 + scsi_req(rq)->result = 0; + ide_complete_rq(drive, BLK_STS_OK, blk_rq_bytes(rq)); + + return ide_stopped; +} + +static ide_startstop_t ide_special_rq(ide_drive_t *drive, struct request *rq) +{ + u8 cmd = scsi_req(rq)->cmd[0]; + + switch (cmd) { + case REQ_PARK_HEADS: + case REQ_UNPARK_HEADS: + return ide_do_park_unpark(drive, rq); + case REQ_DEVSET_EXEC: + return ide_do_devset(drive, rq); + case REQ_DRIVE_RESET: + return ide_do_reset(drive); + default: + BUG(); + } +} + +/** + * 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. + * + * FIXME: this function needs a rename + */ + +static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) +{ + ide_startstop_t startstop; + +#ifdef DEBUG + printk("%s: start_request: current=0x%08lx\n", + drive->hwif->name, (unsigned long) rq); +#endif + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + rq->rq_flags |= RQF_FAILED; + goto kill_rq; + } + + if (drive->prep_rq && !drive->prep_rq(drive, rq)) + return ide_stopped; + + if (ata_pm_request(rq)) + ide_check_pm_state(drive, rq); + + drive->hwif->tp_ops->dev_select(drive); + if (ide_wait_stat(&startstop, drive, drive->ready_stat, + ATA_BUSY | ATA_DRQ, WAIT_READY)) { + printk(KERN_ERR "%s: drive not ready for command\n", drive->name); + return startstop; + } + + if (drive->special_flags == 0) { + struct ide_driver *drv; + + /* + * We reset the drive so we need to issue a SETFEATURES. + * Do it _after_ do_special() restored device parameters. + */ + if (drive->current_speed == 0xff) + ide_config_drive_speed(drive, drive->desired_speed); + + if (ata_taskfile_request(rq)) + return execute_drive_cmd(drive, rq); + else if (ata_pm_request(rq)) { + struct ide_pm_state *pm = ide_req(rq)->special; +#ifdef DEBUG_PM + printk("%s: start_power_step(step: %d)\n", + drive->name, pm->pm_step); +#endif + startstop = ide_start_power_step(drive, rq); + if (startstop == ide_stopped && + pm->pm_step == IDE_PM_COMPLETED) + ide_complete_pm_rq(drive, rq); + return startstop; + } else if (!rq->rq_disk && ata_misc_request(rq)) + /* + * TODO: Once all ULDs have been modified to + * check for specific op codes rather than + * blindly accepting any special request, the + * check for ->rq_disk above may be replaced + * by a more suitable mechanism or even + * dropped entirely. + */ + return ide_special_rq(drive, rq); + + drv = *(struct ide_driver **)rq->rq_disk->private_data; + + return drv->do_request(drive, rq, blk_rq_pos(rq)); + } + 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 port 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->dev_flags |= IDE_DFLAG_SLEEPING; +} +EXPORT_SYMBOL(ide_stall_queue); + +static inline int ide_lock_port(ide_hwif_t *hwif) +{ + if (hwif->busy) + return 1; + + hwif->busy = 1; + + return 0; +} + +static inline void ide_unlock_port(ide_hwif_t *hwif) +{ + hwif->busy = 0; +} + +static inline int ide_lock_host(struct ide_host *host, ide_hwif_t *hwif) +{ + int rc = 0; + + if (host->host_flags & IDE_HFLAG_SERIALIZE) { + rc = test_and_set_bit_lock(IDE_HOST_BUSY, &host->host_busy); + if (rc == 0) { + if (host->get_lock) + host->get_lock(ide_intr, hwif); + } + } + return rc; +} + +static inline void ide_unlock_host(struct ide_host *host) +{ + if (host->host_flags & IDE_HFLAG_SERIALIZE) { + if (host->release_lock) + host->release_lock(); + clear_bit_unlock(IDE_HOST_BUSY, &host->host_busy); + } +} + +void ide_requeue_and_plug(ide_drive_t *drive, struct request *rq) +{ + struct request_queue *q = drive->queue; + + /* Use 3ms as that was the old plug delay */ + if (rq) { + blk_mq_requeue_request(rq, false); + blk_mq_delay_kick_requeue_list(q, 3); + } else + blk_mq_delay_run_hw_queue(q->queue_hw_ctx[0], 3); +} + +blk_status_t ide_issue_rq(ide_drive_t *drive, struct request *rq, + bool local_requeue) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_host *host = hwif->host; + ide_startstop_t startstop; + + if (!blk_rq_is_passthrough(rq) && !(rq->rq_flags & RQF_DONTPREP)) { + rq->rq_flags |= RQF_DONTPREP; + ide_req(rq)->special = NULL; + } + + /* HLD do_request() callback might sleep, make sure it's okay */ + might_sleep(); + + if (ide_lock_host(host, hwif)) + return BLK_STS_DEV_RESOURCE; + + spin_lock_irq(&hwif->lock); + + if (!ide_lock_port(hwif)) { + ide_hwif_t *prev_port; + + WARN_ON_ONCE(hwif->rq); +repeat: + prev_port = hwif->host->cur_port; + if (drive->dev_flags & IDE_DFLAG_SLEEPING && + time_after(drive->sleep, jiffies)) { + ide_unlock_port(hwif); + goto plug_device; + } + + if ((hwif->host->host_flags & IDE_HFLAG_SERIALIZE) && + hwif != prev_port) { + ide_drive_t *cur_dev = + prev_port ? prev_port->cur_dev : NULL; + + /* + * set nIEN for previous port, drives in the + * quirk list may not like intr setups/cleanups + */ + if (cur_dev && + (cur_dev->dev_flags & IDE_DFLAG_NIEN_QUIRK) == 0) + prev_port->tp_ops->write_devctl(prev_port, + ATA_NIEN | + ATA_DEVCTL_OBS); + + hwif->host->cur_port = hwif; + } + hwif->cur_dev = drive; + drive->dev_flags &= ~(IDE_DFLAG_SLEEPING | IDE_DFLAG_PARKED); + + /* + * 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 blk_fetch_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... + */ + if ((drive->dev_flags & IDE_DFLAG_BLOCKED) && + ata_pm_request(rq) == 0 && + (rq->rq_flags & RQF_PM) == 0) { + /* there should be no pending command at this point */ + ide_unlock_port(hwif); + goto plug_device; + } + + scsi_req(rq)->resid_len = blk_rq_bytes(rq); + hwif->rq = rq; + + spin_unlock_irq(&hwif->lock); + startstop = start_request(drive, rq); + spin_lock_irq(&hwif->lock); + + if (startstop == ide_stopped) { + rq = hwif->rq; + hwif->rq = NULL; + if (rq) + goto repeat; + ide_unlock_port(hwif); + goto out; + } + } else { +plug_device: + if (local_requeue) + list_add(&rq->queuelist, &drive->rq_list); + spin_unlock_irq(&hwif->lock); + ide_unlock_host(host); + if (!local_requeue) + ide_requeue_and_plug(drive, rq); + return BLK_STS_OK; + } + +out: + spin_unlock_irq(&hwif->lock); + if (rq == NULL) + ide_unlock_host(host); + return BLK_STS_OK; +} + +/* + * Issue a new request to a device. + */ +blk_status_t ide_queue_rq(struct blk_mq_hw_ctx *hctx, + const struct blk_mq_queue_data *bd) +{ + ide_drive_t *drive = hctx->queue->queuedata; + ide_hwif_t *hwif = drive->hwif; + + spin_lock_irq(&hwif->lock); + if (drive->sense_rq_active) { + spin_unlock_irq(&hwif->lock); + return BLK_STS_DEV_RESOURCE; + } + spin_unlock_irq(&hwif->lock); + + blk_mq_start_request(bd->rq); + return ide_issue_rq(drive, bd->rq, false); +} + +static int drive_is_ready(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 stat = 0; + + if (drive->waiting_for_dma) + return hwif->dma_ops->dma_test_irq(drive); + + if (hwif->io_ports.ctl_addr && + (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0) + stat = hwif->tp_ops->read_altstatus(hwif); + else + /* Note: this may clear a pending IRQ!! */ + stat = hwif->tp_ops->read_status(hwif); + + if (stat & ATA_BUSY) + /* drive busy: definitely not interrupting */ + return 0; + + /* drive ready: *might* be interrupting */ + return 1; +} + +/** + * ide_timer_expiry - handle lack of an IDE interrupt + * @data: timer callback magic (hwif) + * + * 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 (struct timer_list *t) +{ + ide_hwif_t *hwif = from_timer(hwif, t, timer); + ide_drive_t *drive; + ide_handler_t *handler; + unsigned long flags; + int wait = -1; + int plug_device = 0; + struct request *rq_in_flight; + + spin_lock_irqsave(&hwif->lock, flags); + + handler = hwif->handler; + + if (handler == NULL || hwif->req_gen != hwif->req_gen_timer) { + /* + * 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. + */ + } else { + ide_expiry_t *expiry = hwif->expiry; + ide_startstop_t startstop = ide_stopped; + + drive = hwif->cur_dev; + + if (expiry) { + wait = expiry(drive); + if (wait > 0) { /* continue */ + /* reset timer */ + hwif->timer.expires = jiffies + wait; + hwif->req_gen_timer = hwif->req_gen; + add_timer(&hwif->timer); + spin_unlock_irqrestore(&hwif->lock, flags); + return; + } + } + hwif->handler = NULL; + hwif->expiry = 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(&hwif->lock); + /* disable_irq_nosync ?? */ + disable_irq(hwif->irq); + + if (hwif->polling) { + startstop = handler(drive); + } else if (drive_is_ready(drive)) { + if (drive->waiting_for_dma) + hwif->dma_ops->dma_lost_irq(drive); + if (hwif->port_ops && hwif->port_ops->clear_irq) + hwif->port_ops->clear_irq(drive); + + 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->tp_ops->read_status(hwif)); + } + /* Disable interrupts again, `handler' might have enabled it */ + spin_lock_irq(&hwif->lock); + enable_irq(hwif->irq); + if (startstop == ide_stopped && hwif->polling == 0) { + rq_in_flight = hwif->rq; + hwif->rq = NULL; + ide_unlock_port(hwif); + plug_device = 1; + } + } + spin_unlock_irqrestore(&hwif->lock, flags); + + if (plug_device) { + ide_unlock_host(hwif->host); + ide_requeue_and_plug(drive, rq_in_flight); + } +} + +/** + * unexpected_intr - handle an unexpected IDE interrupt + * @irq: interrupt line + * @hwif: port 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. + */ + +static void unexpected_intr(int irq, ide_hwif_t *hwif) +{ + u8 stat = hwif->tp_ops->read_status(hwif); + + if (!OK_STAT(stat, ATA_DRDY, 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: unexpected interrupt, " + "status=0x%02x, count=%ld\n", + hwif->name, stat, count); + } + } +} + +/** + * ide_intr - default IDE interrupt handler + * @irq: interrupt number + * @dev_id: hwif + * @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 + * + * hwif is the interface in the group currently performing + * a command. hwif->cur_dev is the drive and hwif->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 port and the process begins again. + */ + +irqreturn_t ide_intr (int irq, void *dev_id) +{ + ide_hwif_t *hwif = (ide_hwif_t *)dev_id; + struct ide_host *host = hwif->host; + ide_drive_t *drive; + ide_handler_t *handler; + unsigned long flags; + ide_startstop_t startstop; + irqreturn_t irq_ret = IRQ_NONE; + int plug_device = 0; + struct request *rq_in_flight; + + if (host->host_flags & IDE_HFLAG_SERIALIZE) { + if (hwif != host->cur_port) + goto out_early; + } + + spin_lock_irqsave(&hwif->lock, flags); + + if (hwif->port_ops && hwif->port_ops->test_irq && + hwif->port_ops->test_irq(hwif) == 0) + goto out; + + handler = hwif->handler; + + if (handler == NULL || hwif->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. + */ + if ((host->irq_flags & IRQF_SHARED) == 0) { + /* + * Probably not a shared PCI interrupt, + * so we can safely try to do something about it: + */ + unexpected_intr(irq, hwif); + } else { + /* + * Whack the status register, just in case + * we have a leftover pending IRQ. + */ + (void)hwif->tp_ops->read_status(hwif); + } + goto out; + } + + drive = hwif->cur_dev; + + 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. + */ + goto out; + + hwif->handler = NULL; + hwif->expiry = NULL; + hwif->req_gen++; + del_timer(&hwif->timer); + spin_unlock(&hwif->lock); + + if (hwif->port_ops && hwif->port_ops->clear_irq) + hwif->port_ops->clear_irq(drive); + + if (drive->dev_flags & IDE_DFLAG_UNMASK) + local_irq_enable_in_hardirq(); + + /* service this interrupt, may set handler for next interrupt */ + startstop = handler(drive); + + spin_lock_irq(&hwif->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. + */ + if (startstop == ide_stopped && hwif->polling == 0) { + BUG_ON(hwif->handler); + rq_in_flight = hwif->rq; + hwif->rq = NULL; + ide_unlock_port(hwif); + plug_device = 1; + } + irq_ret = IRQ_HANDLED; +out: + spin_unlock_irqrestore(&hwif->lock, flags); +out_early: + if (plug_device) { + ide_unlock_host(hwif->host); + ide_requeue_and_plug(drive, rq_in_flight); + } + + return irq_ret; +} +EXPORT_SYMBOL_GPL(ide_intr); + +void ide_pad_transfer(ide_drive_t *drive, int write, int len) +{ + ide_hwif_t *hwif = drive->hwif; + u8 buf[4] = { 0 }; + + while (len > 0) { + if (write) + hwif->tp_ops->output_data(drive, NULL, buf, min(4, len)); + else + hwif->tp_ops->input_data(drive, NULL, buf, min(4, len)); + len -= 4; + } +} +EXPORT_SYMBOL_GPL(ide_pad_transfer); + +void ide_insert_request_head(ide_drive_t *drive, struct request *rq) +{ + drive->sense_rq_active = true; + list_add_tail(&rq->queuelist, &drive->rq_list); + kblockd_schedule_work(&drive->rq_work); +} +EXPORT_SYMBOL_GPL(ide_insert_request_head); diff --git a/drivers/ide/ide-ioctls.c b/drivers/ide/ide-ioctls.c new file mode 100644 index 000000000..58994da10 --- /dev/null +++ b/drivers/ide/ide-ioctls.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * IDE ioctls handling. + */ + +#include <linux/compat.h> +#include <linux/export.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/slab.h> + +static int put_user_long(long val, unsigned long arg) +{ + if (in_compat_syscall()) + return put_user(val, (compat_long_t __user *)compat_ptr(arg)); + + return put_user(val, (long __user *)arg); +} + +static const struct ide_ioctl_devset ide_ioctl_settings[] = { +{ HDIO_GET_32BIT, HDIO_SET_32BIT, &ide_devset_io_32bit }, +{ HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, &ide_devset_keepsettings }, +{ HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, &ide_devset_unmaskirq }, +{ HDIO_GET_DMA, HDIO_SET_DMA, &ide_devset_using_dma }, +{ -1, HDIO_SET_PIO_MODE, &ide_devset_pio_mode }, +{ 0 } +}; + +int ide_setting_ioctl(ide_drive_t *drive, struct block_device *bdev, + unsigned int cmd, unsigned long arg, + const struct ide_ioctl_devset *s) +{ + const struct ide_devset *ds; + int err = -EOPNOTSUPP; + + for (; (ds = s->setting); s++) { + if (ds->get && s->get_ioctl == cmd) + goto read_val; + else if (ds->set && s->set_ioctl == cmd) + goto set_val; + } + + return err; + +read_val: + mutex_lock(&ide_setting_mtx); + err = ds->get(drive); + mutex_unlock(&ide_setting_mtx); + return err >= 0 ? put_user_long(err, arg) : err; + +set_val: + if (bdev_is_partition(bdev)) + err = -EINVAL; + else { + if (!capable(CAP_SYS_ADMIN)) + err = -EACCES; + else { + mutex_lock(&ide_setting_mtx); + err = ide_devset_execute(drive, ds, arg); + mutex_unlock(&ide_setting_mtx); + } + } + return err; +} +EXPORT_SYMBOL_GPL(ide_setting_ioctl); + +static int ide_get_identity_ioctl(ide_drive_t *drive, unsigned int cmd, + void __user *argp) +{ + u16 *id = NULL; + int size = (cmd == HDIO_GET_IDENTITY) ? (ATA_ID_WORDS * 2) : 142; + int rc = 0; + + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { + rc = -ENOMSG; + goto out; + } + + /* ata_id_to_hd_driveid() relies on 'id' to be fully allocated. */ + id = kmalloc(ATA_ID_WORDS * 2, GFP_KERNEL); + if (id == NULL) { + rc = -ENOMEM; + goto out; + } + + memcpy(id, drive->id, size); + ata_id_to_hd_driveid(id); + + if (copy_to_user(argp, id, size)) + rc = -EFAULT; + + kfree(id); +out: + return rc; +} + +static int ide_get_nice_ioctl(ide_drive_t *drive, unsigned long arg) +{ + return put_user_long((!!(drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) + << IDE_NICE_DSC_OVERLAP) | + (!!(drive->dev_flags & IDE_DFLAG_NICE1) + << IDE_NICE_1), arg); +} + +static int ide_set_nice_ioctl(ide_drive_t *drive, unsigned long arg) +{ + if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) + return -EPERM; + + if (((arg >> IDE_NICE_DSC_OVERLAP) & 1) && + (drive->media != ide_tape)) + return -EPERM; + + if ((arg >> IDE_NICE_DSC_OVERLAP) & 1) + drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; + else + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + + if ((arg >> IDE_NICE_1) & 1) + drive->dev_flags |= IDE_DFLAG_NICE1; + else + drive->dev_flags &= ~IDE_DFLAG_NICE1; + + return 0; +} + +static int ide_cmd_ioctl(ide_drive_t *drive, void __user *argp) +{ + u8 *buf = NULL; + int bufsize = 0, err = 0; + u8 args[4], xfer_rate = 0; + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + + if (NULL == argp) { + struct request *rq; + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_TASKFILE; + blk_execute_rq(drive->queue, NULL, rq, 0); + err = scsi_req(rq)->result ? -EIO : 0; + blk_put_request(rq); + + return err; + } + + if (copy_from_user(args, argp, 4)) + return -EFAULT; + + memset(&cmd, 0, sizeof(cmd)); + tf->feature = args[2]; + if (args[0] == ATA_CMD_SMART) { + tf->nsect = args[3]; + tf->lbal = args[1]; + tf->lbam = ATA_SMART_LBAM_PASS; + tf->lbah = ATA_SMART_LBAH_PASS; + cmd.valid.out.tf = IDE_VALID_OUT_TF; + cmd.valid.in.tf = IDE_VALID_NSECT; + } else { + tf->nsect = args[1]; + cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT; + cmd.valid.in.tf = IDE_VALID_NSECT; + } + tf->command = args[0]; + cmd.protocol = args[3] ? ATA_PROT_PIO : ATA_PROT_NODATA; + + if (args[3]) { + cmd.tf_flags |= IDE_TFLAG_IO_16BIT; + bufsize = SECTOR_SIZE * args[3]; + buf = kzalloc(bufsize, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + } + + if (tf->command == ATA_CMD_SET_FEATURES && + tf->feature == SETFEATURES_XFER && + tf->nsect >= XFER_SW_DMA_0) { + xfer_rate = ide_find_dma_mode(drive, tf->nsect); + if (xfer_rate != tf->nsect) { + err = -EINVAL; + goto abort; + } + + cmd.tf_flags |= IDE_TFLAG_SET_XFER; + } + + err = ide_raw_taskfile(drive, &cmd, buf, args[3]); + + args[0] = tf->status; + args[1] = tf->error; + args[2] = tf->nsect; +abort: + if (copy_to_user(argp, &args, 4)) + err = -EFAULT; + if (buf) { + if (copy_to_user((argp + 4), buf, bufsize)) + err = -EFAULT; + kfree(buf); + } + return err; +} + +static int ide_task_ioctl(ide_drive_t *drive, void __user *p) +{ + int err = 0; + u8 args[7]; + struct ide_cmd cmd; + + if (copy_from_user(args, p, 7)) + return -EFAULT; + + memset(&cmd, 0, sizeof(cmd)); + memcpy(&cmd.tf.feature, &args[1], 6); + cmd.tf.command = args[0]; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + + err = ide_no_data_taskfile(drive, &cmd); + + args[0] = cmd.tf.command; + memcpy(&args[1], &cmd.tf.feature, 6); + + if (copy_to_user(p, args, 7)) + err = -EFAULT; + + return err; +} + +static int generic_drive_reset(ide_drive_t *drive) +{ + struct request *rq; + int ret = 0; + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_MISC; + scsi_req(rq)->cmd_len = 1; + scsi_req(rq)->cmd[0] = REQ_DRIVE_RESET; + blk_execute_rq(drive->queue, NULL, rq, 1); + ret = scsi_req(rq)->result; + blk_put_request(rq); + return ret; +} + +int generic_ide_ioctl(ide_drive_t *drive, struct block_device *bdev, + unsigned int cmd, unsigned long arg) +{ + int err; + void __user *argp = (void __user *)arg; + + if (in_compat_syscall()) + argp = compat_ptr(arg); + + err = ide_setting_ioctl(drive, bdev, cmd, arg, ide_ioctl_settings); + if (err != -EOPNOTSUPP) + return err; + + switch (cmd) { + case HDIO_OBSOLETE_IDENTITY: + case HDIO_GET_IDENTITY: + if (bdev_is_partition(bdev)) + return -EINVAL; + return ide_get_identity_ioctl(drive, cmd, argp); + case HDIO_GET_NICE: + return ide_get_nice_ioctl(drive, arg); + case HDIO_SET_NICE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return ide_set_nice_ioctl(drive, arg); +#ifdef CONFIG_IDE_TASK_IOCTL + case HDIO_DRIVE_TASKFILE: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + /* missing compat handler for HDIO_DRIVE_TASKFILE */ + if (in_compat_syscall()) + return -ENOTTY; + if (drive->media == ide_disk) + return ide_taskfile_ioctl(drive, arg); + return -ENOMSG; +#endif + case HDIO_DRIVE_CMD: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_cmd_ioctl(drive, argp); + case HDIO_DRIVE_TASK: + if (!capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_task_ioctl(drive, argp); + case HDIO_DRIVE_RESET: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return generic_drive_reset(drive); + case HDIO_GET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (put_user_long(BUSSTATE_ON, arg)) + return -EFAULT; + return 0; + case HDIO_SET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + return -EOPNOTSUPP; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(generic_ide_ioctl); diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c new file mode 100644 index 000000000..f2be127ee --- /dev/null +++ b/drivers/ide/ide-iops.c @@ -0,0 +1,536 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat + * + */ + +#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/ide.h> +#include <linux/bitops.h> +#include <linux/nmi.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <linux/uaccess.h> +#include <asm/io.h> + +void SELECT_MASK(ide_drive_t *drive, int mask) +{ + const struct ide_port_ops *port_ops = drive->hwif->port_ops; + + if (port_ops && port_ops->maskproc) + port_ops->maskproc(drive, mask); +} + +u8 ide_read_error(ide_drive_t *drive) +{ + struct ide_taskfile tf; + + drive->hwif->tp_ops->tf_read(drive, &tf, IDE_VALID_ERROR); + + return tf.error; +} +EXPORT_SYMBOL_GPL(ide_read_error); + +void ide_fix_driveid(u16 *id) +{ +#ifndef __LITTLE_ENDIAN +# ifdef __BIG_ENDIAN + int i; + + for (i = 0; i < 256; i++) + id[i] = __le16_to_cpu(id[i]); +# else +# error "Please fix <asm/byteorder.h>" +# endif +#endif +} + +/* + * ide_fixstring() cleans up and (optionally) byte-swaps a text string, + * removing leading/trailing blanks and compressing internal blanks. + * It is primarily used to tidy up the model name/number fields as + * returned by the ATA_CMD_ID_ATA[PI] commands. + */ + +void ide_fixstring(u8 *s, const int bytecount, const int byteswap) +{ + u8 *p, *end = &s[bytecount & ~1]; /* bytecount must be even */ + + if (byteswap) { + /* convert from big-endian to host byte order */ + for (p = s ; p != end ; p += 2) + be16_to_cpus((u16 *) p); + } + + /* strip leading blanks */ + p = s; + 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); + +/* + * 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 error -- caller may then invoke ide_error(). + * + * 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_drive_t *drive, u8 good, u8 bad, + unsigned long timeout, u8 *rstat) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + unsigned long flags; + bool irqs_threaded = force_irqthreads; + int i; + u8 stat; + + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + stat = tp_ops->read_status(hwif); + + if (stat & ATA_BUSY) { + if (!irqs_threaded) { + local_save_flags(flags); + local_irq_enable_in_hardirq(); + } + timeout += jiffies; + while ((stat = tp_ops->read_status(hwif)) & ATA_BUSY) { + 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 = tp_ops->read_status(hwif); + if ((stat & ATA_BUSY) == 0) + break; + + if (!irqs_threaded) + local_irq_restore(flags); + *rstat = stat; + return -EBUSY; + } + } + if (!irqs_threaded) + 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); + stat = tp_ops->read_status(hwif); + + if (OK_STAT(stat, good, bad)) { + *rstat = stat; + return 0; + } + } + *rstat = stat; + return -EFAULT; +} + +/* + * In case of error returns error value after doing "*startstop = ide_error()". + * The caller should return the updated value of "startstop" in this case, + * "startstop" is unchanged when the function returns 0. + */ +int ide_wait_stat(ide_startstop_t *startstop, ide_drive_t *drive, u8 good, + u8 bad, unsigned long timeout) +{ + int err; + u8 stat; + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + *startstop = ide_stopped; + return 1; + } + + err = __ide_wait_stat(drive, good, bad, timeout, &stat); + + if (err) { + char *s = (err == -EBUSY) ? "status timeout" : "status error"; + *startstop = ide_error(drive, s, stat); + } + + return err; +} +EXPORT_SYMBOL(ide_wait_stat); + +/** + * ide_in_drive_list - look for drive in black/white list + * @id: drive identifier + * @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. + */ + +int ide_in_drive_list(u16 *id, const struct drive_list_entry *table) +{ + for ( ; table->id_model; table++) + if ((!strcmp(table->id_model, (char *)&id[ATA_ID_PROD])) && + (!table->id_firmware || + strstr((char *)&id[ATA_ID_FW_REV], table->id_firmware))) + return 1; + return 0; +} +EXPORT_SYMBOL_GPL(ide_in_drive_list); + +/* + * Early UDMA66 devices don't set bit14 to 1, only bit13 is valid. + * Some optical devices with the buggy firmwares have the same problem. + */ +static const struct drive_list_entry ivb_list[] = { + { "QUANTUM FIREBALLlct10 05" , "A03.0900" }, + { "QUANTUM FIREBALLlct20 30" , "APL.0900" }, + { "TSSTcorp CDDVDW SH-S202J" , "SB00" }, + { "TSSTcorp CDDVDW SH-S202J" , "SB01" }, + { "TSSTcorp CDDVDW SH-S202N" , "SB00" }, + { "TSSTcorp CDDVDW SH-S202N" , "SB01" }, + { "TSSTcorp CDDVDW SH-S202H" , "SB00" }, + { "TSSTcorp CDDVDW SH-S202H" , "SB01" }, + { "SAMSUNG SP0822N" , "WA100-10" }, + { NULL , NULL } +}; + +/* + * 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) +{ + ide_hwif_t *hwif = drive->hwif; + u16 *id = drive->id; + int ivb = ide_in_drive_list(id, ivb_list); + + if (hwif->cbl == ATA_CBL_SATA || hwif->cbl == ATA_CBL_PATA40_SHORT) + return 1; + + if (ivb) + printk(KERN_DEBUG "%s: skipping word 93 validity check\n", + drive->name); + + if (ata_id_is_sata(id) && !ivb) + return 1; + + if (hwif->cbl != ATA_CBL_PATA80 && !ivb) + goto no_80w; + + /* + * FIXME: + * - change master/slave IDENTIFY order + * - force bit13 (80c cable present) check also for !ivb devices + * (unless the slave device is pre-ATA3) + */ + if (id[ATA_ID_HW_CONFIG] & 0x4000) + return 1; + + if (ivb) { + const char *model = (char *)&id[ATA_ID_PROD]; + + if (strstr(model, "TSSTcorp CDDVDW SH-S202")) { + /* + * These ATAPI devices always report 80c cable + * so we have to depend on the host in this case. + */ + if (hwif->cbl == ATA_CBL_PATA80) + return 1; + } else { + /* Depend on the device side cable detection. */ + if (id[ATA_ID_HW_CONFIG] & 0x2000) + return 1; + } + } +no_80w: + if (drive->dev_flags & IDE_DFLAG_UDMA33_WARNED) + return 0; + + printk(KERN_WARNING "%s: %s side 80-wire cable detection failed, " + "limiting max speed to UDMA33\n", + drive->name, + hwif->cbl == ATA_CBL_PATA80 ? "drive" : "host"); + + drive->dev_flags |= IDE_DFLAG_UDMA33_WARNED; + + return 0; +} + +static const char *nien_quirk_list[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP KA9.1", + "QUANTUM FIREBALLP KX13.6", + "QUANTUM FIREBALLP KX20.5", + "QUANTUM FIREBALLP KX27.3", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP LM20.5", + "FUJITSU MHZ2160BH G2", + NULL +}; + +void ide_check_nien_quirk_list(ide_drive_t *drive) +{ + const char **list, *m = (char *)&drive->id[ATA_ID_PROD]; + + for (list = nien_quirk_list; *list != NULL; list++) + if (strstr(m, *list) != NULL) { + drive->dev_flags |= IDE_DFLAG_NIEN_QUIRK; + return; + } +} + +int ide_driveid_update(ide_drive_t *drive) +{ + u16 *id; + int rc; + + id = kmalloc(SECTOR_SIZE, GFP_ATOMIC); + if (id == NULL) + return 0; + + SELECT_MASK(drive, 1); + rc = ide_dev_read_id(drive, ATA_CMD_ID_ATA, id, 1); + SELECT_MASK(drive, 0); + + if (rc) + goto out_err; + + drive->id[ATA_ID_UDMA_MODES] = id[ATA_ID_UDMA_MODES]; + drive->id[ATA_ID_MWDMA_MODES] = id[ATA_ID_MWDMA_MODES]; + drive->id[ATA_ID_SWDMA_MODES] = id[ATA_ID_SWDMA_MODES]; + drive->id[ATA_ID_CFA_MODES] = id[ATA_ID_CFA_MODES]; + /* anything more ? */ + + kfree(id); + + return 1; +out_err: + if (rc == 2) + printk(KERN_ERR "%s: %s: bad status\n", drive->name, __func__); + kfree(id); + return 0; +} + +int ide_config_drive_speed(ide_drive_t *drive, u8 speed) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + struct ide_taskfile tf; + u16 *id = drive->id, i; + int error = 0; + u8 stat; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->dma_ops) /* check if host supports DMA */ + hwif->dma_ops->dma_host_set(drive, 0); +#endif + + /* Skip setting PIO flow-control modes on pre-EIDE drives */ + if ((speed & 0xf8) == XFER_PIO_0 && ata_id_has_iordy(drive->id) == 0) + goto skip; + + /* + * 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). + */ + + udelay(1); + tp_ops->dev_select(drive); + SELECT_MASK(drive, 1); + udelay(1); + tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS); + + memset(&tf, 0, sizeof(tf)); + tf.feature = SETFEATURES_XFER; + tf.nsect = speed; + + tp_ops->tf_load(drive, &tf, IDE_VALID_FEATURE | IDE_VALID_NSECT); + + tp_ops->exec_command(hwif, ATA_CMD_SET_FEATURES); + + if (drive->dev_flags & IDE_DFLAG_NIEN_QUIRK) + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + + error = __ide_wait_stat(drive, drive->ready_stat, + ATA_BUSY | ATA_DRQ | ATA_ERR, + WAIT_CMD, &stat); + + SELECT_MASK(drive, 0); + + if (error) { + (void) ide_dump_status(drive, "set_drive_speed_status", stat); + return error; + } + + if (speed >= XFER_SW_DMA_0) { + id[ATA_ID_UDMA_MODES] &= ~0xFF00; + id[ATA_ID_MWDMA_MODES] &= ~0x0700; + id[ATA_ID_SWDMA_MODES] &= ~0x0700; + if (ata_id_is_cfa(id)) + id[ATA_ID_CFA_MODES] &= ~0x0E00; + } else if (ata_id_is_cfa(id)) + id[ATA_ID_CFA_MODES] &= ~0x01C0; + + skip: +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed >= XFER_SW_DMA_0 && (drive->dev_flags & IDE_DFLAG_USING_DMA)) + hwif->dma_ops->dma_host_set(drive, 1); + else if (hwif->dma_ops) /* check if host supports DMA */ + ide_dma_off_quietly(drive); +#endif + + if (speed >= XFER_UDMA_0) { + i = 1 << (speed - XFER_UDMA_0); + id[ATA_ID_UDMA_MODES] |= (i << 8 | i); + } else if (ata_id_is_cfa(id) && speed >= XFER_MW_DMA_3) { + i = speed - XFER_MW_DMA_2; + id[ATA_ID_CFA_MODES] |= i << 9; + } else if (speed >= XFER_MW_DMA_0) { + i = 1 << (speed - XFER_MW_DMA_0); + id[ATA_ID_MWDMA_MODES] |= (i << 8 | i); + } else if (speed >= XFER_SW_DMA_0) { + i = 1 << (speed - XFER_SW_DMA_0); + id[ATA_ID_SWDMA_MODES] |= (i << 8 | i); + } else if (ata_id_is_cfa(id) && speed >= XFER_PIO_5) { + i = speed - XFER_PIO_4; + id[ATA_ID_CFA_MODES] |= i << 6; + } + + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + return error; +} + +/* + * 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 + */ +void __ide_set_handler(ide_drive_t *drive, ide_handler_t *handler, + unsigned int timeout) +{ + ide_hwif_t *hwif = drive->hwif; + + BUG_ON(hwif->handler); + hwif->handler = handler; + hwif->timer.expires = jiffies + timeout; + hwif->req_gen_timer = hwif->req_gen; + add_timer(&hwif->timer); +} + +void ide_set_handler(ide_drive_t *drive, ide_handler_t *handler, + unsigned int timeout) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned long flags; + + spin_lock_irqsave(&hwif->lock, flags); + __ide_set_handler(drive, handler, timeout); + spin_unlock_irqrestore(&hwif->lock, flags); +} +EXPORT_SYMBOL(ide_set_handler); + +/** + * ide_execute_command - execute an IDE command + * @drive: IDE drive to issue the command against + * @cmd: command + * @handler: handler for next phase + * @timeout: timeout for command + * + * 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, struct ide_cmd *cmd, + ide_handler_t *handler, unsigned timeout) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned long flags; + + spin_lock_irqsave(&hwif->lock, flags); + if ((cmd->protocol != ATAPI_PROT_DMA && + cmd->protocol != ATAPI_PROT_PIO) || + (drive->atapi_flags & IDE_AFLAG_DRQ_INTERRUPT)) + __ide_set_handler(drive, handler, timeout); + hwif->tp_ops->exec_command(hwif, cmd->tf.command); + /* + * 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(&hwif->lock, flags); +} + +/* + * ide_wait_not_busy() waits for the currently selected device on the hwif + * to report a non-busy status, see comments in ide_probe_port(). + */ +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->tp_ops->read_status(hwif); + if ((stat & ATA_BUSY) == 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; + touch_nmi_watchdog(); + } + return -EBUSY; +} diff --git a/drivers/ide/ide-legacy.c b/drivers/ide/ide-legacy.c new file mode 100644 index 000000000..be65b411a --- /dev/null +++ b/drivers/ide/ide-legacy.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/ide.h> + +static void ide_legacy_init_one(struct ide_hw **hws, struct ide_hw *hw, + u8 port_no, const struct ide_port_info *d, + unsigned long config) +{ + unsigned long base, ctl; + int irq; + + if (port_no == 0) { + base = 0x1f0; + ctl = 0x3f6; + irq = 14; + } else { + base = 0x170; + ctl = 0x376; + irq = 15; + } + + if (!request_region(base, 8, d->name)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + d->name, base, base + 7); + return; + } + + if (!request_region(ctl, 1, d->name)) { + printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", + d->name, ctl); + release_region(base, 8); + return; + } + + ide_std_init_ports(hw, base, ctl); + hw->irq = irq; + hw->config = config; + + hws[port_no] = hw; +} + +int ide_legacy_device_add(const struct ide_port_info *d, unsigned long config) +{ + struct ide_hw hw[2], *hws[] = { NULL, NULL }; + + memset(&hw, 0, sizeof(hw)); + + if ((d->host_flags & IDE_HFLAG_QD_2ND_PORT) == 0) + ide_legacy_init_one(hws, &hw[0], 0, d, config); + ide_legacy_init_one(hws, &hw[1], 1, d, config); + + if (hws[0] == NULL && hws[1] == NULL && + (d->host_flags & IDE_HFLAG_SINGLE)) + return -ENOENT; + + return ide_host_add(d, hws, 2, NULL); +} +EXPORT_SYMBOL_GPL(ide_legacy_device_add); diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c new file mode 100644 index 000000000..7b9f655ad --- /dev/null +++ b/drivers/ide/ide-lib.c @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +u64 ide_get_lba_addr(struct ide_cmd *cmd, int lba48) +{ + struct ide_taskfile *tf = &cmd->tf; + u32 high, low; + + low = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal; + if (lba48) { + tf = &cmd->hob; + high = (tf->lbah << 16) | (tf->lbam << 8) | tf->lbal; + } else + high = tf->device & 0xf; + + return ((u64)high << 24) | low; +} +EXPORT_SYMBOL_GPL(ide_get_lba_addr); + +static void ide_dump_sector(ide_drive_t *drive) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + u8 lba48 = !!(drive->dev_flags & IDE_DFLAG_LBA48); + + memset(&cmd, 0, sizeof(cmd)); + if (lba48) { + cmd.valid.in.tf = IDE_VALID_LBA; + cmd.valid.in.hob = IDE_VALID_LBA; + cmd.tf_flags = IDE_TFLAG_LBA48; + } else + cmd.valid.in.tf = IDE_VALID_LBA | IDE_VALID_DEVICE; + + ide_tf_readback(drive, &cmd); + + if (lba48 || (tf->device & ATA_LBA)) + printk(KERN_CONT ", LBAsect=%llu", + (unsigned long long)ide_get_lba_addr(&cmd, lba48)); + else + printk(KERN_CONT ", CHS=%d/%d/%d", (tf->lbah << 8) + tf->lbam, + tf->device & 0xf, tf->lbal); +} + +static void ide_dump_ata_error(ide_drive_t *drive, u8 err) +{ + printk(KERN_CONT "{ "); + if (err & ATA_ABORTED) + printk(KERN_CONT "DriveStatusError "); + if (err & ATA_ICRC) + printk(KERN_CONT "%s", + (err & ATA_ABORTED) ? "BadCRC " : "BadSector "); + if (err & ATA_UNC) + printk(KERN_CONT "UncorrectableError "); + if (err & ATA_IDNF) + printk(KERN_CONT "SectorIdNotFound "); + if (err & ATA_TRK0NF) + printk(KERN_CONT "TrackZeroNotFound "); + if (err & ATA_AMNF) + printk(KERN_CONT "AddrMarkNotFound "); + printk(KERN_CONT "}"); + if ((err & (ATA_BBK | ATA_ABORTED)) == ATA_BBK || + (err & (ATA_UNC | ATA_IDNF | ATA_AMNF))) { + struct request *rq = drive->hwif->rq; + + ide_dump_sector(drive); + + if (rq) + printk(KERN_CONT ", sector=%llu", + (unsigned long long)blk_rq_pos(rq)); + } + printk(KERN_CONT "\n"); +} + +static void ide_dump_atapi_error(ide_drive_t *drive, u8 err) +{ + printk(KERN_CONT "{ "); + if (err & ATAPI_ILI) + printk(KERN_CONT "IllegalLengthIndication "); + if (err & ATAPI_EOM) + printk(KERN_CONT "EndOfMedia "); + if (err & ATA_ABORTED) + printk(KERN_CONT "AbortedCommand "); + if (err & ATA_MCR) + printk(KERN_CONT "MediaChangeRequested "); + if (err & ATAPI_LFS) + printk(KERN_CONT "LastFailedSense=0x%02x ", + (err & ATAPI_LFS) >> 4); + printk(KERN_CONT "}\n"); +} + +/** + * ide_dump_status - translate ATA/ATAPI error + * @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). + * 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) +{ + u8 err = 0; + + printk(KERN_ERR "%s: %s: status=0x%02x { ", drive->name, msg, stat); + if (stat & ATA_BUSY) + printk(KERN_CONT "Busy "); + else { + if (stat & ATA_DRDY) + printk(KERN_CONT "DriveReady "); + if (stat & ATA_DF) + printk(KERN_CONT "DeviceFault "); + if (stat & ATA_DSC) + printk(KERN_CONT "SeekComplete "); + if (stat & ATA_DRQ) + printk(KERN_CONT "DataRequest "); + if (stat & ATA_CORR) + printk(KERN_CONT "CorrectedError "); + if (stat & ATA_SENSE) + printk(KERN_CONT "Sense "); + if (stat & ATA_ERR) + printk(KERN_CONT "Error "); + } + printk(KERN_CONT "}\n"); + if ((stat & (ATA_BUSY | ATA_ERR)) == ATA_ERR) { + err = ide_read_error(drive); + printk(KERN_ERR "%s: %s: error=0x%02x ", drive->name, msg, err); + if (drive->media == ide_disk) + ide_dump_ata_error(drive, err); + else + ide_dump_atapi_error(drive, err); + } + + printk(KERN_ERR "%s: possibly failed opcode: 0x%02x\n", + drive->name, drive->hwif->cmd.tf.command); + + return err; +} +EXPORT_SYMBOL(ide_dump_status); diff --git a/drivers/ide/ide-park.c b/drivers/ide/ide-park.c new file mode 100644 index 000000000..8af7af600 --- /dev/null +++ b/drivers/ide/ide-park.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/ide.h> +#include <linux/jiffies.h> +#include <linux/blkdev.h> + +DECLARE_WAIT_QUEUE_HEAD(ide_park_wq); + +static void issue_park_cmd(ide_drive_t *drive, unsigned long timeout) +{ + ide_hwif_t *hwif = drive->hwif; + struct request_queue *q = drive->queue; + struct request *rq; + int rc; + + timeout += jiffies; + spin_lock_irq(&hwif->lock); + if (drive->dev_flags & IDE_DFLAG_PARKED) { + int reset_timer = time_before(timeout, drive->sleep); + int start_queue = 0; + + drive->sleep = timeout; + wake_up_all(&ide_park_wq); + if (reset_timer && del_timer(&hwif->timer)) + start_queue = 1; + spin_unlock_irq(&hwif->lock); + + if (start_queue) + blk_mq_run_hw_queues(q, true); + return; + } + spin_unlock_irq(&hwif->lock); + + rq = blk_get_request(q, REQ_OP_DRV_IN, 0); + scsi_req(rq)->cmd[0] = REQ_PARK_HEADS; + scsi_req(rq)->cmd_len = 1; + ide_req(rq)->type = ATA_PRIV_MISC; + ide_req(rq)->special = &timeout; + blk_execute_rq(q, NULL, rq, 1); + rc = scsi_req(rq)->result ? -EIO : 0; + blk_put_request(rq); + if (rc) + goto out; + + /* + * Make sure that *some* command is sent to the drive after the + * timeout has expired, so power management will be reenabled. + */ + rq = blk_get_request(q, REQ_OP_DRV_IN, BLK_MQ_REQ_NOWAIT); + if (IS_ERR(rq)) + goto out; + + scsi_req(rq)->cmd[0] = REQ_UNPARK_HEADS; + scsi_req(rq)->cmd_len = 1; + ide_req(rq)->type = ATA_PRIV_MISC; + spin_lock_irq(&hwif->lock); + ide_insert_request_head(drive, rq); + spin_unlock_irq(&hwif->lock); + +out: + return; +} + +ide_startstop_t ide_do_park_unpark(ide_drive_t *drive, struct request *rq) +{ + struct ide_cmd cmd; + struct ide_taskfile *tf = &cmd.tf; + + memset(&cmd, 0, sizeof(cmd)); + if (scsi_req(rq)->cmd[0] == REQ_PARK_HEADS) { + drive->sleep = *(unsigned long *)ide_req(rq)->special; + drive->dev_flags |= IDE_DFLAG_SLEEPING; + tf->command = ATA_CMD_IDLEIMMEDIATE; + tf->feature = 0x44; + tf->lbal = 0x4c; + tf->lbam = 0x4e; + tf->lbah = 0x55; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + } else /* cmd == REQ_UNPARK_HEADS */ + tf->command = ATA_CMD_CHK_POWER; + + cmd.tf_flags |= IDE_TFLAG_CUSTOM_HANDLER; + cmd.protocol = ATA_PROT_NODATA; + + cmd.rq = rq; + + return do_rw_taskfile(drive, &cmd); +} + +ssize_t ide_park_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + ide_hwif_t *hwif = drive->hwif; + unsigned long now; + unsigned int msecs; + + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) + return -EOPNOTSUPP; + + spin_lock_irq(&hwif->lock); + now = jiffies; + if (drive->dev_flags & IDE_DFLAG_PARKED && + time_after(drive->sleep, now)) + msecs = jiffies_to_msecs(drive->sleep - now); + else + msecs = 0; + spin_unlock_irq(&hwif->lock); + + return snprintf(buf, 20, "%u\n", msecs); +} + +ssize_t ide_park_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ +#define MAX_PARK_TIMEOUT 30000 + ide_drive_t *drive = to_ide_device(dev); + long int input; + int rc; + + rc = kstrtol(buf, 10, &input); + if (rc) + return rc; + if (input < -2) + return -EINVAL; + if (input > MAX_PARK_TIMEOUT) { + input = MAX_PARK_TIMEOUT; + rc = -EOVERFLOW; + } + + mutex_lock(&ide_setting_mtx); + if (input >= 0) { + if (drive->dev_flags & IDE_DFLAG_NO_UNLOAD) + rc = -EOPNOTSUPP; + else if (input || drive->dev_flags & IDE_DFLAG_PARKED) + issue_park_cmd(drive, msecs_to_jiffies(input)); + } else { + if (drive->media == ide_disk) + switch (input) { + case -1: + drive->dev_flags &= ~IDE_DFLAG_NO_UNLOAD; + break; + case -2: + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; + break; + } + else + rc = -EOPNOTSUPP; + } + mutex_unlock(&ide_setting_mtx); + + return rc ? rc : len; +} diff --git a/drivers/ide/ide-pci-generic.c b/drivers/ide/ide-pci-generic.c new file mode 100644 index 000000000..673420db9 --- /dev/null +++ b/drivers/ide/ide-pci-generic.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Portions (C) Copyright 2002 Red Hat Inc + * + * 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/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "ide_pci_generic" + +static bool ide_generic_all; /* Set to claim all devices */ + +module_param_named(all_generic_ide, ide_generic_all, bool, 0444); +MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers."); + +static void netcell_quirkproc(ide_drive_t *drive) +{ + /* mark words 85-87 as valid */ + drive->id[ATA_ID_CSF_DEFAULT] |= 0x4000; +} + +static const struct ide_port_ops netcell_port_ops = { + .quirkproc = netcell_quirkproc, +}; + +#define DECLARE_GENERIC_PCI_DEV(extra_flags) \ + { \ + .name = DRV_NAME, \ + .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | \ + extra_flags, \ + .swdma_mask = ATA_SWDMA2, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = ATA_UDMA6, \ + } + +static const struct ide_port_info generic_chipsets[] = { + /* 0: Unknown */ + DECLARE_GENERIC_PCI_DEV(0), + + { /* 1: NS87410 */ + .name = DRV_NAME, + .enablebits = { {0x43, 0x08, 0x08}, {0x47, 0x08, 0x08} }, + .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + }, + + /* 2: SAMURAI / HT6565 / HINT_IDE */ + DECLARE_GENERIC_PCI_DEV(0), + /* 3: UM8673F / UM8886A / UM8886BF */ + DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_DMA), + /* 4: VIA_IDE / OPTI621V / Piccolo010{2,3,5} */ + DECLARE_GENERIC_PCI_DEV(IDE_HFLAG_NO_AUTODMA), + + { /* 5: VIA8237SATA */ + .name = DRV_NAME, + .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | + IDE_HFLAG_OFF_BOARD, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + }, + + { /* 6: Revolution */ + .name = DRV_NAME, + .port_ops = &netcell_port_ops, + .host_flags = IDE_HFLAG_CLEAR_SIMPLEX | + IDE_HFLAG_TRUST_BIOS_FOR_DMA | + IDE_HFLAG_OFF_BOARD, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, + } +}; + +/** + * 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 generic_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + const struct ide_port_info *d = &generic_chipsets[id->driver_data]; + int ret = -ENODEV; + + /* Don't use the generic entry unless instructed to do so */ + if (id->driver_data == 0 && ide_generic_all == 0) + goto out; + + switch (dev->vendor) { + case PCI_VENDOR_ID_UMC: + if (dev->device == PCI_DEVICE_ID_UMC_UM8886A && + !(PCI_FUNC(dev->devfn) & 1)) + goto out; /* UM8886A/BF pair */ + break; + case PCI_VENDOR_ID_OPTI: + if (dev->device == PCI_DEVICE_ID_OPTI_82C558 && + !(PCI_FUNC(dev->devfn) & 1)) + goto out; + break; + case PCI_VENDOR_ID_JMICRON: + if (dev->device != PCI_DEVICE_ID_JMICRON_JMB368 && + PCI_FUNC(dev->devfn) != 1) + goto out; + break; + case PCI_VENDOR_ID_NS: + if (dev->device == PCI_DEVICE_ID_NS_87410 && + (dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + goto out; + break; + } + + if (dev->vendor != PCI_VENDOR_ID_JMICRON) { + u16 command; + pci_read_config_word(dev, PCI_COMMAND, &command); + if (!(command & PCI_COMMAND_IO)) { + printk(KERN_INFO "%s %s: skipping disabled " + "controller\n", d->name, pci_name(dev)); + goto out; + } + } + ret = ide_pci_init_one(dev, d, NULL); +out: + return ret; +} + +static const struct pci_device_id generic_pci_tbl[] = { + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87410), 1 }, + { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE), 2 }, + { PCI_VDEVICE(HOLTEK, PCI_DEVICE_ID_HOLTEK_6565), 2 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8673F), 3 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886A), 3 }, + { PCI_VDEVICE(UMC, PCI_DEVICE_ID_UMC_UM8886BF), 3 }, + { PCI_VDEVICE(HINT, PCI_DEVICE_ID_HINT_VXPROII_IDE), 2 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C561), 4 }, + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C558), 4 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_8237_SATA), 5 }, +#endif + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_1), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_2), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_3), 4 }, + { PCI_VDEVICE(TOSHIBA, PCI_DEVICE_ID_TOSHIBA_PICCOLO_5), 4 }, + { PCI_VDEVICE(NETCELL, PCI_DEVICE_ID_REVOLUTION), 6 }, + /* + * Must come last. If you add entries adjust + * this table and generic_chipsets[] appropriately. + */ + { PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xFFFFFF00UL, 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, generic_pci_tbl); + +static struct pci_driver generic_pci_driver = { + .name = "PCI_IDE", + .id_table = generic_pci_tbl, + .probe = generic_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init generic_ide_init(void) +{ + return ide_pci_register_driver(&generic_pci_driver); +} + +static void __exit generic_ide_exit(void) +{ + pci_unregister_driver(&generic_pci_driver); +} + +module_init(generic_ide_init); +module_exit(generic_ide_exit); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for generic PCI IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-pio-blacklist.c b/drivers/ide/ide-pio-blacklist.c new file mode 100644 index 000000000..1fd24798e --- /dev/null +++ b/drivers/ide/ide-pio-blacklist.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PIO blacklist. Some drives incorrectly report their maximal PIO mode, + * at least in respect to CMD640. Here we keep info on some known drives. + * + * Changes to the ide_pio_blacklist[] should be made with EXTREME CAUTION + * to avoid breaking the fragile cmd640.c support. + */ + +#include <linux/string.h> +#include <linux/ide.h> + +static struct ide_pio_info { + const char *name; + int pio; +} ide_pio_blacklist [] = { + { "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 AC31000", 3 }, + { "WDC AC31200", 3 }, + + { "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 }, + + { "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 Seagate's 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[]. + */ + +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; +} diff --git a/drivers/ide/ide-pm.c b/drivers/ide/ide-pm.c new file mode 100644 index 000000000..82ab308f1 --- /dev/null +++ b/drivers/ide/ide-pm.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/gfp.h> +#include <linux/ide.h> + +int generic_ide_suspend(struct device *dev, pm_message_t mesg) +{ + ide_drive_t *drive = to_ide_device(dev); + ide_drive_t *pair = ide_get_pair_dev(drive); + ide_hwif_t *hwif = drive->hwif; + struct request *rq; + struct ide_pm_state rqpm; + int ret; + + if (ide_port_acpi(hwif)) { + /* call ACPI _GTM only once */ + if ((drive->dn & 1) == 0 || pair == NULL) + ide_acpi_get_timing(hwif); + } + + memset(&rqpm, 0, sizeof(rqpm)); + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_PM_SUSPEND; + ide_req(rq)->special = &rqpm; + rqpm.pm_step = IDE_PM_START_SUSPEND; + if (mesg.event == PM_EVENT_PRETHAW) + mesg.event = PM_EVENT_FREEZE; + rqpm.pm_state = mesg.event; + + blk_execute_rq(drive->queue, NULL, rq, 0); + ret = scsi_req(rq)->result ? -EIO : 0; + blk_put_request(rq); + + if (ret == 0 && ide_port_acpi(hwif)) { + /* call ACPI _PS3 only after both devices are suspended */ + if ((drive->dn & 1) || pair == NULL) + ide_acpi_set_state(hwif, 0); + } + + return ret; +} + +static int ide_pm_execute_rq(struct request *rq) +{ + struct request_queue *q = rq->q; + + if (unlikely(blk_queue_dying(q))) { + rq->rq_flags |= RQF_QUIET; + scsi_req(rq)->result = -ENXIO; + blk_mq_end_request(rq, BLK_STS_OK); + return -ENXIO; + } + blk_execute_rq(q, NULL, rq, true); + + return scsi_req(rq)->result ? -EIO : 0; +} + +int generic_ide_resume(struct device *dev) +{ + ide_drive_t *drive = to_ide_device(dev); + ide_drive_t *pair = ide_get_pair_dev(drive); + ide_hwif_t *hwif = drive->hwif; + struct request *rq; + struct ide_pm_state rqpm; + int err; + + blk_mq_start_stopped_hw_queues(drive->queue, true); + + if (ide_port_acpi(hwif)) { + /* call ACPI _PS0 / _STM only once */ + if ((drive->dn & 1) == 0 || pair == NULL) { + ide_acpi_set_state(hwif, 1); + ide_acpi_push_timing(hwif); + } + + ide_acpi_exec_tfs(drive); + } + + memset(&rqpm, 0, sizeof(rqpm)); + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_PM); + ide_req(rq)->type = ATA_PRIV_PM_RESUME; + ide_req(rq)->special = &rqpm; + rqpm.pm_step = IDE_PM_START_RESUME; + rqpm.pm_state = PM_EVENT_ON; + + err = ide_pm_execute_rq(rq); + blk_put_request(rq); + + if (err == 0 && dev->driver) { + struct ide_driver *drv = to_ide_driver(dev->driver); + + if (drv->resume) + drv->resume(drive); + } + + return err; +} + +void ide_complete_power_step(ide_drive_t *drive, struct request *rq) +{ + struct ide_pm_state *pm = ide_req(rq)->special; + +#ifdef DEBUG_PM + printk(KERN_INFO "%s: complete_power_step(step: %d)\n", + drive->name, pm->pm_step); +#endif + if (drive->media != ide_disk) + return; + + switch (pm->pm_step) { + case IDE_PM_FLUSH_CACHE: /* Suspend step 1 (flush cache) */ + if (pm->pm_state == PM_EVENT_FREEZE) + pm->pm_step = IDE_PM_COMPLETED; + else + pm->pm_step = IDE_PM_STANDBY; + break; + case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ + pm->pm_step = IDE_PM_COMPLETED; + break; + case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ + pm->pm_step = IDE_PM_IDLE; + break; + case IDE_PM_IDLE: /* Resume step 2 (idle)*/ + pm->pm_step = IDE_PM_RESTORE_DMA; + break; + } +} + +ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq) +{ + struct ide_pm_state *pm = ide_req(rq)->special; + struct ide_cmd cmd = { }; + + switch (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 (ata_id_flush_enabled(drive->id) == 0 || + (drive->dev_flags & IDE_DFLAG_WCACHE) == 0) { + ide_complete_power_step(drive, rq); + return ide_stopped; + } + if (ata_id_flush_ext_enabled(drive->id)) + cmd.tf.command = ATA_CMD_FLUSH_EXT; + else + cmd.tf.command = ATA_CMD_FLUSH; + goto out_do_tf; + case IDE_PM_STANDBY: /* Suspend step 2 (standby) */ + cmd.tf.command = ATA_CMD_STANDBYNOW1; + goto out_do_tf; + case IDE_PM_RESTORE_PIO: /* Resume step 1 (restore PIO) */ + ide_set_max_pio(drive); + /* + * skip IDE_PM_IDLE for ATAPI devices + */ + if (drive->media != ide_disk) + pm->pm_step = IDE_PM_RESTORE_DMA; + else + ide_complete_power_step(drive, rq); + return ide_stopped; + case IDE_PM_IDLE: /* Resume step 2 (idle) */ + cmd.tf.command = ATA_CMD_IDLEIMMEDIATE; + goto out_do_tf; + case IDE_PM_RESTORE_DMA: /* Resume step 3 (restore DMA) */ + /* + * Right now, all we do is call ide_set_dma(drive), + * we could be smarter and check for current xfer_speed + * in struct drive etc... + */ + if (drive->hwif->dma_ops == NULL) + break; + /* + * TODO: respect IDE_DFLAG_USING_DMA + */ + ide_set_dma(drive); + break; + } + + pm->pm_step = IDE_PM_COMPLETED; + + return ide_stopped; + +out_do_tf: + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + cmd.protocol = ATA_PROT_NODATA; + + return do_rw_taskfile(drive, &cmd); +} + +/** + * ide_complete_pm_rq - 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. + */ +void ide_complete_pm_rq(ide_drive_t *drive, struct request *rq) +{ + struct request_queue *q = drive->queue; + struct ide_pm_state *pm = ide_req(rq)->special; + + ide_complete_power_step(drive, rq); + if (pm->pm_step != IDE_PM_COMPLETED) + return; + +#ifdef DEBUG_PM + printk("%s: completing PM request, %s\n", drive->name, + (ide_req(rq)->type == ATA_PRIV_PM_SUSPEND) ? "suspend" : "resume"); +#endif + if (ide_req(rq)->type == ATA_PRIV_PM_SUSPEND) + blk_mq_stop_hw_queues(q); + else + drive->dev_flags &= ~IDE_DFLAG_BLOCKED; + + drive->hwif->rq = NULL; + + blk_mq_end_request(rq, BLK_STS_OK); +} + +void ide_check_pm_state(ide_drive_t *drive, struct request *rq) +{ + struct ide_pm_state *pm = ide_req(rq)->special; + + if (blk_rq_is_private(rq) && + ide_req(rq)->type == ATA_PRIV_PM_SUSPEND && + pm->pm_step == IDE_PM_START_SUSPEND) + /* Mark drive blocked when starting the suspend sequence. */ + drive->dev_flags |= IDE_DFLAG_BLOCKED; + else if (blk_rq_is_private(rq) && + ide_req(rq)->type == ATA_PRIV_PM_RESUME && + pm->pm_step == IDE_PM_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. + */ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + struct request_queue *q = drive->queue; + int rc; +#ifdef DEBUG_PM + printk("%s: Wakeup request inited, waiting for !BSY...\n", drive->name); +#endif + rc = ide_wait_not_busy(hwif, 35000); + if (rc) + printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name); + tp_ops->dev_select(drive); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + rc = ide_wait_not_busy(hwif, 100000); + if (rc) + printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name); + + blk_mq_start_hw_queues(q); + } +} diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c new file mode 100644 index 000000000..fc541f1cf --- /dev/null +++ b/drivers/ide/ide-pnp.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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> + */ + +#include <linux/init.h> +#include <linux/pnp.h> +#include <linux/ide.h> +#include <linux/module.h> + +#define DRV_NAME "ide-pnp" + +/* Add your devices here :)) */ +static const struct pnp_device_id idepnp_devices[] = { + /* Generic ESDI/IDE/ATA compatible hard disk controller */ + {.id = "PNP0600", .driver_data = 0}, + {.id = ""} +}; + +static const struct ide_port_info ide_pnp_port_info = { + .host_flags = IDE_HFLAG_NO_DMA, + .chipset = ide_generic, +}; + +static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct ide_host *host; + unsigned long base, ctl; + int rc; + struct ide_hw hw, *hws[] = { &hw }; + + printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n"); + + if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0))) + return -1; + + base = pnp_port_start(dev, 0); + ctl = pnp_port_start(dev, 1); + + if (!request_region(base, 8, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX-0x%lX not free.\n", + DRV_NAME, base, base + 7); + return -EBUSY; + } + + if (!request_region(ctl, 1, DRV_NAME)) { + printk(KERN_ERR "%s: I/O resource 0x%lX not free.\n", + DRV_NAME, ctl); + release_region(base, 8); + return -EBUSY; + } + + memset(&hw, 0, sizeof(hw)); + ide_std_init_ports(&hw, base, ctl); + hw.irq = pnp_irq(dev, 0); + + rc = ide_host_add(&ide_pnp_port_info, hws, 1, &host); + if (rc) + goto out; + + pnp_set_drvdata(dev, host); + + return 0; +out: + release_region(ctl, 1); + release_region(base, 8); + + return rc; +} + +static void idepnp_remove(struct pnp_dev *dev) +{ + struct ide_host *host = pnp_get_drvdata(dev); + + ide_host_remove(host); + + release_region(pnp_port_start(dev, 1), 1); + release_region(pnp_port_start(dev, 0), 8); +} + +static struct pnp_driver idepnp_driver = { + .name = "ide", + .id_table = idepnp_devices, + .probe = idepnp_probe, + .remove = idepnp_remove, +}; + +module_pnp_driver(idepnp_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c new file mode 100644 index 000000000..1ddc45a04 --- /dev/null +++ b/drivers/ide/ide-probe.c @@ -0,0 +1,1680 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + * Copyright (C) 2005, 2007 Bartlomiej Zolnierkiewicz + */ + +/* + * 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. + * + * -- increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot + * by Andrea Arcangeli + */ + +#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 <linux/scatterlist.h> + +#include <asm/byteorder.h> +#include <asm/irq.h> +#include <linux/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) +{ + u16 *id = drive->id; + + id[ATA_ID_CUR_CYLS] = id[ATA_ID_CYLS] = drive->cyl; + id[ATA_ID_CUR_HEADS] = id[ATA_ID_HEADS] = drive->head; + id[ATA_ID_CUR_SECTORS] = id[ATA_ID_SECTORS] = drive->sect; +} + +static void ide_disk_init_chs(ide_drive_t *drive) +{ + u16 *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[ATA_ID_CYLS]; + drive->head = drive->bios_head = id[ATA_ID_HEADS]; + drive->sect = drive->bios_sect = id[ATA_ID_SECTORS]; + } + + /* Handle logical geometry translation by the drive */ + if (ata_id_current_chs_valid(id)) { + drive->cyl = id[ATA_ID_CUR_CYLS]; + drive->head = id[ATA_ID_CUR_HEADS]; + drive->sect = id[ATA_ID_CUR_SECTORS]; + } + + /* Use physical geometry if what we have still makes no sense */ + if (drive->head > 16 && id[ATA_ID_HEADS] && id[ATA_ID_HEADS] <= 16) { + drive->cyl = id[ATA_ID_CYLS]; + drive->head = id[ATA_ID_HEADS]; + drive->sect = id[ATA_ID_SECTORS]; + } +} + +static void ide_disk_init_mult_count(ide_drive_t *drive) +{ + u16 *id = drive->id; + u8 max_multsect = id[ATA_ID_MAX_MULTSECT] & 0xff; + + if (max_multsect) { + if ((max_multsect / 2) > 1) + id[ATA_ID_MULTSECT] = max_multsect | 0x100; + else + id[ATA_ID_MULTSECT] &= ~0x1ff; + + drive->mult_req = id[ATA_ID_MULTSECT] & 0xff; + + if (drive->mult_req) + drive->special_flags |= IDE_SFLAG_SET_MULTMODE; + } +} + +static void ide_classify_ata_dev(ide_drive_t *drive) +{ + u16 *id = drive->id; + char *m = (char *)&id[ATA_ID_PROD]; + int is_cfa = ata_id_is_cfa(id); + + /* CF devices are *not* removable in Linux definition of the term */ + if (is_cfa == 0 && (id[ATA_ID_CONFIG] & (1 << 7))) + drive->dev_flags |= IDE_DFLAG_REMOVABLE; + + drive->media = ide_disk; + + if (!ata_id_has_unload(drive->id)) + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; + + printk(KERN_INFO "%s: %s, %s DISK drive\n", drive->name, m, + is_cfa ? "CFA" : "ATA"); +} + +static void ide_classify_atapi_dev(ide_drive_t *drive) +{ + u16 *id = drive->id; + char *m = (char *)&id[ATA_ID_PROD]; + u8 type = (id[ATA_ID_CONFIG] >> 8) & 0x1f; + + printk(KERN_INFO "%s: %s, ATAPI ", drive->name, m); + switch (type) { + case ide_floppy: + if (!strstr(m, "CD-ROM")) { + if (!strstr(m, "oppy") && + !strstr(m, "poyp") && + !strstr(m, "ZIP")) + printk(KERN_CONT "cdrom or floppy?, assuming "); + if (drive->media != ide_cdrom) { + printk(KERN_CONT "FLOPPY"); + drive->dev_flags |= IDE_DFLAG_REMOVABLE; + break; + } + } + /* Early cdrom models used zero */ + type = ide_cdrom; + fallthrough; + case ide_cdrom: + drive->dev_flags |= IDE_DFLAG_REMOVABLE; +#ifdef CONFIG_PPC + /* kludge for Apple PowerBook internal zip */ + if (!strstr(m, "CD-ROM") && strstr(m, "ZIP")) { + printk(KERN_CONT "FLOPPY"); + type = ide_floppy; + break; + } +#endif + printk(KERN_CONT "CD/DVD-ROM"); + break; + case ide_tape: + printk(KERN_CONT "TAPE"); + break; + case ide_optical: + printk(KERN_CONT "OPTICAL"); + drive->dev_flags |= IDE_DFLAG_REMOVABLE; + break; + default: + printk(KERN_CONT "UNKNOWN (type %d)", type); + break; + } + + printk(KERN_CONT " drive\n"); + drive->media = type; + /* an ATAPI device ignores DRDY */ + drive->ready_stat = 0; + if (ata_id_cdb_intr(id)) + drive->atapi_flags |= IDE_AFLAG_DRQ_INTERRUPT; + drive->dev_flags |= IDE_DFLAG_DOORLOCKING; + /* we don't do head unloading on ATAPI devices */ + drive->dev_flags |= IDE_DFLAG_NO_UNLOAD; +} + +/** + * do_identify - identify a drive + * @drive: drive to identify + * @cmd: command used + * @id: buffer for IDENTIFY data + * + * Called when we have issued a drive identify command to + * read and parse the results. This function is run with + * interrupts disabled. + */ + +static void do_identify(ide_drive_t *drive, u8 cmd, u16 *id) +{ + ide_hwif_t *hwif = drive->hwif; + char *m = (char *)&id[ATA_ID_PROD]; + unsigned long flags; + int bswap = 1; + + /* local CPU only; some systems need this */ + local_irq_save(flags); + /* read 512 bytes of id info */ + hwif->tp_ops->input_data(drive, NULL, id, SECTOR_SIZE); + local_irq_restore(flags); + + drive->dev_flags |= IDE_DFLAG_ID_READ; +#ifdef DEBUG + printk(KERN_INFO "%s: dumping identify data\n", drive->name); + ide_dump_identify((u8 *)id); +#endif + ide_fix_driveid(id); + + /* + * ATA_CMD_ID_ATA returns little-endian info, + * ATA_CMD_ID_ATAPI *usually* returns little-endian info. + */ + if (cmd == ATA_CMD_ID_ATAPI) { + if ((m[0] == 'N' && m[1] == 'E') || /* NEC */ + (m[0] == 'F' && m[1] == 'X') || /* Mitsumi */ + (m[0] == 'P' && m[1] == 'i')) /* Pioneer */ + /* Vertos drives may still be weird */ + bswap ^= 1; + } + + ide_fixstring(m, ATA_ID_PROD_LEN, bswap); + ide_fixstring((char *)&id[ATA_ID_FW_REV], ATA_ID_FW_REV_LEN, bswap); + ide_fixstring((char *)&id[ATA_ID_SERNO], ATA_ID_SERNO_LEN, bswap); + + /* we depend on this a lot! */ + m[ATA_ID_PROD_LEN - 1] = '\0'; + + if (strstr(m, "E X A B Y T E N E S T")) + drive->dev_flags &= ~IDE_DFLAG_PRESENT; + else + drive->dev_flags |= IDE_DFLAG_PRESENT; +} + +/** + * ide_dev_read_id - send ATA/ATAPI IDENTIFY command + * @drive: drive to identify + * @cmd: command to use + * @id: buffer for IDENTIFY data + * @irq_ctx: flag set when called from the IRQ context + * + * Sends an ATA(PI) IDENTIFY request to a drive and waits for a response. + * + * Returns: 0 device was identified + * 1 device timed-out (no response to identify request) + * 2 device aborted the command (refused to identify itself) + */ + +int ide_dev_read_id(ide_drive_t *drive, u8 cmd, u16 *id, int irq_ctx) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + int use_altstatus = 0, rc; + unsigned long timeout; + u8 s = 0, a = 0; + + /* + * Disable device IRQ. Otherwise we'll get spurious interrupts + * during the identify phase that the IRQ handler isn't expecting. + */ + if (io_ports->ctl_addr) + tp_ops->write_devctl(hwif, ATA_NIEN | ATA_DEVCTL_OBS); + + /* take a deep breath */ + if (irq_ctx) + mdelay(50); + else + msleep(50); + + if (io_ports->ctl_addr && + (hwif->host_flags & IDE_HFLAG_BROKEN_ALTSTATUS) == 0) { + a = tp_ops->read_altstatus(hwif); + s = tp_ops->read_status(hwif); + if ((a ^ s) & ~ATA_SENSE) + /* ancient Seagate drives, broken interfaces */ + printk(KERN_INFO "%s: probing with STATUS(0x%02x) " + "instead of ALTSTATUS(0x%02x)\n", + drive->name, s, a); + else + /* use non-intrusive polling */ + use_altstatus = 1; + } + + /* set features register for atapi + * identify command to be sure of reply + */ + if (cmd == ATA_CMD_ID_ATAPI) { + struct ide_taskfile tf; + + memset(&tf, 0, sizeof(tf)); + /* disable DMA & overlap */ + tp_ops->tf_load(drive, &tf, IDE_VALID_FEATURE); + } + + /* ask drive for ID */ + tp_ops->exec_command(hwif, cmd); + + timeout = ((cmd == ATA_CMD_ID_ATA) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; + + /* wait for IRQ and ATA_DRQ */ + if (irq_ctx) { + rc = __ide_wait_stat(drive, ATA_DRQ, BAD_R_STAT, timeout, &s); + if (rc) + return 1; + } else { + rc = ide_busy_sleep(drive, timeout, use_altstatus); + if (rc) + return 1; + + msleep(50); + s = tp_ops->read_status(hwif); + } + + if (OK_STAT(s, ATA_DRQ, BAD_R_STAT)) { + /* drive returned ID */ + do_identify(drive, cmd, id); + /* drive responded with ID */ + rc = 0; + /* clear drive IRQ */ + (void)tp_ops->read_status(hwif); + } else { + /* drive refused ID */ + rc = 2; + } + return rc; +} + +int ide_busy_sleep(ide_drive_t *drive, unsigned long timeout, int altstatus) +{ + ide_hwif_t *hwif = drive->hwif; + u8 stat; + + timeout += jiffies; + + do { + msleep(50); /* give drive a breather */ + stat = altstatus ? hwif->tp_ops->read_altstatus(hwif) + : hwif->tp_ops->read_status(hwif); + if ((stat & ATA_BUSY) == 0) + return 0; + } while (time_before(jiffies, timeout)); + + printk(KERN_ERR "%s: timeout in %s\n", drive->name, __func__); + + return 1; /* drive timed-out */ +} + +static u8 ide_read_device(ide_drive_t *drive) +{ + struct ide_taskfile tf; + + drive->hwif->tp_ops->tf_read(drive, &tf, IDE_VALID_DEVICE); + + return tf.device; +} + +/** + * 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) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + u16 *id = drive->id; + int rc; + u8 present = !!(drive->dev_flags & IDE_DFLAG_PRESENT), stat; + + /* avoid waiting for inappropriate probes */ + if (present && drive->media != ide_disk && cmd == ATA_CMD_ID_ATA) + return 4; + +#ifdef DEBUG + printk(KERN_INFO "probing for %s: present=%d, media=%d, probetype=%s\n", + drive->name, present, drive->media, + (cmd == ATA_CMD_ID_ATA) ? "ATA" : "ATAPI"); +#endif + + /* needed for some systems + * (e.g. crw9624 as drive0 with disk as slave) + */ + msleep(50); + tp_ops->dev_select(drive); + msleep(50); + + if (ide_read_device(drive) != drive->select && present == 0) { + if (drive->dn & 1) { + /* exit with drive0 selected */ + tp_ops->dev_select(hwif->devices[0]); + /* allow ATA_BUSY to assert & clear */ + msleep(50); + } + /* no i/f present: mmm.. this should be a 4 -ml */ + return 3; + } + + stat = tp_ops->read_status(hwif); + + if (OK_STAT(stat, ATA_DRDY, ATA_BUSY) || + present || cmd == ATA_CMD_ID_ATAPI) { + rc = ide_dev_read_id(drive, cmd, id, 0); + if (rc) + /* failed: try again */ + rc = ide_dev_read_id(drive, cmd, id, 0); + + stat = tp_ops->read_status(hwif); + + if (stat == (ATA_BUSY | ATA_DRDY)) + return 4; + + if (rc == 1 && cmd == ATA_CMD_ID_ATAPI) { + printk(KERN_ERR "%s: no response (status = 0x%02x), " + "resetting drive\n", drive->name, stat); + msleep(50); + tp_ops->dev_select(drive); + msleep(50); + tp_ops->exec_command(hwif, ATA_CMD_DEV_RESET); + (void)ide_busy_sleep(drive, WAIT_WORSTCASE, 0); + rc = ide_dev_read_id(drive, cmd, id, 0); + } + + /* ensure drive IRQ is clear */ + stat = tp_ops->read_status(hwif); + + if (rc == 1) + printk(KERN_ERR "%s: no response (status = 0x%02x)\n", + drive->name, stat); + } else { + /* not present or maybe ATAPI */ + rc = 3; + } + if (drive->dn & 1) { + /* exit with drive0 selected */ + tp_ops->dev_select(hwif->devices[0]); + msleep(50); + /* ensure drive irq is clear */ + (void)tp_ops->read_status(hwif); + } + return rc; +} + +/** + * 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: IDE_DFLAG_PRESENT might still be not set) + */ + +static u8 probe_for_drive(ide_drive_t *drive) +{ + char *m; + int rc; + u8 cmd; + + drive->dev_flags &= ~IDE_DFLAG_ID_READ; + + m = (char *)&drive->id[ATA_ID_PROD]; + strcpy(m, "UNKNOWN"); + + /* skip probing? */ + if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0) { + /* if !(success||timed-out) */ + cmd = ATA_CMD_ID_ATA; + rc = do_probe(drive, cmd); + if (rc >= 2) { + /* look for ATAPI device */ + cmd = ATA_CMD_ID_ATAPI; + rc = do_probe(drive, cmd); + } + + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) + return 0; + + /* identification failed? */ + if ((drive->dev_flags & IDE_DFLAG_ID_READ) == 0) { + 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->dev_flags &= ~IDE_DFLAG_PRESENT; + } + } else { + if (cmd == ATA_CMD_ID_ATAPI) + ide_classify_atapi_dev(drive); + else + ide_classify_ata_dev(drive); + } + } + + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) + return 0; + + /* The drive wasn't being helpful. Add generic info only */ + if ((drive->dev_flags & IDE_DFLAG_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 1; +} + +static void hwif_release_dev(struct device *dev) +{ + ide_hwif_t *hwif = container_of(dev, ide_hwif_t, gendev); + + complete(&hwif->gendev_rel_comp); +} + +static int ide_register_port(ide_hwif_t *hwif) +{ + int ret; + + /* register with global device tree */ + dev_set_name(&hwif->gendev, "%s", hwif->name); + dev_set_drvdata(&hwif->gendev, hwif); + if (hwif->gendev.parent == NULL) + hwif->gendev.parent = hwif->dev; + hwif->gendev.release = hwif_release_dev; + + ret = device_register(&hwif->gendev); + if (ret < 0) { + printk(KERN_WARNING "IDE: %s: device_register error: %d\n", + __func__, ret); + goto out; + } + + hwif->portdev = device_create(ide_port_class, &hwif->gendev, + MKDEV(0, 0), hwif, "%s", hwif->name); + if (IS_ERR(hwif->portdev)) { + ret = PTR_ERR(hwif->portdev); + device_unregister(&hwif->gendev); + } +out: + return ret; +} + +/** + * ide_port_wait_ready - wait for port to become ready + * @hwif: IDE port + * + * 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-non-busy should not harm any existing + * configuration and fix some issues like the above. + * + * BenH. + * + * Returns 0 on success, error code (< 0) otherwise. + */ + +static int ide_port_wait_ready(ide_hwif_t *hwif) +{ + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + ide_drive_t *drive; + int i, 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 */ + ide_port_for_each_dev(i, drive, hwif) { + /* Ignore disks that we will not probe for later. */ + if ((drive->dev_flags & IDE_DFLAG_NOPROBE) == 0 || + (drive->dev_flags & IDE_DFLAG_PRESENT)) { + tp_ops->dev_select(drive); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + mdelay(2); + rc = ide_wait_not_busy(hwif, 35000); + if (rc) + goto out; + } else + printk(KERN_DEBUG "%s: ide_wait_not_busy() skipped\n", + drive->name); + } +out: + /* Exit function with master reselected (let's be sane) */ + if (i) + tp_ops->dev_select(hwif->devices[0]); + + return rc; +} + +/** + * ide_undecoded_slave - look for bad CF adapters + * @dev1: slave device + * + * 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_drive_t *dev1) +{ + ide_drive_t *dev0 = dev1->hwif->devices[0]; + + if ((dev1->dn & 1) == 0 || (dev0->dev_flags & IDE_DFLAG_PRESENT) == 0) + return; + + /* If the models don't match they are not the same product */ + if (strcmp((char *)&dev0->id[ATA_ID_PROD], + (char *)&dev1->id[ATA_ID_PROD])) + return; + + /* Serial numbers do not match */ + if (strncmp((char *)&dev0->id[ATA_ID_SERNO], + (char *)&dev1->id[ATA_ID_SERNO], ATA_ID_SERNO_LEN)) + return; + + /* No serial number, thankfully very rare for CF */ + if (*(char *)&dev0->id[ATA_ID_SERNO] == 0) + return; + + /* Appears to be an IDE flash adapter with decode bugs */ + printk(KERN_WARNING "ide-probe: ignoring undecoded slave\n"); + + dev1->dev_flags &= ~IDE_DFLAG_PRESENT; +} + +EXPORT_SYMBOL_GPL(ide_undecoded_slave); + +static int ide_probe_port(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + unsigned int irqd; + int i, rc = -ENODEV; + + BUG_ON(hwif->present); + + if ((hwif->devices[0]->dev_flags & IDE_DFLAG_NOPROBE) && + (hwif->devices[1]->dev_flags & IDE_DFLAG_NOPROBE)) + return -EACCES; + + /* + * 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); + + if (ide_port_wait_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. + */ + ide_port_for_each_dev(i, drive, hwif) { + (void) probe_for_drive(drive); + if (drive->dev_flags & IDE_DFLAG_PRESENT) + rc = 0; + } + + /* + * Use cached IRQ number. It might be (and is...) changed by probe + * code above + */ + if (irqd) + enable_irq(irqd); + + return rc; +} + +static void ide_port_tune_devices(ide_hwif_t *hwif) +{ + const struct ide_port_ops *port_ops = hwif->port_ops; + ide_drive_t *drive; + int i; + + ide_port_for_each_present_dev(i, drive, hwif) { + ide_check_nien_quirk_list(drive); + + if (port_ops && port_ops->quirkproc) + port_ops->quirkproc(drive); + } + + ide_port_for_each_present_dev(i, drive, hwif) { + ide_set_max_pio(drive); + + drive->dev_flags |= IDE_DFLAG_NICE1; + + if (hwif->dma_ops) + ide_set_dma(drive); + } +} + +static void ide_initialize_rq(struct request *rq) +{ + struct ide_request *req = blk_mq_rq_to_pdu(rq); + + req->special = NULL; + scsi_req_init(&req->sreq); + req->sreq.sense = req->sense; +} + +static const struct blk_mq_ops ide_mq_ops = { + .queue_rq = ide_queue_rq, + .initialize_rq_fn = ide_initialize_rq, +}; + +/* + * init request queue + */ +static int ide_init_queue(ide_drive_t *drive) +{ + struct request_queue *q; + ide_hwif_t *hwif = drive->hwif; + int max_sectors = 256; + int max_sg_entries = PRD_ENTRIES; + struct blk_mq_tag_set *set; + + /* + * 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. + */ + + set = &drive->tag_set; + set->ops = &ide_mq_ops; + set->nr_hw_queues = 1; + set->queue_depth = 32; + set->reserved_tags = 1; + set->cmd_size = sizeof(struct ide_request); + set->numa_node = hwif_to_node(hwif); + set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; + if (blk_mq_alloc_tag_set(set)) + return 1; + + q = blk_mq_init_queue(set); + if (IS_ERR(q)) { + blk_mq_free_tag_set(set); + return 1; + } + + blk_queue_flag_set(QUEUE_FLAG_SCSI_PASSTHROUGH, q); + + q->queuedata = drive; + blk_queue_segment_boundary(q, 0xffff); + + if (hwif->rqsize < max_sectors) + max_sectors = hwif->rqsize; + blk_queue_max_hw_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* + */ + max_sg_entries >>= 1; +#endif /* CONFIG_PCI */ + + blk_queue_max_segments(q, max_sg_entries); + + /* assign drive queue */ + drive->queue = q; + + return 0; +} + +static DEFINE_MUTEX(ide_cfg_mtx); + +/* + * For any present drive: + * - allocate the block device queue + */ +static int ide_port_setup_devices(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i, j = 0; + + mutex_lock(&ide_cfg_mtx); + ide_port_for_each_present_dev(i, drive, hwif) { + if (ide_init_queue(drive)) { + printk(KERN_ERR "ide: failed to init %s\n", + drive->name); + drive->dev_flags &= ~IDE_DFLAG_PRESENT; + continue; + } + + j++; + } + mutex_unlock(&ide_cfg_mtx); + + return j; +} + +static void ide_host_enable_irqs(struct ide_host *host) +{ + ide_hwif_t *hwif; + int i; + + ide_host_for_each_port(i, hwif, host) { + if (hwif == NULL) + continue; + + /* clear any pending IRQs */ + hwif->tp_ops->read_status(hwif); + + /* unmask IRQs */ + if (hwif->io_ports.ctl_addr) + hwif->tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + } +} + +/* + * This routine sets up the IRQ for an IDE interface. + */ +static int init_irq (ide_hwif_t *hwif) +{ + struct ide_io_ports *io_ports = &hwif->io_ports; + struct ide_host *host = hwif->host; + irq_handler_t irq_handler = host->irq_handler; + int sa = host->irq_flags; + + if (irq_handler == NULL) + irq_handler = ide_intr; + + if (!host->get_lock) + if (request_irq(hwif->irq, irq_handler, sa, hwif->name, hwif)) + goto out_up; + +#if !defined(__mc68000__) + printk(KERN_INFO "%s at 0x%03lx-0x%03lx,0x%03lx on irq %d", hwif->name, + io_ports->data_addr, io_ports->status_addr, + io_ports->ctl_addr, hwif->irq); +#else + printk(KERN_INFO "%s at 0x%08lx on irq %d", hwif->name, + io_ports->data_addr, hwif->irq); +#endif /* __mc68000__ */ + if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) + printk(KERN_CONT " (serialized)"); + printk(KERN_CONT "\n"); + + return 0; +out_up: + 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->devices[unit]; + + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) + return NULL; + + if (drive->media == ide_disk) + request_module("ide-disk"); + 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 &disk_to_dev(p)->kobj; +} + +static int exact_lock(dev_t dev, void *data) +{ + struct gendisk *p = data; + + if (!get_disk_and_module(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->dn & 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); + + ide_proc_unregister_device(drive); + + if (drive->sense_rq) + blk_mq_free_request(drive->sense_rq); + + blk_cleanup_queue(drive->queue); + drive->queue = NULL; + blk_mq_free_tag_set(&drive->tag_set); + + drive->dev_flags &= ~IDE_DFLAG_PRESENT; + + complete(&drive->gendev_rel_comp); +} + +static int hwif_init(ide_hwif_t *hwif) +{ + if (!hwif->irq) { + printk(KERN_ERR "%s: disabled, no IRQ\n", hwif->name); + return 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_array(hwif->sg_max_nents, + sizeof(struct scatterlist), + GFP_KERNEL); + if (!hwif->sg_table) { + printk(KERN_ERR "%s: unable to allocate SG table.\n", hwif->name); + goto out; + } + + sg_init_table(hwif->sg_table, hwif->sg_max_nents); + + if (init_irq(hwif)) { + printk(KERN_ERR "%s: disabled, unable to get IRQ %d\n", + hwif->name, hwif->irq); + goto out; + } + + blk_register_region(MKDEV(hwif->major, 0), MAX_DRIVES << PARTN_BITS, + THIS_MODULE, ata_probe, ata_lock, hwif); + return 1; + +out: + unregister_blkdev(hwif->major, hwif->name); + return 0; +} + +static void hwif_register_devices(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + unsigned int i; + + ide_port_for_each_present_dev(i, drive, hwif) { + struct device *dev = &drive->gendev; + int ret; + + dev_set_name(dev, "%u.%u", hwif->index, i); + dev_set_drvdata(dev, drive); + dev->parent = &hwif->gendev; + dev->bus = &ide_bus_type; + dev->release = drive_release_dev; + + ret = device_register(dev); + if (ret < 0) + printk(KERN_WARNING "IDE: %s: device_register error: " + "%d\n", __func__, ret); + } +} + +static void ide_port_init_devices(ide_hwif_t *hwif) +{ + const struct ide_port_ops *port_ops = hwif->port_ops; + ide_drive_t *drive; + int i; + + ide_port_for_each_dev(i, drive, hwif) { + drive->dn = i + hwif->channel * 2; + + if (hwif->host_flags & IDE_HFLAG_IO_32BIT) + drive->io_32bit = 1; + if (hwif->host_flags & IDE_HFLAG_NO_IO_32BIT) + drive->dev_flags |= IDE_DFLAG_NO_IO_32BIT; + if (hwif->host_flags & IDE_HFLAG_UNMASK_IRQS) + drive->dev_flags |= IDE_DFLAG_UNMASK; + if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS) + drive->dev_flags |= IDE_DFLAG_NO_UNMASK; + + drive->pio_mode = XFER_PIO_0; + + if (port_ops && port_ops->init_dev) + port_ops->init_dev(drive); + } +} + +static void ide_init_port(ide_hwif_t *hwif, unsigned int port, + const struct ide_port_info *d) +{ + hwif->channel = port; + + hwif->chipset = d->chipset ? d->chipset : ide_pci; + + if (d->init_iops) + d->init_iops(hwif); + + /* ->host_flags may be set by ->init_iops (or even earlier...) */ + hwif->host_flags |= d->host_flags; + hwif->pio_mask = d->pio_mask; + + if (d->tp_ops) + hwif->tp_ops = d->tp_ops; + + /* ->set_pio_mode for DTC2278 is currently limited to port 0 */ + if ((hwif->host_flags & IDE_HFLAG_DTC2278) == 0 || hwif->channel == 0) + hwif->port_ops = d->port_ops; + + hwif->swdma_mask = d->swdma_mask; + hwif->mwdma_mask = d->mwdma_mask; + hwif->ultra_mask = d->udma_mask; + + if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) { + int rc; + + hwif->dma_ops = d->dma_ops; + + if (d->init_dma) + rc = d->init_dma(hwif, d); + else + rc = ide_hwif_setup_dma(hwif, d); + + if (rc < 0) { + printk(KERN_INFO "%s: DMA disabled\n", hwif->name); + + hwif->dma_ops = NULL; + hwif->dma_base = 0; + hwif->swdma_mask = 0; + hwif->mwdma_mask = 0; + hwif->ultra_mask = 0; + } + } + + if ((d->host_flags & IDE_HFLAG_SERIALIZE) || + ((d->host_flags & IDE_HFLAG_SERIALIZE_DMA) && hwif->dma_base)) + hwif->host->host_flags |= IDE_HFLAG_SERIALIZE; + + if (d->max_sectors) + hwif->rqsize = d->max_sectors; + else { + if ((hwif->host_flags & IDE_HFLAG_NO_LBA48) || + (hwif->host_flags & IDE_HFLAG_NO_LBA48_DMA)) + hwif->rqsize = 256; + else + hwif->rqsize = 65536; + } + + /* call chipset specific routine for each enabled port */ + if (d->init_hwif) + d->init_hwif(hwif); +} + +static void ide_port_cable_detect(ide_hwif_t *hwif) +{ + const struct ide_port_ops *port_ops = hwif->port_ops; + + if (port_ops && port_ops->cable_detect && (hwif->ultra_mask & 0x78)) { + if (hwif->cbl != ATA_CBL_PATA40_SHORT) + hwif->cbl = port_ops->cable_detect(hwif); + } +} + +/* + * Deferred request list insertion handler + */ +static void drive_rq_insert_work(struct work_struct *work) +{ + ide_drive_t *drive = container_of(work, ide_drive_t, rq_work); + ide_hwif_t *hwif = drive->hwif; + struct request *rq; + blk_status_t ret; + LIST_HEAD(list); + + blk_mq_quiesce_queue(drive->queue); + + ret = BLK_STS_OK; + spin_lock_irq(&hwif->lock); + while (!list_empty(&drive->rq_list)) { + rq = list_first_entry(&drive->rq_list, struct request, queuelist); + list_del_init(&rq->queuelist); + + spin_unlock_irq(&hwif->lock); + ret = ide_issue_rq(drive, rq, true); + spin_lock_irq(&hwif->lock); + } + spin_unlock_irq(&hwif->lock); + + blk_mq_unquiesce_queue(drive->queue); + + if (ret != BLK_STS_OK) + kblockd_schedule_work(&drive->rq_work); +} + +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 void ide_port_init_devices_data(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i; + + ide_port_for_each_dev(i, drive, hwif) { + u8 j = (hwif->index * MAX_DRIVES) + i; + u16 *saved_id = drive->id; + + memset(drive, 0, sizeof(*drive)); + memset(saved_id, 0, SECTOR_SIZE); + drive->id = saved_id; + + drive->media = ide_disk; + drive->select = (i << 4) | ATA_DEVICE_OBS; + drive->hwif = hwif; + drive->ready_stat = ATA_DRDY; + drive->bad_wstat = BAD_W_STAT; + drive->special_flags = IDE_SFLAG_RECALIBRATE | + IDE_SFLAG_SET_GEOMETRY; + drive->name[0] = 'h'; + drive->name[1] = 'd'; + drive->name[2] = 'a' + j; + drive->max_failures = IDE_DEFAULT_MAX_FAILURES; + + INIT_LIST_HEAD(&drive->list); + init_completion(&drive->gendev_rel_comp); + + INIT_WORK(&drive->rq_work, drive_rq_insert_work); + INIT_LIST_HEAD(&drive->rq_list); + } +} + +static void ide_init_port_data(ide_hwif_t *hwif, unsigned int index) +{ + /* 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; + + spin_lock_init(&hwif->lock); + + timer_setup(&hwif->timer, ide_timer_expiry, 0); + + init_completion(&hwif->gendev_rel_comp); + + hwif->tp_ops = &default_tp_ops; + + ide_port_init_devices_data(hwif); +} + +static void ide_init_port_hw(ide_hwif_t *hwif, struct ide_hw *hw) +{ + memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports)); + hwif->irq = hw->irq; + hwif->dev = hw->dev; + hwif->gendev.parent = hw->parent ? hw->parent : hw->dev; + hwif->config_data = hw->config; +} + +static unsigned int ide_indexes; + +/** + * ide_find_port_slot - find free port slot + * @d: IDE port info + * + * Return the new port slot index or -ENOENT if we are out of free slots. + */ + +static int ide_find_port_slot(const struct ide_port_info *d) +{ + int idx = -ENOENT; + u8 bootable = (d && (d->host_flags & IDE_HFLAG_NON_BOOTABLE)) ? 0 : 1; + u8 i = (d && (d->host_flags & IDE_HFLAG_QD_2ND_PORT)) ? 1 : 0; + + /* + * 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 0x1f0/0x170 (the ide0/ide1 defaults). + * + * Unless there is a bootable card that does not use the standard + * ports 0x1f0/0x170 (the ide0/ide1 defaults). + */ + mutex_lock(&ide_cfg_mtx); + if (bootable) { + if ((ide_indexes | i) != (1 << MAX_HWIFS) - 1) + idx = ffz(ide_indexes | i); + } else { + if ((ide_indexes | 3) != (1 << MAX_HWIFS) - 1) + idx = ffz(ide_indexes | 3); + else if ((ide_indexes & 3) != 3) + idx = ffz(ide_indexes); + } + if (idx >= 0) + ide_indexes |= (1 << idx); + mutex_unlock(&ide_cfg_mtx); + + return idx; +} + +static void ide_free_port_slot(int idx) +{ + mutex_lock(&ide_cfg_mtx); + ide_indexes &= ~(1 << idx); + mutex_unlock(&ide_cfg_mtx); +} + +static void ide_port_free_devices(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i; + + ide_port_for_each_dev(i, drive, hwif) { + kfree(drive->id); + kfree(drive); + } +} + +static int ide_port_alloc_devices(ide_hwif_t *hwif, int node) +{ + ide_drive_t *drive; + int i; + + for (i = 0; i < MAX_DRIVES; i++) { + drive = kzalloc_node(sizeof(*drive), GFP_KERNEL, node); + if (drive == NULL) + goto out_nomem; + + /* + * 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 = kzalloc_node(SECTOR_SIZE, GFP_KERNEL, node); + if (drive->id == NULL) + goto out_free_drive; + + hwif->devices[i] = drive; + } + return 0; + +out_free_drive: + kfree(drive); +out_nomem: + ide_port_free_devices(hwif); + return -ENOMEM; +} + +struct ide_host *ide_host_alloc(const struct ide_port_info *d, + struct ide_hw **hws, unsigned int n_ports) +{ + struct ide_host *host; + struct device *dev = hws[0] ? hws[0]->dev : NULL; + int node = dev ? dev_to_node(dev) : -1; + int i; + + host = kzalloc_node(sizeof(*host), GFP_KERNEL, node); + if (host == NULL) + return NULL; + + for (i = 0; i < n_ports; i++) { + ide_hwif_t *hwif; + int idx; + + if (hws[i] == NULL) + continue; + + hwif = kzalloc_node(sizeof(*hwif), GFP_KERNEL, node); + if (hwif == NULL) + continue; + + if (ide_port_alloc_devices(hwif, node) < 0) { + kfree(hwif); + continue; + } + + idx = ide_find_port_slot(d); + if (idx < 0) { + printk(KERN_ERR "%s: no free slot for interface\n", + d ? d->name : "ide"); + ide_port_free_devices(hwif); + kfree(hwif); + continue; + } + + ide_init_port_data(hwif, idx); + + hwif->host = host; + + host->ports[i] = hwif; + host->n_ports++; + } + + if (host->n_ports == 0) { + kfree(host); + return NULL; + } + + host->dev[0] = dev; + + if (d) { + host->init_chipset = d->init_chipset; + host->get_lock = d->get_lock; + host->release_lock = d->release_lock; + host->host_flags = d->host_flags; + host->irq_flags = d->irq_flags; + } + + return host; +} +EXPORT_SYMBOL_GPL(ide_host_alloc); + +static void ide_port_free(ide_hwif_t *hwif) +{ + ide_port_free_devices(hwif); + ide_free_port_slot(hwif->index); + kfree(hwif); +} + +static void ide_disable_port(ide_hwif_t *hwif) +{ + struct ide_host *host = hwif->host; + int i; + + printk(KERN_INFO "%s: disabling port\n", hwif->name); + + for (i = 0; i < MAX_HOST_PORTS; i++) { + if (host->ports[i] == hwif) { + host->ports[i] = NULL; + host->n_ports--; + } + } + + ide_port_free(hwif); +} + +int ide_host_register(struct ide_host *host, const struct ide_port_info *d, + struct ide_hw **hws) +{ + ide_hwif_t *hwif, *mate = NULL; + int i, j = 0; + + pr_warn("legacy IDE will be removed in 2021, please switch to libata\n" + "Report any missing HW support to linux-ide@vger.kernel.org\n"); + + ide_host_for_each_port(i, hwif, host) { + if (hwif == NULL) { + mate = NULL; + continue; + } + + ide_init_port_hw(hwif, hws[i]); + ide_port_apply_params(hwif); + + if ((i & 1) && mate) { + hwif->mate = mate; + mate->mate = hwif; + } + + mate = (i & 1) ? NULL : hwif; + + ide_init_port(hwif, i & 1, d); + ide_port_cable_detect(hwif); + + hwif->port_flags |= IDE_PFLAG_PROBING; + + ide_port_init_devices(hwif); + } + + ide_host_for_each_port(i, hwif, host) { + if (hwif == NULL) + continue; + + if (ide_probe_port(hwif) == 0) + hwif->present = 1; + + hwif->port_flags &= ~IDE_PFLAG_PROBING; + + if ((hwif->host_flags & IDE_HFLAG_4DRIVES) == 0 || + hwif->mate == NULL || hwif->mate->present == 0) { + if (ide_register_port(hwif)) { + ide_disable_port(hwif); + continue; + } + } + + if (hwif->present) + ide_port_tune_devices(hwif); + } + + ide_host_enable_irqs(host); + + ide_host_for_each_port(i, hwif, host) { + if (hwif == NULL) + continue; + + if (hwif_init(hwif) == 0) { + printk(KERN_INFO "%s: failed to initialize IDE " + "interface\n", hwif->name); + device_unregister(hwif->portdev); + device_unregister(&hwif->gendev); + ide_disable_port(hwif); + continue; + } + + if (hwif->present) + if (ide_port_setup_devices(hwif) == 0) { + hwif->present = 0; + continue; + } + + j++; + + ide_acpi_init_port(hwif); + + if (hwif->present) + ide_acpi_port_init_devices(hwif); + } + + ide_host_for_each_port(i, hwif, host) { + if (hwif == NULL) + continue; + + ide_sysfs_register_port(hwif); + ide_proc_register_port(hwif); + + if (hwif->present) { + ide_proc_port_register_devices(hwif); + hwif_register_devices(hwif); + } + } + + return j ? 0 : -1; +} +EXPORT_SYMBOL_GPL(ide_host_register); + +int ide_host_add(const struct ide_port_info *d, struct ide_hw **hws, + unsigned int n_ports, struct ide_host **hostp) +{ + struct ide_host *host; + int rc; + + host = ide_host_alloc(d, hws, n_ports); + if (host == NULL) + return -ENOMEM; + + rc = ide_host_register(host, d, hws); + if (rc) { + ide_host_free(host); + return rc; + } + + if (hostp) + *hostp = host; + + return 0; +} +EXPORT_SYMBOL_GPL(ide_host_add); + +static void __ide_port_unregister_devices(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i; + + ide_port_for_each_present_dev(i, drive, hwif) { + device_unregister(&drive->gendev); + wait_for_completion(&drive->gendev_rel_comp); + } +} + +void ide_port_unregister_devices(ide_hwif_t *hwif) +{ + mutex_lock(&ide_cfg_mtx); + __ide_port_unregister_devices(hwif); + hwif->present = 0; + ide_port_init_devices_data(hwif); + mutex_unlock(&ide_cfg_mtx); +} +EXPORT_SYMBOL_GPL(ide_port_unregister_devices); + +/** + * ide_unregister - free an IDE interface + * @hwif: IDE interface + * + * Perform the final unregister of an IDE interface. + * + * Locking: + * The caller must not hold the IDE locks. + * + * It is up to the caller to be sure there is no pending I/O here, + * and that the interface will not be reopened (present/vanishing + * locking isn't yet done BTW). + */ + +static void ide_unregister(ide_hwif_t *hwif) +{ + BUG_ON(in_interrupt()); + BUG_ON(irqs_disabled()); + + mutex_lock(&ide_cfg_mtx); + + if (hwif->present) { + __ide_port_unregister_devices(hwif); + hwif->present = 0; + } + + ide_proc_unregister_port(hwif); + + if (!hwif->host->get_lock) + free_irq(hwif->irq, hwif); + + device_unregister(hwif->portdev); + device_unregister(&hwif->gendev); + wait_for_completion(&hwif->gendev_rel_comp); + + /* + * 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); + + ide_release_dma_engine(hwif); + + mutex_unlock(&ide_cfg_mtx); +} + +void ide_host_free(struct ide_host *host) +{ + ide_hwif_t *hwif; + int i; + + ide_host_for_each_port(i, hwif, host) { + if (hwif) + ide_port_free(hwif); + } + + kfree(host); +} +EXPORT_SYMBOL_GPL(ide_host_free); + +void ide_host_remove(struct ide_host *host) +{ + ide_hwif_t *hwif; + int i; + + ide_host_for_each_port(i, hwif, host) { + if (hwif) + ide_unregister(hwif); + } + + ide_host_free(host); +} +EXPORT_SYMBOL_GPL(ide_host_remove); + +void ide_port_scan(ide_hwif_t *hwif) +{ + int rc; + + ide_port_apply_params(hwif); + ide_port_cable_detect(hwif); + + hwif->port_flags |= IDE_PFLAG_PROBING; + + ide_port_init_devices(hwif); + + rc = ide_probe_port(hwif); + + hwif->port_flags &= ~IDE_PFLAG_PROBING; + + if (rc < 0) + return; + + hwif->present = 1; + + ide_port_tune_devices(hwif); + ide_port_setup_devices(hwif); + ide_acpi_port_init_devices(hwif); + hwif_register_devices(hwif); + ide_proc_port_register_devices(hwif); +} +EXPORT_SYMBOL_GPL(ide_port_scan); diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c new file mode 100644 index 000000000..15c17f378 --- /dev/null +++ b/drivers/ide/ide-proc.c @@ -0,0 +1,633 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1997-1998 Mark Lord + * Copyright (C) 2003 Red Hat + * + * Some code was moved here from ide.c, see it for original copyrights. + */ + +/* + * 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 + */ + +#include <linux/module.h> + +#include <linux/uaccess.h> +#include <linux/errno.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> +#include <linux/mm.h> +#include <linux/pci.h> +#include <linux/ctype.h> +#include <linux/ide.h> +#include <linux/seq_file.h> +#include <linux/slab.h> + +#include <asm/io.h> + +static struct proc_dir_entry *proc_ide_root; + +static int ide_imodel_proc_show(struct seq_file *m, void *v) +{ + ide_hwif_t *hwif = (ide_hwif_t *) m->private; + const char *name; + + 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_4drives: name = "4drives"; break; + case ide_pmac: name = "mac-io"; break; + case ide_au1xxx: name = "au1xxx"; break; + case ide_palm3710: name = "palm3710"; break; + case ide_acorn: name = "acorn"; break; + default: name = "(unknown)"; break; + } + seq_printf(m, "%s\n", name); + return 0; +} + +static int ide_mate_proc_show(struct seq_file *m, void *v) +{ + ide_hwif_t *hwif = (ide_hwif_t *) m->private; + + if (hwif && hwif->mate) + seq_printf(m, "%s\n", hwif->mate->name); + else + seq_printf(m, "(none)\n"); + return 0; +} + +static int ide_channel_proc_show(struct seq_file *m, void *v) +{ + ide_hwif_t *hwif = (ide_hwif_t *) m->private; + + seq_printf(m, "%c\n", hwif->channel ? '1' : '0'); + return 0; +} + +static int ide_identify_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *)m->private; + u8 *buf; + + if (!drive) { + seq_putc(m, '\n'); + return 0; + } + + buf = kmalloc(SECTOR_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + if (taskfile_lib_get_identify(drive, buf) == 0) { + __le16 *val = (__le16 *)buf; + int i; + + for (i = 0; i < SECTOR_SIZE / 2; i++) { + seq_printf(m, "%04x%c", le16_to_cpu(val[i]), + (i % 8) == 7 ? '\n' : ' '); + } + } else + seq_putc(m, buf[0]); + kfree(buf); + return 0; +} + +/** + * ide_find_setting - find a specific setting + * @st: setting table pointer + * @name: setting name + * + * Scan's the setting table for a matching entry and returns + * this or NULL if no entry is found. The caller must hold the + * setting semaphore + */ + +static +const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st, + char *name) +{ + while (st->name) { + if (strcmp(st->name, name) == 0) + break; + st++; + } + return st->name ? st : NULL; +} + +/** + * 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_mtx 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 + */ + +static int ide_read_setting(ide_drive_t *drive, + const struct ide_proc_devset *setting) +{ + const struct ide_devset *ds = setting->setting; + int val = -EINVAL; + + if (ds->get) + val = ds->get(drive); + + return val; +} + +/** + * 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_mtx 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. + */ + +static int ide_write_setting(ide_drive_t *drive, + const struct ide_proc_devset *setting, int val) +{ + const struct ide_devset *ds = setting->setting; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!ds->set) + return -EPERM; + if ((ds->flags & DS_SYNC) + && (val < setting->min || val > setting->max)) + return -EINVAL; + return ide_devset_execute(drive, ds, val); +} + +ide_devset_get(xfer_rate, current_speed); + +static int set_xfer_rate (ide_drive_t *drive, int arg) +{ + struct ide_cmd cmd; + + if (arg < XFER_PIO_0 || arg > XFER_UDMA_6) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tf.command = ATA_CMD_SET_FEATURES; + cmd.tf.feature = SETFEATURES_XFER; + cmd.tf.nsect = (u8)arg; + cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT; + cmd.valid.in.tf = IDE_VALID_NSECT; + cmd.tf_flags = IDE_TFLAG_SET_XFER; + + return ide_no_data_taskfile(drive, &cmd); +} + +ide_devset_rw(current_speed, xfer_rate); +ide_devset_rw_field(init_speed, init_speed); +ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1); +ide_devset_ro_field(number, dn); + +static const struct ide_proc_devset ide_generic_settings[] = { + IDE_PROC_DEVSET(current_speed, 0, 70), + IDE_PROC_DEVSET(init_speed, 0, 70), + IDE_PROC_DEVSET(io_32bit, 0, 1 + (SUPPORT_VLB_SYNC << 1)), + IDE_PROC_DEVSET(keepsettings, 0, 1), + IDE_PROC_DEVSET(nice1, 0, 1), + IDE_PROC_DEVSET(number, 0, 3), + IDE_PROC_DEVSET(pio_mode, 0, 255), + IDE_PROC_DEVSET(unmaskirq, 0, 1), + IDE_PROC_DEVSET(using_dma, 0, 1), + { NULL }, +}; + +static void proc_ide_settings_warn(void) +{ + printk_once(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is " + "obsolete, and will be removed soon!\n"); +} + +static int ide_settings_proc_show(struct seq_file *m, void *v) +{ + const struct ide_proc_devset *setting, *g, *d; + const struct ide_devset *ds; + ide_drive_t *drive = (ide_drive_t *) m->private; + int rc, mul_factor, div_factor; + + proc_ide_settings_warn(); + + mutex_lock(&ide_setting_mtx); + g = ide_generic_settings; + d = drive->settings; + seq_printf(m, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n"); + seq_printf(m, "----\t\t\t-----\t\t---\t\t---\t\t----\n"); + while (g->name || (d && d->name)) { + /* read settings in the alphabetical order */ + if (g->name && d && d->name) { + if (strcmp(d->name, g->name) < 0) + setting = d++; + else + setting = g++; + } else if (d && d->name) { + setting = d++; + } else + setting = g++; + mul_factor = setting->mulf ? setting->mulf(drive) : 1; + div_factor = setting->divf ? setting->divf(drive) : 1; + seq_printf(m, "%-24s", setting->name); + rc = ide_read_setting(drive, setting); + if (rc >= 0) + seq_printf(m, "%-16d", rc * mul_factor / div_factor); + else + seq_printf(m, "%-16s", "write-only"); + seq_printf(m, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor); + ds = setting->setting; + if (ds->get) + seq_printf(m, "r"); + if (ds->set) + seq_printf(m, "w"); + seq_printf(m, "\n"); + } + mutex_unlock(&ide_setting_mtx); + return 0; +} + +static int ide_settings_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ide_settings_proc_show, PDE_DATA(inode)); +} + +#define MAX_LEN 30 + +static ssize_t ide_settings_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + ide_drive_t *drive = PDE_DATA(file_inode(file)); + char name[MAX_LEN + 1]; + int for_real = 0, mul_factor, div_factor; + unsigned long n; + + const struct ide_proc_devset *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; + } + + mutex_lock(&ide_setting_mtx); + /* generic settings first, then driver specific ones */ + setting = ide_find_setting(ide_generic_settings, name); + if (!setting) { + if (drive->settings) + setting = ide_find_setting(drive->settings, name); + if (!setting) { + mutex_unlock(&ide_setting_mtx); + goto parse_error; + } + } + if (for_real) { + mul_factor = setting->mulf ? setting->mulf(drive) : 1; + div_factor = setting->divf ? setting->divf(drive) : 1; + ide_write_setting(drive, setting, val * div_factor / mul_factor); + } + mutex_unlock(&ide_setting_mtx); + } + } while (!for_real++); + free_page((unsigned long)buf); + return count; +parse_error: + free_page((unsigned long)buf); + printk("%s(): parse error\n", __func__); + return -EINVAL; +} + +static const struct proc_ops ide_settings_proc_ops = { + .proc_open = ide_settings_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = ide_settings_proc_write, +}; + +int ide_capacity_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%llu\n", (long long)0x7fffffff); + return 0; +} +EXPORT_SYMBOL_GPL(ide_capacity_proc_show); + +int ide_geometry_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *) m->private; + + seq_printf(m, "physical %d/%d/%d\n", + drive->cyl, drive->head, drive->sect); + seq_printf(m, "logical %d/%d/%d\n", + drive->bios_cyl, drive->bios_head, drive->bios_sect); + return 0; +} +EXPORT_SYMBOL(ide_geometry_proc_show); + +static int ide_dmodel_proc_show(struct seq_file *seq, void *v) +{ + ide_drive_t *drive = (ide_drive_t *) seq->private; + char *m = (char *)&drive->id[ATA_ID_PROD]; + + seq_printf(seq, "%.40s\n", m[0] ? m : "(none)"); + return 0; +} + +static int ide_driver_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *)m->private; + struct device *dev = &drive->gendev; + struct ide_driver *ide_drv; + + if (dev->driver) { + ide_drv = to_ide_driver(dev->driver); + seq_printf(m, "%s version %s\n", + dev->driver->name, ide_drv->version); + } else + seq_printf(m, "ide-default version 0.9.newide\n"); + return 0; +} + +static int ide_media_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *) m->private; + const char *media; + + 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; + case ide_optical: media = "optical\n"; break; + default: media = "UNKNOWN\n"; break; + } + seq_puts(m, media); + return 0; +} + +static int ide_media_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ide_media_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations ide_media_proc_fops = { + .owner = THIS_MODULE, + .open = ide_media_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ide_proc_entry_t generic_drive_entries[] = { + { "driver", S_IFREG|S_IRUGO, ide_driver_proc_show }, + { "identify", S_IFREG|S_IRUSR, ide_identify_proc_show }, + { "media", S_IFREG|S_IRUGO, ide_media_proc_show }, + { "model", S_IFREG|S_IRUGO, ide_dmodel_proc_show }, + {} +}; + +static 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 = proc_create_single_data(p->name, p->mode, dir, p->show, data); + if (!ent) return; + p++; + } +} + +static 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++; + } +} + +void ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver) +{ + mutex_lock(&ide_setting_mtx); + drive->settings = driver->proc_devsets(drive); + mutex_unlock(&ide_setting_mtx); + + ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive); +} + +EXPORT_SYMBOL(ide_proc_register_driver); + +/** + * ide_proc_unregister_driver - remove driver specific data + * @drive: drive + * @driver: driver + * + * Clean up the driver specific /proc files and IDE settings + * for a given drive. + * + * Takes ide_setting_mtx. + */ + +void ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver) +{ + ide_remove_proc_entries(drive->proc, driver->proc_entries(drive)); + + mutex_lock(&ide_setting_mtx); + /* + * ide_setting_mtx protects both the settings list and the use + * of settings (we cannot take a setting out that is being used). + */ + drive->settings = NULL; + mutex_unlock(&ide_setting_mtx); +} +EXPORT_SYMBOL(ide_proc_unregister_driver); + +void ide_proc_port_register_devices(ide_hwif_t *hwif) +{ + struct proc_dir_entry *ent; + struct proc_dir_entry *parent = hwif->proc; + ide_drive_t *drive; + char name[64]; + int i; + + ide_port_for_each_dev(i, drive, hwif) { + if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0) + continue; + + drive->proc = proc_mkdir(drive->name, parent); + if (drive->proc) { + ide_add_proc_entries(drive->proc, generic_drive_entries, drive); + proc_create_data("settings", S_IFREG|S_IRUSR|S_IWUSR, + drive->proc, &ide_settings_proc_ops, + drive); + } + sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name); + ent = proc_symlink(drive->name, proc_ide_root, name); + if (!ent) return; + } +} + +void ide_proc_unregister_device(ide_drive_t *drive) +{ + if (drive->proc) { + remove_proc_entry("settings", drive->proc); + ide_remove_proc_entries(drive->proc, generic_drive_entries); + remove_proc_entry(drive->name, proc_ide_root); + remove_proc_entry(drive->name, drive->hwif->proc); + drive->proc = NULL; + } +} + +static ide_proc_entry_t hwif_entries[] = { + { "channel", S_IFREG|S_IRUGO, ide_channel_proc_show }, + { "mate", S_IFREG|S_IRUGO, ide_mate_proc_show }, + { "model", S_IFREG|S_IRUGO, ide_imodel_proc_show }, + {} +}; + +void ide_proc_register_port(ide_hwif_t *hwif) +{ + 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); + } +} + +void ide_proc_unregister_port(ide_hwif_t *hwif) +{ + if (hwif->proc) { + ide_remove_proc_entries(hwif->proc, hwif_entries); + remove_proc_entry(hwif->name, proc_ide_root); + hwif->proc = NULL; + } +} + +static int proc_print_driver(struct device_driver *drv, void *data) +{ + struct ide_driver *ide_drv = to_ide_driver(drv); + struct seq_file *s = data; + + seq_printf(s, "%s version %s\n", drv->name, ide_drv->version); + + return 0; +} + +static int ide_drivers_show(struct seq_file *s, void *p) +{ + int err; + + err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver); + if (err < 0) + printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n", + __func__, err); + return 0; +} + +DEFINE_PROC_SHOW_ATTRIBUTE(ide_drivers); + +void proc_ide_create(void) +{ + proc_ide_root = proc_mkdir("ide", NULL); + + if (!proc_ide_root) + return; + + proc_create("drivers", 0, proc_ide_root, &ide_drivers_proc_ops); +} + +void proc_ide_destroy(void) +{ + remove_proc_entry("drivers", proc_ide_root); + remove_proc_entry("ide", NULL); +} diff --git a/drivers/ide/ide-scan-pci.c b/drivers/ide/ide-scan-pci.c new file mode 100644 index 000000000..b0411a182 --- /dev/null +++ b/drivers/ide/ide-scan-pci.c @@ -0,0 +1,113 @@ +/* + * support for probing IDE PCI devices in the PCI bus order + * + * 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 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/ide.h> + +/* + * Module interfaces + */ + +static int pre_init = 1; /* Before first ordered IDE scan */ +static LIST_HEAD(ide_pci_drivers); + +/* + * __ide_pci_register_driver - attach IDE driver + * @driver: pci driver + * @module: owner module of the 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. + * + * Returns are the same as for pci_register_driver + */ + +int __ide_pci_register_driver(struct pci_driver *driver, struct module *module, + const char *mod_name) +{ + if (!pre_init) + return __pci_register_driver(driver, module, mod_name); + driver->driver.owner = module; + list_add_tail(&driver->node, &ide_pci_drivers); + return 0; +} +EXPORT_SYMBOL_GPL(__ide_pci_register_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; + int ret; + + 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_id(d->id_table, dev); + + if (id != NULL) { + pci_assign_irq(dev); + ret = d->probe(dev, id); + if (ret >= 0) { + dev->driver = d; + pci_dev_get(dev); + return 1; + } + } + } + } + return 0; +} + +/** + * ide_scan_pcibus - perform the initial IDE driver scan + * + * 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. + */ + +static int __init ide_scan_pcibus(void) +{ + struct pci_dev *dev = NULL; + struct pci_driver *d, *tmp; + + pre_init = 0; + for_each_pci_dev(dev) + ide_scan_pcidev(dev); + + /* + * Hand the drivers over to the PCI layer now we + * are post init. + */ + + list_for_each_entry_safe(d, tmp, &ide_pci_drivers, node) { + list_del(&d->node); + if (__pci_register_driver(d, d->driver.owner, + d->driver.mod_name)) + printk(KERN_ERR "%s: failed to register %s driver\n", + __func__, d->driver.mod_name); + } + + return 0; +} +device_initcall(ide_scan_pcibus); diff --git a/drivers/ide/ide-sysfs.c b/drivers/ide/ide-sysfs.c new file mode 100644 index 000000000..c08a8a091 --- /dev/null +++ b/drivers/ide/ide-sysfs.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/kernel.h> +#include <linux/ide.h> + +char *ide_media_string(ide_drive_t *drive) +{ + switch (drive->media) { + case ide_disk: + return "disk"; + case ide_cdrom: + return "cdrom"; + case ide_tape: + return "tape"; + case ide_floppy: + return "floppy"; + case ide_optical: + return "optical"; + default: + return "UNKNOWN"; + } +} + +static ssize_t media_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", ide_media_string(drive)); +} +static DEVICE_ATTR_RO(media); + +static ssize_t drivename_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", drive->name); +} +static DEVICE_ATTR_RO(drivename); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "ide:m-%s\n", ide_media_string(drive)); +} +static DEVICE_ATTR_RO(modalias); + +static ssize_t model_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_PROD]); +} +static DEVICE_ATTR_RO(model); + +static ssize_t firmware_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_FW_REV]); +} +static DEVICE_ATTR_RO(firmware); + +static ssize_t serial_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ide_drive_t *drive = to_ide_device(dev); + return sprintf(buf, "%s\n", (char *)&drive->id[ATA_ID_SERNO]); +} +static DEVICE_ATTR(serial, 0400, serial_show, NULL); + +static DEVICE_ATTR(unload_heads, 0644, ide_park_show, ide_park_store); + +static struct attribute *ide_attrs[] = { + &dev_attr_media.attr, + &dev_attr_drivename.attr, + &dev_attr_modalias.attr, + &dev_attr_model.attr, + &dev_attr_firmware.attr, + &dev_attr_serial.attr, + &dev_attr_unload_heads.attr, + NULL, +}; + +static const struct attribute_group ide_attr_group = { + .attrs = ide_attrs, +}; + +const struct attribute_group *ide_dev_groups[] = { + &ide_attr_group, + NULL, +}; + +static ssize_t store_delete_devices(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + + return n; +}; + +static DEVICE_ATTR(delete_devices, S_IWUSR, NULL, store_delete_devices); + +static ssize_t store_scan(struct device *portdev, + struct device_attribute *attr, + const char *buf, size_t n) +{ + ide_hwif_t *hwif = dev_get_drvdata(portdev); + + if (strncmp(buf, "1", n)) + return -EINVAL; + + ide_port_unregister_devices(hwif); + ide_port_scan(hwif); + + return n; +}; + +static DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); + +static struct device_attribute *ide_port_attrs[] = { + &dev_attr_delete_devices, + &dev_attr_scan, + NULL +}; + +int ide_sysfs_register_port(ide_hwif_t *hwif) +{ + int i, rc; + + for (i = 0; ide_port_attrs[i]; i++) { + rc = device_create_file(hwif->portdev, ide_port_attrs[i]); + if (rc) + break; + } + + return rc; +} diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c new file mode 100644 index 000000000..6f26634b2 --- /dev/null +++ b/drivers/ide/ide-tape.c @@ -0,0 +1,2085 @@ +/* + * IDE ATAPI streaming tape driver. + * + * Copyright (C) 1995-1999 Gadi Oxman <gadio@netvision.net.il> + * Copyright (C) 2003-2005 Bartlomiej Zolnierkiewicz + * + * 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). + * + * For a historical changelog see + * Documentation/ide/ChangeLog.ide-tape.1995-2002 + */ + +#define DRV_NAME "ide-tape" + +#define IDETAPE_VERSION "1.20" + +#include <linux/compat.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/jiffies.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/genhd.h> +#include <linux/seq_file.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/completion.h> +#include <linux/bitops.h> +#include <linux/mutex.h> +#include <scsi/scsi.h> + +#include <asm/byteorder.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <asm/unaligned.h> +#include <linux/mtio.h> + +/* define to see debug info */ +#undef IDETAPE_DEBUG_LOG + +#ifdef IDETAPE_DEBUG_LOG +#define ide_debug_log(lvl, fmt, args...) __ide_debug_log(lvl, fmt, ## args) +#else +#define ide_debug_log(lvl, fmt, args...) do {} while (0) +#endif + +/**************************** Tunable parameters *****************************/ +/* + * 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 + +/* + * 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 system 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 could service requests from the other device + * on the same interface in the meantime. + * + * 2. After the successful initialization of a "media access packet command", + * which is a command that can take a long time to complete (the interval can + * range from several seconds to 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 + * (1 second), and if we don't receive DSC after IDETAPE_DSC_MA_THRESHOLD + * (5 min), we switch it to a lower frequency - IDETAPE_DSC_MA_SLOW (1 min). + * + * 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 ***********************/ + +/* tape directions */ +enum { + IDETAPE_DIR_NONE = (1 << 0), + IDETAPE_DIR_READ = (1 << 1), + IDETAPE_DIR_WRITE = (1 << 2), +}; + +/* Tape door status */ +#define DOOR_UNLOCKED 0 +#define DOOR_LOCKED 1 +#define DOOR_EXPLICITLY_LOCKED 2 + +/* 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 + +/* Structures related to the SELECT SENSE / MODE SENSE packet commands. */ +#define IDETAPE_BLOCK_DESCRIPTOR 0 +#define IDETAPE_CAPABILITIES_PAGE 0x2a + +/* + * 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 the struct defined below. + */ +typedef struct ide_tape_obj { + ide_drive_t *drive; + struct ide_driver *driver; + struct gendisk *disk; + struct device dev; + + /* used by REQ_IDETAPE_{READ,WRITE} requests */ + struct ide_atapi_pc queued_pc; + + /* + * 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. + */ + bool 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_freq; + unsigned long dsc_poll_freq; + unsigned long dsc_timeout; + + /* Read position information */ + u8 partition; + /* Current block */ + unsigned int first_frame; + + /* 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 */ + u8 chrdev_dir; + + /* tape block size, usually 512 or 1024 bytes */ + unsigned short blk_size; + int user_bs_factor; + + /* Copy of the tape's Capabilities and Mechanical Page */ + u8 caps[20]; + + /* + * 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. + */ + + /* Data buffer size chosen based on the tape's recommendation */ + int buffer_size; + /* Staging buffer of buffer_size bytes */ + void *buf; + /* The read/write cursor */ + void *cur; + /* The number of valid bytes in buf */ + size_t valid; + + /* Measures average tape speed */ + unsigned long avg_time; + int avg_size; + int avg_speed; + + /* 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; +} idetape_tape_t; + +static DEFINE_MUTEX(ide_tape_mutex); +static DEFINE_MUTEX(idetape_ref_mutex); + +static DEFINE_MUTEX(idetape_chrdev_mutex); + +static struct class *idetape_sysfs_class; + +static void ide_tape_release(struct device *); + +static struct ide_tape_obj *idetape_devs[MAX_HWIFS * MAX_DRIVES]; + +static struct ide_tape_obj *ide_tape_get(struct gendisk *disk, bool cdev, + unsigned int i) +{ + struct ide_tape_obj *tape = NULL; + + mutex_lock(&idetape_ref_mutex); + + if (cdev) + tape = idetape_devs[i]; + else + tape = ide_drv_g(disk, ide_tape_obj); + + if (tape) { + if (ide_device_get(tape->drive)) + tape = NULL; + else + get_device(&tape->dev); + } + + mutex_unlock(&idetape_ref_mutex); + return tape; +} + +static void ide_tape_put(struct ide_tape_obj *tape) +{ + ide_drive_t *drive = tape->drive; + + mutex_lock(&idetape_ref_mutex); + put_device(&tape->dev); + ide_device_put(drive); + mutex_unlock(&idetape_ref_mutex); +} + +/* + * 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_tape_t *tape = drive->driver_data; + struct ide_atapi_pc *pc = drive->failed_pc; + struct request *rq = drive->hwif->rq; + u8 *sense = bio_data(rq->bio); + + tape->sense_key = sense[2] & 0xF; + tape->asc = sense[12]; + tape->ascq = sense[13]; + + ide_debug_log(IDE_DBG_FUNC, + "cmd: 0x%x, sense key = %x, asc = %x, ascq = %x", + rq->cmd[0], tape->sense_key, tape->asc, tape->ascq); + + /* correct remaining bytes to transfer */ + if (pc->flags & PC_FLAG_DMA_ERROR) + scsi_req(rq)->resid_len = tape->blk_size * get_unaligned_be32(&sense[3]); + + /* + * 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] == READ_6 || pc->c[0] == WRITE_6) + /* length == 0 */ + && pc->c[4] == 0 && pc->c[3] == 0 && pc->c[2] == 0) { + if (tape->sense_key == 5) { + /* don't report an error, everything's ok */ + pc->error = 0; + /* don't retry read/write */ + pc->flags |= PC_FLAG_ABORT; + } + } + if (pc->c[0] == READ_6 && (sense[2] & 0x80)) { + pc->error = IDE_DRV_ERROR_FILEMARK; + pc->flags |= PC_FLAG_ABORT; + } + if (pc->c[0] == WRITE_6) { + if ((sense[2] & 0x40) || (tape->sense_key == 0xd + && tape->asc == 0x0 && tape->ascq == 0x2)) { + pc->error = IDE_DRV_ERROR_EOD; + pc->flags |= PC_FLAG_ABORT; + } + } + if (pc->c[0] == READ_6 || pc->c[0] == WRITE_6) { + if (tape->sense_key == 8) { + pc->error = IDE_DRV_ERROR_EOD; + pc->flags |= PC_FLAG_ABORT; + } + if (!(pc->flags & PC_FLAG_ABORT) && + (blk_rq_bytes(rq) - scsi_req(rq)->resid_len)) + pc->retries = IDETAPE_MAX_PC_RETRIES + 1; + } +} + +static void ide_tape_handle_dsc(ide_drive_t *); + +static int ide_tape_callback(ide_drive_t *drive, int dsc) +{ + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc *pc = drive->pc; + struct request *rq = drive->hwif->rq; + int uptodate = pc->error ? 0 : 1; + int err = uptodate ? 0 : IDE_DRV_ERROR_GENERAL; + + ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x, dsc: %d, err: %d", rq->cmd[0], + dsc, err); + + if (dsc) + ide_tape_handle_dsc(drive); + + if (drive->failed_pc == pc) + drive->failed_pc = NULL; + + if (pc->c[0] == REQUEST_SENSE) { + if (uptodate) + idetape_analyze_error(drive); + else + printk(KERN_ERR "ide-tape: Error in REQUEST SENSE " + "itself - Aborting request!\n"); + } else if (pc->c[0] == READ_6 || pc->c[0] == WRITE_6) { + unsigned int blocks = + (blk_rq_bytes(rq) - scsi_req(rq)->resid_len) / tape->blk_size; + + tape->avg_size += blocks * tape->blk_size; + + if (time_after_eq(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; + } + + tape->first_frame += blocks; + + if (pc->error) { + uptodate = 0; + err = pc->error; + } + } + scsi_req(rq)->result = err; + + return uptodate; +} + +/* + * Postpone the current request so that ide.c will be able to service requests + * from another device on the same port while we are polling for DSC. + */ +static void ide_tape_stall_queue(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x, dsc_poll_freq: %lu", + drive->hwif->rq->cmd[0], tape->dsc_poll_freq); + + tape->postponed_rq = true; + + ide_stall_queue(drive, tape->dsc_poll_freq); +} + +static void ide_tape_handle_dsc(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + /* Media access command */ + tape->dsc_polling_start = jiffies; + tape->dsc_poll_freq = IDETAPE_DSC_MA_FAST; + tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT; + /* Allow ide.c to handle other requests */ + ide_tape_stall_queue(drive); +} + +/* + * Packet Command Interface + * + * The current Packet Command is available in drive->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. ide_tape_issue_pc will send the packet command to the drive, and will set + * the interrupt handler to ide_pc_intr. + * + * 2. On each interrupt, ide_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 ide_tape_issue_pc(ide_drive_t *drive, + struct ide_cmd *cmd, + struct ide_atapi_pc *pc) +{ + idetape_tape_t *tape = drive->driver_data; + struct request *rq = drive->hwif->rq; + + if (drive->failed_pc == NULL && pc->c[0] != REQUEST_SENSE) + drive->failed_pc = pc; + + /* Set the current packet command */ + drive->pc = pc; + + if (pc->retries > IDETAPE_MAX_PC_RETRIES || + (pc->flags & PC_FLAG_ABORT)) { + + /* + * We will "abort" retrying a packet command in case legitimate + * error code was received (crossing a filemark, or end of the + * media, for example). + */ + if (!(pc->flags & PC_FLAG_ABORT)) { + if (!(pc->c[0] == TEST_UNIT_READY && + 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 = IDE_DRV_ERROR_GENERAL; + } + + drive->failed_pc = NULL; + drive->pc_callback(drive, 0); + ide_complete_rq(drive, BLK_STS_IOERR, blk_rq_bytes(rq)); + return ide_stopped; + } + ide_debug_log(IDE_DBG_SENSE, "retry #%d, cmd: 0x%02x", pc->retries, + pc->c[0]); + + pc->retries++; + + return ide_issue_pc(drive, cmd); +} + +/* A mode sense command is used to "sense" tape parameters. */ +static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code) +{ + ide_init_pc(pc); + pc->c[0] = MODE_SENSE; + if (page_code != IDETAPE_BLOCK_DESCRIPTOR) + /* DBD = 1 - Don't return block descriptors */ + pc->c[1] = 8; + 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; + /* We will just discard data in that case */ + pc->c[4] = 255; + if (page_code == IDETAPE_BLOCK_DESCRIPTOR) + pc->req_xfer = 12; + else if (page_code == IDETAPE_CAPABILITIES_PAGE) + pc->req_xfer = 24; + else + pc->req_xfer = 50; +} + +static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc *pc = drive->pc; + u8 stat; + + stat = hwif->tp_ops->read_status(hwif); + + if (stat & ATA_DSC) { + if (stat & ATA_ERR) { + /* Error detected */ + if (pc->c[0] != TEST_UNIT_READY) + printk(KERN_ERR "ide-tape: %s: I/O error, ", + tape->name); + /* Retry operation */ + ide_retry_pc(drive); + return ide_stopped; + } + pc->error = 0; + } else { + pc->error = IDE_DRV_ERROR_GENERAL; + drive->failed_pc = NULL; + } + drive->pc_callback(drive, 0); + return ide_stopped; +} + +static void ide_tape_create_rw_cmd(idetape_tape_t *tape, + struct ide_atapi_pc *pc, struct request *rq, + u8 opcode) +{ + unsigned int length = blk_rq_sectors(rq) / (tape->blk_size >> 9); + + ide_init_pc(pc); + put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]); + pc->c[1] = 1; + + if (blk_rq_bytes(rq) == tape->buffer_size) + pc->flags |= PC_FLAG_DMA_OK; + + if (opcode == READ_6) + pc->c[0] = READ_6; + else if (opcode == WRITE_6) { + pc->c[0] = WRITE_6; + pc->flags |= PC_FLAG_WRITING; + } + + memcpy(scsi_req(rq)->cmd, pc->c, 12); +} + +static ide_startstop_t idetape_do_request(ide_drive_t *drive, + struct request *rq, sector_t block) +{ + ide_hwif_t *hwif = drive->hwif; + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc *pc = NULL; + struct ide_cmd cmd; + struct scsi_request *req = scsi_req(rq); + u8 stat; + + ide_debug_log(IDE_DBG_RQ, "cmd: 0x%x, sector: %llu, nr_sectors: %u", + req->cmd[0], (unsigned long long)blk_rq_pos(rq), + blk_rq_sectors(rq)); + + BUG_ON(!blk_rq_is_private(rq)); + BUG_ON(ide_req(rq)->type != ATA_PRIV_MISC && + ide_req(rq)->type != ATA_PRIV_SENSE); + + /* Retry a failed packet command */ + if (drive->failed_pc && drive->pc->c[0] == REQUEST_SENSE) { + pc = drive->failed_pc; + goto out; + } + + /* + * If the tape is still busy, postpone our request and service + * the other device meanwhile. + */ + stat = hwif->tp_ops->read_status(hwif); + + if ((drive->dev_flags & IDE_DFLAG_DSC_OVERLAP) == 0 && + (req->cmd[13] & REQ_IDETAPE_PC2) == 0) + drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC; + + if (drive->dev_flags & IDE_DFLAG_POST_RESET) { + drive->atapi_flags |= IDE_AFLAG_IGNORE_DSC; + drive->dev_flags &= ~IDE_DFLAG_POST_RESET; + } + + if (!(drive->atapi_flags & IDE_AFLAG_IGNORE_DSC) && + !(stat & ATA_DSC)) { + if (!tape->postponed_rq) { + tape->dsc_polling_start = jiffies; + tape->dsc_poll_freq = tape->best_dsc_rw_freq; + 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 (req->cmd[13] & REQ_IDETAPE_PC2) { + idetape_media_access_finished(drive); + return ide_stopped; + } else { + return ide_do_reset(drive); + } + } else if (time_after(jiffies, + tape->dsc_polling_start + + IDETAPE_DSC_MA_THRESHOLD)) + tape->dsc_poll_freq = IDETAPE_DSC_MA_SLOW; + ide_tape_stall_queue(drive); + return ide_stopped; + } else { + drive->atapi_flags &= ~IDE_AFLAG_IGNORE_DSC; + tape->postponed_rq = false; + } + + if (req->cmd[13] & REQ_IDETAPE_READ) { + pc = &tape->queued_pc; + ide_tape_create_rw_cmd(tape, pc, rq, READ_6); + goto out; + } + if (req->cmd[13] & REQ_IDETAPE_WRITE) { + pc = &tape->queued_pc; + ide_tape_create_rw_cmd(tape, pc, rq, WRITE_6); + goto out; + } + if (req->cmd[13] & REQ_IDETAPE_PC1) { + pc = (struct ide_atapi_pc *)ide_req(rq)->special; + req->cmd[13] &= ~(REQ_IDETAPE_PC1); + req->cmd[13] |= REQ_IDETAPE_PC2; + goto out; + } + if (req->cmd[13] & REQ_IDETAPE_PC2) { + idetape_media_access_finished(drive); + return ide_stopped; + } + BUG(); + +out: + /* prepare sense request for this command */ + ide_prep_sense(drive, rq); + + memset(&cmd, 0, sizeof(cmd)); + + if (rq_data_dir(rq)) + cmd.tf_flags |= IDE_TFLAG_WRITE; + + cmd.rq = rq; + + ide_init_sg_cmd(&cmd, blk_rq_bytes(rq)); + ide_map_sg(drive, &cmd); + + return ide_tape_issue_pc(drive, &cmd, pc); +} + +/* + * Write a filemark if write_filemark=1. Flush the device buffers without + * writing a filemark otherwise. + */ +static void idetape_create_write_filemark_cmd(ide_drive_t *drive, + struct ide_atapi_pc *pc, int write_filemark) +{ + ide_init_pc(pc); + pc->c[0] = WRITE_FILEMARKS; + pc->c[4] = write_filemark; + pc->flags |= PC_FLAG_WAIT_FOR_DSC; +} + +static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout) +{ + idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; + int load_attempted = 0; + + /* Wait for the tape to become ready */ + set_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT), &drive->atapi_flags); + timeout += jiffies; + while (time_before(jiffies, timeout)) { + if (ide_do_test_unit_ready(drive, disk) == 0) + return 0; + if ((tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) + || (tape->asc == 0x3A)) { + /* no media */ + if (load_attempted) + return -ENOMEDIUM; + ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK); + 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; + msleep(100); + } + return -EIO; +} + +static int idetape_flush_tape_buffers(ide_drive_t *drive) +{ + struct ide_tape_obj *tape = drive->driver_data; + struct ide_atapi_pc pc; + int rc; + + idetape_create_write_filemark_cmd(drive, &pc, 0); + rc = ide_queue_pc_tail(drive, tape->disk, &pc, NULL, 0); + if (rc) + return rc; + idetape_wait_ready(drive, 60 * 5 * HZ); + return 0; +} + +static int ide_tape_read_position(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc pc; + u8 buf[20]; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + /* prep cmd */ + ide_init_pc(&pc); + pc.c[0] = READ_POSITION; + pc.req_xfer = 20; + + if (ide_queue_pc_tail(drive, tape->disk, &pc, buf, pc.req_xfer)) + return -1; + + if (!pc.error) { + ide_debug_log(IDE_DBG_FUNC, "BOP - %s", + (buf[0] & 0x80) ? "Yes" : "No"); + ide_debug_log(IDE_DBG_FUNC, "EOP - %s", + (buf[0] & 0x40) ? "Yes" : "No"); + + if (buf[0] & 0x4) { + printk(KERN_INFO "ide-tape: Block location is unknown" + "to the tape\n"); + clear_bit(ilog2(IDE_AFLAG_ADDRESS_VALID), + &drive->atapi_flags); + return -1; + } else { + ide_debug_log(IDE_DBG_FUNC, "Block Location: %u", + be32_to_cpup((__be32 *)&buf[4])); + + tape->partition = buf[1]; + tape->first_frame = be32_to_cpup((__be32 *)&buf[4]); + set_bit(ilog2(IDE_AFLAG_ADDRESS_VALID), + &drive->atapi_flags); + } + } + + return tape->first_frame; +} + +static void idetape_create_locate_cmd(ide_drive_t *drive, + struct ide_atapi_pc *pc, + unsigned int block, u8 partition, int skip) +{ + ide_init_pc(pc); + pc->c[0] = POSITION_TO_ELEMENT; + pc->c[1] = 2; + put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[3]); + pc->c[8] = partition; + pc->flags |= PC_FLAG_WAIT_FOR_DSC; +} + +static void __ide_tape_discard_merge_buffer(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->chrdev_dir != IDETAPE_DIR_READ) + return; + + clear_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags); + tape->valid = 0; + if (tape->buf != NULL) { + kfree(tape->buf); + tape->buf = NULL; + } + + tape->chrdev_dir = IDETAPE_DIR_NONE; +} + +/* + * Position 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; + struct gendisk *disk = tape->disk; + int ret; + struct ide_atapi_pc pc; + + if (tape->chrdev_dir == IDETAPE_DIR_READ) + __ide_tape_discard_merge_buffer(drive); + idetape_wait_ready(drive, 60 * 5 * HZ); + idetape_create_locate_cmd(drive, &pc, block, partition, skip); + ret = ide_queue_pc_tail(drive, disk, &pc, NULL, 0); + if (ret) + return ret; + + ret = ide_tape_read_position(drive); + if (ret < 0) + return ret; + return 0; +} + +static void ide_tape_discard_merge_buffer(ide_drive_t *drive, + int restore_position) +{ + idetape_tape_t *tape = drive->driver_data; + int seek, position; + + __ide_tape_discard_merge_buffer(drive); + if (restore_position) { + position = ide_tape_read_position(drive); + seek = position > 0 ? position : 0; + if (idetape_position_tape(drive, seek, 0, 0)) { + printk(KERN_INFO "ide-tape: %s: position_tape failed in" + " %s\n", tape->name, __func__); + return; + } + } +} + +/* + * Generate 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 size) +{ + idetape_tape_t *tape = drive->driver_data; + struct request *rq; + int ret; + + ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x, size: %d", cmd, size); + + BUG_ON(cmd != REQ_IDETAPE_READ && cmd != REQ_IDETAPE_WRITE); + BUG_ON(size < 0 || size % tape->blk_size); + + rq = blk_get_request(drive->queue, REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_MISC; + scsi_req(rq)->cmd[13] = cmd; + rq->rq_disk = tape->disk; + rq->__sector = tape->first_frame; + + if (size) { + ret = blk_rq_map_kern(drive->queue, rq, tape->buf, size, + GFP_NOIO); + if (ret) + goto out_put; + } + + blk_execute_rq(drive->queue, tape->disk, rq, 0); + + /* calculate the number of transferred bytes and update buffer state */ + size -= scsi_req(rq)->resid_len; + tape->cur = tape->buf; + if (cmd == REQ_IDETAPE_READ) + tape->valid = size; + else + tape->valid = 0; + + ret = size; + if (scsi_req(rq)->result == IDE_DRV_ERROR_GENERAL) + ret = -EIO; +out_put: + blk_put_request(rq); + return ret; +} + +static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = INQUIRY; + pc->c[4] = 254; + pc->req_xfer = 254; +} + +static void idetape_create_rewind_cmd(ide_drive_t *drive, + struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = REZERO_UNIT; + pc->flags |= PC_FLAG_WAIT_FOR_DSC; +} + +static void idetape_create_erase_cmd(struct ide_atapi_pc *pc) +{ + ide_init_pc(pc); + pc->c[0] = ERASE; + pc->c[1] = 1; + pc->flags |= PC_FLAG_WAIT_FOR_DSC; +} + +static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd) +{ + ide_init_pc(pc); + pc->c[0] = SPACE; + put_unaligned(cpu_to_be32(count), (unsigned int *) &pc->c[1]); + pc->c[1] = cmd; + pc->flags |= PC_FLAG_WAIT_FOR_DSC; +} + +static void ide_tape_flush_merge_buffer(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->chrdev_dir != IDETAPE_DIR_WRITE) { + printk(KERN_ERR "ide-tape: bug: Trying to empty merge buffer" + " but we are not writing.\n"); + return; + } + if (tape->buf) { + size_t aligned = roundup(tape->valid, tape->blk_size); + + memset(tape->cur, 0, aligned - tape->valid); + idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, aligned); + kfree(tape->buf); + tape->buf = NULL; + } + tape->chrdev_dir = IDETAPE_DIR_NONE; +} + +static int idetape_init_rw(ide_drive_t *drive, int dir) +{ + idetape_tape_t *tape = drive->driver_data; + int rc; + + BUG_ON(dir != IDETAPE_DIR_READ && dir != IDETAPE_DIR_WRITE); + + if (tape->chrdev_dir == dir) + return 0; + + if (tape->chrdev_dir == IDETAPE_DIR_READ) + ide_tape_discard_merge_buffer(drive, 1); + else if (tape->chrdev_dir == IDETAPE_DIR_WRITE) { + ide_tape_flush_merge_buffer(drive); + idetape_flush_tape_buffers(drive); + } + + if (tape->buf || tape->valid) { + printk(KERN_ERR "ide-tape: valid should be 0 now\n"); + tape->valid = 0; + } + + tape->buf = kmalloc(tape->buffer_size, GFP_KERNEL); + if (!tape->buf) + return -ENOMEM; + tape->chrdev_dir = dir; + tape->cur = tape->buf; + + /* + * Issue a 0 rw 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->dev_flags & IDE_DFLAG_DSC_OVERLAP) { + int cmd = dir == IDETAPE_DIR_READ ? REQ_IDETAPE_READ + : REQ_IDETAPE_WRITE; + + rc = idetape_queue_rw_tail(drive, cmd, 0); + if (rc < 0) { + kfree(tape->buf); + tape->buf = NULL; + tape->chrdev_dir = IDETAPE_DIR_NONE; + return rc; + } + } + + return 0; +} + +static void idetape_pad_zeros(ide_drive_t *drive, int bcount) +{ + idetape_tape_t *tape = drive->driver_data; + + memset(tape->buf, 0, tape->buffer_size); + + while (bcount) { + unsigned int count = min(tape->buffer_size, bcount); + + idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, count); + bcount -= count; + } +} + +/* + * 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) +{ + struct ide_tape_obj *tape = drive->driver_data; + struct gendisk *disk = tape->disk; + struct ide_atapi_pc pc; + int ret; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + idetape_create_rewind_cmd(drive, &pc); + ret = ide_queue_pc_tail(drive, disk, &pc, NULL, 0); + if (ret) + return ret; + + ret = ide_tape_read_position(drive); + if (ret < 0) + return ret; + return 0; +} + +/* mtio.h compatible commands should be issued to the chrdev interface. */ +static int idetape_blkdev_ioctl(ide_drive_t *drive, unsigned int cmd, + unsigned long arg) +{ + idetape_tape_t *tape = drive->driver_data; + void __user *argp = (void __user *)arg; + + struct idetape_config { + int dsc_rw_frequency; + int dsc_media_access_frequency; + int nr_stages; + } config; + + ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%04x", cmd); + + switch (cmd) { + case 0x0340: + if (copy_from_user(&config, argp, sizeof(config))) + return -EFAULT; + tape->best_dsc_rw_freq = config.dsc_rw_frequency; + break; + case 0x0350: + memset(&config, 0, sizeof(config)); + config.dsc_rw_frequency = (int) tape->best_dsc_rw_freq; + config.nr_stages = 1; + if (copy_to_user(argp, &config, sizeof(config))) + return -EFAULT; + break; + default: + return -EIO; + } + return 0; +} + +static int idetape_space_over_filemarks(ide_drive_t *drive, short mt_op, + int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + struct gendisk *disk = tape->disk; + struct ide_atapi_pc pc; + int retval, count = 0; + int sprev = !!(tape->caps[4] & 0x20); + + + ide_debug_log(IDE_DBG_FUNC, "mt_op: %d, mt_count: %d", mt_op, mt_count); + + if (mt_count == 0) + return 0; + if (MTBSF == mt_op || MTBSFM == mt_op) { + if (!sprev) + return -EIO; + mt_count = -mt_count; + } + + if (tape->chrdev_dir == IDETAPE_DIR_READ) { + tape->valid = 0; + if (test_and_clear_bit(ilog2(IDE_AFLAG_FILEMARK), + &drive->atapi_flags)) + ++count; + ide_tape_discard_merge_buffer(drive, 0); + } + + switch (mt_op) { + case MTFSF: + case MTBSF: + idetape_create_space_cmd(&pc, mt_count - count, + IDETAPE_SPACE_OVER_FILEMARK); + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); + case MTFSFM: + case MTBSFM: + if (!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 (26kB on my particular tape, 32kB 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 = file->private_data; + ide_drive_t *drive = tape->drive; + size_t done = 0; + ssize_t ret = 0; + int rc; + + ide_debug_log(IDE_DBG_FUNC, "count %zd", count); + + if (tape->chrdev_dir != IDETAPE_DIR_READ) { + if (test_bit(ilog2(IDE_AFLAG_DETECT_BS), &drive->atapi_flags)) + if (count > tape->blk_size && + (count % tape->blk_size) == 0) + tape->user_bs_factor = count / tape->blk_size; + } + + rc = idetape_init_rw(drive, IDETAPE_DIR_READ); + if (rc < 0) + return rc; + + while (done < count) { + size_t todo; + + /* refill if staging buffer is empty */ + if (!tape->valid) { + /* If we are at a filemark, nothing more to read */ + if (test_bit(ilog2(IDE_AFLAG_FILEMARK), + &drive->atapi_flags)) + break; + /* read */ + if (idetape_queue_rw_tail(drive, REQ_IDETAPE_READ, + tape->buffer_size) <= 0) + break; + } + + /* copy out */ + todo = min_t(size_t, count - done, tape->valid); + if (copy_to_user(buf + done, tape->cur, todo)) + ret = -EFAULT; + + tape->cur += todo; + tape->valid -= todo; + done += todo; + } + + if (!done && test_bit(ilog2(IDE_AFLAG_FILEMARK), &drive->atapi_flags)) { + idetape_space_over_filemarks(drive, MTFSF, 1); + return 0; + } + + return ret ? ret : done; +} + +static ssize_t idetape_chrdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ide_tape_obj *tape = file->private_data; + ide_drive_t *drive = tape->drive; + size_t done = 0; + ssize_t ret = 0; + int rc; + + /* The drive is write protected. */ + if (tape->write_prot) + return -EACCES; + + ide_debug_log(IDE_DBG_FUNC, "count %zd", count); + + /* Initialize write operation */ + rc = idetape_init_rw(drive, IDETAPE_DIR_WRITE); + if (rc < 0) + return rc; + + while (done < count) { + size_t todo; + + /* flush if staging buffer is full */ + if (tape->valid == tape->buffer_size && + idetape_queue_rw_tail(drive, REQ_IDETAPE_WRITE, + tape->buffer_size) <= 0) + return rc; + + /* copy in */ + todo = min_t(size_t, count - done, + tape->buffer_size - tape->valid); + if (copy_from_user(tape->cur, buf + done, todo)) + ret = -EFAULT; + + tape->cur += todo; + tape->valid += todo; + done += todo; + } + + return ret ? ret : done; +} + +static int idetape_write_filemark(ide_drive_t *drive) +{ + struct ide_tape_obj *tape = drive->driver_data; + struct ide_atapi_pc pc; + + /* Write a filemark */ + idetape_create_write_filemark_cmd(drive, &pc, 1); + if (ide_queue_pc_tail(drive, tape->disk, &pc, NULL, 0)) { + printk(KERN_ERR "ide-tape: Couldn't write a filemark\n"); + return -EIO; + } + return 0; +} + +/* + * Called from idetape_chrdev_ioctl when the general mtio MTIOCTOP ioctl is + * requested. + * + * 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. + * + * 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; + struct gendisk *disk = tape->disk; + struct ide_atapi_pc pc; + int i, retval; + + ide_debug_log(IDE_DBG_FUNC, "MTIOCTOP ioctl: mt_op: %d, mt_count: %d", + mt_op, mt_count); + + 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; + ide_tape_discard_merge_buffer(drive, 1); + for (i = 0; i < mt_count; i++) { + retval = idetape_write_filemark(drive); + if (retval) + return retval; + } + return 0; + case MTREW: + ide_tape_discard_merge_buffer(drive, 0); + if (idetape_rewind_tape(drive)) + return -EIO; + return 0; + case MTLOAD: + ide_tape_discard_merge_buffer(drive, 0); + return ide_do_start_stop(drive, disk, IDETAPE_LU_LOAD_MASK); + case MTUNLOAD: + case MTOFFL: + /* + * If door is locked, attempt to unlock before + * attempting to eject. + */ + if (tape->door_locked) { + if (!ide_set_media_lock(drive, disk, 0)) + tape->door_locked = DOOR_UNLOCKED; + } + ide_tape_discard_merge_buffer(drive, 0); + retval = ide_do_start_stop(drive, disk, !IDETAPE_LU_LOAD_MASK); + if (!retval) + clear_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT), + &drive->atapi_flags); + return retval; + case MTNOP: + ide_tape_discard_merge_buffer(drive, 0); + return idetape_flush_tape_buffers(drive); + case MTRETEN: + ide_tape_discard_merge_buffer(drive, 0); + return ide_do_start_stop(drive, disk, + IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); + case MTEOM: + idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD); + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); + case MTERASE: + (void)idetape_rewind_tape(drive); + idetape_create_erase_cmd(&pc); + return ide_queue_pc_tail(drive, disk, &pc, NULL, 0); + case MTSETBLK: + if (mt_count) { + if (mt_count < tape->blk_size || + mt_count % tape->blk_size) + return -EIO; + tape->user_bs_factor = mt_count / tape->blk_size; + clear_bit(ilog2(IDE_AFLAG_DETECT_BS), + &drive->atapi_flags); + } else + set_bit(ilog2(IDE_AFLAG_DETECT_BS), + &drive->atapi_flags); + return 0; + case MTSEEK: + ide_tape_discard_merge_buffer(drive, 0); + return idetape_position_tape(drive, + mt_count * tape->user_bs_factor, tape->partition, 0); + case MTSETPART: + ide_tape_discard_merge_buffer(drive, 0); + return idetape_position_tape(drive, 0, mt_count, 0); + case MTFSR: + case MTBSR: + case MTLOCK: + retval = ide_set_media_lock(drive, disk, 1); + if (retval) + return retval; + tape->door_locked = DOOR_EXPLICITLY_LOCKED; + return 0; + case MTUNLOCK: + retval = ide_set_media_lock(drive, disk, 0); + 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. Our own + * ide-tape ioctls are supported on both interfaces. + */ +static long do_idetape_chrdev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ide_tape_obj *tape = file->private_data; + ide_drive_t *drive = tape->drive; + struct mtop mtop; + struct mtget mtget; + struct mtpos mtpos; + int block_offset = 0, position = tape->first_frame; + void __user *argp = (void __user *)arg; + + ide_debug_log(IDE_DBG_FUNC, "cmd: 0x%x", cmd); + + if (tape->chrdev_dir == IDETAPE_DIR_WRITE) { + ide_tape_flush_merge_buffer(drive); + idetape_flush_tape_buffers(drive); + } + if (cmd == MTIOCGET || cmd == MTIOCPOS) { + block_offset = tape->valid / + (tape->blk_size * tape->user_bs_factor); + position = ide_tape_read_position(drive); + if (position < 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->blk_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); + + return put_user_mtget(argp, &mtget); + case MTIOCPOS: + mtpos.mt_blkno = position / tape->user_bs_factor - block_offset; + return put_user_mtpos(argp, &mtpos); + default: + if (tape->chrdev_dir == IDETAPE_DIR_READ) + ide_tape_discard_merge_buffer(drive, 1); + return idetape_blkdev_ioctl(drive, cmd, arg); + } +} + +static long idetape_chrdev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + mutex_lock(&ide_tape_mutex); + ret = do_idetape_chrdev_ioctl(file, cmd, arg); + mutex_unlock(&ide_tape_mutex); + return ret; +} + +static long idetape_chrdev_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + + if (cmd == MTIOCPOS32) + cmd = MTIOCPOS; + else if (cmd == MTIOCGET32) + cmd = MTIOCGET; + + mutex_lock(&ide_tape_mutex); + ret = do_idetape_chrdev_ioctl(file, cmd, arg); + mutex_unlock(&ide_tape_mutex); + return ret; +} + +/* + * Do a mode sense page 0 with block descriptor and if it succeeds set the tape + * block size with the reported value. + */ +static void ide_tape_get_bsize_from_bdesc(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc pc; + u8 buf[12]; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_DESCRIPTOR); + if (ide_queue_pc_tail(drive, tape->disk, &pc, buf, pc.req_xfer)) { + printk(KERN_ERR "ide-tape: Can't get block descriptor\n"); + if (tape->blk_size == 0) { + printk(KERN_WARNING "ide-tape: Cannot deal with zero " + "block size, assuming 32k\n"); + tape->blk_size = 32768; + } + return; + } + tape->blk_size = (buf[4 + 5] << 16) + + (buf[4 + 6] << 8) + + buf[4 + 7]; + tape->drv_write_prot = (buf[2] & 0x80) >> 7; + + ide_debug_log(IDE_DBG_FUNC, "blk_size: %d, write_prot: %d", + tape->blk_size, tape->drv_write_prot); +} + +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; + int retval; + + if (i >= MAX_HWIFS * MAX_DRIVES) + return -ENXIO; + + mutex_lock(&idetape_chrdev_mutex); + + tape = ide_tape_get(NULL, true, i); + if (!tape) { + mutex_unlock(&idetape_chrdev_mutex); + return -ENXIO; + } + + drive = tape->drive; + filp->private_data = tape; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + /* + * 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 (test_and_set_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags)) { + retval = -EBUSY; + goto out_put_tape; + } + + retval = idetape_wait_ready(drive, 60 * HZ); + if (retval) { + clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags); + printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); + goto out_put_tape; + } + + ide_tape_read_position(drive); + if (!test_bit(ilog2(IDE_AFLAG_ADDRESS_VALID), &drive->atapi_flags)) + (void)idetape_rewind_tape(drive); + + /* Read block size and write protect status from drive. */ + ide_tape_get_bsize_from_bdesc(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(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags); + retval = -EROFS; + goto out_put_tape; + } + } + + /* Lock the tape drive door so user can't eject. */ + if (tape->chrdev_dir == IDETAPE_DIR_NONE) { + if (!ide_set_media_lock(drive, tape->disk, 1)) { + if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) + tape->door_locked = DOOR_LOCKED; + } + } + mutex_unlock(&idetape_chrdev_mutex); + + return 0; + +out_put_tape: + ide_tape_put(tape); + + mutex_unlock(&idetape_chrdev_mutex); + + return retval; +} + +static void idetape_write_release(ide_drive_t *drive, unsigned int minor) +{ + idetape_tape_t *tape = drive->driver_data; + + ide_tape_flush_merge_buffer(drive); + tape->buf = kmalloc(tape->buffer_size, GFP_KERNEL); + if (tape->buf != NULL) { + idetape_pad_zeros(drive, tape->blk_size * + (tape->user_bs_factor - 1)); + kfree(tape->buf); + tape->buf = NULL; + } + idetape_write_filemark(drive); + idetape_flush_tape_buffers(drive); + idetape_flush_tape_buffers(drive); +} + +static int idetape_chrdev_release(struct inode *inode, struct file *filp) +{ + struct ide_tape_obj *tape = filp->private_data; + ide_drive_t *drive = tape->drive; + unsigned int minor = iminor(inode); + + mutex_lock(&idetape_chrdev_mutex); + + tape = drive->driver_data; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (tape->chrdev_dir == IDETAPE_DIR_WRITE) + idetape_write_release(drive, minor); + if (tape->chrdev_dir == IDETAPE_DIR_READ) { + if (minor < 128) + ide_tape_discard_merge_buffer(drive, 1); + } + + if (minor < 128 && test_bit(ilog2(IDE_AFLAG_MEDIUM_PRESENT), + &drive->atapi_flags)) + (void) idetape_rewind_tape(drive); + + if (tape->chrdev_dir == IDETAPE_DIR_NONE) { + if (tape->door_locked == DOOR_LOCKED) { + if (!ide_set_media_lock(drive, tape->disk, 0)) + tape->door_locked = DOOR_UNLOCKED; + } + } + clear_bit(ilog2(IDE_AFLAG_BUSY), &drive->atapi_flags); + ide_tape_put(tape); + + mutex_unlock(&idetape_chrdev_mutex); + + return 0; +} + +static void idetape_get_inquiry_results(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + struct ide_atapi_pc pc; + u8 pc_buf[256]; + char fw_rev[4], vendor_id[8], product_id[16]; + + idetape_create_inquiry_cmd(&pc); + if (ide_queue_pc_tail(drive, tape->disk, &pc, pc_buf, pc.req_xfer)) { + printk(KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", + tape->name); + return; + } + memcpy(vendor_id, &pc_buf[8], 8); + memcpy(product_id, &pc_buf[16], 16); + memcpy(fw_rev, &pc_buf[32], 4); + + ide_fixstring(vendor_id, 8, 0); + ide_fixstring(product_id, 16, 0); + ide_fixstring(fw_rev, 4, 0); + + printk(KERN_INFO "ide-tape: %s <-> %s: %.8s %.16s rev %.4s\n", + drive->name, tape->name, vendor_id, product_id, fw_rev); +} + +/* + * Ask 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; + struct ide_atapi_pc pc; + u8 buf[24], *caps; + u8 speed, max_speed; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_CAPABILITIES_PAGE); + if (ide_queue_pc_tail(drive, tape->disk, &pc, buf, pc.req_xfer)) { + printk(KERN_ERR "ide-tape: Can't get tape parameters - assuming" + " some default values\n"); + tape->blk_size = 512; + put_unaligned(52, (u16 *)&tape->caps[12]); + put_unaligned(540, (u16 *)&tape->caps[14]); + put_unaligned(6*52, (u16 *)&tape->caps[16]); + return; + } + caps = buf + 4 + buf[3]; + + /* convert to host order and save for later use */ + speed = be16_to_cpup((__be16 *)&caps[14]); + max_speed = be16_to_cpup((__be16 *)&caps[8]); + + *(u16 *)&caps[8] = max_speed; + *(u16 *)&caps[12] = be16_to_cpup((__be16 *)&caps[12]); + *(u16 *)&caps[14] = speed; + *(u16 *)&caps[16] = be16_to_cpup((__be16 *)&caps[16]); + + if (!speed) { + printk(KERN_INFO "ide-tape: %s: invalid tape speed " + "(assuming 650KB/sec)\n", drive->name); + *(u16 *)&caps[14] = 650; + } + if (!max_speed) { + printk(KERN_INFO "ide-tape: %s: invalid max_speed " + "(assuming 650KB/sec)\n", drive->name); + *(u16 *)&caps[8] = 650; + } + + memcpy(&tape->caps, caps, 20); + + /* device lacks locking support according to capabilities page */ + if ((caps[6] & 1) == 0) + drive->dev_flags &= ~IDE_DFLAG_DOORLOCKING; + + if (caps[7] & 0x02) + tape->blk_size = 512; + else if (caps[7] & 0x04) + tape->blk_size = 1024; +} + +#ifdef CONFIG_IDE_PROC_FS +#define ide_tape_devset_get(name, field) \ +static int get_##name(ide_drive_t *drive) \ +{ \ + idetape_tape_t *tape = drive->driver_data; \ + return tape->field; \ +} + +#define ide_tape_devset_set(name, field) \ +static int set_##name(ide_drive_t *drive, int arg) \ +{ \ + idetape_tape_t *tape = drive->driver_data; \ + tape->field = arg; \ + return 0; \ +} + +#define ide_tape_devset_rw_field(_name, _field) \ +ide_tape_devset_get(_name, _field) \ +ide_tape_devset_set(_name, _field) \ +IDE_DEVSET(_name, DS_SYNC, get_##_name, set_##_name) + +#define ide_tape_devset_r_field(_name, _field) \ +ide_tape_devset_get(_name, _field) \ +IDE_DEVSET(_name, 0, get_##_name, NULL) + +static int mulf_tdsc(ide_drive_t *drive) { return 1000; } +static int divf_tdsc(ide_drive_t *drive) { return HZ; } +static int divf_buffer(ide_drive_t *drive) { return 2; } +static int divf_buffer_size(ide_drive_t *drive) { return 1024; } + +ide_devset_rw_flag(dsc_overlap, IDE_DFLAG_DSC_OVERLAP); + +ide_tape_devset_rw_field(tdsc, best_dsc_rw_freq); + +ide_tape_devset_r_field(avg_speed, avg_speed); +ide_tape_devset_r_field(speed, caps[14]); +ide_tape_devset_r_field(buffer, caps[16]); +ide_tape_devset_r_field(buffer_size, buffer_size); + +static const struct ide_proc_devset idetape_settings[] = { + __IDE_PROC_DEVSET(avg_speed, 0, 0xffff, NULL, NULL), + __IDE_PROC_DEVSET(buffer, 0, 0xffff, NULL, divf_buffer), + __IDE_PROC_DEVSET(buffer_size, 0, 0xffff, NULL, divf_buffer_size), + __IDE_PROC_DEVSET(dsc_overlap, 0, 1, NULL, NULL), + __IDE_PROC_DEVSET(speed, 0, 0xffff, NULL, NULL), + __IDE_PROC_DEVSET(tdsc, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, + mulf_tdsc, divf_tdsc), + { NULL }, +}; +#endif + +/* + * The function below 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 t; + int speed; + u16 *ctl = (u16 *)&tape->caps[12]; + + ide_debug_log(IDE_DBG_FUNC, "minor: %d", minor); + + drive->pc_callback = ide_tape_callback; + + drive->dev_flags |= IDE_DFLAG_DSC_OVERLAP; + + if (drive->hwif->host_flags & IDE_HFLAG_NO_DSC) { + printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", + tape->name); + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + } + + /* Seagate Travan drives do not support DSC overlap. */ + if (strstr((char *)&drive->id[ATA_ID_PROD], "Seagate STT3401")) + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + + tape->minor = minor; + tape->name[0] = 'h'; + tape->name[1] = 't'; + tape->name[2] = '0' + minor; + tape->chrdev_dir = IDETAPE_DIR_NONE; + + idetape_get_inquiry_results(drive); + idetape_get_mode_sense_results(drive); + ide_tape_get_bsize_from_bdesc(drive); + tape->user_bs_factor = 1; + tape->buffer_size = *ctl * tape->blk_size; + while (tape->buffer_size > 0xffff) { + printk(KERN_NOTICE "ide-tape: decreasing stage size\n"); + *ctl /= 2; + tape->buffer_size = *ctl * tape->blk_size; + } + + /* select the "best" DSC read/write polling freq */ + speed = max(*(u16 *)&tape->caps[14], *(u16 *)&tape->caps[8]); + + t = (IDETAPE_FIFO_THRESHOLD * tape->buffer_size * HZ) / (speed * 1000); + + /* + * Ensure that the number we got makes sense; limit it within + * IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX. + */ + tape->best_dsc_rw_freq = clamp_t(unsigned long, t, IDETAPE_DSC_RW_MIN, + IDETAPE_DSC_RW_MAX); + printk(KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, " + "%ums tDSC%s\n", + drive->name, tape->name, *(u16 *)&tape->caps[14], + (*(u16 *)&tape->caps[16] * 512) / tape->buffer_size, + tape->buffer_size / 1024, + jiffies_to_msecs(tape->best_dsc_rw_freq), + (drive->dev_flags & IDE_DFLAG_USING_DMA) ? ", DMA" : ""); + + ide_proc_register_driver(drive, tape->driver); +} + +static void ide_tape_remove(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + ide_proc_unregister_driver(drive, tape->driver); + device_del(&tape->dev); + ide_unregister_region(tape->disk); + + mutex_lock(&idetape_ref_mutex); + put_device(&tape->dev); + mutex_unlock(&idetape_ref_mutex); +} + +static void ide_tape_release(struct device *dev) +{ + struct ide_tape_obj *tape = to_ide_drv(dev, ide_tape_obj); + ide_drive_t *drive = tape->drive; + struct gendisk *g = tape->disk; + + BUG_ON(tape->valid); + + drive->dev_flags &= ~IDE_DFLAG_DSC_OVERLAP; + drive->driver_data = NULL; + device_destroy(idetape_sysfs_class, MKDEV(IDETAPE_MAJOR, tape->minor)); + device_destroy(idetape_sysfs_class, + MKDEV(IDETAPE_MAJOR, tape->minor + 128)); + idetape_devs[tape->minor] = NULL; + g->private_data = NULL; + put_disk(g); + kfree(tape); +} + +#ifdef CONFIG_IDE_PROC_FS +static int idetape_name_proc_show(struct seq_file *m, void *v) +{ + ide_drive_t *drive = (ide_drive_t *) m->private; + idetape_tape_t *tape = drive->driver_data; + + seq_printf(m, "%s\n", tape->name); + return 0; +} + +static ide_proc_entry_t idetape_proc[] = { + { "capacity", S_IFREG|S_IRUGO, ide_capacity_proc_show }, + { "name", S_IFREG|S_IRUGO, idetape_name_proc_show }, + {} +}; + +static ide_proc_entry_t *ide_tape_proc_entries(ide_drive_t *drive) +{ + return idetape_proc; +} + +static const struct ide_proc_devset *ide_tape_proc_devsets(ide_drive_t *drive) +{ + return idetape_settings; +} +#endif + +static int ide_tape_probe(ide_drive_t *); + +static struct ide_driver idetape_driver = { + .gen_driver = { + .owner = THIS_MODULE, + .name = "ide-tape", + .bus = &ide_bus_type, + }, + .probe = ide_tape_probe, + .remove = ide_tape_remove, + .version = IDETAPE_VERSION, + .do_request = idetape_do_request, +#ifdef CONFIG_IDE_PROC_FS + .proc_entries = ide_tape_proc_entries, + .proc_devsets = ide_tape_proc_devsets, +#endif +}; + +/* Our character device supporting functions, passed to register_chrdev. */ +static const struct file_operations idetape_fops = { + .owner = THIS_MODULE, + .read = idetape_chrdev_read, + .write = idetape_chrdev_write, + .unlocked_ioctl = idetape_chrdev_ioctl, + .compat_ioctl = IS_ENABLED(CONFIG_COMPAT) ? + idetape_chrdev_compat_ioctl : NULL, + .open = idetape_chrdev_open, + .release = idetape_chrdev_release, + .llseek = noop_llseek, +}; + +static int idetape_open(struct block_device *bdev, fmode_t mode) +{ + struct ide_tape_obj *tape; + + mutex_lock(&ide_tape_mutex); + tape = ide_tape_get(bdev->bd_disk, false, 0); + mutex_unlock(&ide_tape_mutex); + + if (!tape) + return -ENXIO; + + return 0; +} + +static void idetape_release(struct gendisk *disk, fmode_t mode) +{ + struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj); + + mutex_lock(&ide_tape_mutex); + ide_tape_put(tape); + mutex_unlock(&ide_tape_mutex); +} + +static int idetape_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + struct ide_tape_obj *tape = ide_drv_g(bdev->bd_disk, ide_tape_obj); + ide_drive_t *drive = tape->drive; + int err; + + mutex_lock(&ide_tape_mutex); + err = generic_ide_ioctl(drive, bdev, cmd, arg); + if (err == -EINVAL) + err = idetape_blkdev_ioctl(drive, cmd, arg); + mutex_unlock(&ide_tape_mutex); + + return err; +} + +static int idetape_compat_ioctl(struct block_device *bdev, fmode_t mode, + unsigned int cmd, unsigned long arg) +{ + if (cmd == 0x0340 || cmd == 0x350) + arg = (unsigned long)compat_ptr(arg); + + return idetape_ioctl(bdev, mode, cmd, arg); +} + +static const struct block_device_operations idetape_block_ops = { + .owner = THIS_MODULE, + .open = idetape_open, + .release = idetape_release, + .ioctl = idetape_ioctl, + .compat_ioctl = IS_ENABLED(CONFIG_COMPAT) ? + idetape_compat_ioctl : NULL, +}; + +static int ide_tape_probe(ide_drive_t *drive) +{ + idetape_tape_t *tape; + struct gendisk *g; + int minor; + + ide_debug_log(IDE_DBG_FUNC, "enter"); + + if (!strstr(DRV_NAME, drive->driver_req)) + goto failed; + + if (drive->media != ide_tape) + goto failed; + + if ((drive->dev_flags & IDE_DFLAG_ID_READ) && + ide_check_atapi_device(drive, DRV_NAME) == 0) { + printk(KERN_ERR "ide-tape: %s: not supported by this version of" + " the driver\n", drive->name); + goto failed; + } + tape = kzalloc(sizeof(idetape_tape_t), GFP_KERNEL); + if (tape == NULL) { + printk(KERN_ERR "ide-tape: %s: Can't allocate a tape struct\n", + drive->name); + goto failed; + } + + g = alloc_disk(1 << PARTN_BITS); + if (!g) + goto out_free_tape; + + ide_init_disk(g, drive); + + tape->dev.parent = &drive->gendev; + tape->dev.release = ide_tape_release; + dev_set_name(&tape->dev, "%s", dev_name(&drive->gendev)); + + if (device_register(&tape->dev)) + goto out_free_disk; + + tape->drive = drive; + tape->driver = &idetape_driver; + tape->disk = g; + + g->private_data = &tape->driver; + + drive->driver_data = tape; + + mutex_lock(&idetape_ref_mutex); + for (minor = 0; idetape_devs[minor]; minor++) + ; + idetape_devs[minor] = tape; + mutex_unlock(&idetape_ref_mutex); + + idetape_setup(drive, tape, minor); + + device_create(idetape_sysfs_class, &drive->gendev, + MKDEV(IDETAPE_MAJOR, minor), NULL, "%s", tape->name); + device_create(idetape_sysfs_class, &drive->gendev, + MKDEV(IDETAPE_MAJOR, minor + 128), NULL, + "n%s", tape->name); + + g->fops = &idetape_block_ops; + ide_register_region(g); + + return 0; + +out_free_disk: + put_disk(g); +out_free_tape: + kfree(tape); +failed: + return -ENODEV; +} + +static void __exit idetape_exit(void) +{ + driver_unregister(&idetape_driver.gen_driver); + class_destroy(idetape_sysfs_class); + unregister_chrdev(IDETAPE_MAJOR, "ht"); +} + +static int __init idetape_init(void) +{ + int error = 1; + idetape_sysfs_class = class_create(THIS_MODULE, "ide_tape"); + if (IS_ERR(idetape_sysfs_class)) { + idetape_sysfs_class = NULL; + printk(KERN_ERR "Unable to create sysfs class for ide tapes\n"); + error = -EBUSY; + goto out; + } + + if (register_chrdev(IDETAPE_MAJOR, "ht", &idetape_fops)) { + printk(KERN_ERR "ide-tape: Failed to register chrdev" + " interface\n"); + error = -EBUSY; + goto out_free_class; + } + + error = driver_register(&idetape_driver.gen_driver); + if (error) + goto out_free_chrdev; + + return 0; + +out_free_chrdev: + unregister_chrdev(IDETAPE_MAJOR, "ht"); +out_free_class: + class_destroy(idetape_sysfs_class); +out: + return error; +} + +MODULE_ALIAS("ide:*m-tape*"); +module_init(idetape_init); +module_exit(idetape_exit); +MODULE_ALIAS_CHARDEV_MAJOR(IDETAPE_MAJOR); +MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c new file mode 100644 index 000000000..d016cbe68 --- /dev/null +++ b/drivers/ide/ide-taskfile.c @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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, 2007 Bartlomiej Zolnierkiewicz + * + * The big the bad and the ugly. + */ + +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/nmi.h> +#include <linux/scatterlist.h> +#include <linux/uaccess.h> + +#include <asm/io.h> + +void ide_tf_readback(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + + /* Be sure we're looking at the low order bytes */ + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + + tp_ops->tf_read(drive, &cmd->tf, cmd->valid.in.tf); + + if (cmd->tf_flags & IDE_TFLAG_LBA48) { + tp_ops->write_devctl(hwif, ATA_HOB | ATA_DEVCTL_OBS); + + tp_ops->tf_read(drive, &cmd->hob, cmd->valid.in.hob); + } +} + +void ide_tf_dump(const char *s, struct ide_cmd *cmd) +{ +#ifdef DEBUG + printk("%s: tf: feat 0x%02x nsect 0x%02x lbal 0x%02x " + "lbam 0x%02x lbah 0x%02x dev 0x%02x cmd 0x%02x\n", + s, cmd->tf.feature, cmd->tf.nsect, + cmd->tf.lbal, cmd->tf.lbam, cmd->tf.lbah, + cmd->tf.device, cmd->tf.command); + printk("%s: hob: nsect 0x%02x lbal 0x%02x lbam 0x%02x lbah 0x%02x\n", + s, cmd->hob.nsect, cmd->hob.lbal, cmd->hob.lbam, cmd->hob.lbah); +#endif +} + +int taskfile_lib_get_identify(ide_drive_t *drive, u8 *buf) +{ + struct ide_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.tf.nsect = 0x01; + if (drive->media == ide_disk) + cmd.tf.command = ATA_CMD_ID_ATA; + else + cmd.tf.command = ATA_CMD_ID_ATAPI; + cmd.valid.out.tf = IDE_VALID_OUT_TF | IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_IN_TF | IDE_VALID_DEVICE; + cmd.protocol = ATA_PROT_PIO; + + return ide_raw_taskfile(drive, &cmd, buf, 1); +} + +static ide_startstop_t task_no_data_intr(ide_drive_t *); +static ide_startstop_t pre_task_out_intr(ide_drive_t *, struct ide_cmd *); +static ide_startstop_t task_pio_intr(ide_drive_t *); + +ide_startstop_t do_rw_taskfile(ide_drive_t *drive, struct ide_cmd *orig_cmd) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; + struct ide_taskfile *tf = &cmd->tf; + ide_handler_t *handler = NULL; + const struct ide_tp_ops *tp_ops = hwif->tp_ops; + const struct ide_dma_ops *dma_ops = hwif->dma_ops; + + if (orig_cmd->protocol == ATA_PROT_PIO && + (orig_cmd->tf_flags & IDE_TFLAG_MULTI_PIO) && + drive->mult_count == 0) { + pr_err("%s: multimode not set!\n", drive->name); + return ide_stopped; + } + + if (orig_cmd->ftf_flags & IDE_FTFLAG_FLAGGED) + orig_cmd->ftf_flags |= IDE_FTFLAG_SET_IN_FLAGS; + + memcpy(cmd, orig_cmd, sizeof(*cmd)); + + if ((cmd->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) { + ide_tf_dump(drive->name, cmd); + tp_ops->write_devctl(hwif, ATA_DEVCTL_OBS); + + if (cmd->ftf_flags & IDE_FTFLAG_OUT_DATA) { + u8 data[2] = { cmd->tf.data, cmd->hob.data }; + + tp_ops->output_data(drive, cmd, data, 2); + } + + if (cmd->valid.out.tf & IDE_VALID_DEVICE) { + u8 HIHI = (cmd->tf_flags & IDE_TFLAG_LBA48) ? + 0xE0 : 0xEF; + + if (!(cmd->ftf_flags & IDE_FTFLAG_FLAGGED)) + cmd->tf.device &= HIHI; + cmd->tf.device |= drive->select; + } + + tp_ops->tf_load(drive, &cmd->hob, cmd->valid.out.hob); + tp_ops->tf_load(drive, &cmd->tf, cmd->valid.out.tf); + } + + switch (cmd->protocol) { + case ATA_PROT_PIO: + if (cmd->tf_flags & IDE_TFLAG_WRITE) { + tp_ops->exec_command(hwif, tf->command); + ndelay(400); /* FIXME */ + return pre_task_out_intr(drive, cmd); + } + handler = task_pio_intr; + fallthrough; + case ATA_PROT_NODATA: + if (handler == NULL) + handler = task_no_data_intr; + ide_execute_command(drive, cmd, handler, WAIT_WORSTCASE); + return ide_started; + case ATA_PROT_DMA: + if (ide_dma_prepare(drive, cmd)) + return ide_stopped; + hwif->expiry = dma_ops->dma_timer_expiry; + ide_execute_command(drive, cmd, ide_dma_intr, 2 * WAIT_CMD); + dma_ops->dma_start(drive); + fallthrough; + default: + return ide_started; + } +} +EXPORT_SYMBOL_GPL(do_rw_taskfile); + +static ide_startstop_t task_no_data_intr(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &hwif->cmd; + struct ide_taskfile *tf = &cmd->tf; + int custom = (cmd->tf_flags & IDE_TFLAG_CUSTOM_HANDLER) ? 1 : 0; + int retries = (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) ? 5 : 1; + u8 stat; + + local_irq_enable_in_hardirq(); + + while (1) { + stat = hwif->tp_ops->read_status(hwif); + if ((stat & ATA_BUSY) == 0 || retries-- == 0) + break; + udelay(10); + }; + + if (!OK_STAT(stat, ATA_DRDY, BAD_STAT)) { + if (custom && tf->command == ATA_CMD_SET_MULTI) { + drive->mult_req = drive->mult_count = 0; + drive->special_flags |= IDE_SFLAG_RECALIBRATE; + (void)ide_dump_status(drive, __func__, stat); + return ide_stopped; + } else if (custom && tf->command == ATA_CMD_INIT_DEV_PARAMS) { + if ((stat & (ATA_ERR | ATA_DRQ)) == 0) { + ide_set_handler(drive, &task_no_data_intr, + WAIT_WORSTCASE); + return ide_started; + } + } + return ide_error(drive, "task_no_data_intr", stat); + } + + if (custom && tf->command == ATA_CMD_SET_MULTI) + drive->mult_count = drive->mult_req; + + if (custom == 0 || tf->command == ATA_CMD_IDLEIMMEDIATE || + tf->command == ATA_CMD_CHK_POWER) { + struct request *rq = hwif->rq; + + if (ata_pm_request(rq)) + ide_complete_pm_rq(drive, rq); + else + ide_finish_cmd(drive, cmd, stat); + } + + return ide_stopped; +} + +static u8 wait_drive_not_busy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + int retries; + u8 stat; + + /* + * Last sector was transferred, wait until device is ready. This can + * take up to 6 ms on some ATAPI devices, so we will wait max 10 ms. + */ + for (retries = 0; retries < 1000; retries++) { + stat = hwif->tp_ops->read_status(hwif); + + if (stat & ATA_BUSY) + udelay(10); + else + break; + } + + if (stat & ATA_BUSY) + pr_err("%s: drive still BUSY!\n", drive->name); + + return stat; +} + +void ide_pio_bytes(ide_drive_t *drive, struct ide_cmd *cmd, + unsigned int write, unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct scatterlist *sg = hwif->sg_table; + struct scatterlist *cursg = cmd->cursg; + struct page *page; + unsigned int offset; + u8 *buf; + + if (cursg == NULL) + cursg = cmd->cursg = sg; + + while (len) { + unsigned nr_bytes = min(len, cursg->length - cmd->cursg_ofs); + + page = sg_page(cursg); + offset = cursg->offset + cmd->cursg_ofs; + + /* get the current page and offset */ + page = nth_page(page, (offset >> PAGE_SHIFT)); + offset %= PAGE_SIZE; + + nr_bytes = min_t(unsigned, nr_bytes, (PAGE_SIZE - offset)); + + buf = kmap_atomic(page) + offset; + + cmd->nleft -= nr_bytes; + cmd->cursg_ofs += nr_bytes; + + if (cmd->cursg_ofs == cursg->length) { + cursg = cmd->cursg = sg_next(cmd->cursg); + cmd->cursg_ofs = 0; + } + + /* do the actual data transfer */ + if (write) + hwif->tp_ops->output_data(drive, cmd, buf, nr_bytes); + else + hwif->tp_ops->input_data(drive, cmd, buf, nr_bytes); + + kunmap_atomic(buf); + + len -= nr_bytes; + } +} +EXPORT_SYMBOL_GPL(ide_pio_bytes); + +static void ide_pio_datablock(ide_drive_t *drive, struct ide_cmd *cmd, + unsigned int write) +{ + unsigned int nr_bytes; + + u8 saved_io_32bit = drive->io_32bit; + + if (cmd->tf_flags & IDE_TFLAG_FS) + scsi_req(cmd->rq)->result = 0; + + if (cmd->tf_flags & IDE_TFLAG_IO_16BIT) + drive->io_32bit = 0; + + touch_softlockup_watchdog(); + + if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) + nr_bytes = min_t(unsigned, cmd->nleft, drive->mult_count << 9); + else + nr_bytes = SECTOR_SIZE; + + ide_pio_bytes(drive, cmd, write, nr_bytes); + + drive->io_32bit = saved_io_32bit; +} + +static void ide_error_cmd(ide_drive_t *drive, struct ide_cmd *cmd) +{ + if (cmd->tf_flags & IDE_TFLAG_FS) { + int nr_bytes = cmd->nbytes - cmd->nleft; + + if (cmd->protocol == ATA_PROT_PIO && + ((cmd->tf_flags & IDE_TFLAG_WRITE) || cmd->nleft == 0)) { + if (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) + nr_bytes -= drive->mult_count << 9; + else + nr_bytes -= SECTOR_SIZE; + } + + if (nr_bytes > 0) + ide_complete_rq(drive, BLK_STS_OK, nr_bytes); + } +} + +void ide_finish_cmd(ide_drive_t *drive, struct ide_cmd *cmd, u8 stat) +{ + struct request *rq = drive->hwif->rq; + u8 err = ide_read_error(drive), nsect = cmd->tf.nsect; + u8 set_xfer = !!(cmd->tf_flags & IDE_TFLAG_SET_XFER); + + ide_complete_cmd(drive, cmd, stat, err); + scsi_req(rq)->result = err; + + if (err == 0 && set_xfer) { + ide_set_xfer_rate(drive, nsect); + ide_driveid_update(drive); + } + + ide_complete_rq(drive, err ? BLK_STS_IOERR : BLK_STS_OK, blk_rq_bytes(rq)); +} + +/* + * Handler for command with PIO data phase. + */ +static ide_startstop_t task_pio_intr(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_cmd *cmd = &drive->hwif->cmd; + u8 stat = hwif->tp_ops->read_status(hwif); + u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); + + if (write == 0) { + /* Error? */ + if (stat & ATA_ERR) + goto out_err; + + /* Didn't want any data? Odd. */ + if ((stat & ATA_DRQ) == 0) { + /* Command all done? */ + if (OK_STAT(stat, ATA_DRDY, ATA_BUSY)) + goto out_end; + + /* Assume it was a spurious irq */ + goto out_wait; + } + } else { + if (!OK_STAT(stat, DRIVE_READY, drive->bad_wstat)) + goto out_err; + + /* Deal with unexpected ATA data phase. */ + if (((stat & ATA_DRQ) == 0) ^ (cmd->nleft == 0)) + goto out_err; + } + + if (write && cmd->nleft == 0) + goto out_end; + + /* Still data left to transfer. */ + ide_pio_datablock(drive, cmd, write); + + /* Are we done? Check status and finish transfer. */ + if (write == 0 && cmd->nleft == 0) { + stat = wait_drive_not_busy(drive); + if (!OK_STAT(stat, 0, BAD_STAT)) + goto out_err; + + goto out_end; + } +out_wait: + /* Still data left to transfer. */ + ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); + return ide_started; +out_end: + if ((cmd->tf_flags & IDE_TFLAG_FS) == 0) + ide_finish_cmd(drive, cmd, stat); + else + ide_complete_rq(drive, BLK_STS_OK, blk_rq_sectors(cmd->rq) << 9); + return ide_stopped; +out_err: + ide_error_cmd(drive, cmd); + return ide_error(drive, __func__, stat); +} + +static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, + struct ide_cmd *cmd) +{ + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop, drive, ATA_DRQ, + drive->bad_wstat, WAIT_DRQ)) { + pr_err("%s: no DRQ after issuing %sWRITE%s\n", drive->name, + (cmd->tf_flags & IDE_TFLAG_MULTI_PIO) ? "MULT" : "", + (drive->dev_flags & IDE_DFLAG_LBA48) ? "_EXT" : ""); + return startstop; + } + + if (!force_irqthreads && (drive->dev_flags & IDE_DFLAG_UNMASK) == 0) + local_irq_disable(); + + ide_set_handler(drive, &task_pio_intr, WAIT_WORSTCASE); + + ide_pio_datablock(drive, cmd, 1); + + return ide_started; +} + +int ide_raw_taskfile(ide_drive_t *drive, struct ide_cmd *cmd, u8 *buf, + u16 nsect) +{ + struct request *rq; + int error; + + rq = blk_get_request(drive->queue, + (cmd->tf_flags & IDE_TFLAG_WRITE) ? + REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); + ide_req(rq)->type = ATA_PRIV_TASKFILE; + + /* + * (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 (nsect) { + error = blk_rq_map_kern(drive->queue, rq, buf, + nsect * SECTOR_SIZE, GFP_NOIO); + if (error) + goto put_req; + } + + ide_req(rq)->special = cmd; + cmd->rq = rq; + + blk_execute_rq(drive->queue, NULL, rq, 0); + error = scsi_req(rq)->result ? -EIO : 0; +put_req: + blk_put_request(rq); + return error; +} +EXPORT_SYMBOL(ide_raw_taskfile); + +int ide_no_data_taskfile(ide_drive_t *drive, struct ide_cmd *cmd) +{ + cmd->protocol = ATA_PROT_NODATA; + + return ide_raw_taskfile(drive, cmd, NULL, 0); +} +EXPORT_SYMBOL_GPL(ide_no_data_taskfile); + +#ifdef CONFIG_IDE_TASK_IOCTL +int ide_taskfile_ioctl(ide_drive_t *drive, unsigned long arg) +{ + ide_task_request_t *req_task; + struct ide_cmd cmd; + u8 *outbuf = NULL; + u8 *inbuf = NULL; + u8 *data_buf = NULL; + int err = 0; + int tasksize = sizeof(struct ide_task_request_s); + unsigned int taskin = 0; + unsigned int taskout = 0; + u16 nsect = 0; + char __user *buf = (char __user *)arg; + + req_task = memdup_user(buf, tasksize); + if (IS_ERR(req_task)) + return PTR_ERR(req_task); + + taskout = req_task->out_size; + taskin = req_task->in_size; + + if (taskin > 65536 || taskout > 65536) { + err = -EINVAL; + goto abort; + } + + if (taskout) { + int outtotal = tasksize; + outbuf = kzalloc(taskout, GFP_KERNEL); + if (outbuf == NULL) { + err = -ENOMEM; + goto abort; + } + if (copy_from_user(outbuf, buf + outtotal, taskout)) { + err = -EFAULT; + goto abort; + } + } + + if (taskin) { + int intotal = tasksize + taskout; + inbuf = kzalloc(taskin, GFP_KERNEL); + if (inbuf == NULL) { + err = -ENOMEM; + goto abort; + } + if (copy_from_user(inbuf, buf + intotal, taskin)) { + err = -EFAULT; + goto abort; + } + } + + memset(&cmd, 0, sizeof(cmd)); + + memcpy(&cmd.hob, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE - 2); + memcpy(&cmd.tf, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); + + cmd.valid.out.tf = IDE_VALID_DEVICE; + cmd.valid.in.tf = IDE_VALID_DEVICE | IDE_VALID_IN_TF; + cmd.tf_flags = IDE_TFLAG_IO_16BIT; + + if (drive->dev_flags & IDE_DFLAG_LBA48) { + cmd.tf_flags |= IDE_TFLAG_LBA48; + cmd.valid.in.hob = IDE_VALID_IN_HOB; + } + + if (req_task->out_flags.all) { + cmd.ftf_flags |= IDE_FTFLAG_FLAGGED; + + if (req_task->out_flags.b.data) + cmd.ftf_flags |= IDE_FTFLAG_OUT_DATA; + + if (req_task->out_flags.b.nsector_hob) + cmd.valid.out.hob |= IDE_VALID_NSECT; + if (req_task->out_flags.b.sector_hob) + cmd.valid.out.hob |= IDE_VALID_LBAL; + if (req_task->out_flags.b.lcyl_hob) + cmd.valid.out.hob |= IDE_VALID_LBAM; + if (req_task->out_flags.b.hcyl_hob) + cmd.valid.out.hob |= IDE_VALID_LBAH; + + if (req_task->out_flags.b.error_feature) + cmd.valid.out.tf |= IDE_VALID_FEATURE; + if (req_task->out_flags.b.nsector) + cmd.valid.out.tf |= IDE_VALID_NSECT; + if (req_task->out_flags.b.sector) + cmd.valid.out.tf |= IDE_VALID_LBAL; + if (req_task->out_flags.b.lcyl) + cmd.valid.out.tf |= IDE_VALID_LBAM; + if (req_task->out_flags.b.hcyl) + cmd.valid.out.tf |= IDE_VALID_LBAH; + } else { + cmd.valid.out.tf |= IDE_VALID_OUT_TF; + if (cmd.tf_flags & IDE_TFLAG_LBA48) + cmd.valid.out.hob |= IDE_VALID_OUT_HOB; + } + + if (req_task->in_flags.b.data) + cmd.ftf_flags |= IDE_FTFLAG_IN_DATA; + + if (req_task->req_cmd == IDE_DRIVE_TASK_RAW_WRITE) { + /* fixup data phase if needed */ + if (req_task->data_phase == TASKFILE_IN_DMAQ || + req_task->data_phase == TASKFILE_IN_DMA) + cmd.tf_flags |= IDE_TFLAG_WRITE; + } + + cmd.protocol = ATA_PROT_DMA; + + switch (req_task->data_phase) { + case TASKFILE_MULTI_OUT: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + pr_err("%s: %s Multimode Write multcount is not set\n", + drive->name, __func__); + err = -EPERM; + goto abort; + } + cmd.tf_flags |= IDE_TFLAG_MULTI_PIO; + fallthrough; + case TASKFILE_OUT: + cmd.protocol = ATA_PROT_PIO; + fallthrough; + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + cmd.tf_flags |= IDE_TFLAG_WRITE; + nsect = taskout / SECTOR_SIZE; + data_buf = outbuf; + break; + case TASKFILE_MULTI_IN: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + pr_err("%s: %s Multimode Read multcount is not set\n", + drive->name, __func__); + err = -EPERM; + goto abort; + } + cmd.tf_flags |= IDE_TFLAG_MULTI_PIO; + fallthrough; + case TASKFILE_IN: + cmd.protocol = ATA_PROT_PIO; + fallthrough; + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + nsect = taskin / SECTOR_SIZE; + data_buf = inbuf; + break; + case TASKFILE_NO_DATA: + cmd.protocol = ATA_PROT_NODATA; + break; + default: + err = -EFAULT; + goto abort; + } + + if (req_task->req_cmd == IDE_DRIVE_TASK_NO_DATA) + nsect = 0; + else if (!nsect) { + nsect = (cmd.hob.nsect << 8) | cmd.tf.nsect; + + if (!nsect) { + pr_err("%s: in/out command without data\n", + drive->name); + err = -EFAULT; + goto abort; + } + } + + err = ide_raw_taskfile(drive, &cmd, data_buf, nsect); + + memcpy(req_task->hob_ports, &cmd.hob, HDIO_DRIVE_HOB_HDR_SIZE - 2); + memcpy(req_task->io_ports, &cmd.tf, HDIO_DRIVE_TASK_HDR_SIZE); + + if ((cmd.ftf_flags & IDE_FTFLAG_SET_IN_FLAGS) && + req_task->in_flags.all == 0) { + req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; + if (drive->dev_flags & IDE_DFLAG_LBA48) + req_task->in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8); + } + + 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); + kfree(outbuf); + kfree(inbuf); + + return err; +} +#endif diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c new file mode 100644 index 000000000..cfe78df74 --- /dev/null +++ b/drivers/ide/ide-timings.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * Copyright (c) 2007-2008 Bartlomiej Zolnierkiewicz + * + * 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/kernel.h> +#include <linux/ide.h> +#include <linux/module.h> + +/* + * 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_MW_DMA_4, 25, 0, 0, 0, 55, 20, 80, 0 }, + { XFER_MW_DMA_3, 25, 0, 0, 0, 65, 25, 100, 0 }, + { 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_6, 10, 55, 20, 80, 55, 20, 80, 0 }, + { XFER_PIO_5, 15, 65, 25, 100, 65, 25, 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 }, + + { 0xff } +}; + +struct ide_timing *ide_timing_find_mode(u8 speed) +{ + struct ide_timing *t; + + for (t = ide_timing; t->mode != speed; t++) + if (t->mode == 0xff) + return NULL; + return t; +} +EXPORT_SYMBOL_GPL(ide_timing_find_mode); + +u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) +{ + u16 *id = drive->id; + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + u16 cycle = 0; + + if (id[ATA_ID_FIELD_VALID] & 2) { + if (ata_id_has_iordy(drive->id)) + cycle = id[ATA_ID_EIDE_PIO_IORDY]; + else + cycle = id[ATA_ID_EIDE_PIO]; + + /* conservative "downgrade" for all pre-ATA2 drives */ + if (pio < 3 && cycle < t->cycle) + cycle = 0; /* use standard timing */ + + /* Use the standard timing for the CF specific modes too */ + if (pio > 4 && ata_id_is_cfa(id)) + cycle = 0; + } + + return cycle ? cycle : t->cycle; +} +EXPORT_SYMBOL_GPL(ide_pio_cycle_time); + +#define ENOUGH(v, unit) (((v) - 1) / (unit) + 1) +#define EZ(v, unit) ((v) ? ENOUGH((v) * 1000, unit) : 0) + +static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, + int T, int UT) +{ + q->setup = EZ(t->setup, T); + q->act8b = EZ(t->act8b, T); + q->rec8b = EZ(t->rec8b, T); + q->cyc8b = EZ(t->cyc8b, T); + q->active = EZ(t->active, T); + q->recover = EZ(t->recover, T); + q->cycle = EZ(t->cycle, T); + q->udma = EZ(t->udma, UT); +} + +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); +} +EXPORT_SYMBOL_GPL(ide_timing_merge); + +int ide_timing_compute(ide_drive_t *drive, u8 speed, + struct ide_timing *t, int T, int UT) +{ + u16 *id = drive->id; + struct ide_timing *s, p; + + /* + * Find the mode. + */ + s = ide_timing_find_mode(speed); + if (s == NULL) + return -EINVAL; + + /* + * Copy the timing from the table. + */ + *t = *s; + + /* + * If the drive is an EIDE drive, it can tell us it needs extended + * PIO/MWDMA cycle timing. + */ + if (id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */ + memset(&p, 0, sizeof(p)); + + if (speed >= XFER_PIO_0 && speed < XFER_SW_DMA_0) { + if (speed <= XFER_PIO_2) + p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO]; + else if ((speed <= XFER_PIO_4) || + (speed == XFER_PIO_5 && !ata_id_is_cfa(id))) + p.cycle = p.cyc8b = id[ATA_ID_EIDE_PIO_IORDY]; + } else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) + p.cycle = id[ATA_ID_EIDE_DMA_MIN]; + + ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); + } + + /* + * Convert the timing to bus clock counts. + */ + ide_timing_quantize(t, 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 current PIO timing. + */ + if (speed >= XFER_SW_DMA_0) { + ide_timing_compute(drive, drive->pio_mode, &p, T, UT); + ide_timing_merge(&p, t, t, IDE_TIMING_ALL); + } + + /* + * Lengthen 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; +} +EXPORT_SYMBOL_GPL(ide_timing_compute); diff --git a/drivers/ide/ide-xfer-mode.c b/drivers/ide/ide-xfer-mode.c new file mode 100644 index 000000000..0b9709b48 --- /dev/null +++ b/drivers/ide/ide-xfer-mode.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/interrupt.h> +#include <linux/ide.h> +#include <linux/bitops.h> + +static const char *udma_str[] = + { "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44", + "UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" }; +static const char *mwdma_str[] = + { "MWDMA0", "MWDMA1", "MWDMA2", "MWDMA3", "MWDMA4" }; +static const char *swdma_str[] = + { "SWDMA0", "SWDMA1", "SWDMA2" }; +static const char *pio_str[] = + { "PIO0", "PIO1", "PIO2", "PIO3", "PIO4", "PIO5", "PIO6" }; + +/** + * ide_xfer_verbose - return IDE mode names + * @mode: transfer mode + * + * Returns a constant string giving the name of the mode + * requested. + */ + +const char *ide_xfer_verbose(u8 mode) +{ + const char *s; + u8 i = mode & 0xf; + + if (mode >= XFER_UDMA_0 && mode <= XFER_UDMA_7) + s = udma_str[i]; + else if (mode >= XFER_MW_DMA_0 && mode <= XFER_MW_DMA_4) + s = mwdma_str[i]; + else if (mode >= XFER_SW_DMA_0 && mode <= XFER_SW_DMA_2) + s = swdma_str[i]; + else if (mode >= XFER_PIO_0 && mode <= XFER_PIO_6) + s = pio_str[i & 0x7]; + else if (mode == XFER_PIO_SLOW) + s = "PIO SLOW"; + else + s = "XFER ERROR"; + + return s; +} +EXPORT_SYMBOL(ide_xfer_verbose); + +/** + * ide_get_best_pio_mode - get PIO mode from drive + * @drive: drive to consider + * @mode_wanted: preferred mode + * @max_mode: highest allowed mode + * + * This routine returns the recommended PIO settings for a given drive, + * based on the drive->id information and the ide_pio_blacklist[]. + * + * Drive PIO mode is auto-selected if 255 is passed as mode_wanted. + * This is used by most chipset support modules when "auto-tuning". + */ + +static u8 ide_get_best_pio_mode(ide_drive_t *drive, u8 mode_wanted, u8 max_mode) +{ + u16 *id = drive->id; + int pio_mode = -1, overridden = 0; + + if (mode_wanted != 255) + return min_t(u8, mode_wanted, max_mode); + + if ((drive->hwif->host_flags & IDE_HFLAG_PIO_NO_BLACKLIST) == 0) + pio_mode = ide_scan_pio_blacklist((char *)&id[ATA_ID_PROD]); + + if (pio_mode != -1) { + printk(KERN_INFO "%s: is on PIO blacklist\n", drive->name); + } else { + pio_mode = id[ATA_ID_OLD_PIO_MODES] >> 8; + if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ + pio_mode = 2; + overridden = 1; + } + + if (id[ATA_ID_FIELD_VALID] & 2) { /* ATA2? */ + if (ata_id_is_cfa(id) && (id[ATA_ID_CFA_MODES] & 7)) + pio_mode = 4 + min_t(int, 2, + id[ATA_ID_CFA_MODES] & 7); + else if (ata_id_has_iordy(id)) { + if (id[ATA_ID_PIO_MODES] & 7) { + overridden = 0; + if (id[ATA_ID_PIO_MODES] & 4) + pio_mode = 5; + else if (id[ATA_ID_PIO_MODES] & 2) + pio_mode = 4; + else + pio_mode = 3; + } + } + } + + if (overridden) + printk(KERN_INFO "%s: tPIO > 2, assuming tPIO = 2\n", + drive->name); + } + + if (pio_mode > max_mode) + pio_mode = max_mode; + + return pio_mode; +} + +int ide_pio_need_iordy(ide_drive_t *drive, const u8 pio) +{ + /* + * IORDY may lead to controller lock up on certain controllers + * if the port is not occupied. + */ + if (pio == 0 && (drive->hwif->port_flags & IDE_PFLAG_PROBING)) + return 0; + return ata_id_pio_need_iordy(drive->id, pio); +} +EXPORT_SYMBOL_GPL(ide_pio_need_iordy); + +int ide_set_pio_mode(ide_drive_t *drive, const u8 mode) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + + if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE) + return 0; + + if (port_ops == NULL || port_ops->set_pio_mode == NULL) + return -1; + + /* + * TODO: temporary hack for some legacy host drivers that didn't + * set transfer mode on the device in ->set_pio_mode method... + */ + if (port_ops->set_dma_mode == NULL) { + drive->pio_mode = mode; + port_ops->set_pio_mode(hwif, drive); + return 0; + } + + if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) { + if (ide_config_drive_speed(drive, mode)) + return -1; + drive->pio_mode = mode; + port_ops->set_pio_mode(hwif, drive); + return 0; + } else { + drive->pio_mode = mode; + port_ops->set_pio_mode(hwif, drive); + return ide_config_drive_speed(drive, mode); + } +} + +int ide_set_dma_mode(ide_drive_t *drive, const u8 mode) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + + if (hwif->host_flags & IDE_HFLAG_NO_SET_MODE) + return 0; + + if (port_ops == NULL || port_ops->set_dma_mode == NULL) + return -1; + + if (hwif->host_flags & IDE_HFLAG_POST_SET_MODE) { + if (ide_config_drive_speed(drive, mode)) + return -1; + drive->dma_mode = mode; + port_ops->set_dma_mode(hwif, drive); + return 0; + } else { + drive->dma_mode = mode; + port_ops->set_dma_mode(hwif, drive); + return ide_config_drive_speed(drive, mode); + } +} +EXPORT_SYMBOL_GPL(ide_set_dma_mode); + +/* req_pio == "255" for auto-tune */ +void ide_set_pio(ide_drive_t *drive, u8 req_pio) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + u8 host_pio, pio; + + if (port_ops == NULL || port_ops->set_pio_mode == NULL || + (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)) + return; + + BUG_ON(hwif->pio_mask == 0x00); + + host_pio = fls(hwif->pio_mask) - 1; + + pio = ide_get_best_pio_mode(drive, req_pio, host_pio); + + /* + * TODO: + * - report device max PIO mode + * - check req_pio != 255 against device max PIO mode + */ + printk(KERN_DEBUG "%s: host max PIO%d wanted PIO%d%s selected PIO%d\n", + drive->name, host_pio, req_pio, + req_pio == 255 ? "(auto-tune)" : "", pio); + + (void)ide_set_pio_mode(drive, XFER_PIO_0 + pio); +} +EXPORT_SYMBOL_GPL(ide_set_pio); + +/** + * ide_rate_filter - filter transfer mode + * @drive: IDE device + * @speed: desired speed + * + * Given the available transfer modes this function returns + * the best available speed at or below the speed requested. + * + * TODO: check device PIO capabilities + */ + +static u8 ide_rate_filter(ide_drive_t *drive, u8 speed) +{ + ide_hwif_t *hwif = drive->hwif; + u8 mode = ide_find_dma_mode(drive, speed); + + if (mode == 0) { + if (hwif->pio_mask) + mode = fls(hwif->pio_mask) - 1 + XFER_PIO_0; + else + mode = XFER_PIO_4; + } + +/* printk("%s: mode 0x%02x, speed 0x%02x\n", __func__, mode, speed); */ + + return min(speed, mode); +} + +/** + * ide_set_xfer_rate - set transfer rate + * @drive: drive to set + * @rate: 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 ->set_pio_mode/->set_dma_mode does not. + */ + +int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) +{ + ide_hwif_t *hwif = drive->hwif; + const struct ide_port_ops *port_ops = hwif->port_ops; + + if (port_ops == NULL || port_ops->set_dma_mode == NULL || + (hwif->host_flags & IDE_HFLAG_NO_SET_MODE)) + return -1; + + rate = ide_rate_filter(drive, rate); + + BUG_ON(rate < XFER_PIO_0); + + if (rate >= XFER_PIO_0 && rate <= XFER_PIO_6) + return ide_set_pio_mode(drive, rate); + + return ide_set_dma_mode(drive, rate); +} diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c new file mode 100644 index 000000000..9a9c64fd1 --- /dev/null +++ b/drivers/ide/ide.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + * Copyright (C) 2003-2005, 2007 Bartlomiej Zolnierkiewicz + */ + +/* + * 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. + * + * ... + * + * 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 + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/major.h> +#include <linux/errno.h> +#include <linux/genhd.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/hdreg.h> +#include <linux/completion.h> +#include <linux/device.h> + +struct class *ide_port_class; + +/** + * ide_device_get - get an additional reference to a ide_drive_t + * @drive: device to get a reference to + * + * Gets a reference to the ide_drive_t and increments the use count of the + * underlying LLDD module. + */ +int ide_device_get(ide_drive_t *drive) +{ + struct device *host_dev; + struct module *module; + + if (!get_device(&drive->gendev)) + return -ENXIO; + + host_dev = drive->hwif->host->dev[0]; + module = host_dev ? host_dev->driver->owner : NULL; + + if (module && !try_module_get(module)) { + put_device(&drive->gendev); + return -ENXIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_device_get); + +/** + * ide_device_put - release a reference to a ide_drive_t + * @drive: device to release a reference on + * + * Release a reference to the ide_drive_t and decrements the use count of + * the underlying LLDD module. + */ +void ide_device_put(ide_drive_t *drive) +{ +#ifdef CONFIG_MODULE_UNLOAD + struct device *host_dev = drive->hwif->host->dev[0]; + struct module *module = host_dev ? host_dev->driver->owner : NULL; + + module_put(module); +#endif + put_device(&drive->gendev); +} +EXPORT_SYMBOL_GPL(ide_device_put); + +static int ide_bus_match(struct device *dev, struct device_driver *drv) +{ + return 1; +} + +static int ide_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + ide_drive_t *drive = to_ide_device(dev); + + add_uevent_var(env, "MEDIA=%s", ide_media_string(drive)); + add_uevent_var(env, "DRIVENAME=%s", drive->name); + add_uevent_var(env, "MODALIAS=ide:m-%s", ide_media_string(drive)); + return 0; +} + +static int generic_ide_probe(struct device *dev) +{ + ide_drive_t *drive = to_ide_device(dev); + struct ide_driver *drv = to_ide_driver(dev->driver); + + return drv->probe ? drv->probe(drive) : -ENODEV; +} + +static int generic_ide_remove(struct device *dev) +{ + ide_drive_t *drive = to_ide_device(dev); + struct ide_driver *drv = to_ide_driver(dev->driver); + + if (drv->remove) + drv->remove(drive); + + return 0; +} + +static void generic_ide_shutdown(struct device *dev) +{ + ide_drive_t *drive = to_ide_device(dev); + struct ide_driver *drv = to_ide_driver(dev->driver); + + if (dev->driver && drv->shutdown) + drv->shutdown(drive); +} + +struct bus_type ide_bus_type = { + .name = "ide", + .match = ide_bus_match, + .uevent = ide_uevent, + .probe = generic_ide_probe, + .remove = generic_ide_remove, + .shutdown = generic_ide_shutdown, + .dev_groups = ide_dev_groups, + .suspend = generic_ide_suspend, + .resume = generic_ide_resume, +}; + +EXPORT_SYMBOL_GPL(ide_bus_type); + +int ide_vlb_clk; +EXPORT_SYMBOL_GPL(ide_vlb_clk); + +module_param_named(vlb_clock, ide_vlb_clk, int, 0); +MODULE_PARM_DESC(vlb_clock, "VLB clock frequency (in MHz)"); + +int ide_pci_clk; +EXPORT_SYMBOL_GPL(ide_pci_clk); + +module_param_named(pci_clock, ide_pci_clk, int, 0); +MODULE_PARM_DESC(pci_clock, "PCI bus clock frequency (in MHz)"); + +static int ide_set_dev_param_mask(const char *s, const struct kernel_param *kp) +{ + unsigned int a, b, i, j = 1; + unsigned int *dev_param_mask = (unsigned int *)kp->arg; + + /* controller . device (0 or 1) [ : 1 (set) | 0 (clear) ] */ + if (sscanf(s, "%u.%u:%u", &a, &b, &j) != 3 && + sscanf(s, "%u.%u", &a, &b) != 2) + return -EINVAL; + + i = a * MAX_DRIVES + b; + + if (i >= MAX_HWIFS * MAX_DRIVES || j > 1) + return -EINVAL; + + if (j) + *dev_param_mask |= (1 << i); + else + *dev_param_mask &= ~(1 << i); + + return 0; +} + +static const struct kernel_param_ops param_ops_ide_dev_mask = { + .set = ide_set_dev_param_mask +}; + +#define param_check_ide_dev_mask(name, p) param_check_uint(name, p) + +static unsigned int ide_nodma; + +module_param_named(nodma, ide_nodma, ide_dev_mask, 0); +MODULE_PARM_DESC(nodma, "disallow DMA for a device"); + +static unsigned int ide_noflush; + +module_param_named(noflush, ide_noflush, ide_dev_mask, 0); +MODULE_PARM_DESC(noflush, "disable flush requests for a device"); + +static unsigned int ide_nohpa; + +module_param_named(nohpa, ide_nohpa, ide_dev_mask, 0); +MODULE_PARM_DESC(nohpa, "disable Host Protected Area for a device"); + +static unsigned int ide_noprobe; + +module_param_named(noprobe, ide_noprobe, ide_dev_mask, 0); +MODULE_PARM_DESC(noprobe, "skip probing for a device"); + +static unsigned int ide_nowerr; + +module_param_named(nowerr, ide_nowerr, ide_dev_mask, 0); +MODULE_PARM_DESC(nowerr, "ignore the ATA_DF bit for a device"); + +static unsigned int ide_cdroms; + +module_param_named(cdrom, ide_cdroms, ide_dev_mask, 0); +MODULE_PARM_DESC(cdrom, "force device as a CD-ROM"); + +struct chs_geom { + unsigned int cyl; + u8 head; + u8 sect; +}; + +static unsigned int ide_disks; +static struct chs_geom ide_disks_chs[MAX_HWIFS * MAX_DRIVES]; + +static int ide_set_disk_chs(const char *str, const struct kernel_param *kp) +{ + unsigned int a, b, c = 0, h = 0, s = 0, i, j = 1; + + /* controller . device (0 or 1) : Cylinders , Heads , Sectors */ + /* controller . device (0 or 1) : 1 (use CHS) | 0 (ignore CHS) */ + if (sscanf(str, "%u.%u:%u,%u,%u", &a, &b, &c, &h, &s) != 5 && + sscanf(str, "%u.%u:%u", &a, &b, &j) != 3) + return -EINVAL; + + i = a * MAX_DRIVES + b; + + if (i >= MAX_HWIFS * MAX_DRIVES || j > 1) + return -EINVAL; + + if (c > INT_MAX || h > 255 || s > 255) + return -EINVAL; + + if (j) + ide_disks |= (1 << i); + else + ide_disks &= ~(1 << i); + + ide_disks_chs[i].cyl = c; + ide_disks_chs[i].head = h; + ide_disks_chs[i].sect = s; + + return 0; +} + +module_param_call(chs, ide_set_disk_chs, NULL, NULL, 0); +MODULE_PARM_DESC(chs, "force device as a disk (using CHS)"); + +static void ide_dev_apply_params(ide_drive_t *drive, u8 unit) +{ + int i = drive->hwif->index * MAX_DRIVES + unit; + + if (ide_nodma & (1 << i)) { + printk(KERN_INFO "ide: disallowing DMA for %s\n", drive->name); + drive->dev_flags |= IDE_DFLAG_NODMA; + } + if (ide_noflush & (1 << i)) { + printk(KERN_INFO "ide: disabling flush requests for %s\n", + drive->name); + drive->dev_flags |= IDE_DFLAG_NOFLUSH; + } + if (ide_nohpa & (1 << i)) { + printk(KERN_INFO "ide: disabling Host Protected Area for %s\n", + drive->name); + drive->dev_flags |= IDE_DFLAG_NOHPA; + } + if (ide_noprobe & (1 << i)) { + printk(KERN_INFO "ide: skipping probe for %s\n", drive->name); + drive->dev_flags |= IDE_DFLAG_NOPROBE; + } + if (ide_nowerr & (1 << i)) { + printk(KERN_INFO "ide: ignoring the ATA_DF bit for %s\n", + drive->name); + drive->bad_wstat = BAD_R_STAT; + } + if (ide_cdroms & (1 << i)) { + printk(KERN_INFO "ide: forcing %s as a CD-ROM\n", drive->name); + drive->dev_flags |= IDE_DFLAG_PRESENT; + drive->media = ide_cdrom; + /* an ATAPI device ignores DRDY */ + drive->ready_stat = 0; + } + if (ide_disks & (1 << i)) { + drive->cyl = drive->bios_cyl = ide_disks_chs[i].cyl; + drive->head = drive->bios_head = ide_disks_chs[i].head; + drive->sect = drive->bios_sect = ide_disks_chs[i].sect; + + printk(KERN_INFO "ide: forcing %s as a disk (%d/%d/%d)\n", + drive->name, + drive->cyl, drive->head, drive->sect); + + drive->dev_flags |= IDE_DFLAG_FORCED_GEOM | IDE_DFLAG_PRESENT; + drive->media = ide_disk; + drive->ready_stat = ATA_DRDY; + } +} + +static unsigned int ide_ignore_cable; + +static int ide_set_ignore_cable(const char *s, const struct kernel_param *kp) +{ + int i, j = 1; + + /* controller (ignore) */ + /* controller : 1 (ignore) | 0 (use) */ + if (sscanf(s, "%d:%d", &i, &j) != 2 && sscanf(s, "%d", &i) != 1) + return -EINVAL; + + if (i >= MAX_HWIFS || j < 0 || j > 1) + return -EINVAL; + + if (j) + ide_ignore_cable |= (1 << i); + else + ide_ignore_cable &= ~(1 << i); + + return 0; +} + +module_param_call(ignore_cable, ide_set_ignore_cable, NULL, NULL, 0); +MODULE_PARM_DESC(ignore_cable, "ignore cable detection"); + +void ide_port_apply_params(ide_hwif_t *hwif) +{ + ide_drive_t *drive; + int i; + + if (ide_ignore_cable & (1 << hwif->index)) { + printk(KERN_INFO "ide: ignoring cable detection for %s\n", + hwif->name); + hwif->cbl = ATA_CBL_PATA40_SHORT; + } + + ide_port_for_each_dev(i, drive, hwif) + ide_dev_apply_params(drive, i); +} + +/* + * This is gets invoked once during initialization, to set *everything* up + */ +static int __init ide_init(void) +{ + int ret; + + printk(KERN_INFO "Uniform Multi-Platform E-IDE driver\n"); + + ret = bus_register(&ide_bus_type); + if (ret < 0) { + printk(KERN_WARNING "IDE: bus_register error: %d\n", ret); + return ret; + } + + ide_port_class = class_create(THIS_MODULE, "ide_port"); + if (IS_ERR(ide_port_class)) { + ret = PTR_ERR(ide_port_class); + goto out_port_class; + } + + ide_acpi_init(); + + proc_ide_create(); + + return 0; + +out_port_class: + bus_unregister(&ide_bus_type); + + return ret; +} + +static void __exit ide_exit(void) +{ + proc_ide_destroy(); + + class_destroy(ide_port_class); + + bus_unregister(&ide_bus_type); +} + +module_init(ide_init); +module_exit(ide_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ide_platform.c b/drivers/ide/ide_platform.c new file mode 100644 index 000000000..91639fd6c --- /dev/null +++ b/drivers/ide/ide_platform.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Platform IDE driver + * + * Copyright (C) 2007 MontaVista Software + * + * Maintainer: Kumar Gala <galak@kernel.crashing.org> + */ + +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/ide.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/ata_platform.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/io.h> + +static void plat_ide_setup_ports(struct ide_hw *hw, void __iomem *base, + void __iomem *ctrl, + struct pata_platform_info *pdata, int irq) +{ + unsigned long port = (unsigned long)base; + int i; + + hw->io_ports.data_addr = port; + + port += (1 << pdata->ioport_shift); + for (i = 1; i <= 7; + i++, port += (1 << pdata->ioport_shift)) + hw->io_ports_array[i] = port; + + hw->io_ports.ctl_addr = (unsigned long)ctrl; + + hw->irq = irq; +} + +static const struct ide_port_info platform_ide_port_info = { + .host_flags = IDE_HFLAG_NO_DMA, + .chipset = ide_generic, +}; + +static int plat_ide_probe(struct platform_device *pdev) +{ + struct resource *res_base, *res_alt, *res_irq; + void __iomem *base, *alt_base; + struct pata_platform_info *pdata; + struct ide_host *host; + int ret = 0, mmio = 0; + struct ide_hw hw, *hws[] = { &hw }; + struct ide_port_info d = platform_ide_port_info; + + pdata = dev_get_platdata(&pdev->dev); + + /* get a pointer to the register memory */ + res_base = platform_get_resource(pdev, IORESOURCE_IO, 0); + res_alt = platform_get_resource(pdev, IORESOURCE_IO, 1); + + if (!res_base || !res_alt) { + res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res_alt = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_base || !res_alt) { + ret = -ENOMEM; + goto out; + } + mmio = 1; + } + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) { + ret = -EINVAL; + goto out; + } + + if (mmio) { + base = devm_ioremap(&pdev->dev, + res_base->start, resource_size(res_base)); + alt_base = devm_ioremap(&pdev->dev, + res_alt->start, resource_size(res_alt)); + } else { + base = devm_ioport_map(&pdev->dev, + res_base->start, resource_size(res_base)); + alt_base = devm_ioport_map(&pdev->dev, + res_alt->start, resource_size(res_alt)); + } + + memset(&hw, 0, sizeof(hw)); + plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start); + hw.dev = &pdev->dev; + + d.irq_flags = res_irq->flags & IRQF_TRIGGER_MASK; + if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE) + d.irq_flags |= IRQF_SHARED; + + if (mmio) + d.host_flags |= IDE_HFLAG_MMIO; + + ret = ide_host_add(&d, hws, 1, &host); + if (ret) + goto out; + + platform_set_drvdata(pdev, host); + + return 0; + +out: + return ret; +} + +static int plat_ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = dev_get_drvdata(&pdev->dev); + + ide_host_remove(host); + + return 0; +} + +static struct platform_driver platform_ide_driver = { + .driver = { + .name = "pata_platform", + }, + .probe = plat_ide_probe, + .remove = plat_ide_remove, +}; + +module_platform_driver(platform_ide_driver); + +MODULE_DESCRIPTION("Platform IDE driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pata_platform"); diff --git a/drivers/ide/it8172.c b/drivers/ide/it8172.c new file mode 100644 index 000000000..b6f674ab4 --- /dev/null +++ b/drivers/ide/it8172.c @@ -0,0 +1,165 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 IDE controller support + * + * Copyright (C) 2000 MontaVista Software Inc. + * Copyright (C) 2008 Shane McDonald + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "IT8172" + +static void it8172_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u16 drive_enables; + u32 drive_timing; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* + * The highest value of DIOR/DIOW pulse width and recovery time + * that can be set in the IT8172 is 8 PCI clock cycles. As a result, + * it cannot be configured for PIO mode 0. This table sets these + * parameters to the maximum supported by the IT8172. + */ + static const u8 timings[] = { 0x3f, 0x3c, 0x1b, 0x12, 0x0a }; + + pci_read_config_word(dev, 0x40, &drive_enables); + pci_read_config_dword(dev, 0x44, &drive_timing); + + /* + * Enable port 0x44. The IT8172 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; + + drive_enables &= drive->dn ? 0xc006 : 0xc060; + if (drive->media == ide_disk) + /* enable prefetch */ + drive_enables |= 0x0004 << (drive->dn * 4); + if (ide_pio_need_iordy(drive, pio)) + /* enable IORDY sample-point */ + drive_enables |= 0x0002 << (drive->dn * 4); + + drive_timing &= drive->dn ? 0x00003f00 : 0x000fc000; + drive_timing |= timings[pio] << (drive->dn * 6 + 8); + + pci_write_config_word(dev, 0x40, drive_enables); + pci_write_config_dword(dev, 0x44, drive_timing); +} + +static void it8172_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + u8 reg48, reg4a; + const u8 speed = drive->dma_mode; + + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x4a, ®4a); + + if (speed >= XFER_UDMA_0) { + u8 udma = speed - XFER_UDMA_0; + u_speed = udma << (drive->dn * 4); + + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + reg4a &= ~a_speed; + pci_write_config_byte(dev, 0x4a, reg4a | u_speed); + } else { + const u8 mwdma_to_pio[] = { 0, 3, 4 }; + + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + + drive->pio_mode = + mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0; + + it8172_set_pio_mode(hwif, drive); + } +} + + +static const struct ide_port_ops it8172_port_ops = { + .set_pio_mode = it8172_set_pio_mode, + .set_dma_mode = it8172_set_dma_mode, +}; + +static const struct ide_port_info it8172_port_info = { + .name = DRV_NAME, + .port_ops = &it8172_port_ops, + .enablebits = { {0x41, 0x80, 0x80}, {0x00, 0x00, 0x00} }, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4 & ~ATA_PIO0, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, +}; + +static int it8172_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) + return -ENODEV; /* IT8172 is more than an IDE controller */ + return ide_pci_init_one(dev, &it8172_port_info, NULL); +} + +static struct pci_device_id it8172_pci_tbl[] = { + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8172), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, it8172_pci_tbl); + +static struct pci_driver it8172_pci_driver = { + .name = "IT8172_IDE", + .id_table = it8172_pci_tbl, + .probe = it8172_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init it8172_ide_init(void) +{ + return ide_pci_register_driver(&it8172_pci_driver); +} + +static void __exit it8172_ide_exit(void) +{ + pci_unregister_driver(&it8172_pci_driver); +} + +module_init(it8172_ide_init); +module_exit(it8172_ide_exit); + +MODULE_AUTHOR("Steve Longerbeam"); +MODULE_DESCRIPTION("PCI driver module for ITE 8172 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/it8213.c b/drivers/ide/it8213.c new file mode 100644 index 000000000..d0bf4430c --- /dev/null +++ b/drivers/ide/it8213.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ITE 8213 IDE driver + * + * Copyright (C) 2006 Jack Lee + * Copyright (C) 2006 Alan Cox + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "it8213" + +/** + * it8213_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Set the interface PIO mode. + */ + +static void it8213_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int is_slave = drive->dn & 1; + int master_port = 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + u8 slave_data; + static DEFINE_SPINLOCK(tune_lock); + int control = 0; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + static const u8 timings[][2] = { + { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + spin_lock_irqsave(&tune_lock, flags); + pci_read_config_word(dev, master_port, &master_data); + + if (pio > 1) + control |= 1; /* Programmable timing on */ + if (drive->media != ide_disk) + control |= 4; /* ATAPI */ + if (ide_pio_need_iordy(drive, pio)) + control |= 2; /* IORDY */ + if (is_slave) { + master_data |= 0x4000; + master_data &= ~0x0070; + if (pio > 1) + master_data = master_data | (control << 4); + pci_read_config_byte(dev, slave_port, &slave_data); + slave_data = slave_data & 0xf0; + slave_data = slave_data | (timings[pio][0] << 2) | timings[pio][1]; + } else { + master_data &= ~0x3307; + if (pio > 1) + master_data = master_data | control; + 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(&tune_lock, flags); +} + +/** + * it8213_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Tune the ITE chipset for the DMA mode. + */ + +static void it8213_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 maslave = 0x40; + 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; + u16 reg4042, reg4a; + u8 reg48, reg54, reg55; + const u8 speed = drive->dma_mode; + + pci_read_config_word(dev, maslave, ®4042); + 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); + + if (speed >= XFER_UDMA_0) { + u8 udma = speed - XFER_UDMA_0; + + u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4); + + 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 { + const u8 mwdma_to_pio[] = { 0, 3, 4 }; + + 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); + + if (speed >= XFER_MW_DMA_0) + drive->pio_mode = + mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0; + else + drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */ + + it8213_set_pio_mode(hwif, drive); + } +} + +static u8 it8213_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 reg42h = 0; + + pci_read_config_byte(dev, 0x42, ®42h); + + return (reg42h & 0x02) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +static const struct ide_port_ops it8213_port_ops = { + .set_pio_mode = it8213_set_pio_mode, + .set_dma_mode = it8213_set_dma_mode, + .cable_detect = it8213_cable_detect, +}; + +static const struct ide_port_info it8213_chipset = { + .name = DRV_NAME, + .enablebits = { {0x41, 0x80, 0x80} }, + .port_ops = &it8213_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .swdma_mask = ATA_SWDMA2_ONLY, + .mwdma_mask = ATA_MWDMA12_ONLY, + .udma_mask = ATA_UDMA6, +}; + +/** + * it8213_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE8213 controller. As + * this device follows the standard interfaces we can use the + * standard helper functions to do almost all the work for us. + */ + +static int it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &it8213_chipset, NULL); +} + +static const struct pci_device_id it8213_pci_tbl[] = { + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8213), 0 }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it8213_pci_tbl); + +static struct pci_driver it8213_pci_driver = { + .name = "ITE8213_IDE", + .id_table = it8213_pci_tbl, + .probe = it8213_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init it8213_ide_init(void) +{ + return ide_pci_register_driver(&it8213_pci_driver); +} + +static void __exit it8213_ide_exit(void) +{ + pci_unregister_driver(&it8213_pci_driver); +} + +module_init(it8213_ide_init); +module_exit(it8213_ide_exit); + +MODULE_AUTHOR("Jack Lee, Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 8213"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/it821x.c b/drivers/ide/it821x.c new file mode 100644 index 000000000..36a64c8ea --- /dev/null +++ b/drivers/ide/it821x.c @@ -0,0 +1,715 @@ +/* + * Copyright (C) 2004 Red Hat + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + * + * May be copied or modified under the terms of the GNU General Public License + * Based in part on the ITE vendor provided SCSI driver. + * + * Documentation: + * Datasheet is freely available, some other documents under NDA. + * + * The ITE8212 isn't exactly a standard IDE controller. It has two + * modes. In pass through mode then it is an IDE controller. In its smart + * mode its actually quite a capable hardware raid controller disguised + * as an IDE controller. Smart mode only understands DMA read/write and + * identify, none of the fancier commands apply. The IT8211 is identical + * in other respects but lacks the raid mode. + * + * Errata: + * o Rev 0x10 also requires master/slave hold the same DMA timings and + * cannot do ATAPI MWDMA. + * o The identify data for raid volumes lacks CHS info (technically ok) + * but also fails to set the LBA28 and other bits. We fix these in + * the IDE probe quirk code. + * o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode + * raid then the controller firmware dies + * o Smart mode without RAID doesn't clear all the necessary identify + * bits to reduce the command set to the one used + * + * This has a few impacts on the driver + * - In pass through mode we do all the work you would expect + * - In smart mode the clocking set up is done by the controller generally + * but we must watch the other limits and filter. + * - There are a few extra vendor commands that actually talk to the + * controller but only work PIO with no IRQ. + * + * Vendor areas of the identify block in smart mode are used for the + * timing and policy set up. Each HDD in raid mode also has a serial + * block on the disk. The hardware extra commands are get/set chip status, + * rebuild, get rebuild status. + * + * In Linux the driver supports pass through mode as if the device was + * just another IDE controller. If the smart mode is running then + * volumes are managed by the controller firmware and each IDE "disk" + * is a raid volume. Even more cute - the controller can do automated + * hotplug and rebuild. + * + * The pass through controller itself is a little demented. It has a + * flaw that it has a single set of PIO/MWDMA timings per channel so + * non UDMA devices restrict each others performance. It also has a + * single clock source per channel so mixed UDMA100/133 performance + * isn't perfect and we have to pick a clock. Thankfully none of this + * matters in smart mode. ATAPI DMA is not currently supported. + * + * It seems the smart mode is a win for RAID1/RAID10 but otherwise not. + * + * TODO + * - ATAPI UDMA is ok but not MWDMA it seems + * - RAID configuration ioctls + * - Move to libata once it grows up + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "it821x" + +#define QUIRK_VORTEX86 1 + +struct it821x_dev +{ + unsigned int smart:1, /* Are we in smart raid mode */ + timing10:1; /* Rev 0x10 */ + u8 clock_mode; /* 0, ATA_50 or ATA_66 */ + u8 want[2][2]; /* Mode/Pri log for master slave */ + /* We need these for switching the clock when DMA goes on/off + The high byte is the 66Mhz timing */ + u16 pio[2]; /* Cached PIO values */ + u16 mwdma[2]; /* Cached MWDMA values */ + u16 udma[2]; /* Cached UDMA values (per drive) */ + u16 quirks; +}; + +#define ATA_66 0 +#define ATA_50 1 +#define ATA_ANY 2 + +#define UDMA_OFF 0 +#define MWDMA_OFF 0 + +/* + * We allow users to force the card into non raid mode without + * flashing the alternative BIOS. This is also necessary right now + * for embedded platforms that cannot run a PC BIOS but are using this + * device. + */ + +static int it8212_noraid; + +/** + * it821x_program - program the PIO/MWDMA registers + * @drive: drive to tune + * @timing: timing info + * + * Program the PIO/MWDMA timing for this channel according to the + * current clock. + */ + +static void it821x_program(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + u8 conf; + + /* Program PIO/MWDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + + pci_write_config_byte(dev, 0x54 + 4 * channel, conf); +} + +/** + * it821x_program_udma - program the UDMA registers + * @drive: drive to tune + * @timing: timing info + * + * Program the UDMA timing for this drive according to the + * current clock. + */ + +static void it821x_program_udma(ide_drive_t *drive, u16 timing) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int channel = hwif->channel; + u8 unit = drive->dn & 1, conf; + + /* Program UDMA timing bits */ + if(itdev->clock_mode == ATA_66) + conf = timing >> 8; + else + conf = timing & 0xFF; + + if (itdev->timing10 == 0) + pci_write_config_byte(dev, 0x56 + 4 * channel + unit, conf); + else { + pci_write_config_byte(dev, 0x56 + 4 * channel, conf); + pci_write_config_byte(dev, 0x56 + 4 * channel + 1, conf); + } +} + +/** + * it821x_clock_strategy + * @drive: drive to set up + * + * Select between the 50 and 66Mhz base clocks to get the best + * results for this interface. + */ + +static void it821x_clock_strategy(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + ide_drive_t *pair = ide_get_pair_dev(drive); + int clock, altclock, sel = 0; + u8 unit = drive->dn & 1, v; + + if(itdev->want[0][0] > itdev->want[1][0]) { + clock = itdev->want[0][1]; + altclock = itdev->want[1][1]; + } else { + clock = itdev->want[1][1]; + altclock = itdev->want[0][1]; + } + + /* + * if both clocks can be used for the mode with the higher priority + * use the clock needed by the mode with the lower priority + */ + if (clock == ATA_ANY) + clock = altclock; + + /* Nobody cares - keep the same clock */ + if(clock == ATA_ANY) + return; + /* No change */ + if(clock == itdev->clock_mode) + return; + + /* Load this into the controller ? */ + if(clock == ATA_66) + itdev->clock_mode = ATA_66; + else { + itdev->clock_mode = ATA_50; + sel = 1; + } + + pci_read_config_byte(dev, 0x50, &v); + v &= ~(1 << (1 + hwif->channel)); + v |= sel << (1 + hwif->channel); + pci_write_config_byte(dev, 0x50, v); + + /* + * Reprogram the UDMA/PIO of the pair drive for the switch + * MWDMA will be dealt with by the dma switcher + */ + if(pair && itdev->udma[1-unit] != UDMA_OFF) { + it821x_program_udma(pair, itdev->udma[1-unit]); + it821x_program(pair, itdev->pio[1-unit]); + } + /* + * Reprogram the UDMA/PIO of our drive for the switch. + * MWDMA will be dealt with by the dma switcher + */ + if(itdev->udma[unit] != UDMA_OFF) { + it821x_program_udma(drive, itdev->udma[unit]); + it821x_program(drive, itdev->pio[unit]); + } +} + +/** + * it821x_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Tune the host to the desired PIO mode taking into the consideration + * the maximum PIO mode supported by the other device on the cable. + */ + +static void it821x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + ide_drive_t *pair = ide_get_pair_dev(drive); + const u8 pio = drive->pio_mode - XFER_PIO_0; + u8 unit = drive->dn & 1, set_pio = pio; + + /* Spec says 89 ref driver uses 88 */ + static u16 pio_timings[]= { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 }; + static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY }; + + /* + * Compute the best PIO mode we can for a given device. We must + * pick a speed that does not cause problems with the other device + * on the cable. + */ + if (pair) { + u8 pair_pio = pair->pio_mode - XFER_PIO_0; + /* trim PIO to the slowest of the master/slave */ + if (pair_pio < set_pio) + set_pio = pair_pio; + } + + /* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */ + itdev->want[unit][1] = pio_want[set_pio]; + itdev->want[unit][0] = 1; /* PIO is lowest priority */ + itdev->pio[unit] = pio_timings[set_pio]; + it821x_clock_strategy(drive); + it821x_program(drive, itdev->pio[unit]); +} + +/** + * it821x_tune_mwdma - tune a channel for MWDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing MWDMA in pass through mode. The caller + * must manage the whole lack of per device MWDMA/PIO timings and + * the shared MWDMA/PIO timing register. + */ + +static void it821x_tune_mwdma(ide_drive_t *drive, u8 mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif); + u8 unit = drive->dn & 1, channel = hwif->channel, conf; + + static u16 dma[] = { 0x8866, 0x3222, 0x3121 }; + static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY }; + + itdev->want[unit][1] = mwdma_want[mode_wanted]; + itdev->want[unit][0] = 2; /* MWDMA is low priority */ + itdev->mwdma[unit] = dma[mode_wanted]; + itdev->udma[unit] = UDMA_OFF; + + /* UDMA bits off - Revision 0x10 do them in pairs */ + pci_read_config_byte(dev, 0x50, &conf); + if (itdev->timing10) + conf |= channel ? 0x60: 0x18; + else + conf |= 1 << (3 + 2 * channel + unit); + pci_write_config_byte(dev, 0x50, conf); + + it821x_clock_strategy(drive); + /* FIXME: do we need to program this ? */ + /* it821x_program(drive, itdev->mwdma[unit]); */ +} + +/** + * it821x_tune_udma - tune a channel for UDMA + * @drive: drive to set up + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller when doing UDMA modes in pass through. + */ + +static void it821x_tune_udma(ide_drive_t *drive, u8 mode_wanted) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + u8 unit = drive->dn & 1, channel = hwif->channel, conf; + + static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 }; + static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 }; + + itdev->want[unit][1] = udma_want[mode_wanted]; + itdev->want[unit][0] = 3; /* UDMA is high priority */ + itdev->mwdma[unit] = MWDMA_OFF; + itdev->udma[unit] = udma[mode_wanted]; + if(mode_wanted >= 5) + itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */ + + /* UDMA on. Again revision 0x10 must do the pair */ + pci_read_config_byte(dev, 0x50, &conf); + if (itdev->timing10) + conf &= channel ? 0x9F: 0xE7; + else + conf &= ~ (1 << (3 + 2 * channel + unit)); + pci_write_config_byte(dev, 0x50, conf); + + it821x_clock_strategy(drive); + it821x_program_udma(drive, itdev->udma[unit]); + +} + +/** + * it821x_dma_read - DMA hook + * @drive: drive for DMA + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. In addition the rev 0x10 device only works if the same + * timing value is loaded into the master and slave UDMA clock + * so we must also reload that. + * + * FIXME: we could figure out in advance if we need to do reloads + */ + +static void it821x_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + u8 unit = drive->dn & 1; + + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->mwdma[unit]); + else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10) + it821x_program_udma(drive, itdev->udma[unit]); + ide_dma_start(drive); +} + +/** + * it821x_dma_write - DMA hook + * @drive: drive for DMA stop + * + * The IT821x has a single timing register for MWDMA and for PIO + * operations. As we flip back and forth we have to reload the + * clock. + */ + +static int it821x_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct it821x_dev *itdev = ide_get_hwifdata(hwif); + int ret = ide_dma_end(drive); + u8 unit = drive->dn & 1; + + if(itdev->mwdma[unit] != MWDMA_OFF) + it821x_program(drive, itdev->pio[unit]); + return ret; +} + +/** + * it821x_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Tune the ITE chipset for the desired DMA mode. + */ + +static void it821x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + const u8 speed = drive->dma_mode; + + /* + * MWDMA tuning is really hard because our MWDMA and PIO + * timings are kept in the same place. We can switch in the + * host dma on/off callbacks. + */ + if (speed >= XFER_UDMA_0 && speed <= XFER_UDMA_6) + it821x_tune_udma(drive, speed - XFER_UDMA_0); + else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) + it821x_tune_mwdma(drive, speed - XFER_MW_DMA_0); +} + +/** + * it821x_cable_detect - cable detection + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the + * interface. Problematic as it seems some cards don't have + * the needed logic onboard. + */ + +static u8 it821x_cable_detect(ide_hwif_t *hwif) +{ + /* The reference driver also only does disk side */ + return ATA_CBL_PATA80; +} + +/** + * it821x_quirkproc - post init callback + * @drive: drive + * + * This callback is run after the drive has been probed but + * before anything gets attached. It allows drivers to do any + * final tuning that is needed, or fixups to work around bugs. + */ + +static void it821x_quirkproc(ide_drive_t *drive) +{ + struct it821x_dev *itdev = ide_get_hwifdata(drive->hwif); + u16 *id = drive->id; + + if (!itdev->smart) { + /* + * If we are in pass through mode then not much + * needs to be done, but we do bother to clear the + * IRQ mask as we may well be in PIO (eg rev 0x10) + * for now and we know unmasking is safe on this chipset. + */ + drive->dev_flags |= IDE_DFLAG_UNMASK; + } else { + /* + * Perform fixups on smart mode. We need to "lose" some + * capabilities the firmware lacks but does not filter, and + * also patch up some capability bits that it forgets to set + * in RAID mode. + */ + + /* Check for RAID v native */ + if (strstr((char *)&id[ATA_ID_PROD], + "Integrated Technology Express")) { + /* In raid mode the ident block is slightly buggy + We need to set the bits so that the IDE layer knows + LBA28. LBA48 and DMA ar valid */ + id[ATA_ID_CAPABILITY] |= (3 << 8); /* LBA28, DMA */ + id[ATA_ID_COMMAND_SET_2] |= 0x0400; /* LBA48 valid */ + id[ATA_ID_CFS_ENABLE_2] |= 0x0400; /* LBA48 on */ + /* Reporting logic */ + printk(KERN_INFO "%s: IT8212 %sRAID %d volume", + drive->name, id[147] ? "Bootable " : "", + id[ATA_ID_CSFO]); + if (id[ATA_ID_CSFO] != 1) + printk(KERN_CONT "(%dK stripe)", id[146]); + printk(KERN_CONT ".\n"); + } else { + /* Non RAID volume. Fixups to stop the core code + doing unsupported things */ + id[ATA_ID_FIELD_VALID] &= 3; + id[ATA_ID_QUEUE_DEPTH] = 0; + id[ATA_ID_COMMAND_SET_1] = 0; + id[ATA_ID_COMMAND_SET_2] &= 0xC400; + id[ATA_ID_CFSSE] &= 0xC000; + id[ATA_ID_CFS_ENABLE_1] = 0; + id[ATA_ID_CFS_ENABLE_2] &= 0xC400; + id[ATA_ID_CSF_DEFAULT] &= 0xC000; + id[127] = 0; + id[ATA_ID_DLF] = 0; + id[ATA_ID_CSFO] = 0; + id[ATA_ID_CFA_POWER] = 0; + printk(KERN_INFO "%s: Performing identify fixups.\n", + drive->name); + } + + /* + * Set MWDMA0 mode as enabled/support - just to tell + * IDE core that DMA is supported (it821x hardware + * takes care of DMA mode programming). + */ + if (ata_id_has_dma(id)) { + id[ATA_ID_MWDMA_MODES] |= 0x0101; + drive->current_speed = XFER_MW_DMA_0; + } + } + +} + +static const struct ide_dma_ops it821x_pass_through_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = it821x_dma_start, + .dma_end = it821x_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +/** + * init_hwif_it821x - set up hwif structs + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The IT8212 + * requires several custom handlers so we override the default + * ide DMA handlers appropriately + */ + +static void init_hwif_it821x(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct it821x_dev *itdevs = host->host_priv; + struct it821x_dev *idev = itdevs + hwif->channel; + u8 conf; + + ide_set_hwifdata(hwif, idev); + + pci_read_config_byte(dev, 0x50, &conf); + if (conf & 1) { + idev->smart = 1; + hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA; + /* Long I/O's although allowed in LBA48 space cause the + onboard firmware to enter the twighlight zone */ + hwif->rqsize = 256; + } + + /* Pull the current clocks from 0x50 also */ + if (conf & (1 << (1 + hwif->channel))) + idev->clock_mode = ATA_50; + else + idev->clock_mode = ATA_66; + + idev->want[0][1] = ATA_ANY; + idev->want[1][1] = ATA_ANY; + + /* + * Not in the docs but according to the reference driver + * this is necessary. + */ + + if (dev->revision == 0x10) { + idev->timing10 = 1; + hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA; + if (idev->smart == 0) + printk(KERN_WARNING DRV_NAME " %s: revision 0x10, " + "workarounds activated\n", pci_name(dev)); + } + + if (idev->smart == 0) { + /* MWDMA/PIO clock switching for pass through mode */ + hwif->dma_ops = &it821x_pass_through_dma_ops; + } else + hwif->host_flags |= IDE_HFLAG_NO_SET_MODE; + + if (hwif->dma_base == 0) + return; + + hwif->ultra_mask = ATA_UDMA6; + hwif->mwdma_mask = ATA_MWDMA2; + + /* Vortex86SX quirk: prevent Ultra-DMA mode to fix BadCRC issue */ + if (idev->quirks & QUIRK_VORTEX86) { + if (dev->revision == 0x11) + hwif->ultra_mask = 0; + } +} + +static void it8212_disable_raid(struct pci_dev *dev) +{ + /* Reset local CPU, and set BIOS not ready */ + pci_write_config_byte(dev, 0x5E, 0x01); + + /* Set to bypass mode, and reset PCI bus */ + pci_write_config_byte(dev, 0x50, 0x00); + pci_write_config_word(dev, PCI_COMMAND, + PCI_COMMAND_PARITY | PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + pci_write_config_word(dev, 0x40, 0xA0F3); + + pci_write_config_dword(dev,0x4C, 0x02040204); + pci_write_config_byte(dev, 0x42, 0x36); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); +} + +static int init_chipset_it821x(struct pci_dev *dev) +{ + u8 conf; + static char *mode[2] = { "pass through", "smart" }; + + /* Force the card into bypass mode if so requested */ + if (it8212_noraid) { + printk(KERN_INFO DRV_NAME " %s: forcing bypass mode\n", + pci_name(dev)); + it8212_disable_raid(dev); + } + pci_read_config_byte(dev, 0x50, &conf); + printk(KERN_INFO DRV_NAME " %s: controller in %s mode\n", + pci_name(dev), mode[conf & 1]); + return 0; +} + +static const struct ide_port_ops it821x_port_ops = { + /* it821x_set_{pio,dma}_mode() are only used in pass-through mode */ + .set_pio_mode = it821x_set_pio_mode, + .set_dma_mode = it821x_set_dma_mode, + .quirkproc = it821x_quirkproc, + .cable_detect = it821x_cable_detect, +}; + +static const struct ide_port_info it821x_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_it821x, + .init_hwif = init_hwif_it821x, + .port_ops = &it821x_port_ops, + .pio_mask = ATA_PIO4, +}; + +/** + * it821x_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an ITE821x controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct it821x_dev *itdevs; + int rc; + + itdevs = kcalloc(2, sizeof(*itdevs), GFP_KERNEL); + if (itdevs == NULL) { + printk(KERN_ERR DRV_NAME " %s: out of memory\n", pci_name(dev)); + return -ENOMEM; + } + + itdevs->quirks = id->driver_data; + + rc = ide_pci_init_one(dev, &it821x_chipset, itdevs); + if (rc) + kfree(itdevs); + + return rc; +} + +static void it821x_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct it821x_dev *itdevs = host->host_priv; + + ide_pci_remove(dev); + kfree(itdevs); +} + +static const struct pci_device_id it821x_pci_tbl[] = { + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), 0 }, + { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), 0 }, + { PCI_VDEVICE(RDC, PCI_DEVICE_ID_RDC_D1010), QUIRK_VORTEX86 }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, it821x_pci_tbl); + +static struct pci_driver it821x_pci_driver = { + .name = "ITE821x IDE", + .id_table = it821x_pci_tbl, + .probe = it821x_init_one, + .remove = it821x_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init it821x_ide_init(void) +{ + return ide_pci_register_driver(&it821x_pci_driver); +} + +static void __exit it821x_ide_exit(void) +{ + pci_unregister_driver(&it821x_pci_driver); +} + +module_init(it821x_ide_init); +module_exit(it821x_ide_exit); + +module_param_named(noraid, it8212_noraid, int, S_IRUGO); +MODULE_PARM_DESC(noraid, "Force card into bypass mode"); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the ITE 821x"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/jmicron.c b/drivers/ide/jmicron.c new file mode 100644 index 000000000..ae6480dcb --- /dev/null +++ b/drivers/ide/jmicron.c @@ -0,0 +1,176 @@ + +/* + * Copyright (C) 2006 Red Hat + * + * May be copied or modified under the terms of the GNU General Public License + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "jmicron" + +typedef enum { + PORT_PATA0 = 0, + PORT_PATA1 = 1, + PORT_SATA = 2, +} port_type; + +/** + * jmicron_cable_detect - cable detection + * @hwif: IDE port + * + * Returns the cable type. + */ + +static u8 jmicron_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + + u32 control; + u32 control5; + + int port = hwif->channel; + port_type port_map[2]; + + pci_read_config_dword(pdev, 0x40, &control); + + /* There are two basic mappings. One has the two SATA ports merged + as master/slave and the secondary as PATA, the other has only the + SATA port mapped */ + if (control & (1 << 23)) { + port_map[0] = PORT_SATA; + port_map[1] = PORT_PATA0; + } else { + port_map[0] = PORT_SATA; + port_map[1] = PORT_SATA; + } + + /* The 365/366 may have this bit set to map the second PATA port + as the internal primary channel */ + pci_read_config_dword(pdev, 0x80, &control5); + if (control5 & (1<<24)) + port_map[0] = PORT_PATA1; + + /* The two ports may then be logically swapped by the firmware */ + if (control & (1 << 22)) + port = port ^ 1; + + /* + * Now we know which physical port we are talking about we can + * actually do our cable checking etc. Thankfully we don't need + * to do the plumbing for other cases. + */ + switch (port_map[port]) { + case PORT_PATA0: + if (control & (1 << 3)) /* 40/80 pin primary */ + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; + case PORT_PATA1: + if (control5 & (1 << 19)) /* 40/80 pin secondary */ + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; + case PORT_SATA: + break; + } + /* Avoid bogus "control reaches end of non-void function" */ + return ATA_CBL_PATA80; +} + +static void jmicron_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ +} + +/** + * jmicron_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * As the JMicron snoops for timings we don't need to do anything here. + */ + +static void jmicron_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ +} + +static const struct ide_port_ops jmicron_port_ops = { + .set_pio_mode = jmicron_set_pio_mode, + .set_dma_mode = jmicron_set_dma_mode, + .cable_detect = jmicron_cable_detect, +}; + +static const struct ide_port_info jmicron_chipset = { + .name = DRV_NAME, + .enablebits = { { 0x40, 0x01, 0x01 }, { 0x40, 0x10, 0x10 } }, + .port_ops = &jmicron_port_ops, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA6, +}; + +/** + * jmicron_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds a Jmicron controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &jmicron_chipset, NULL); +} + +/* All JMB PATA controllers have and will continue to have the same + * interface. Matching vendor and device class is enough for all + * current and future controllers if the controller is programmed + * properly. + * + * If libata is configured, jmicron PCI quirk programs the controller + * into the correct mode. If libata isn't configured, match known + * device IDs too to maintain backward compatibility. + */ +static struct pci_device_id jmicron_pci_tbl[] = { +#if !defined(CONFIG_ATA) && !defined(CONFIG_ATA_MODULE) + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB361) }, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB363) }, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB365) }, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB366) }, + { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB368) }, +#endif + { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 0 }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, jmicron_pci_tbl); + +static struct pci_driver jmicron_pci_driver = { + .name = "JMicron IDE", + .id_table = jmicron_pci_tbl, + .probe = jmicron_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init jmicron_ide_init(void) +{ + return ide_pci_register_driver(&jmicron_pci_driver); +} + +static void __exit jmicron_ide_exit(void) +{ + pci_unregister_driver(&jmicron_pci_driver); +} + +module_init(jmicron_ide_init); +module_exit(jmicron_ide_exit); + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/macide.c b/drivers/ide/macide.c new file mode 100644 index 000000000..8d2bf73bc --- /dev/null +++ b/drivers/ide/macide.c @@ -0,0 +1,161 @@ +/* + * Macintosh IDE Driver + * + * Copyright (C) 1998 by Michael Schmitz + * + * This driver was written based on information obtained from the MacOS IDE + * driver binary by Mikael Forselius + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/macintosh.h> + +#define DRV_NAME "mac_ide" + +#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_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); + +int macide_test_irq(ide_hwif_t *hwif) +{ + if (*ide_ifr & 0x20) + return 1; + return 0; +} + +static void macide_clear_irq(ide_drive_t *drive) +{ + *ide_ifr &= ~0x20; +} + +static void __init macide_setup_ports(struct ide_hw *hw, unsigned long base, + int irq) +{ + int i; + + memset(hw, 0, sizeof(*hw)); + + for (i = 0; i < 8; i++) + hw->io_ports_array[i] = base + i * 4; + + hw->io_ports.ctl_addr = base + IDE_CONTROL; + + hw->irq = irq; +} + +static const struct ide_port_ops macide_port_ops = { + .clear_irq = macide_clear_irq, + .test_irq = macide_test_irq, +}; + +static const struct ide_port_info macide_port_info = { + .port_ops = &macide_port_ops, + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_generic, +}; + +static const char *mac_ide_name[] = + { "Quadra", "Powerbook", "Powerbook Baboon" }; + +/* + * Probe for a Macintosh IDE interface + */ + +static int mac_ide_probe(struct platform_device *pdev) +{ + struct resource *mem, *irq; + struct ide_hw hw, *hws[] = { &hw }; + struct ide_port_info d = macide_port_info; + struct ide_host *host; + int rc; + + if (!MACH_IS_MAC) + return -ENODEV; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) + return -ENODEV; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME)) { + dev_err(&pdev->dev, "resources busy\n"); + return -EBUSY; + } + + printk(KERN_INFO "ide: Macintosh %s IDE controller\n", + mac_ide_name[macintosh_config->ide_type - 1]); + + macide_setup_ports(&hw, mem->start, irq->start); + + rc = ide_host_add(&d, hws, 1, &host); + if (rc) + return rc; + + platform_set_drvdata(pdev, host); + return 0; +} + +static int mac_ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = platform_get_drvdata(pdev); + + ide_host_remove(host); + return 0; +} + +static struct platform_driver mac_ide_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = mac_ide_probe, + .remove = mac_ide_remove, +}; + +module_platform_driver(mac_ide_driver); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c new file mode 100644 index 000000000..11a672aba --- /dev/null +++ b/drivers/ide/ns87415.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "ns87415" + +#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> + +#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) +{ + 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; +} + +static u8 superio_read_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->io_ports.status_addr); +} + +static u8 superio_dma_sff_read_status(ide_hwif_t *hwif) +{ + return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS); +} + +static void superio_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, + u8 valid) +{ + struct ide_io_ports *io_ports = &drive->hwif->io_ports; + + if (valid & IDE_VALID_ERROR) + tf->error = inb(io_ports->feature_addr); + if (valid & IDE_VALID_NSECT) + tf->nsect = inb(io_ports->nsect_addr); + if (valid & IDE_VALID_LBAL) + tf->lbal = inb(io_ports->lbal_addr); + if (valid & IDE_VALID_LBAM) + tf->lbam = inb(io_ports->lbam_addr); + if (valid & IDE_VALID_LBAH) + tf->lbah = inb(io_ports->lbah_addr); + if (valid & IDE_VALID_DEVICE) + tf->device = superio_ide_inb(io_ports->device_addr); +} + +static void ns87415_dev_select(ide_drive_t *drive); + +static const struct ide_tp_ops superio_tp_ops = { + .exec_command = ide_exec_command, + .read_status = superio_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ns87415_dev_select, + .tf_load = ide_tf_load, + .tf_read = superio_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static void superio_init_iops(struct hwif_s *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + u32 dma_stat; + u8 port = hwif->channel, tmp; + + dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa); + + /* Clear error/interrupt, enable dma */ + tmp = superio_ide_inb(dma_stat); + outb(tmp | 0x66, dma_stat); +} +#else +#define superio_dma_sff_read_status ide_dma_sff_read_status +#endif + +static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; + +/* + * This routine either enables/disables (according to IDE_DFLAG_PRESENT) + * the IRQ associated with the port, + * 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 = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data; + unsigned long flags; + + local_irq_save(flags); + new = *old; + + /* Adjust IRQ enable bit */ + bit = 1 << (8 + hwif->channel); + + if (drive->dev_flags & IDE_DFLAG_PRESENT) + new &= ~bit; + else + new |= bit; + + /* Select PIO or DMA, DMA may only be selected for one drive/channel. */ + bit = 1 << (20 + (drive->dn & 1) + (hwif->channel << 1)); + other = 1 << (20 + (1 - (drive->dn & 1)) + (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_dev_select(ide_drive_t *drive) +{ + ns87415_prepare_drive(drive, + !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); +} + +static void ns87415_dma_start(ide_drive_t *drive) +{ + ns87415_prepare_drive(drive, 1); + ide_dma_start(drive); +} + +static int ns87415_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat = 0, dma_cmd = 0; + + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + /* get DMA command mode */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + /* stop DMA */ + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); + /* from ERRATA: clear the INTR & ERROR bits */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD); + + ns87415_prepare_drive(drive, 0); + + /* verify good DMA status */ + return (dma_stat & 7) != 4; +} + +static void init_hwif_ns87415 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned int ctrl, using_inta; + u8 progif; +#ifdef __sparc_v9__ + int timeout; + u8 stat; +#endif + + /* + * We cannot probe for IRQ: both ports share common IRQ on INTA. + * Also, leave IRQ masked during drive probing, to prevent infinite + * interrupts from a potentially floating INTA.. + * + * IRQs get unmasked in dev_select() 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 + * dev_select() properly during first ide_probe_port(). + */ + timeout = 10000; + outb(12, hwif->io_ports.ctl_addr); + udelay(10); + outb(8, hwif->io_ports.ctl_addr); + do { + udelay(50); + stat = hwif->tp_ops->read_status(hwif); + if (stat == 0xff) + break; + } while ((stat & ATA_BUSY) && --timeout); +#endif + } + + if (!using_inta) + hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel); + + if (!hwif->dma_base) + return; + + outb(0x60, hwif->dma_base + ATA_DMA_STATUS); +} + +static const struct ide_tp_ops ns87415_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ns87415_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_dma_ops ns87415_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ns87415_dma_start, + .dma_end = ns87415_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = superio_dma_sff_read_status, +}; + +static const struct ide_port_info ns87415_chipset = { + .name = DRV_NAME, + .init_hwif = init_hwif_ns87415, + .tp_ops = &ns87415_tp_ops, + .dma_ops = &ns87415_dma_ops, + .host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA | + IDE_HFLAG_NO_ATAPI_DMA, +}; + +static int ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d = ns87415_chipset; + +#ifdef CONFIG_SUPERIO + if (PCI_SLOT(dev->devfn) == 0xE) { + /* Built-in - assume it's under superio. */ + d.init_iops = superio_init_iops; + d.tp_ops = &superio_tp_ops; + } +#endif + return ide_pci_init_one(dev, &d, NULL); +} + +static const struct pci_device_id ns87415_pci_tbl[] = { + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87415), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl); + +static struct pci_driver ns87415_pci_driver = { + .name = "NS87415_IDE", + .id_table = ns87415_pci_tbl, + .probe = ns87415_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init ns87415_ide_init(void) +{ + return ide_pci_register_driver(&ns87415_pci_driver); +} + +static void __exit ns87415_ide_exit(void) +{ + pci_unregister_driver(&ns87415_pci_driver); +} + +module_init(ns87415_ide_init); +module_exit(ns87415_ide_exit); + +MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for NS87415 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c new file mode 100644 index 000000000..c374f8233 --- /dev/null +++ b/drivers/ide/opti621.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> + +#include <asm/io.h> + +#define DRV_NAME "opti621" + +#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; + +static DEFINE_SPINLOCK(opti621_lock); + +/* 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. + */ +static void write_reg(u8 value, int reg) +{ + inw(reg_base + 1); + inw(reg_base + 1); + outb(3, reg_base + 2); + outb(value, reg_base + reg); + outb(0x83, reg_base + 2); +} + +/* 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. + */ +static u8 read_reg(int reg) +{ + u8 ret = 0; + + inw(reg_base + 1); + inw(reg_base + 1); + outb(3, reg_base + 2); + ret = inb(reg_base + reg); + outb(0x83, reg_base + 2); + + return ret; +} + +static void opti621_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_drive_t *pair = ide_get_pair_dev(drive); + unsigned long flags; + unsigned long mode = drive->pio_mode, pair_mode; + const u8 pio = mode - XFER_PIO_0; + u8 tim, misc, addr_pio = pio, clk; + + /* DRDY is default 2 (by OPTi Databook) */ + static const u8 addr_timings[2][5] = { + { 0x20, 0x10, 0x00, 0x00, 0x00 }, /* 33 MHz */ + { 0x10, 0x10, 0x00, 0x00, 0x00 }, /* 25 MHz */ + }; + static const u8 data_rec_timings[2][5] = { + { 0x5b, 0x45, 0x32, 0x21, 0x20 }, /* 33 MHz */ + { 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */ + }; + + ide_set_drivedata(drive, (void *)mode); + + if (pair) { + pair_mode = (unsigned long)ide_get_drivedata(pair); + if (pair_mode && pair_mode < mode) + addr_pio = pair_mode - XFER_PIO_0; + } + + spin_lock_irqsave(&opti621_lock, flags); + + reg_base = hwif->io_ports.data_addr; + + /* allow Register-B */ + outb(0xc0, reg_base + CNTRL_REG); + /* hmm, setupvic.exe does this ;-) */ + outb(0xff, reg_base + 5); + /* if reads 0xff, adapter not exist? */ + (void)inb(reg_base + CNTRL_REG); + /* if reads 0xc0, no interface exist? */ + read_reg(CNTRL_REG); + + /* check CLK speed */ + clk = read_reg(STRAP_REG) & 1; + + printk(KERN_INFO "%s: CLK = %d MHz\n", hwif->name, clk ? 25 : 33); + + tim = data_rec_timings[clk][pio]; + misc = addr_timings[clk][addr_pio]; + + /* select Index-0/1 for Register-A/B */ + write_reg(drive->dn & 1, MISC_REG); + /* set read cycle timings */ + write_reg(tim, READ_REG); + /* set write cycle timings */ + write_reg(tim, WRITE_REG); + + /* use Register-A for drive 0 */ + /* use Register-B for drive 1 */ + write_reg(0x85, CNTRL_REG); + + /* set address setup, DRDY timings, */ + /* and read prefetch for both drives */ + write_reg(misc, MISC_REG); + + spin_unlock_irqrestore(&opti621_lock, flags); +} + +static const struct ide_port_ops opti621_port_ops = { + .set_pio_mode = opti621_set_pio_mode, +}; + +static const struct ide_port_info opti621_chipset = { + .name = DRV_NAME, + .enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} }, + .port_ops = &opti621_port_ops, + .host_flags = IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO4, +}; + +static int opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &opti621_chipset, NULL); +} + +static const struct pci_device_id opti621_pci_tbl[] = { + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 }, + { PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, opti621_pci_tbl); + +static struct pci_driver opti621_pci_driver = { + .name = "Opti621_IDE", + .id_table = opti621_pci_tbl, + .probe = opti621_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init opti621_ide_init(void) +{ + return ide_pci_register_driver(&opti621_pci_driver); +} + +static void __exit opti621_ide_exit(void) +{ + pci_unregister_driver(&opti621_pci_driver); +} + +module_init(opti621_ide_init); +module_exit(opti621_ide_exit); + +MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Opti621 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/palm_bk3710.c b/drivers/ide/palm_bk3710.c new file mode 100644 index 000000000..d1fe4c13e --- /dev/null +++ b/drivers/ide/palm_bk3710.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Palmchip bk3710 IDE controller + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com> + * + * ---------------------------------------------------------------------------- + * + * ---------------------------------------------------------------------------- + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/ide.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/platform_device.h> + +/* Offset of the primary interface registers */ +#define IDE_PALM_ATA_PRI_REG_OFFSET 0x1F0 + +/* Primary Control Offset */ +#define IDE_PALM_ATA_PRI_CTL_OFFSET 0x3F6 + +#define BK3710_BMICP 0x00 +#define BK3710_BMISP 0x02 +#define BK3710_BMIDTP 0x04 +#define BK3710_IDETIMP 0x40 +#define BK3710_IDESTATUS 0x47 +#define BK3710_UDMACTL 0x48 +#define BK3710_MISCCTL 0x50 +#define BK3710_REGSTB 0x54 +#define BK3710_REGRCVR 0x58 +#define BK3710_DATSTB 0x5C +#define BK3710_DATRCVR 0x60 +#define BK3710_DMASTB 0x64 +#define BK3710_DMARCVR 0x68 +#define BK3710_UDMASTB 0x6C +#define BK3710_UDMATRP 0x70 +#define BK3710_UDMAENV 0x74 +#define BK3710_IORDYTMP 0x78 + +static unsigned ideclk_period; /* in nanoseconds */ + +struct palm_bk3710_udmatiming { + unsigned int rptime; /* tRP -- Ready to pause time (nsec) */ + unsigned int cycletime; /* tCYCTYP2/2 -- avg Cycle Time (nsec) */ + /* tENV is always a minimum of 20 nsec */ +}; + +static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = { + { 160, 240 / 2 }, /* UDMA Mode 0 */ + { 125, 160 / 2 }, /* UDMA Mode 1 */ + { 100, 120 / 2 }, /* UDMA Mode 2 */ + { 100, 90 / 2 }, /* UDMA Mode 3 */ + { 100, 60 / 2 }, /* UDMA Mode 4 */ + { 85, 40 / 2 }, /* UDMA Mode 5 */ +}; + +static void palm_bk3710_setudmamode(void __iomem *base, unsigned int dev, + unsigned int mode) +{ + u8 tenv, trp, t0; + u32 val32; + u16 val16; + + /* DMA Data Setup */ + t0 = DIV_ROUND_UP(palm_bk3710_udmatimings[mode].cycletime, + ideclk_period) - 1; + tenv = DIV_ROUND_UP(20, ideclk_period) - 1; + trp = DIV_ROUND_UP(palm_bk3710_udmatimings[mode].rptime, + ideclk_period) - 1; + + /* udmastb Ultra DMA Access Strobe Width */ + val32 = readl(base + BK3710_UDMASTB) & (0xFF << (dev ? 0 : 8)); + val32 |= (t0 << (dev ? 8 : 0)); + writel(val32, base + BK3710_UDMASTB); + + /* udmatrp Ultra DMA Ready to Pause Time */ + val32 = readl(base + BK3710_UDMATRP) & (0xFF << (dev ? 0 : 8)); + val32 |= (trp << (dev ? 8 : 0)); + writel(val32, base + BK3710_UDMATRP); + + /* udmaenv Ultra DMA envelop Time */ + val32 = readl(base + BK3710_UDMAENV) & (0xFF << (dev ? 0 : 8)); + val32 |= (tenv << (dev ? 8 : 0)); + writel(val32, base + BK3710_UDMAENV); + + /* Enable UDMA for Device */ + val16 = readw(base + BK3710_UDMACTL) | (1 << dev); + writew(val16, base + BK3710_UDMACTL); +} + +static void palm_bk3710_setdmamode(void __iomem *base, unsigned int dev, + unsigned short min_cycle, + unsigned int mode) +{ + u8 td, tkw, t0; + u32 val32; + u16 val16; + struct ide_timing *t; + int cycletime; + + t = ide_timing_find_mode(mode); + cycletime = max_t(int, t->cycle, min_cycle); + + /* DMA Data Setup */ + t0 = DIV_ROUND_UP(cycletime, ideclk_period); + td = DIV_ROUND_UP(t->active, ideclk_period); + tkw = t0 - td - 1; + td -= 1; + + val32 = readl(base + BK3710_DMASTB) & (0xFF << (dev ? 0 : 8)); + val32 |= (td << (dev ? 8 : 0)); + writel(val32, base + BK3710_DMASTB); + + val32 = readl(base + BK3710_DMARCVR) & (0xFF << (dev ? 0 : 8)); + val32 |= (tkw << (dev ? 8 : 0)); + writel(val32, base + BK3710_DMARCVR); + + /* Disable UDMA for Device */ + val16 = readw(base + BK3710_UDMACTL) & ~(1 << dev); + writew(val16, base + BK3710_UDMACTL); +} + +static void palm_bk3710_setpiomode(void __iomem *base, ide_drive_t *mate, + unsigned int dev, unsigned int cycletime, + unsigned int mode) +{ + u8 t2, t2i, t0; + u32 val32; + struct ide_timing *t; + + t = ide_timing_find_mode(XFER_PIO_0 + mode); + + /* PIO Data Setup */ + t0 = DIV_ROUND_UP(cycletime, ideclk_period); + t2 = DIV_ROUND_UP(t->active, ideclk_period); + + t2i = t0 - t2 - 1; + t2 -= 1; + + val32 = readl(base + BK3710_DATSTB) & (0xFF << (dev ? 0 : 8)); + val32 |= (t2 << (dev ? 8 : 0)); + writel(val32, base + BK3710_DATSTB); + + val32 = readl(base + BK3710_DATRCVR) & (0xFF << (dev ? 0 : 8)); + val32 |= (t2i << (dev ? 8 : 0)); + writel(val32, base + BK3710_DATRCVR); + + if (mate) { + u8 mode2 = mate->pio_mode - XFER_PIO_0; + + if (mode2 < mode) + mode = mode2; + } + + /* TASKFILE Setup */ + t0 = DIV_ROUND_UP(t->cyc8b, ideclk_period); + t2 = DIV_ROUND_UP(t->act8b, ideclk_period); + + t2i = t0 - t2 - 1; + t2 -= 1; + + val32 = readl(base + BK3710_REGSTB) & (0xFF << (dev ? 0 : 8)); + val32 |= (t2 << (dev ? 8 : 0)); + writel(val32, base + BK3710_REGSTB); + + val32 = readl(base + BK3710_REGRCVR) & (0xFF << (dev ? 0 : 8)); + val32 |= (t2i << (dev ? 8 : 0)); + writel(val32, base + BK3710_REGRCVR); +} + +static void palm_bk3710_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + int is_slave = drive->dn & 1; + void __iomem *base = (void __iomem *)hwif->dma_base; + const u8 xferspeed = drive->dma_mode; + + if (xferspeed >= XFER_UDMA_0) { + palm_bk3710_setudmamode(base, is_slave, + xferspeed - XFER_UDMA_0); + } else { + palm_bk3710_setdmamode(base, is_slave, + drive->id[ATA_ID_EIDE_DMA_MIN], + xferspeed); + } +} + +static void palm_bk3710_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned int cycle_time; + int is_slave = drive->dn & 1; + ide_drive_t *mate; + void __iomem *base = (void __iomem *)hwif->dma_base; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* + * Obtain the drive PIO data for tuning the Palm Chip registers + */ + cycle_time = ide_pio_cycle_time(drive, pio); + mate = ide_get_pair_dev(drive); + palm_bk3710_setpiomode(base, mate, is_slave, cycle_time, pio); +} + +static void palm_bk3710_chipinit(void __iomem *base) +{ + /* + * REVISIT: the ATA reset signal needs to be managed through a + * GPIO, which means it should come from platform_data. Until + * we get and use such information, we have to trust that things + * have been reset before we get here. + */ + + /* + * Program the IDETIMP Register Value based on the following assumptions + * + * (ATA_IDETIMP_IDEEN , ENABLE ) | + * (ATA_IDETIMP_PREPOST1 , DISABLE) | + * (ATA_IDETIMP_PREPOST0 , DISABLE) | + * + * DM6446 silicon rev 2.1 and earlier have no observed net benefit + * from enabling prefetch/postwrite. + */ + writew(BIT(15), base + BK3710_IDETIMP); + + /* + * UDMACTL Ultra-ATA DMA Control + * (ATA_UDMACTL_UDMAP1 , 0 ) | + * (ATA_UDMACTL_UDMAP0 , 0 ) + * + */ + writew(0, base + BK3710_UDMACTL); + + /* + * MISCCTL Miscellaneous Conrol Register + * (ATA_MISCCTL_HWNHLD1P , 1 cycle) + * (ATA_MISCCTL_HWNHLD0P , 1 cycle) + * (ATA_MISCCTL_TIMORIDE , 1) + */ + writel(0x001, base + BK3710_MISCCTL); + + /* + * IORDYTMP IORDY Timer for Primary Register + * (ATA_IORDYTMP_IORDYTMP , 0xffff ) + */ + writel(0xFFFF, base + BK3710_IORDYTMP); + + /* + * Configure BMISP Register + * (ATA_BMISP_DMAEN1 , DISABLE ) | + * (ATA_BMISP_DMAEN0 , DISABLE ) | + * (ATA_BMISP_IORDYINT , CLEAR) | + * (ATA_BMISP_INTRSTAT , CLEAR) | + * (ATA_BMISP_DMAERROR , CLEAR) + */ + writew(0, base + BK3710_BMISP); + + palm_bk3710_setpiomode(base, NULL, 0, 600, 0); + palm_bk3710_setpiomode(base, NULL, 1, 600, 0); +} + +static u8 palm_bk3710_cable_detect(ide_hwif_t *hwif) +{ + return ATA_CBL_PATA80; +} + +static int palm_bk3710_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); + + if (ide_allocate_dma_engine(hwif)) + return -1; + + hwif->dma_base = hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET; + + return 0; +} + +static const struct ide_port_ops palm_bk3710_ports_ops = { + .set_pio_mode = palm_bk3710_set_pio_mode, + .set_dma_mode = palm_bk3710_set_dma_mode, + .cable_detect = palm_bk3710_cable_detect, +}; + +static struct ide_port_info palm_bk3710_port_info __initdata = { + .init_dma = palm_bk3710_init_dma, + .port_ops = &palm_bk3710_ports_ops, + .dma_ops = &sff_dma_ops, + .host_flags = IDE_HFLAG_MMIO, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .chipset = ide_palm3710, +}; + +static int __init palm_bk3710_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct resource *mem, *irq; + void __iomem *base; + unsigned long rate, mem_size; + int i, rc; + struct ide_hw hw, *hws[] = { &hw }; + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) + return -ENODEV; + + clk_enable(clk); + rate = clk_get_rate(clk); + if (!rate) + return -EINVAL; + + /* NOTE: round *down* to meet minimum timings; we count in clocks */ + ideclk_period = 1000000000UL / rate; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (mem == NULL) { + printk(KERN_ERR "failed to get memory region resource\n"); + return -ENODEV; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq == NULL) { + printk(KERN_ERR "failed to get IRQ resource\n"); + return -ENODEV; + } + + mem_size = resource_size(mem); + if (request_mem_region(mem->start, mem_size, "palm_bk3710") == NULL) { + printk(KERN_ERR "failed to request memory region\n"); + return -EBUSY; + } + + base = ioremap(mem->start, mem_size); + if (!base) { + printk(KERN_ERR "failed to map IO memory\n"); + release_mem_region(mem->start, mem_size); + return -ENOMEM; + } + + /* Configure the Palm Chip controller */ + palm_bk3710_chipinit(base); + + memset(&hw, 0, sizeof(hw)); + for (i = 0; i < IDE_NR_PORTS - 2; i++) + hw.io_ports_array[i] = (unsigned long) + (base + IDE_PALM_ATA_PRI_REG_OFFSET + i); + hw.io_ports.ctl_addr = (unsigned long) + (base + IDE_PALM_ATA_PRI_CTL_OFFSET); + hw.irq = irq->start; + hw.dev = &pdev->dev; + + palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 : + ATA_UDMA5; + + /* Register the IDE interface with Linux */ + rc = ide_host_add(&palm_bk3710_port_info, hws, 1, NULL); + if (rc) + goto out; + + return 0; +out: + printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n"); + return rc; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:palm_bk3710"); + +static struct platform_driver platform_bk_driver = { + .driver = { + .name = "palm_bk3710", + }, +}; + +static int __init palm_bk3710_init(void) +{ + return platform_driver_probe(&platform_bk_driver, palm_bk3710_probe); +} + +module_init(palm_bk3710_init); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pdc202xx_new.c b/drivers/ide/pdc202xx_new.c new file mode 100644 index 000000000..4fcafb912 --- /dev/null +++ b/drivers/ide/pdc202xx_new.c @@ -0,0 +1,557 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Promise TX2/TX4/TX2000/133 IDE driver + * + * Split from: + * linux/drivers/ide/pdc202xx.c Version 0.35 Mar. 30, 2002 + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2005-2007 MontaVista Software, Inc. + * Portions Copyright (C) 1999 Promise Technology, Inc. + * Author: Frank Tiernan (frankt@promise.com) + * Released under terms of General Public License + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/ktime.h> + +#include <asm/io.h> + +#ifdef CONFIG_PPC_PMAC +#include <asm/prom.h> +#endif + +#define DRV_NAME "pdc202xx_new" + +#undef DEBUG + +#ifdef DEBUG +#define DBG(fmt, args...) printk("%s: " fmt, __func__, ## args) +#else +#define DBG(fmt, args...) +#endif + +static u8 max_dma_rate(struct pci_dev *pdev) +{ + u8 mode; + + switch(pdev->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; + } + + return mode; +} + +/** + * get_indexed_reg - Get indexed register + * @hwif: for the port address + * @index: index of the indexed register + */ +static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index) +{ + u8 value; + + outb(index, hwif->dma_base + 1); + value = inb(hwif->dma_base + 3); + + DBG("index[%02X] value[%02X]\n", index, value); + return value; +} + +/** + * set_indexed_reg - Set indexed register + * @hwif: for the port address + * @index: index of the indexed register + */ +static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value) +{ + outb(index, hwif->dma_base + 1); + outb(value, hwif->dma_base + 3); + DBG("index[%02X] value[%02X]\n", index, value); +} + +/* + * ATA Timing Tables based on 133 MHz PLL output clock. + * + * If the PLL outputs 100 MHz clock, the ASIC hardware will set + * the timing registers automatically when "set features" command is + * issued to the device. However, if the PLL output clock is 133 MHz, + * the following tables must be used. + */ +static struct pio_timing { + u8 reg0c, reg0d, reg13; +} pio_timings [] = { + { 0xfb, 0x2b, 0xac }, /* PIO mode 0, IORDY off, Prefetch off */ + { 0x46, 0x29, 0xa4 }, /* PIO mode 1, IORDY off, Prefetch off */ + { 0x23, 0x26, 0x64 }, /* PIO mode 2, IORDY off, Prefetch off */ + { 0x27, 0x0d, 0x35 }, /* PIO mode 3, IORDY on, Prefetch off */ + { 0x23, 0x09, 0x25 }, /* PIO mode 4, IORDY on, Prefetch off */ +}; + +static struct mwdma_timing { + u8 reg0e, reg0f; +} mwdma_timings [] = { + { 0xdf, 0x5f }, /* MWDMA mode 0 */ + { 0x6b, 0x27 }, /* MWDMA mode 1 */ + { 0x69, 0x25 }, /* MWDMA mode 2 */ +}; + +static struct udma_timing { + u8 reg10, reg11, reg12; +} udma_timings [] = { + { 0x4a, 0x0f, 0xd5 }, /* UDMA mode 0 */ + { 0x3a, 0x0a, 0xd0 }, /* UDMA mode 1 */ + { 0x2a, 0x07, 0xcd }, /* UDMA mode 2 */ + { 0x1a, 0x05, 0xcd }, /* UDMA mode 3 */ + { 0x1a, 0x03, 0xcd }, /* UDMA mode 4 */ + { 0x1a, 0x02, 0xcb }, /* UDMA mode 5 */ + { 0x1a, 0x01, 0xcb }, /* UDMA mode 6 */ +}; + +static void pdcnew_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 adj = (drive->dn & 1) ? 0x08 : 0x00; + const u8 speed = drive->dma_mode; + + /* + * IDE core issues SETFEATURES_XFER to the drive first (thanks to + * IDE_HFLAG_POST_SET_MODE in ->host_flags). PDC202xx hardware will + * automatically set the timing registers based on 100 MHz PLL output. + * + * As we set up the PLL to output 133 MHz for UltraDMA/133 capable + * chips, we must override the default register settings... + */ + if (max_dma_rate(dev) == 4) { + u8 mode = speed & 0x07; + + if (speed >= XFER_UDMA_0) { + set_indexed_reg(hwif, 0x10 + adj, + udma_timings[mode].reg10); + set_indexed_reg(hwif, 0x11 + adj, + udma_timings[mode].reg11); + set_indexed_reg(hwif, 0x12 + adj, + udma_timings[mode].reg12); + } else { + set_indexed_reg(hwif, 0x0e + adj, + mwdma_timings[mode].reg0e); + set_indexed_reg(hwif, 0x0f + adj, + mwdma_timings[mode].reg0f); + } + } else if (speed == XFER_UDMA_2) { + /* Set tHOLD bit to 0 if using UDMA mode 2 */ + u8 tmp = get_indexed_reg(hwif, 0x10 + adj); + + set_indexed_reg(hwif, 0x10 + adj, tmp & 0x7f); + } +} + +static void pdcnew_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 adj = (drive->dn & 1) ? 0x08 : 0x00; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + if (max_dma_rate(dev) == 4) { + set_indexed_reg(hwif, 0x0c + adj, pio_timings[pio].reg0c); + set_indexed_reg(hwif, 0x0d + adj, pio_timings[pio].reg0d); + set_indexed_reg(hwif, 0x13 + adj, pio_timings[pio].reg13); + } +} + +static u8 pdcnew_cable_detect(ide_hwif_t *hwif) +{ + if (get_indexed_reg(hwif, 0x0b) & 0x04) + return ATA_CBL_PATA40; + else + return ATA_CBL_PATA80; +} + +static void pdcnew_reset(ide_drive_t *drive) +{ + /* + * Deleted this because it is redundant from the caller. + */ + printk(KERN_WARNING "pdc202xx_new: %s channel reset.\n", + drive->hwif->channel ? "Secondary" : "Primary"); +} + +/** + * read_counter - Read the byte count registers + * @dma_base: for the port address + */ +static long read_counter(u32 dma_base) +{ + u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08; + u8 cnt0, cnt1, cnt2, cnt3; + long count = 0, last; + int retry = 3; + + do { + last = count; + + /* Read the current count */ + outb(0x20, pri_dma_base + 0x01); + cnt0 = inb(pri_dma_base + 0x03); + outb(0x21, pri_dma_base + 0x01); + cnt1 = inb(pri_dma_base + 0x03); + outb(0x20, sec_dma_base + 0x01); + cnt2 = inb(sec_dma_base + 0x03); + outb(0x21, sec_dma_base + 0x01); + cnt3 = inb(sec_dma_base + 0x03); + + count = (cnt3 << 23) | (cnt2 << 15) | (cnt1 << 8) | cnt0; + + /* + * The 30-bit decrementing counter is read in 4 pieces. + * Incorrect value may be read when the most significant bytes + * are changing... + */ + } while (retry-- && (((last ^ count) & 0x3fff8000) || last < count)); + + DBG("cnt0[%02X] cnt1[%02X] cnt2[%02X] cnt3[%02X]\n", + cnt0, cnt1, cnt2, cnt3); + + return count; +} + +/** + * detect_pll_input_clock - Detect the PLL input clock in Hz. + * @dma_base: for the port address + * E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock. + */ +static long detect_pll_input_clock(unsigned long dma_base) +{ + ktime_t start_time, end_time; + long start_count, end_count; + long pll_input, usec_elapsed; + u8 scr1; + + start_count = read_counter(dma_base); + start_time = ktime_get(); + + /* Start the test mode */ + outb(0x01, dma_base + 0x01); + scr1 = inb(dma_base + 0x03); + DBG("scr1[%02X]\n", scr1); + outb(scr1 | 0x40, dma_base + 0x03); + + /* Let the counter run for 10 ms. */ + mdelay(10); + + end_count = read_counter(dma_base); + end_time = ktime_get(); + + /* Stop the test mode */ + outb(0x01, dma_base + 0x01); + scr1 = inb(dma_base + 0x03); + DBG("scr1[%02X]\n", scr1); + outb(scr1 & ~0x40, dma_base + 0x03); + + /* + * Calculate the input clock in Hz + * (the clock counter is 30 bit wide and counts down) + */ + usec_elapsed = ktime_us_delta(end_time, start_time); + pll_input = ((start_count - end_count) & 0x3fffffff) / 10 * + (10000000 / usec_elapsed); + + DBG("start[%ld] end[%ld]\n", start_count, end_count); + + return pll_input; +} + +#ifdef CONFIG_PPC_PMAC +static void apple_kiwi_init(struct pci_dev *pdev) +{ + struct device_node *np = pci_device_to_OF_node(pdev); + u8 conf; + + if (np == NULL || !of_device_is_compatible(np, "kiwi-root")) + return; + + if (pdev->revision >= 0x03) { + /* Setup chip magic config stuff (from darwin) */ + pci_read_config_byte (pdev, 0x40, &conf); + pci_write_config_byte(pdev, 0x40, (conf | 0x01)); + } +} +#endif /* CONFIG_PPC_PMAC */ + +static int init_chipset_pdcnew(struct pci_dev *dev) +{ + const char *name = DRV_NAME; + unsigned long dma_base = pci_resource_start(dev, 4); + unsigned long sec_dma_base = dma_base + 0x08; + long pll_input, pll_output, ratio; + int f, r; + u8 pll_ctl0, pll_ctl1; + + if (dma_base == 0) + return -EFAULT; + +#ifdef CONFIG_PPC_PMAC + apple_kiwi_init(dev); +#endif + + /* Calculate the required PLL output frequency */ + switch(max_dma_rate(dev)) { + case 4: /* it's 133 MHz for Ultra133 chips */ + pll_output = 133333333; + break; + case 3: /* and 100 MHz for Ultra100 chips */ + default: + pll_output = 100000000; + break; + } + + /* + * Detect PLL input clock. + * On some systems, where PCI bus is running at non-standard clock rate + * (e.g. 25 or 40 MHz), we have to adjust the cycle time. + * PDC20268 and newer chips employ PLL circuit to help correct timing + * registers setting. + */ + pll_input = detect_pll_input_clock(dma_base); + printk(KERN_INFO "%s %s: PLL input clock is %ld kHz\n", + name, pci_name(dev), pll_input / 1000); + + /* Sanity check */ + if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) { + printk(KERN_ERR "%s %s: Bad PLL input clock %ld Hz, giving up!" + "\n", name, pci_name(dev), pll_input); + goto out; + } + +#ifdef DEBUG + DBG("pll_output is %ld Hz\n", pll_output); + + /* Show the current clock value of PLL control register + * (maybe already configured by the BIOS) + */ + outb(0x02, sec_dma_base + 0x01); + pll_ctl0 = inb(sec_dma_base + 0x03); + outb(0x03, sec_dma_base + 0x01); + pll_ctl1 = inb(sec_dma_base + 0x03); + + DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1); +#endif + + /* + * Calculate the ratio of F, R and NO + * POUT = (F + 2) / (( R + 2) * NO) + */ + ratio = pll_output / (pll_input / 1000); + if (ratio < 8600L) { /* 8.6x */ + /* Using NO = 0x01, R = 0x0d */ + r = 0x0d; + } else if (ratio < 12900L) { /* 12.9x */ + /* Using NO = 0x01, R = 0x08 */ + r = 0x08; + } else if (ratio < 16100L) { /* 16.1x */ + /* Using NO = 0x01, R = 0x06 */ + r = 0x06; + } else if (ratio < 64000L) { /* 64x */ + r = 0x00; + } else { + /* Invalid ratio */ + printk(KERN_ERR "%s %s: Bad ratio %ld, giving up!\n", + name, pci_name(dev), ratio); + goto out; + } + + f = (ratio * (r + 2)) / 1000 - 2; + + DBG("F[%d] R[%d] ratio*1000[%ld]\n", f, r, ratio); + + if (unlikely(f < 0 || f > 127)) { + /* Invalid F */ + printk(KERN_ERR "%s %s: F[%d] invalid!\n", + name, pci_name(dev), f); + goto out; + } + + pll_ctl0 = (u8) f; + pll_ctl1 = (u8) r; + + DBG("Writing pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1); + + outb(0x02, sec_dma_base + 0x01); + outb(pll_ctl0, sec_dma_base + 0x03); + outb(0x03, sec_dma_base + 0x01); + outb(pll_ctl1, sec_dma_base + 0x03); + + /* Wait the PLL circuit to be stable */ + mdelay(30); + +#ifdef DEBUG + /* + * Show the current clock value of PLL control register + */ + outb(0x02, sec_dma_base + 0x01); + pll_ctl0 = inb(sec_dma_base + 0x03); + outb(0x03, sec_dma_base + 0x01); + pll_ctl1 = inb(sec_dma_base + 0x03); + + DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1); +#endif + + out: + return 0; +} + +static struct pci_dev *pdc20270_get_dev2(struct pci_dev *dev) +{ + struct pci_dev *dev2; + + dev2 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn) + 1, + PCI_FUNC(dev->devfn))); + + if (dev2 && + dev2->vendor == dev->vendor && + dev2->device == dev->device) { + + if (dev2->irq != dev->irq) { + dev2->irq = dev->irq; + printk(KERN_INFO DRV_NAME " %s: PCI config space " + "interrupt fixed\n", pci_name(dev)); + } + + return dev2; + } + + return NULL; +} + +static const struct ide_port_ops pdcnew_port_ops = { + .set_pio_mode = pdcnew_set_pio_mode, + .set_dma_mode = pdcnew_set_dma_mode, + .resetproc = pdcnew_reset, + .cable_detect = pdcnew_cable_detect, +}; + +#define DECLARE_PDCNEW_DEV(udma) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_pdcnew, \ + .port_ops = &pdcnew_port_ops, \ + .host_flags = IDE_HFLAG_POST_SET_MODE | \ + IDE_HFLAG_ERROR_STOPS_FIFO | \ + IDE_HFLAG_OFF_BOARD, \ + .pio_mask = ATA_PIO4, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = udma, \ + } + +static const struct ide_port_info pdcnew_chipsets[] = { + /* 0: PDC202{68,70} */ DECLARE_PDCNEW_DEV(ATA_UDMA5), + /* 1: PDC202{69,71,75,76,77} */ DECLARE_PDCNEW_DEV(ATA_UDMA6), +}; + +/** + * 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 pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + const struct ide_port_info *d = &pdcnew_chipsets[id->driver_data]; + struct pci_dev *bridge = dev->bus->self; + + if (dev->device == PCI_DEVICE_ID_PROMISE_20270 && bridge && + bridge->vendor == PCI_VENDOR_ID_DEC && + bridge->device == PCI_DEVICE_ID_DEC_21150) { + struct pci_dev *dev2; + + if (PCI_SLOT(dev->devfn) & 2) + return -ENODEV; + + dev2 = pdc20270_get_dev2(dev); + + if (dev2) { + int ret = ide_pci_init_two(dev, dev2, d, NULL); + if (ret < 0) + pci_dev_put(dev2); + return ret; + } + } + + if (dev->device == PCI_DEVICE_ID_PROMISE_20276 && bridge && + bridge->vendor == PCI_VENDOR_ID_INTEL && + (bridge->device == PCI_DEVICE_ID_INTEL_I960 || + bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) { + printk(KERN_INFO DRV_NAME " %s: attached to I2O RAID controller," + " skipping\n", pci_name(dev)); + return -ENODEV; + } + + return ide_pci_init_one(dev, d, NULL); +} + +static void pdc202new_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + + ide_pci_remove(dev); + pci_dev_put(dev2); +} + +static const struct pci_device_id pdc202new_pci_tbl[] = { + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), 0 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 0 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 1 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl); + +static struct pci_driver pdc202new_pci_driver = { + .name = "Promise_IDE", + .id_table = pdc202new_pci_tbl, + .probe = pdc202new_init_one, + .remove = pdc202new_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init pdc202new_ide_init(void) +{ + return ide_pci_register_driver(&pdc202new_pci_driver); +} + +static void __exit pdc202new_ide_exit(void) +{ + pci_unregister_driver(&pdc202new_pci_driver); +} + +module_init(pdc202new_ide_init); +module_exit(pdc202new_ide_exit); + +MODULE_AUTHOR("Andre Hedrick, Frank Tiernan"); +MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pdc202xx_old.c b/drivers/ide/pdc202xx_old.c new file mode 100644 index 000000000..5248ac064 --- /dev/null +++ b/drivers/ide/pdc202xx_old.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2006-2007, 2009 MontaVista Software, Inc. + * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz + * + * Portions Copyright (C) 1999 Promise Technology, Inc. + * Author: Frank Tiernan (frankt@promise.com) + * Released under terms of General Public License + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/blkdev.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> + +#include <asm/io.h> + +#define DRV_NAME "pdc202xx_old" + +static void pdc202xx_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 drive_pci = 0x60 + (drive->dn << 2); + const u8 speed = drive->dma_mode; + + u8 AP = 0, BP = 0, CP = 0; + u8 TA = 0, TB = 0, TC = 0; + + pci_read_config_byte(dev, drive_pci, &AP); + pci_read_config_byte(dev, drive_pci + 1, &BP); + pci_read_config_byte(dev, drive_pci + 2, &CP); + + switch(speed) { + 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: TB = 0xE0; TC = 0x0F; 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) { + /* + * preserve SYNC_INT / ERDDY_EN bits while clearing + * Prefetch_EN / IORDY_EN / PA[3:0] bits of register A + */ + AP &= ~0x3f; + if (ide_pio_need_iordy(drive, speed - XFER_PIO_0)) + AP |= 0x20; /* set IORDY_EN bit */ + if (drive->media == ide_disk) + AP |= 0x10; /* set Prefetch_EN bit */ + /* clear PB[4:0] bits of register B */ + BP &= ~0x1f; + pci_write_config_byte(dev, drive_pci, AP | TA); + pci_write_config_byte(dev, drive_pci + 1, BP | TB); + } else { + /* clear MB[2:0] bits of register B */ + BP &= ~0xe0; + /* clear MC[3:0] bits of register C */ + CP &= ~0x0f; + pci_write_config_byte(dev, drive_pci + 1, BP | TB); + pci_write_config_byte(dev, drive_pci + 2, CP | TC); + } +} + +static void pdc202xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + pdc202xx_set_mode(hwif, drive); +} + +static int pdc202xx_test_irq(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long high_16 = pci_resource_start(dev, 4); + u8 sc1d = inb(high_16 + 0x1d); + + if (hwif->channel) { + /* + * bit 7: error, bit 6: interrupting, + * bit 5: FIFO full, bit 4: FIFO empty + */ + return (sc1d & 0x40) ? 1 : 0; + } else { + /* + * bit 3: error, bit 2: interrupting, + * bit 1: FIFO full, bit 0: FIFO empty + */ + return (sc1d & 0x04) ? 1 : 0; + } +} + +static u8 pdc2026x_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u16 CIS, mask = hwif->channel ? (1 << 11) : (1 << 10); + + pci_read_config_word(dev, 0x50, &CIS); + + return (CIS & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +/* + * Set the control register to use the 66MHz system + * clock for UDMA 3/4/5 mode operation when necessary. + * + * FIXME: this register is shared by both channels, some locking is needed + * + * 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->extra_base + 0x01; + u8 clock = inb(clock_reg); + + outb(clock | (hwif->channel ? 0x08 : 0x02), clock_reg); +} + +static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif) +{ + unsigned long clock_reg = hwif->extra_base + 0x01; + u8 clock = inb(clock_reg); + + outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg); +} + +static void pdc2026x_init_hwif(ide_hwif_t *hwif) +{ + pdc_old_disable_66MHz_clock(hwif); +} + +static void pdc202xx_dma_start(ide_drive_t *drive) +{ + if (drive->current_speed > XFER_UDMA_2) + pdc_old_enable_66MHz_clock(drive->hwif); + if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { + ide_hwif_t *hwif = drive->hwif; + struct request *rq = hwif->rq; + unsigned long high_16 = hwif->extra_base - 16; + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); + u32 word_count = 0; + u8 clock = inb(high_16 + 0x11); + + outb(clock | (hwif->channel ? 0x08 : 0x02), high_16 + 0x11); + word_count = (blk_rq_sectors(rq) << 8); + word_count = (rq_data_dir(rq) == READ) ? + word_count | 0x05000000 : + word_count | 0x06000000; + outl(word_count, atapi_reg); + } + ide_dma_start(drive); +} + +static int pdc202xx_dma_end(ide_drive_t *drive) +{ + if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) { + ide_hwif_t *hwif = drive->hwif; + unsigned long high_16 = hwif->extra_base - 16; + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); + u8 clock = 0; + + outl(0, atapi_reg); /* zero out extra */ + clock = inb(high_16 + 0x11); + 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 init_chipset_pdc202xx(struct pci_dev *dev) +{ + unsigned long dmabase = pci_resource_start(dev, 4); + u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0; + + if (dmabase == 0) + goto out; + + udma_speed_flag = inb(dmabase | 0x1f); + primary_mode = inb(dmabase | 0x1a); + secondary_mode = inb(dmabase | 0x1b); + printk(KERN_INFO "%s: (U)DMA Burst Bit %sABLED " \ + "Primary %s Mode " \ + "Secondary %s Mode.\n", pci_name(dev), + (udma_speed_flag & 1) ? "EN" : "DIS", + (primary_mode & 1) ? "MASTER" : "PCI", + (secondary_mode & 1) ? "MASTER" : "PCI" ); + + if (!(udma_speed_flag & 1)) { + printk(KERN_INFO "%s: FORCING BURST BIT 0x%02x->0x%02x ", + pci_name(dev), udma_speed_flag, + (udma_speed_flag|1)); + outb(udma_speed_flag | 1, dmabase | 0x1f); + printk("%sACTIVE\n", (inb(dmabase | 0x1f) & 1) ? "" : "IN"); + } +out: + return 0; +} + +static void pdc202ata4_fixup_irq(struct pci_dev *dev, const char *name) +{ + 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 %s: PCI config space interrupt " + "mirror fixed\n", name, pci_name(dev)); + } + } +} + +#define IDE_HFLAGS_PDC202XX \ + (IDE_HFLAG_ERROR_STOPS_FIFO | \ + IDE_HFLAG_OFF_BOARD) + +static const struct ide_port_ops pdc20246_port_ops = { + .set_pio_mode = pdc202xx_set_pio_mode, + .set_dma_mode = pdc202xx_set_mode, + .test_irq = pdc202xx_test_irq, +}; + +static const struct ide_port_ops pdc2026x_port_ops = { + .set_pio_mode = pdc202xx_set_pio_mode, + .set_dma_mode = pdc202xx_set_mode, + .test_irq = pdc202xx_test_irq, + .cable_detect = pdc2026x_cable_detect, +}; + +static const struct ide_dma_ops pdc2026x_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = pdc202xx_dma_start, + .dma_end = pdc202xx_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +#define DECLARE_PDC2026X_DEV(udma, sectors) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_pdc202xx, \ + .init_hwif = pdc2026x_init_hwif, \ + .port_ops = &pdc2026x_port_ops, \ + .dma_ops = &pdc2026x_dma_ops, \ + .host_flags = IDE_HFLAGS_PDC202XX, \ + .pio_mask = ATA_PIO4, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = udma, \ + .max_sectors = sectors, \ + } + +static const struct ide_port_info pdc202xx_chipsets[] = { + { /* 0: PDC20246 */ + .name = DRV_NAME, + .init_chipset = init_chipset_pdc202xx, + .port_ops = &pdc20246_port_ops, + .dma_ops = &sff_dma_ops, + .host_flags = IDE_HFLAGS_PDC202XX, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, + }, + + /* 1: PDC2026{2,3} */ + DECLARE_PDC2026X_DEV(ATA_UDMA4, 0), + /* 2: PDC2026{5,7}: UDMA5, limit LBA48 requests to 256 sectors */ + DECLARE_PDC2026X_DEV(ATA_UDMA5, 256), +}; + +/** + * 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 pdc202xx_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + const struct ide_port_info *d; + u8 idx = id->driver_data; + + d = &pdc202xx_chipsets[idx]; + + if (idx < 2) + pdc202ata4_fixup_irq(dev, d->name); + + if (dev->vendor == PCI_DEVICE_ID_PROMISE_20265) { + struct pci_dev *bridge = dev->bus->self; + + if (bridge && + bridge->vendor == PCI_VENDOR_ID_INTEL && + (bridge->device == PCI_DEVICE_ID_INTEL_I960 || + bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) { + printk(KERN_INFO DRV_NAME " %s: skipping Promise " + "PDC20265 attached to I2O RAID controller\n", + pci_name(dev)); + return -ENODEV; + } + } + + return ide_pci_init_one(dev, d, NULL); +} + +static const struct pci_device_id pdc202xx_pci_tbl[] = { + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 }, + { PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl); + +static struct pci_driver pdc202xx_pci_driver = { + .name = "Promise_Old_IDE", + .id_table = pdc202xx_pci_tbl, + .probe = pdc202xx_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init pdc202xx_ide_init(void) +{ + return ide_pci_register_driver(&pdc202xx_pci_driver); +} + +static void __exit pdc202xx_ide_exit(void) +{ + pci_unregister_driver(&pdc202xx_pci_driver); +} + +module_init(pdc202xx_ide_init); +module_exit(pdc202xx_ide_exit); + +MODULE_AUTHOR("Andre Hedrick, Frank Tiernan, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("PCI driver module for older Promise IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/piix.c b/drivers/ide/piix.c new file mode 100644 index 000000000..a671cead6 --- /dev/null +++ b/drivers/ide/piix.c @@ -0,0 +1,476 @@ +/* + * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat + * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com> + * + * May be copied or modified under the terms of the GNU General Public License + * + * Documentation: + * + * Publicly available from Intel web site. Errata documentation + * is also publicly 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/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "piix" + +static int no_piix_dma; + +/** + * piix_set_pio_mode - set host controller for PIO mode + * @port: port + * @drive: drive + * + * Set the interface PIO mode based upon the settings done by AMI BIOS. + */ + +static void piix_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int is_slave = drive->dn & 1; + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + u8 slave_data; + static DEFINE_SPINLOCK(tune_lock); + int control = 0; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* ISP RTC */ + static const u8 timings[][2]= { + { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + /* + * Master vs slave is synchronized above us but the slave register is + * shared by the two hwifs so the corner case of two slave timeouts in + * parallel must be locked. + */ + spin_lock_irqsave(&tune_lock, flags); + pci_read_config_word(dev, master_port, &master_data); + + if (pio > 1) + control |= 1; /* Programmable timing on */ + if (drive->media == ide_disk) + control |= 4; /* Prefetch, post write */ + if (ide_pio_need_iordy(drive, pio)) + control |= 2; /* IORDY */ + if (is_slave) { + master_data |= 0x4000; + master_data &= ~0x0070; + if (pio > 1) { + /* Set PPE, IE and TIME */ + master_data |= control << 4; + } + pci_read_config_byte(dev, slave_port, &slave_data); + slave_data &= hwif->channel ? 0x0f : 0xf0; + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << + (hwif->channel ? 4 : 0); + } else { + master_data &= ~0x3307; + if (pio > 1) { + /* enable PPE, IE and TIME */ + master_data |= control; + } + 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(&tune_lock, flags); +} + +/** + * piix_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Set a PIIX host controller to the desired DMA mode. This involves + * programming the right timing data into the PCI configuration space. + */ + +static void piix_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 maslave = hwif->channel ? 0x42 : 0x40; + 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; + const u8 speed = drive->dma_mode; + + 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); + + if (speed >= XFER_UDMA_0) { + u8 udma = speed - XFER_UDMA_0; + + u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4); + + 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 { + const u8 mwdma_to_pio[] = { 0, 3, 4 }; + + 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); + + if (speed >= XFER_MW_DMA_0) + drive->pio_mode = + mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0; + else + drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */ + + piix_set_pio_mode(hwif, drive); + } +} + +/** + * init_chipset_ich - set up the ICH chipset + * @dev: PCI device to set up + * + * Initialize the PCI device as required. For the ICH this turns + * out to be nice and simple. + */ + +static int init_chipset_ich(struct pci_dev *dev) +{ + u32 extra = 0; + + pci_read_config_dword(dev, 0x54, &extra); + pci_write_config_dword(dev, 0x54, extra | 0x400); + + return 0; +} + +/** + * ich_clear_irq - clear BMDMA status + * @drive: IDE drive + * + * ICHx contollers set DMA INTR no matter DMA or PIO. + * BMDMA status might need to be cleared even for + * PIO interrupts to prevent spurious/lost IRQ. + */ +static void ich_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat; + + /* + * ide_dma_end() needs BMDMA status for error checking. + * So, skip clearing BMDMA status here and leave it + * to ide_dma_end() if this is DMA interrupt. + */ + if (drive->waiting_for_dma || hwif->dma_base == 0) + return; + + /* clear the INTR & ERROR bits */ + dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + /* Should we force the bit as well ? */ + outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS); +} + +struct ich_laptop { + u16 device; + u16 subvendor; + u16 subdevice; +}; + +/* + * List of laptops that use short cables rather than 80 wire + */ + +static const struct ich_laptop ich_laptop[] = { + /* devid, subvendor, subdev */ + { 0x27DF, 0x1025, 0x0102 }, /* ICH7 on Acer 5602aWLMi */ + { 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */ + { 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */ + { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ + { 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */ + { 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */ + { 0x24CA, 0x1025, 0x0061 }, /* ICH4 on Acer Aspire 2023WLMi */ + { 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */ + { 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */ + { 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */ + { 0x27df, 0x104d, 0x900e }, /* ICH7 on Sony TZ-90 */ + /* end marker */ + { 0, } +}; + +static u8 piix_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + const struct ich_laptop *lap = &ich_laptop[0]; + u8 reg54h = 0, mask = hwif->channel ? 0xc0 : 0x30; + + /* check for specials */ + while (lap->device) { + if (lap->device == pdev->device && + lap->subvendor == pdev->subsystem_vendor && + lap->subdevice == pdev->subsystem_device) { + return ATA_CBL_PATA40_SHORT; + } + lap++; + } + + pci_read_config_byte(pdev, 0x54, ®54h); + + return (reg54h & mask) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; +} + +/** + * 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 init_hwif_piix(ide_hwif_t *hwif) +{ + if (!hwif->dma_base) + return; + + if (no_piix_dma) + hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0; +} + +static const struct ide_port_ops piix_port_ops = { + .set_pio_mode = piix_set_pio_mode, + .set_dma_mode = piix_set_dma_mode, + .cable_detect = piix_cable_detect, +}; + +static const struct ide_port_ops ich_port_ops = { + .set_pio_mode = piix_set_pio_mode, + .set_dma_mode = piix_set_dma_mode, + .clear_irq = ich_clear_irq, + .cable_detect = piix_cable_detect, +}; + +#define DECLARE_PIIX_DEV(udma) \ + { \ + .name = DRV_NAME, \ + .init_hwif = init_hwif_piix, \ + .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ + .port_ops = &piix_port_ops, \ + .pio_mask = ATA_PIO4, \ + .swdma_mask = ATA_SWDMA2_ONLY, \ + .mwdma_mask = ATA_MWDMA12_ONLY, \ + .udma_mask = udma, \ + } + +#define DECLARE_ICH_DEV(mwdma, udma) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_ich, \ + .init_hwif = init_hwif_piix, \ + .enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \ + .port_ops = &ich_port_ops, \ + .pio_mask = ATA_PIO4, \ + .swdma_mask = ATA_SWDMA2_ONLY, \ + .mwdma_mask = mwdma, \ + .udma_mask = udma, \ + } + +static const struct ide_port_info piix_pci_info[] = { + /* 0: MPIIX */ + { /* + * MPIIX actually has only a single IDE channel mapped to + * the primary or secondary ports depending on the value + * of the bit 14 of the IDETIM register at offset 0x6c + */ + .name = DRV_NAME, + .enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}}, + .host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO4, + /* This is a painful system best to let it self tune for now */ + }, + /* 1: PIIXa/PIIXb/PIIX3 */ + DECLARE_PIIX_DEV(0x00), /* no udma */ + /* 2: PIIX4 */ + DECLARE_PIIX_DEV(ATA_UDMA2), + /* 3: ICH0 */ + DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA2), + /* 4: ICH */ + DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA4), + /* 5: PIIX4 */ + DECLARE_PIIX_DEV(ATA_UDMA4), + /* 6: ICH[2-6]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */ + DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA5), + /* 7: ICH7/7-R, no MWDMA1 */ + DECLARE_ICH_DEV(ATA_MWDMA2_ONLY, ATA_UDMA5), +}; + +/** + * 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 piix_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL); +} + +/** + * 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 piix_check_450nx(void) +{ + struct pci_dev *pdev = NULL; + u16 cfg; + while((pdev=pci_get_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_word(pdev, 0x41, &cfg); + /* Only on the original revision: IDE DMA can hang */ + if (pdev->revision == 0x00) + no_piix_dma = 1; + /* On all revisions below 5 PXB bus lock must be disabled for IDE */ + else if (cfg & (1<<14) && pdev->revision < 5) + no_piix_dma = 2; + } + if(no_piix_dma) + printk(KERN_WARNING DRV_NAME ": 450NX errata present, disabling IDE DMA.\n"); + if(no_piix_dma == 2) + printk(KERN_WARNING DRV_NAME ": A BIOS update may resolve this.\n"); +} + +static const struct pci_device_id piix_pci_tbl[] = { + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 0 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 1 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 3 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 4 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 5 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 2 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 6 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 6 }, +#endif + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 7 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 6 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 7 }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 6 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, piix_pci_tbl); + +static struct pci_driver piix_pci_driver = { + .name = "PIIX_IDE", + .id_table = piix_pci_tbl, + .probe = piix_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init piix_ide_init(void) +{ + piix_check_450nx(); + return ide_pci_register_driver(&piix_pci_driver); +} + +static void __exit piix_ide_exit(void) +{ + pci_unregister_driver(&piix_pci_driver); +} + +module_init(piix_ide_init); +module_exit(piix_ide_exit); + +MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz"); +MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pmac.c b/drivers/ide/pmac.c new file mode 100644 index 000000000..ea0b064b5 --- /dev/null +++ b/drivers/ide/pmac.c @@ -0,0 +1,1703 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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 + * Copyright (C) 2007-2008 Bartlomiej Zolnierkiewicz + * + * 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/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/ide.h> +#include <linux/notifier.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/pci.h> +#include <linux/adb.h> +#include <linux/pmu.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> + +#include <asm/prom.h> +#include <asm/io.h> +#include <asm/dbdma.h> +#include <asm/ide.h> +#include <asm/machdep.h> +#include <asm/pmac_feature.h> +#include <asm/sections.h> +#include <asm/irq.h> +#include <asm/mediabay.h> + +#define DRV_NAME "ide-pmac" + +#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 broken_dma : 1; + unsigned broken_dma_warn : 1; + struct device_node* node; + struct macio_dev *mdev; + u32 timings[4]; + volatile u32 __iomem * *kauai_fcr; + ide_hwif_t *hwif; + + /* 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; +} pmac_ide_hwif_t; + +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 + +/* 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[] = +{ + { 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[] = +{ + { 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[] = +{ + { 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[] = +{ + { 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[] = +{ + { 930 , 0x08000fff }, + { 600 , 0x08000a92 }, + { 383 , 0x0800060f }, + { 360 , 0x08000492 }, + { 330 , 0x0800048f }, + { 300 , 0x080003cf }, + { 270 , 0x080003cc }, + { 240 , 0x0800038b }, + { 239 , 0x0800030c }, + { 180 , 0x05000249 }, + { 120 , 0x04000148 }, + { 0 , 0 }, +}; + +static struct kauai_timing kauai_mdma_timings[] = +{ + { 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[] = +{ + { 120 , 0x000070c0 }, + { 90 , 0x00005d80 }, + { 60 , 0x00004a60 }, + { 45 , 0x00003a50 }, + { 30 , 0x00002a30 }, + { 20 , 0x00002921 }, + { 0 , 0 }, +}; + +static struct kauai_timing shasta_pio_timings[] = +{ + { 930 , 0x08000fff }, + { 600 , 0x0A000c97 }, + { 383 , 0x07000712 }, + { 360 , 0x040003cd }, + { 330 , 0x040003cd }, + { 300 , 0x040003cd }, + { 270 , 0x040003cd }, + { 240 , 0x040003cd }, + { 239 , 0x040003cd }, + { 180 , 0x0400028b }, + { 120 , 0x0400010a }, + { 0 , 0 }, +}; + +static struct kauai_timing shasta_mdma_timings[] = +{ + { 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[] = +{ + { 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; + BUG(); + 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 int pmac_ide_init_dma(ide_hwif_t *, const struct ide_port_info *); + +#define PMAC_IDE_REG(x) \ + ((void __iomem *)((drive)->hwif->io_ports.data_addr + (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_ide_apply_timings(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + + if (drive->dn & 1) + 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_ide_kauai_apply_timings(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + + if (drive->dn & 1) { + 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_ide_do_update_timings(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + + if (pmif->kind == controller_sh_ata6 || + pmif->kind == controller_un_ata6 || + pmif->kind == controller_k2_ata6) + pmac_ide_kauai_apply_timings(drive); + else + pmac_ide_apply_timings(drive); +} + +static void pmac_dev_select(ide_drive_t *drive) +{ + pmac_ide_apply_timings(drive); + + writeb(drive->select | ATA_DEVICE_OBS, + (void __iomem *)drive->hwif->io_ports.device_addr); +} + +static void pmac_kauai_dev_select(ide_drive_t *drive) +{ + pmac_ide_kauai_apply_timings(drive); + + writeb(drive->select | ATA_DEVICE_OBS, + (void __iomem *)drive->hwif->io_ports.device_addr); +} + +static void pmac_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + writeb(cmd, (void __iomem *)hwif->io_ports.command_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + +static void pmac_write_devctl(ide_hwif_t *hwif, u8 ctl) +{ + writeb(ctl, (void __iomem *)hwif->io_ports.ctl_addr); + (void)readl((void __iomem *)(hwif->io_ports.data_addr + + IDE_TIMING_CONFIG)); +} + +/* + * Old tuning functions (called on hdparm -p), sets up drive PIO timings + */ +static void pmac_ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + const u8 pio = drive->pio_mode - XFER_PIO_0; + struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio); + u32 *timings, t; + unsigned accessTicks, recTicks; + unsigned accessTime, recTime; + unsigned int cycle_time; + + /* which drive is it ? */ + timings = &pmif->timings[drive->dn & 1]; + t = *timings; + + cycle_time = ide_pio_cycle_time(drive, pio); + + switch (pmif->kind) { + case controller_sh_ata6: { + /* 133Mhz cell */ + u32 tr = kauai_lookup_timing(shasta_pio_timings, cycle_time); + t = (t & ~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, cycle_time); + t = (t & ~TR_100_PIOREG_PIO_MASK) | tr; + break; + } + case controller_kl_ata4: + /* 66Mhz cell */ + recTime = cycle_time - tim->active - tim->setup; + recTime = max(recTime, 150U); + accessTime = tim->active; + accessTime = max(accessTime, 150U); + accessTicks = SYSCLK_TICKS_66(accessTime); + accessTicks = min(accessTicks, 0x1fU); + recTicks = SYSCLK_TICKS_66(recTime); + recTicks = min(recTicks, 0x1fU); + t = (t & ~TR_66_PIO_MASK) | + (accessTicks << TR_66_PIO_ACCESS_SHIFT) | + (recTicks << TR_66_PIO_RECOVERY_SHIFT); + break; + default: { + /* 33Mhz cell */ + int ebit = 0; + recTime = cycle_time - tim->active - tim->setup; + recTime = max(recTime, 150U); + accessTime = tim->active; + 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; + } + t = (t & ~TR_33_PIO_MASK) | + (accessTicks << TR_33_PIO_ACCESS_SHIFT) | + (recTicks << TR_33_PIO_RECOVERY_SHIFT); + if (ebit) + t |= 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 + + *timings = t; + pmac_ide_do_update_timings(drive); +} + +/* + * Calculate KeyLargo ATA/66 UDMA timings + */ +static int +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 +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); + *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 +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); + *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 void +set_timings_mdma(ide_drive_t *drive, int intf_type, u32 *timings, u32 *timings2, + u8 speed) +{ + u16 *id = drive->id; + 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: + BUG(); + break; + } + + /* Check if drive provides explicit DMA cycle time */ + if ((id[ATA_ID_FIELD_VALID] & 2) && id[ATA_ID_EIDE_DMA_TIME]) + cycleTime = max_t(int, id[ATA_ID_EIDE_DMA_TIME], cycleTime); + + /* 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++; + } + 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); + *timings = ((*timings) & ~TR_133_PIOREG_MDMA_MASK) | tr; + *timings2 = (*timings2) & ~TR_133_UDMAREG_UDMA_EN; + } + break; + case controller_un_ata6: + case controller_k2_ata6: { + /* 100Mhz cell */ + u32 tr = kauai_lookup_timing(kauai_mdma_timings, cycleTime); + *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 +} + +static void pmac_ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + int ret = 0; + u32 *timings, *timings2, tl[2]; + u8 unit = drive->dn & 1; + const u8 speed = drive->dma_mode; + + timings = &pmif->timings[unit]; + timings2 = &pmif->timings[unit+2]; + + /* Copy timings to local image */ + tl[0] = *timings; + tl[1] = *timings2; + + if (speed >= XFER_UDMA_0) { + if (pmif->kind == controller_kl_ata4) + ret = set_timings_udma_ata4(&tl[0], speed); + else if (pmif->kind == controller_un_ata6 + || pmif->kind == controller_k2_ata6) + ret = set_timings_udma_ata6(&tl[0], &tl[1], speed); + else if (pmif->kind == controller_sh_ata6) + ret = set_timings_udma_shasta(&tl[0], &tl[1], speed); + else + ret = -1; + } else + set_timings_mdma(drive, pmif->kind, &tl[0], &tl[1], speed); + + if (ret) + return; + + /* Apply timings to controller */ + *timings = tl[0]; + *timings2 = tl[1]; + + pmac_ide_do_update_timings(drive); +} + +/* + * Blast some well known "safe" values to the timing registers at init or + * wakeup from sleep time, before we do real calculation + */ +static void +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; +} + +static int on_media_bay(pmac_ide_hwif_t *pmif) +{ + return pmif->mdev && pmif->mdev->media_bay != NULL; +} + +/* Suspend call back, should be called after the child devices + * have actually been suspended + */ +static int pmac_ide_do_suspend(pmac_ide_hwif_t *pmif) +{ + /* We clear the timings */ + pmif->timings[0] = 0; + pmif->timings[1] = 0; + + disable_irq(pmif->irq); + + /* The media bay will handle itself just fine */ + if (on_media_bay(pmif)) + 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(pmac_ide_hwif_t *pmif) +{ + /* Hard reset & re-enable controller (do we really need to reset ? -BenH) */ + if (!on_media_bay(pmif)) { + 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); + + /* 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); + } + + msleep(jiffies_to_msecs(IDE_WAKEUP_DELAY)); + } + + /* Sanitize drive timings */ + sanitize_timings(pmif); + + enable_irq(pmif->irq); + + return 0; +} + +static u8 pmac_ide_cable_detect(ide_hwif_t *hwif) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + struct device_node *np = pmif->node; + const char *cable = of_get_property(np, "cable-type", NULL); + struct device_node *root = of_find_node_by_path("/"); + const char *model = of_get_property(root, "model", NULL); + + of_node_put(root); + /* Get cable type from device-tree. */ + if (cable && !strncmp(cable, "80-", 3)) { + /* Some drives fail to detect 80c cable in PowerBook */ + /* These machine use proprietary short IDE cable anyway */ + if (!strncmp(model, "PowerBook", 9)) + return ATA_CBL_PATA40_SHORT; + else + return ATA_CBL_PATA80; + } + + /* + * 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 (of_device_is_compatible(np, "K2-UATA") || + of_device_is_compatible(np, "shasta-ata")) + return ATA_CBL_PATA80; + + return ATA_CBL_PATA40; +} + +static void pmac_ide_init_dev(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + + if (on_media_bay(pmif)) { + if (check_media_bay(pmif->mdev->media_bay) == MB_CD) { + drive->dev_flags &= ~IDE_DFLAG_NOPROBE; + return; + } + drive->dev_flags |= IDE_DFLAG_NOPROBE; + } +} + +static const struct ide_tp_ops pmac_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = pmac_write_devctl, + + .dev_select = pmac_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_tp_ops pmac_ata6_tp_ops = { + .exec_command = pmac_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = pmac_write_devctl, + + .dev_select = pmac_kauai_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_port_ops pmac_ide_ata4_port_ops = { + .init_dev = pmac_ide_init_dev, + .set_pio_mode = pmac_ide_set_pio_mode, + .set_dma_mode = pmac_ide_set_dma_mode, + .cable_detect = pmac_ide_cable_detect, +}; + +static const struct ide_port_ops pmac_ide_port_ops = { + .init_dev = pmac_ide_init_dev, + .set_pio_mode = pmac_ide_set_pio_mode, + .set_dma_mode = pmac_ide_set_dma_mode, +}; + +static const struct ide_dma_ops pmac_dma_ops; + +static const struct ide_port_info pmac_port_info = { + .name = DRV_NAME, + .init_dma = pmac_ide_init_dma, + .chipset = ide_pmac, + .tp_ops = &pmac_tp_ops, + .port_ops = &pmac_ide_port_ops, + .dma_ops = &pmac_dma_ops, + .host_flags = IDE_HFLAG_SET_PIO_MODE_KEEP_DMA | + IDE_HFLAG_POST_SET_MODE | + IDE_HFLAG_MMIO | + IDE_HFLAG_UNMASK_IRQS, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, +}; + +/* + * Setup, register & probe an IDE channel driven by this driver, this is + * called by one of the 2 probe functions (macio or PCI). + */ +static int pmac_ide_setup_device(pmac_ide_hwif_t *pmif, struct ide_hw *hw) +{ + struct device_node *np = pmif->node; + const int *bidp; + struct ide_host *host; + struct ide_hw *hws[] = { hw }; + struct ide_port_info d = pmac_port_info; + int rc; + + pmif->broken_dma = pmif->broken_dma_warn = 0; + if (of_device_is_compatible(np, "shasta-ata")) { + pmif->kind = controller_sh_ata6; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; + d.udma_mask = ATA_UDMA6; + } else if (of_device_is_compatible(np, "kauai-ata")) { + pmif->kind = controller_un_ata6; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; + d.udma_mask = ATA_UDMA5; + } else if (of_device_is_compatible(np, "K2-UATA")) { + pmif->kind = controller_k2_ata6; + d.tp_ops = &pmac_ata6_tp_ops; + d.port_ops = &pmac_ide_ata4_port_ops; + d.udma_mask = ATA_UDMA5; + } else if (of_device_is_compatible(np, "keylargo-ata")) { + if (of_node_name_eq(np, "ata-4")) { + pmif->kind = controller_kl_ata4; + d.port_ops = &pmac_ide_ata4_port_ops; + d.udma_mask = ATA_UDMA4; + } else + pmif->kind = controller_kl_ata3; + } else if (of_device_is_compatible(np, "heathrow-ata")) { + pmif->kind = controller_heathrow; + } else { + pmif->kind = controller_ohare; + pmif->broken_dma = 1; + } + + bidp = of_get_property(np, "AAPL,bus-id", NULL); + pmif->aapl_bus_id = bidp ? *bidp : 0; + + /* 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); + + /* Make sure we have sane timings */ + sanitize_timings(pmif); + + /* If we are on a media bay, wait for it to settle and lock it */ + if (pmif->mdev) + lock_media_bay(pmif->mdev->media_bay); + + host = ide_host_alloc(&d, hws, 1); + if (host == NULL) { + rc = -ENOMEM; + goto bail; + } + pmif->hwif = host->ports[0]; + + if (on_media_bay(pmif)) { + /* Fixup bus ID for media bay */ + 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 { + /* 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)); + } + + printk(KERN_INFO DRV_NAME ": Found Apple %s controller (%s), " + "bus ID %d%s, irq %d\n", model_name[pmif->kind], + pmif->mdev ? "macio" : "PCI", pmif->aapl_bus_id, + on_media_bay(pmif) ? " (mediabay)" : "", hw->irq); + + rc = ide_host_register(host, &d, hws); + if (rc) + pmif->hwif = NULL; + + if (pmif->mdev) + unlock_media_bay(pmif->mdev->media_bay); + + bail: + if (rc && host) + ide_host_free(host); + return rc; +} + +static void pmac_ide_init_ports(struct ide_hw *hw, unsigned long base) +{ + int i; + + for (i = 0; i < 8; ++i) + hw->io_ports_array[i] = base + i * 0x10; + + hw->io_ports.ctl_addr = base + 0x160; +} + +/* + * Attach to a macio probed interface + */ +static int pmac_ide_macio_attach(struct macio_dev *mdev, + const struct of_device_id *match) +{ + void __iomem *base; + unsigned long regbase; + pmac_ide_hwif_t *pmif; + int irq, rc; + struct ide_hw hw; + + pmif = kzalloc(sizeof(*pmif), GFP_KERNEL); + if (pmif == NULL) + return -ENOMEM; + + if (macio_resource_count(mdev) == 0) { + printk(KERN_WARNING "ide-pmac: no address for %pOF\n", + mdev->ofdev.dev.of_node); + rc = -ENXIO; + goto out_free_pmif; + } + + /* Request memory resource for IO ports */ + if (macio_request_resource(mdev, 0, "ide-pmac (ports)")) { + printk(KERN_ERR "ide-pmac: can't request MMIO resource for " + "%pOF!\n", mdev->ofdev.dev.of_node); + rc = -EBUSY; + goto out_free_pmif; + } + + /* 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-pmac: no intrs for device %pOF, using " + "13\n", mdev->ofdev.dev.of_node); + irq = irq_create_mapping(NULL, 13); + } else + irq = macio_irq(mdev, 0); + + base = ioremap(macio_resource_start(mdev, 0), 0x400); + regbase = (unsigned long) base; + + pmif->mdev = mdev; + pmif->node = mdev->ofdev.dev.of_node; + pmif->regbase = regbase; + pmif->irq = irq; + pmif->kauai_fcr = NULL; + + if (macio_resource_count(mdev) >= 2) { + if (macio_request_resource(mdev, 1, "ide-pmac (dma)")) + printk(KERN_WARNING "ide-pmac: can't request DMA " + "resource for %pOF!\n", + mdev->ofdev.dev.of_node); + else + pmif->dma_regs = ioremap(macio_resource_start(mdev, 1), 0x1000); + } else + pmif->dma_regs = NULL; + + dev_set_drvdata(&mdev->ofdev.dev, pmif); + + memset(&hw, 0, sizeof(hw)); + pmac_ide_init_ports(&hw, pmif->regbase); + hw.irq = irq; + hw.dev = &mdev->bus->pdev->dev; + hw.parent = &mdev->ofdev.dev; + + rc = pmac_ide_setup_device(pmif, &hw); + 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); + macio_release_resource(mdev, 1); + } + macio_release_resource(mdev, 0); + kfree(pmif); + } + + return rc; + +out_free_pmif: + kfree(pmif); + return rc; +} + +static int +pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; + + if (mesg.event != mdev->ofdev.dev.power.power_state.event + && (mesg.event & PM_EVENT_SLEEP)) { + rc = pmac_ide_do_suspend(pmif); + if (rc == 0) + mdev->ofdev.dev.power.power_state = mesg; + } + + return rc; +} + +static int +pmac_ide_macio_resume(struct macio_dev *mdev) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev); + int rc = 0; + + if (mdev->ofdev.dev.power.power_state.event != PM_EVENT_ON) { + rc = pmac_ide_do_resume(pmif); + if (rc == 0) + mdev->ofdev.dev.power.power_state = PMSG_ON; + } + + return rc; +} + +/* + * Attach to a PCI probed interface + */ +static int pmac_ide_pci_attach(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct device_node *np; + pmac_ide_hwif_t *pmif; + void __iomem *base; + unsigned long rbase, rlen; + int rc; + struct ide_hw hw; + + 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; + } + + pmif = kzalloc(sizeof(*pmif), GFP_KERNEL); + if (pmif == NULL) + return -ENOMEM; + + if (pci_enable_device(pdev)) { + printk(KERN_WARNING "ide-pmac: Can't enable PCI device for " + "%pOF\n", np); + rc = -ENXIO; + goto out_free_pmif; + } + pci_set_master(pdev); + + if (pci_request_regions(pdev, "Kauai ATA")) { + printk(KERN_ERR "ide-pmac: Cannot obtain PCI resources for " + "%pOF\n", np); + rc = -ENXIO; + goto out_free_pmif; + } + + 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; + pmif->dma_regs = base + 0x1000; + pmif->kauai_fcr = base; + pmif->irq = pdev->irq; + + pci_set_drvdata(pdev, pmif); + + memset(&hw, 0, sizeof(hw)); + pmac_ide_init_ports(&hw, pmif->regbase); + hw.irq = pdev->irq; + hw.dev = &pdev->dev; + + rc = pmac_ide_setup_device(pmif, &hw); + if (rc != 0) { + /* The inteface is released to the common IDE layer */ + iounmap(base); + pci_release_regions(pdev); + kfree(pmif); + } + + return rc; + +out_free_pmif: + kfree(pmif); + return rc; +} + +static int +pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + pmac_ide_hwif_t *pmif = pci_get_drvdata(pdev); + int rc = 0; + + if (mesg.event != pdev->dev.power.power_state.event + && (mesg.event & PM_EVENT_SLEEP)) { + rc = pmac_ide_do_suspend(pmif); + if (rc == 0) + pdev->dev.power.power_state = mesg; + } + + return rc; +} + +static int +pmac_ide_pci_resume(struct pci_dev *pdev) +{ + pmac_ide_hwif_t *pmif = pci_get_drvdata(pdev); + int rc = 0; + + if (pdev->dev.power.power_state.event != PM_EVENT_ON) { + rc = pmac_ide_do_resume(pmif); + if (rc == 0) + pdev->dev.power.power_state = PMSG_ON; + } + + return rc; +} + +#ifdef CONFIG_PMAC_MEDIABAY +static void pmac_ide_macio_mb_event(struct macio_dev* mdev, int mb_state) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(&mdev->ofdev.dev); + + switch(mb_state) { + case MB_CD: + if (!pmif->hwif->present) + ide_port_scan(pmif->hwif); + break; + default: + if (pmif->hwif->present) + ide_port_unregister_devices(pmif->hwif); + } +} +#endif /* CONFIG_PMAC_MEDIABAY */ + + +static struct of_device_id pmac_ide_macio_match[] = +{ + { + .name = "IDE", + }, + { + .name = "ATA", + }, + { + .type = "ide", + }, + { + .type = "ata", + }, + {}, +}; + +static struct macio_driver pmac_ide_macio_driver = +{ + .driver = { + .name = "ide-pmac", + .owner = THIS_MODULE, + .of_match_table = pmac_ide_macio_match, + }, + .probe = pmac_ide_macio_attach, + .suspend = pmac_ide_macio_suspend, + .resume = pmac_ide_macio_resume, +#ifdef CONFIG_PMAC_MEDIABAY + .mediabay_event = pmac_ide_macio_mb_event, +#endif +}; + +static const struct pci_device_id pmac_ide_pci_match[] = { + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_UNI_N_ATA), 0 }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_IPID_ATA100), 0 }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_K2_ATA100), 0 }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_SH_ATA), 0 }, + { PCI_VDEVICE(APPLE, PCI_DEVICE_ID_APPLE_IPID2_ATA), 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); + +int __init pmac_ide_probe(void) +{ + int error; + + if (!machine_is(powermac)) + return -ENODEV; + +#ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST + error = pci_register_driver(&pmac_ide_pci_driver); + if (error) + goto out; + error = macio_register_driver(&pmac_ide_macio_driver); + if (error) { + pci_unregister_driver(&pmac_ide_pci_driver); + goto out; + } +#else + error = macio_register_driver(&pmac_ide_macio_driver); + if (error) + goto out; + error = pci_register_driver(&pmac_ide_pci_driver); + if (error) { + macio_unregister_driver(&pmac_ide_macio_driver); + goto out; + } +#endif +out: + return error; +} + +/* + * pmac_ide_build_dmatable builds the DBDMA command list + * for a transfer and sets the DBDMA channel to point to it. + */ +static int pmac_ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + struct dbdma_cmd *table; + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; + struct scatterlist *sg; + int wr = !!(cmd->tf_flags & IDE_TFLAG_WRITE); + int i = cmd->sg_nents, count = 0; + + /* 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); + + /* 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; + } + return 0; + } + 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); + return 0; + } + table->command = cpu_to_le16(wr? OUTPUT_MORE: INPUT_MORE); + table->req_count = cpu_to_le16(tc); + table->phy_addr = cpu_to_le32(cur_addr); + table->cmd_dep = 0; + table->xfer_status = 0; + table->res_count = 0; + cur_addr += tc; + cur_len -= tc; + ++table; + } + sg = sg_next(sg); + i--; + } + + /* convert the last command to an input/output last command */ + if (count) { + table[-1].command = cpu_to_le16(wr? OUTPUT_LAST: INPUT_LAST); + /* add the stop command to the end of the list */ + memset(table, 0, sizeof(struct dbdma_cmd)); + table->command = cpu_to_le16(DBDMA_STOP); + mb(); + writel(hwif->dmatable_dma, &dma->cmdptr); + return 1; + } + + printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); + + return 0; /* revert to PIO for this request */ +} + +/* + * 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_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + u8 unit = drive->dn & 1, ata4 = (pmif->kind == controller_kl_ata4); + u8 write = !!(cmd->tf_flags & IDE_TFLAG_WRITE); + + if (pmac_ide_build_dmatable(drive, cmd) == 0) + return 1; + + /* Apple adds 60ns to wrDataSetup on reads */ + if (ata4 && (pmif->timings[unit] & TR_66_UDMA_EN)) { + writel(pmif->timings[unit] + (write ? 0 : 0x00800000UL), + PMAC_IDE_REG(IDE_TIMING_CONFIG)); + (void)readl(PMAC_IDE_REG(IDE_TIMING_CONFIG)); + } + + return 0; +} + +/* + * Kick the DMA controller into life after the DMA command has been issued + * to the drive. + */ +static void +pmac_ide_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + 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_ide_dma_end (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; + u32 dstat; + + dstat = readl(&dma->status); + writel(((RUN|WAKE|DEAD) << 16), &dma->control); + + /* 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_ide_dma_test_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; + unsigned long status, timeout; + + /* 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 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->index); + break; + } + } + return 1; +} + +static void pmac_ide_dma_host_set(ide_drive_t *drive, int on) +{ +} + +static void +pmac_ide_dma_lost_irq (ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + volatile struct dbdma_regs __iomem *dma = pmif->dma_regs; + unsigned long status = readl(&dma->status); + + printk(KERN_ERR "ide-pmac lost interrupt, dma status: %lx\n", status); +} + +static const struct ide_dma_ops pmac_dma_ops = { + .dma_host_set = pmac_ide_dma_host_set, + .dma_setup = pmac_ide_dma_setup, + .dma_start = pmac_ide_dma_start, + .dma_end = pmac_ide_dma_end, + .dma_test_irq = pmac_ide_dma_test_irq, + .dma_lost_irq = pmac_ide_dma_lost_irq, +}; + +/* + * Allocate the data structures needed for using DMA with an interface + * and fill the proper list of functions pointers + */ +static int pmac_ide_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + pmac_ide_hwif_t *pmif = dev_get_drvdata(hwif->gendev.parent); + struct pci_dev *dev = to_pci_dev(hwif->dev); + + /* We won't need pci_dev if we switch to generic consistent + * DMA routines ... + */ + if (dev == NULL || pmif->dma_regs == 0) + return -ENODEV; + /* + * 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 = dma_alloc_coherent(&dev->dev, + (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), + &hwif->dmatable_dma, GFP_KERNEL); + if (pmif->dma_table_cpu == NULL) { + printk(KERN_ERR "%s: unable to allocate DMA command list\n", + hwif->name); + return -ENOMEM; + } + + hwif->sg_max_nents = MAX_DCMDS; + + return 0; +} + +module_init(pmac_ide_probe); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c new file mode 100644 index 000000000..ecd0a6924 --- /dev/null +++ b/drivers/ide/q40ide.c @@ -0,0 +1,168 @@ +/* + * 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/ide.h> +#include <linux/module.h> + +#include <asm/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 */ +}; + +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; + } +} + + +/* + * Addresses are pretranslated for Q40 ISA access. + */ +static void q40_ide_setup_ports(struct ide_hw *hw, unsigned long base, int irq) +{ + memset(hw, 0, sizeof(*hw)); + /* BIG FAT WARNING: + assumption: only DATA port is ever used in 16 bit mode */ + hw->io_ports.data_addr = Q40_ISA_IO_W(base); + hw->io_ports.error_addr = Q40_ISA_IO_B(base + 1); + hw->io_ports.nsect_addr = Q40_ISA_IO_B(base + 2); + hw->io_ports.lbal_addr = Q40_ISA_IO_B(base + 3); + hw->io_ports.lbam_addr = Q40_ISA_IO_B(base + 4); + hw->io_ports.lbah_addr = Q40_ISA_IO_B(base + 5); + hw->io_ports.device_addr = Q40_ISA_IO_B(base + 6); + hw->io_ports.status_addr = Q40_ISA_IO_B(base + 7); + hw->io_ports.ctl_addr = Q40_ISA_IO_B(base + 0x206); + + hw->irq = irq; +} + +static void q40ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long data_addr = drive->hwif->io_ports.data_addr; + + if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) { + __ide_mm_insw(data_addr, buf, (len + 1) / 2); + return; + } + + raw_insw_swapw((u16 *)data_addr, buf, (len + 1) / 2); +} + +static void q40ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long data_addr = drive->hwif->io_ports.data_addr; + + if (drive->media == ide_disk && cmd && (cmd->tf_flags & IDE_TFLAG_FS)) { + __ide_mm_outsw(data_addr, buf, (len + 1) / 2); + return; + } + + raw_outsw_swapw((u16 *)data_addr, buf, (len + 1) / 2); +} + +/* Q40 has a byte-swapped IDE interface */ +static const struct ide_tp_ops q40ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = q40ide_input_data, + .output_data = q40ide_output_data, +}; + +static const struct ide_port_info q40ide_port_info = { + .tp_ops = &q40ide_tp_ops, + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA, + .irq_flags = IRQF_SHARED, + .chipset = ide_generic, +}; + +/* + * 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 + */ + +static int __init q40ide_init(void) +{ + int i; + struct ide_hw hw[Q40IDE_NUM_HWIFS], *hws[] = { NULL, NULL }; + + if (!MACH_IS_Q40) + return -ENODEV; + + printk(KERN_INFO "ide: Q40 IDE controller\n"); + + for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { + const char *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[i], pcide_bases[i], + q40ide_default_irq(pcide_bases[i])); + + hws[i] = &hw[i]; + } + + return ide_host_add(&q40ide_port_info, hws, Q40IDE_NUM_HWIFS, NULL); +} + +module_init(q40ide_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c new file mode 100644 index 000000000..ab79b6289 --- /dev/null +++ b/drivers/ide/qd65xx.c @@ -0,0 +1,446 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + * + * To activate controller support, use "ide0=qd65xx" + */ + +/* + * Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by + * Samuel Thibault <samuel.thibault@ens-lyon.org> + */ + +#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/ide.h> +#include <linux/init.h> +#include <asm/io.h> + +#define DRV_NAME "qd65xx" + +#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 */ + +/* + * qd65xx_select: + * + * This routine is invoked to prepare for access to a given drive. + */ + +static void qd65xx_dev_select(ide_drive_t *drive) +{ + u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) | + (QD_TIMREG(drive) & 0x02); + + if (timings[index] != QD_TIMING(drive)) + outb(timings[index] = QD_TIMING(drive), QD_TIMREG(drive)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); +} + +/* + * 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) +{ + int clk = ide_vlb_clk ? ide_vlb_clk : 50; + u8 act_cyc, rec_cyc; + + if (clk <= 33) { + act_cyc = 9 - IDE_IN(active_time * clk / 1000 + 1, 2, 9); + rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 0, 15); + } else { + act_cyc = 8 - IDE_IN(active_time * clk / 1000 + 1, 1, 8); + rec_cyc = 18 - IDE_IN(recovery_time * clk / 1000 + 1, 3, 18); + } + + return (rec_cyc << 4) | 0x08 | act_cyc; +} + +/* + * qd6580_compute_timing + * + * idem for qd6580 + */ + +static u8 qd6580_compute_timing (int active_time, int recovery_time) +{ + int clk = ide_vlb_clk ? ide_vlb_clk : 50; + u8 act_cyc, rec_cyc; + + act_cyc = 17 - IDE_IN(active_time * clk / 1000 + 1, 2, 17); + rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 2, 15); + + return (rec_cyc << 4) | act_cyc; +} + +/* + * 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 *m = (char *)&drive->id[ATA_ID_PROD]; + char model[ATA_ID_PROD_LEN]; + + if (*m == 0) + return 0; + + strncpy(model, m, ATA_ID_PROD_LEN); + ide_fixstring(model, ATA_ID_PROD_LEN, 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_set_timing: + * + * records the timing + */ + +static void qd_set_timing (ide_drive_t *drive, u8 timing) +{ + unsigned long data = (unsigned long)ide_get_drivedata(drive); + + data &= 0xff00; + data |= timing; + ide_set_drivedata(drive, (void *)data); + + printk(KERN_DEBUG "%s: %#x\n", drive->name, timing); +} + +static void qd6500_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + u16 *id = drive->id; + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + + /* FIXME: use drive->pio_mode value */ + if (!qd_find_disk_type(drive, &active_time, &recovery_time) && + (id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) && + id[ATA_ID_EIDE_PIO] >= 240) { + printk(KERN_INFO "%s: PIO mode%d\n", drive->name, + id[ATA_ID_OLD_PIO_MODES] & 0xff); + active_time = 110; + recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120; + } + + qd_set_timing(drive, qd6500_compute_timing(drive->hwif, + active_time, recovery_time)); +} + +static void qd6580_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + const u8 pio = drive->pio_mode - XFER_PIO_0; + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + unsigned int cycle_time; + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + u8 base = (hwif->config_data & 0xff00) >> 8; + + if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)) { + cycle_time = ide_pio_cycle_time(drive, pio); + + switch (pio) { + case 0: break; + case 3: + if (cycle_time >= 110) { + active_time = 86; + recovery_time = cycle_time - 102; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + case 4: + if (cycle_time >= 69) { + active_time = 70; + recovery_time = cycle_time - 61; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + default: + if (cycle_time >= 180) { + active_time = 110; + recovery_time = cycle_time - 120; + } else { + active_time = t->active; + recovery_time = cycle_time - active_time; + } + } + printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio); + } + + if (!hwif->channel && drive->media != ide_disk) { + outb(0x5f, QD_CONTROL_PORT); + printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO " + "and post-write buffer on %s.\n", + drive->name, hwif->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) +{ + unsigned long flags; + u8 savereg, readreg; + + local_irq_save(flags); + savereg = inb_p(port); + outb_p(QD_TESTVAL, port); /* safe value */ + readreg = inb_p(port); + outb(savereg, port); + local_irq_restore(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); +} + +static void __init qd6500_init_dev(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 base = (hwif->config_data & 0xff00) >> 8; + u8 config = QD_CONFIG(hwif); + + ide_set_drivedata(drive, (void *)QD6500_DEF_DATA); +} + +static void __init qd6580_init_dev(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned long t1, t2; + u8 base = (hwif->config_data & 0xff00) >> 8; + u8 config = QD_CONFIG(hwif); + + if (hwif->host_flags & IDE_HFLAG_SINGLE) { + t1 = QD6580_DEF_DATA; + t2 = QD6580_DEF_DATA2; + } else + t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA; + + ide_set_drivedata(drive, (void *)((drive->dn & 1) ? t2 : t1)); +} + +static const struct ide_tp_ops qd65xx_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = qd65xx_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_port_ops qd6500_port_ops = { + .init_dev = qd6500_init_dev, + .set_pio_mode = qd6500_set_pio_mode, +}; + +static const struct ide_port_ops qd6580_port_ops = { + .init_dev = qd6580_init_dev, + .set_pio_mode = qd6580_set_pio_mode, +}; + +static const struct ide_port_info qd65xx_port_info __initconst = { + .name = DRV_NAME, + .tp_ops = &qd65xx_tp_ops, + .chipset = ide_qd65xx, + .host_flags = IDE_HFLAG_IO_32BIT | + IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO4, +}; + +/* + * 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) +{ + int rc; + u8 config, unit, control; + struct ide_port_info d = qd65xx_port_info; + + config = inb(QD_CONFIG_PORT); + + if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) ) + return -ENODEV; + + unit = ! (config & QD_CONFIG_IDE_BASEPORT); + + if (unit) + d.host_flags |= IDE_HFLAG_QD_2ND_PORT; + + switch (config & 0xf0) { + case QD_CONFIG_QD6500: + if (qd_testreg(base)) + return -ENODEV; /* bad register */ + + if (config & QD_CONFIG_DISABLED) { + printk(KERN_WARNING "qd6500 is disabled !\n"); + return -ENODEV; + } + + printk(KERN_NOTICE "qd6500 at %#x\n", base); + printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n", + config, QD_ID3); + + d.port_ops = &qd6500_port_ops; + d.host_flags |= IDE_HFLAG_SINGLE; + break; + case QD_CONFIG_QD6580_A: + case QD_CONFIG_QD6580_B: + if (qd_testreg(base) || qd_testreg(base + 0x02)) + return -ENODEV; /* bad registers */ + + control = inb(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); + + outb(QD_DEF_CONTR, QD_CONTROL_PORT); + + d.port_ops = &qd6580_port_ops; + if (control & QD_CONTR_SEC_DISABLED) + d.host_flags |= IDE_HFLAG_SINGLE; + + printk(KERN_INFO "qd6580: %s IDE board\n", + (control & QD_CONTR_SEC_DISABLED) ? "single" : "dual"); + break; + default: + return -ENODEV; + } + + rc = ide_legacy_device_add(&d, (base << 8) | config); + + if (d.host_flags & IDE_HFLAG_SINGLE) + return (rc == 0) ? 1 : rc; + + return rc; +} + +static bool probe_qd65xx; + +module_param_named(probe, probe_qd65xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for QD65xx chipsets"); + +static int __init qd65xx_init(void) +{ + int rc1, rc2 = -ENODEV; + + if (probe_qd65xx == 0) + return -ENODEV; + + rc1 = qd_probe(0x30); + if (rc1) + rc2 = qd_probe(0xb0); + + if (rc1 < 0 && rc2 < 0) + return -ENODEV; + + return 0; +} + +module_init(qd65xx_init); + +MODULE_AUTHOR("Samuel Thibault"); +MODULE_DESCRIPTION("support of qd65xx vlb ide chipset"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/qd65xx.h b/drivers/ide/qd65xx.h new file mode 100644 index 000000000..01a43ab45 --- /dev/null +++ b/drivers/ide/qd65xx.h @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2000 Linus Torvalds & authors + */ + +/* + * Authors: Petr Soucek <petr@ryston.cz> + * Samuel Thibault <samuel.thibault@ens-lyon.org> + */ + +/* 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) + +static inline u8 QD_TIMING(ide_drive_t *drive) +{ + return (unsigned long)ide_get_drivedata(drive) & 0x00ff; +} + +static inline u8 QD_TIMREG(ide_drive_t *drive) +{ + return ((unsigned long)ide_get_drivedata(drive) & 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/rapide.c b/drivers/ide/rapide.c new file mode 100644 index 000000000..0ab8b86b7 --- /dev/null +++ b/drivers/ide/rapide.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 1996-2002 Russell King. + */ + +#include <linux/module.h> +#include <linux/blkdev.h> +#include <linux/errno.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/ecard.h> + +static const struct ide_port_info rapide_port_info = { + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA, + .chipset = ide_generic, +}; + +static void rapide_setup_ports(struct ide_hw *hw, void __iomem *base, + void __iomem *ctrl, unsigned int sz, int irq) +{ + unsigned long port = (unsigned long)base; + int i; + + for (i = 0; i <= 7; i++) { + hw->io_ports_array[i] = port; + port += sz; + } + hw->io_ports.ctl_addr = (unsigned long)ctrl; + hw->irq = irq; +} + +static int rapide_probe(struct expansion_card *ec, const struct ecard_id *id) +{ + void __iomem *base; + struct ide_host *host; + int ret; + struct ide_hw hw, *hws[] = { &hw }; + + ret = ecard_request_resources(ec); + if (ret) + goto out; + + base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0); + if (!base) { + ret = -ENOMEM; + goto release; + } + + memset(&hw, 0, sizeof(hw)); + rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq); + hw.dev = &ec->dev; + + ret = ide_host_add(&rapide_port_info, hws, 1, &host); + if (ret) + goto release; + + ecard_set_drvdata(ec, host); + goto out; + + release: + ecard_release_resources(ec); + out: + return ret; +} + +static void rapide_remove(struct expansion_card *ec) +{ + struct ide_host *host = ecard_get_drvdata(ec); + + ecard_set_drvdata(ec, NULL); + + ide_host_remove(host); + + 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 = rapide_remove, + .id_table = rapide_ids, + .drv = { + .name = "rapide", + }, +}; + +static int __init rapide_init(void) +{ + return ecard_register_driver(&rapide_driver); +} + +static void __exit rapide_exit(void) +{ + ecard_remove_driver(&rapide_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Yellowstone RAPIDE driver"); + +module_init(rapide_init); +module_exit(rapide_exit); diff --git a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c new file mode 100644 index 000000000..fce2b7de5 --- /dev/null +++ b/drivers/ide/rz1000.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 (?). + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "rz1000" + +static int rz1000_disable_readahead(struct pci_dev *dev) +{ + u16 reg; + + 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", pci_name(dev)); + return 0; + } else { + printk(KERN_INFO "%s: serialized, disabled unmasking " + "(buggy RZ1000/RZ1001)\n", pci_name(dev)); + return 1; + } +} + +static const struct ide_port_info rz1000_chipset = { + .name = DRV_NAME, + .host_flags = IDE_HFLAG_NO_DMA, +}; + +static int rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d = rz1000_chipset; + int rc; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (rz1000_disable_readahead(dev)) { + d.host_flags |= IDE_HFLAG_SERIALIZE; + d.host_flags |= IDE_HFLAG_NO_UNMASK_IRQS; + } + + return ide_pci_init_one(dev, &d, NULL); +} + +static void rz1000_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_disable_device(dev); +} + +static const struct pci_device_id rz1000_pci_tbl[] = { + { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), 0 }, + { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, rz1000_pci_tbl); + +static struct pci_driver rz1000_pci_driver = { + .name = "RZ1000_IDE", + .id_table = rz1000_pci_tbl, + .probe = rz1000_init_one, + .remove = rz1000_remove, +}; + +static int __init rz1000_ide_init(void) +{ + return ide_pci_register_driver(&rz1000_pci_driver); +} + +static void __exit rz1000_ide_exit(void) +{ + pci_unregister_driver(&rz1000_pci_driver); +} + +module_init(rz1000_ide_init); +module_exit(rz1000_ide_exit); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/ide/sc1200.c b/drivers/ide/sc1200.c new file mode 100644 index 000000000..a5b701818 --- /dev/null +++ b/drivers/ide/sc1200.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2000-2002 Mark Lord <mlord@pobox.com> + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/pm.h> + +#include <asm/io.h> + +#define DRV_NAME "sc1200" + +#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; +} + +/* + * 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 void sc1200_tunepio(ide_drive_t *drive, u8 pio) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *pdev = to_pci_dev(hwif->dev); + unsigned int basereg = hwif->channel ? 0x50 : 0x40, format = 0; + + pci_read_config_dword(pdev, basereg + 4, &format); + format = (format >> 31) & 1; + if (format) + format += sc1200_get_pci_clock(); + pci_write_config_dword(pdev, basereg + ((drive->dn & 1) << 3), + sc1200_pio_timings[format][pio]); +} + +/* + * 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. + */ +static u8 sc1200_udma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + ide_drive_t *mate = ide_get_pair_dev(drive); + u16 *mateid; + u8 mask = hwif->ultra_mask; + + if (mate == NULL) + goto out; + mateid = mate->id; + + if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) { + if ((mateid[ATA_ID_FIELD_VALID] & 4) && + (mateid[ATA_ID_UDMA_MODES] & 7)) + goto out; + if (mateid[ATA_ID_MWDMA_MODES] & 7) + mask = 0; + } +out: + return mask; +} + +static void sc1200_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned int reg, timings; + unsigned short pci_clock; + unsigned int basereg = hwif->channel ? 0x50 : 0x40; + const u8 mode = drive->dma_mode; + + static const u32 udma_timing[3][3] = { + { 0x00921250, 0x00911140, 0x00911030 }, + { 0x00932470, 0x00922260, 0x00922140 }, + { 0x009436a1, 0x00933481, 0x00923261 }, + }; + + static const u32 mwdma_timing[3][3] = { + { 0x00077771, 0x00012121, 0x00002020 }, + { 0x000bbbb2, 0x00024241, 0x00013131 }, + { 0x000ffff3, 0x00035352, 0x00015151 }, + }; + + pci_clock = sc1200_get_pci_clock(); + + /* + * Note that each DMA mode has several timings associated with it. + * The correct timing depends on the fast PCI clock freq. + */ + + if (mode >= XFER_UDMA_0) + timings = udma_timing[pci_clock][mode - XFER_UDMA_0]; + else + timings = mwdma_timing[pci_clock][mode - XFER_MW_DMA_0]; + + if ((drive->dn & 1) == 0) { + pci_read_config_dword(dev, basereg + 4, ®); + timings |= reg & 0x80000000; /* preserve PIO format bit */ + pci_write_config_dword(dev, basereg + 4, timings); + } else + pci_write_config_dword(dev, basereg + 12, timings); +} + +/* Replacement for the standard ide_dma_end action in + * dma_proc. + * + * returns 1 on error, 0 otherwise + */ +static int sc1200_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned long dma_base = hwif->dma_base; + u8 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 */ + + return (dma_stat & 7) != 4; /* verify good DMA status */ +} + +/* + * sc1200_set_pio_mode() handles 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_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + int mode = -1; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* + * bad abuse of ->set_pio_mode interface + */ + 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); + ide_dma_off_quietly(drive); + if (ide_set_dma_mode(drive, mode) == 0 && + (drive->dev_flags & IDE_DFLAG_USING_DMA)) + hwif->dma_ops->dma_host_set(drive, 1); + return; + } + + sc1200_tunepio(drive, pio); +} + +#ifdef CONFIG_PM +struct sc1200_saved_state { + u32 regs[8]; +}; + +static int sc1200_suspend (struct pci_dev *dev, pm_message_t state) +{ + printk("SC1200: suspend(%u)\n", state.event); + + /* + * we only save state when going from full power to less + */ + if (state.event == PM_EVENT_ON) { + struct ide_host *host = pci_get_drvdata(dev); + struct sc1200_saved_state *ss = host->host_priv; + unsigned int r; + + /* + * save timing registers + * (this may be unnecessary if BIOS also does it) + */ + for (r = 0; r < 8; r++) + pci_read_config_dword(dev, 0x40 + r * 4, &ss->regs[r]); + } + + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + return 0; +} + +static int sc1200_resume (struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct sc1200_saved_state *ss = host->host_priv; + unsigned int r; + int i; + + i = pci_enable_device(dev); + if (i) + return i; + + /* + * restore timing registers + * (this may be unnecessary if BIOS also does it) + */ + for (r = 0; r < 8; r++) + pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]); + + return 0; +} +#endif + +static const struct ide_port_ops sc1200_port_ops = { + .set_pio_mode = sc1200_set_pio_mode, + .set_dma_mode = sc1200_set_dma_mode, + .udma_filter = sc1200_udma_filter, +}; + +static const struct ide_dma_ops sc1200_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = sc1200_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info sc1200_chipset = { + .name = DRV_NAME, + .port_ops = &sc1200_port_ops, + .dma_ops = &sc1200_dma_ops, + .host_flags = IDE_HFLAG_SERIALIZE | + IDE_HFLAG_POST_SET_MODE | + IDE_HFLAG_ABUSE_DMA_MODES, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA2, +}; + +static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct sc1200_saved_state *ss = NULL; + int rc; + +#ifdef CONFIG_PM + ss = kmalloc(sizeof(*ss), GFP_KERNEL); + if (ss == NULL) + return -ENOMEM; +#endif + rc = ide_pci_init_one(dev, &sc1200_chipset, ss); + if (rc) + kfree(ss); + + return rc; +} + +static const struct pci_device_id sc1200_pci_tbl[] = { + { PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_IDE), 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl); + +static struct pci_driver sc1200_pci_driver = { + .name = "SC1200_IDE", + .id_table = sc1200_pci_tbl, + .probe = sc1200_init_one, + .remove = ide_pci_remove, +#ifdef CONFIG_PM + .suspend = sc1200_suspend, + .resume = sc1200_resume, +#endif +}; + +static int __init sc1200_ide_init(void) +{ + return ide_pci_register_driver(&sc1200_pci_driver); +} + +static void __exit sc1200_ide_exit(void) +{ + pci_unregister_driver(&sc1200_pci_driver); +} + +module_init(sc1200_ide_init); +module_exit(sc1200_ide_exit); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c new file mode 100644 index 000000000..458e72e03 --- /dev/null +++ b/drivers/ide/serverworks.c @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 1998-2000 Michel Aubry + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz + * 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) + * + * HT1000: AKA BCM5785 - Hypertransport Southbridge for Opteron systems. IDE + * controller same as the CSB6. Single channel ATA100 only. + * + * Documentation: + * Available under NDA only. Errata info very hard to get. + * + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "serverworks" + +#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 int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + char *m = (char *)&drive->id[ATA_ID_PROD]; + + while (*list) + if (!strcmp(*list++, m)) + return 1; + return 0; +} + +static u8 svwks_udma_filter(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) { + return 0x1f; + } else if (dev->revision < SVWKS_CSB5_REVISION_NEW) { + return 0x07; + } else { + u8 btr = 0, mode, mask; + + pci_read_config_byte(dev, 0x5A, &btr); + mode = btr & 0x3; + + /* 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; + + switch(mode) { + case 3: mask = 0x3f; break; + case 2: mask = 0x1f; break; + case 1: mask = 0x07; break; + default: mask = 0x00; break; + } + + return mask; + } +} + +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: + case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE: + return 1; + default: + break; + } + return 0; +} + +static void svwks_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; + static const u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 }; + + struct pci_dev *dev = to_pci_dev(hwif->dev); + const u8 pio = drive->pio_mode - XFER_PIO_0; + + if (drive->dn >= ARRAY_SIZE(drive_pci)) + return; + + pci_write_config_byte(dev, drive_pci[drive->dn], pio_modes[pio]); + + if (svwks_csb_check(dev)) { + u16 csb_pio = 0; + + pci_read_config_word(dev, 0x4a, &csb_pio); + + csb_pio &= ~(0x0f << (4 * drive->dn)); + csb_pio |= (pio << (4 * drive->dn)); + + pci_write_config_word(dev, 0x4a, csb_pio); + } +} + +static void svwks_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; + static const u8 dma_modes[] = { 0x77, 0x21, 0x20 }; + static const u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 }; + + struct pci_dev *dev = to_pci_dev(hwif->dev); + const u8 speed = drive->dma_mode; + u8 unit = drive->dn & 1; + + u8 ultra_enable = 0, ultra_timing = 0, dma_timing = 0; + + if (drive->dn >= ARRAY_SIZE(drive_pci2)) + return; + + pci_read_config_byte(dev, (0x56|hwif->channel), &ultra_timing); + pci_read_config_byte(dev, 0x54, &ultra_enable); + + ultra_timing &= ~(0x0F << (4*unit)); + ultra_enable &= ~(0x01 << drive->dn); + + if (speed >= XFER_UDMA_0) { + dma_timing |= dma_modes[2]; + ultra_timing |= (udma_modes[speed - XFER_UDMA_0] << (4 * unit)); + ultra_enable |= (0x01 << drive->dn); + } else if (speed >= XFER_MW_DMA_0) + dma_timing |= dma_modes[speed - XFER_MW_DMA_0]; + + 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); +} + +static int init_chipset_svwks(struct pci_dev *dev) +{ + unsigned int reg; + u8 btr; + + /* 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) { + struct pci_dev *isa_dev = + pci_get_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 DRV_NAME " %s: UDMA not BIOS " + "enabled.\n", pci_name(dev)); + reg |= 0x00004000; /* enable UDMA/33 support */ + pci_write_config_dword(isa_dev, 0x64, reg); + pci_dev_put(isa_dev); + } + } + + /* 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_get_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); + pci_dev_put(findev); + } + outb_p(0x06, 0x0c00); + dev->irq = inb_p(0x0c01); + } else { + struct pci_dev * findev = NULL; + u8 reg41 = 0; + + findev = pci_get_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); + pci_dev_put(findev); + } + /* + * 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 |= (dev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; + pci_write_config_byte(dev, 0x5A, btr); + } + /* Setup HT1000 SouthBridge Controller - Single Channel Only */ + else if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) { + pci_read_config_byte(dev, 0x5A, &btr); + btr &= ~0x40; + btr |= 0x3; + pci_write_config_byte(dev, 0x5A, btr); + } + + return 0; +} + +static u8 ata66_svwks_svwks(ide_hwif_t *hwif) +{ + return ATA_CBL_PATA80; +} + +/* 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 u8 ata66_svwks_dell(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->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) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; + return ATA_CBL_PATA40; +} + +/* 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 u8 ata66_svwks_cobalt(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->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) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; + return ATA_CBL_PATA40; +} + +static u8 svwks_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + + /* 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); + + /* Per Specified Design by OEM, and ASIC Architect */ + if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) + return ATA_CBL_PATA80; + + return ATA_CBL_PATA40; +} + +static const struct ide_port_ops osb4_port_ops = { + .set_pio_mode = svwks_set_pio_mode, + .set_dma_mode = svwks_set_dma_mode, +}; + +static const struct ide_port_ops svwks_port_ops = { + .set_pio_mode = svwks_set_pio_mode, + .set_dma_mode = svwks_set_dma_mode, + .udma_filter = svwks_udma_filter, + .cable_detect = svwks_cable_detect, +}; + +static const struct ide_port_info serverworks_chipsets[] = { + { /* 0: OSB4 */ + .name = DRV_NAME, + .init_chipset = init_chipset_svwks, + .port_ops = &osb4_port_ops, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = 0x00, /* UDMA is problematic on OSB4 */ + }, + { /* 1: CSB5 */ + .name = DRV_NAME, + .init_chipset = init_chipset_svwks, + .port_ops = &svwks_port_ops, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, + { /* 2: CSB6 */ + .name = DRV_NAME, + .init_chipset = init_chipset_svwks, + .port_ops = &svwks_port_ops, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, + { /* 3: CSB6-2 */ + .name = DRV_NAME, + .init_chipset = init_chipset_svwks, + .port_ops = &svwks_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + }, + { /* 4: HT1000 */ + .name = DRV_NAME, + .init_chipset = init_chipset_svwks, + .port_ops = &svwks_port_ops, + .host_flags = IDE_HFLAG_SINGLE, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + } +}; + +/** + * 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 svwks_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d; + u8 idx = id->driver_data; + + d = serverworks_chipsets[idx]; + + if (idx == 1) + d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX; + else if (idx == 2 || idx == 3) { + if ((PCI_FUNC(dev->devfn) & 1) == 0) { + if (pci_resource_start(dev, 0) != 0x01f1) + d.host_flags |= IDE_HFLAG_NON_BOOTABLE; + d.host_flags |= IDE_HFLAG_SINGLE; + } else + d.host_flags &= ~IDE_HFLAG_SINGLE; + } + + return ide_pci_init_one(dev, &d, NULL); +} + +static const struct pci_device_id svwks_pci_tbl[] = { + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0 }, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 1 }, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2 }, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 3 }, + { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 4 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, svwks_pci_tbl); + +static struct pci_driver svwks_pci_driver = { + .name = "Serverworks_IDE", + .id_table = svwks_pci_tbl, + .probe = svwks_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init svwks_ide_init(void) +{ + return ide_pci_register_driver(&svwks_pci_driver); +} + +static void __exit svwks_ide_exit(void) +{ + pci_unregister_driver(&svwks_pci_driver); +} + +module_init(svwks_ide_init); +module_exit(svwks_ide_exit); + +MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick, Bartlomiej Zolnierkiewicz"); +MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c new file mode 100644 index 000000000..fdc8e8131 --- /dev/null +++ b/drivers/ide/setup-pci.c @@ -0,0 +1,682 @@ +/* + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 1995-1998 Mark Lord + * Copyright (C) 2007-2009 Bartlomiej Zolnierkiewicz + * + * May be copied or modified under the terms of the GNU General Public License + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ide.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> + +/** + * 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 %s: device not capable of full " + "native PCI mode\n", name, pci_name(dev)); + return -EOPNOTSUPP; + } + printk(KERN_INFO "%s %s: placing both ports into native PCI " + "mode\n", name, pci_name(dev)); + (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 %s: rewrite of PROGIF failed, " + "wanted 0x%04x, got 0x%04x\n", + name, pci_name(dev), progif | 5, progif); + return -EOPNOTSUPP; + } + } + return 0; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI +static int ide_pci_clear_simplex(unsigned long dma_base, const char *name) +{ + u8 dma_stat = inb(dma_base + 2); + + outb(dma_stat & 0x60, dma_base + 2); + dma_stat = inb(dma_base + 2); + + return (dma_stat & 0x80) ? 1 : 0; +} + +/** + * ide_pci_dma_base - setup BMIBA + * @hwif: IDE interface + * @d: IDE port info + * + * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space. + */ + +unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long dma_base = 0; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + return hwif->dma_base; + + if (hwif->mate && hwif->mate->dma_base) { + dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + } else { + u8 baridx = (d->host_flags & IDE_HFLAG_CS5520) ? 2 : 4; + + dma_base = pci_resource_start(dev, baridx); + + if (dma_base == 0) { + printk(KERN_ERR "%s %s: DMA base is invalid\n", + d->name, pci_name(dev)); + return 0; + } + } + + if (hwif->channel) + dma_base += 8; + + return dma_base; +} +EXPORT_SYMBOL_GPL(ide_pci_dma_base); + +int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 dma_stat; + + if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520)) + goto out; + + if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) { + if (ide_pci_clear_simplex(hwif->dma_base, d->name)) + printk(KERN_INFO "%s %s: simplex device: DMA forced\n", + d->name, pci_name(dev)); + goto out; + } + + /* + * If the device claims "simplex" DMA, this means that 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). + * + * FIXME: 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. + */ + dma_stat = hwif->dma_ops->dma_sff_read_status(hwif); + if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) { + printk(KERN_INFO "%s %s: simplex device: DMA disabled\n", + d->name, pci_name(dev)); + return -1; + } +out: + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_check_simplex); + +/* + * Set up BM-DMA capability (PnP BIOS should have done this) + */ +int ide_pci_set_master(struct pci_dev *dev, const char *name) +{ + u16 pcicmd; + + pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + + if ((pcicmd & PCI_COMMAND_MASTER) == 0) { + pci_set_master(dev); + + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || + (pcicmd & PCI_COMMAND_MASTER) == 0) { + printk(KERN_ERR "%s %s: error updating PCICMD\n", + name, pci_name(dev)); + return -EIO; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_set_master); +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ + +void ide_setup_pci_noise(struct pci_dev *dev, const struct ide_port_info *d) +{ + printk(KERN_INFO "%s %s: IDE controller (0x%04x:0x%04x rev 0x%02x)\n", + d->name, pci_name(dev), + dev->vendor, dev->device, dev->revision); +} +EXPORT_SYMBOL_GPL(ide_setup_pci_noise); + + +/** + * ide_pci_enable - do PCI enables + * @dev: PCI device + * @bars: PCI BARs mask + * @d: IDE port info + * + * Enable the IDE PCI device. We attempt to enable the device in full + * but if that fails then we only need IO space. The PCI code should + * have setup the proper resources for us already for controllers in + * legacy mode. + * + * Returns zero on success or an error code + */ + +static int ide_pci_enable(struct pci_dev *dev, int bars, + const struct ide_port_info *d) +{ + int ret; + + if (pci_enable_device(dev)) { + ret = pci_enable_device_io(dev); + if (ret < 0) { + printk(KERN_WARNING "%s %s: couldn't enable device\n", + d->name, pci_name(dev)); + goto out; + } + printk(KERN_WARNING "%s %s: BIOS configuration fixed\n", + d->name, pci_name(dev)); + } + + /* + * assume all devices can do 32-bit DMA for now, we can add + * a DMA mask field to the struct ide_port_info if we need it + * (or let lower level driver set the DMA mask) + */ + ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(32)); + if (ret < 0) { + printk(KERN_ERR "%s %s: can't set DMA mask\n", + d->name, pci_name(dev)); + goto out; + } + + ret = pci_request_selected_regions(dev, bars, d->name); + if (ret < 0) + printk(KERN_ERR "%s %s: can't reserve resources\n", + d->name, pci_name(dev)); +out: + return ret; +} + +/** + * ide_pci_configure - configure an unconfigured device + * @dev: PCI device + * @d: IDE port info + * + * 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, const struct ide_port_info *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 %s: device disabled (BIOS)\n", + d->name, pci_name(dev)); + return -ENODEV; + } + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { + printk(KERN_ERR "%s %s: error accessing PCI regs\n", + d->name, pci_name(dev)); + return -EIO; + } + if (!(pcicmd & PCI_COMMAND_IO)) { + printk(KERN_ERR "%s %s: unable to enable IDE controller\n", + d->name, pci_name(dev)); + return -ENXIO; + } + return 0; +} + +/** + * ide_pci_check_iomem - check a register is I/O + * @dev: PCI device + * @d: IDE port info + * @bar: BAR number + * + * Checks if a BAR is configured and points to MMIO space. If so, + * return an error code. Otherwise return 0 + */ + +static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *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 & IORESOURCE_IO) + return 0; + + /* Bad */ + return -EINVAL; +} + +/** + * ide_hw_configure - configure a struct ide_hw instance + * @dev: PCI device holding interface + * @d: IDE port info + * @port: port number + * @hw: struct ide_hw instance corresponding to this port + * + * 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 zero on success or an error code. + */ + +static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d, + unsigned int port, struct ide_hw *hw) +{ + unsigned long ctl = 0, base = 0; + + if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) { + if (ide_pci_check_iomem(dev, d, 2 * port) || + ide_pci_check_iomem(dev, d, 2 * port + 1)) { + printk(KERN_ERR "%s %s: I/O baseregs (BIOS) are " + "reported as MEM for port %d!\n", + d->name, pci_name(dev), port); + return -EINVAL; + } + + ctl = pci_resource_start(dev, 2*port+1); + base = pci_resource_start(dev, 2*port); + } else { + /* Use default values */ + ctl = port ? 0x374 : 0x3f4; + base = port ? 0x170 : 0x1f0; + } + + if (!base || !ctl) { + printk(KERN_ERR "%s %s: bad PCI BARs for port %d, skipping\n", + d->name, pci_name(dev), port); + return -EINVAL; + } + + memset(hw, 0, sizeof(*hw)); + hw->dev = &dev->dev; + ide_std_init_ports(hw, base, ctl | 2); + + return 0; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PCI +/** + * ide_hwif_setup_dma - configure DMA interface + * @hwif: IDE interface + * @d: IDE port info + * + * 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 + */ + +int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + + if ((d->host_flags & IDE_HFLAG_NO_AUTODMA) == 0 || + ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && + (dev->class & 0x80))) { + unsigned long base = ide_pci_dma_base(hwif, d); + + if (base == 0) + return -1; + + hwif->dma_base = base; + + if (hwif->dma_ops == NULL) + hwif->dma_ops = &sff_dma_ops; + + if (ide_pci_check_simplex(hwif, d) < 0) + return -1; + + if (ide_pci_set_master(dev, d->name) < 0) + return -1; + + if (hwif->host_flags & IDE_HFLAG_MMIO) + printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name); + else + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", + hwif->name, base, base + 7); + + hwif->extra_base = base + (hwif->channel ? 8 : 16); + + if (ide_allocate_dma_engine(hwif)) + return -1; + } + + return 0; +} +#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ + +/** + * ide_setup_pci_controller - set up IDE PCI + * @dev: PCI device + * @bars: PCI BARs mask + * @d: IDE port info + * @noisy: verbose flag + * + * 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, int bars, + const struct ide_port_info *d, int noisy) +{ + int ret; + u16 pcicmd; + + if (noisy) + ide_setup_pci_noise(dev, d); + + ret = ide_pci_enable(dev, bars, d); + if (ret < 0) + goto out; + + ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + if (ret < 0) { + printk(KERN_ERR "%s %s: error accessing PCI regs\n", + d->name, pci_name(dev)); + goto out_free_bars; + } + if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */ + ret = ide_pci_configure(dev, d); + if (ret < 0) + goto out_free_bars; + printk(KERN_INFO "%s %s: device enabled (Linux)\n", + d->name, pci_name(dev)); + } + + goto out; + +out_free_bars: + pci_release_selected_regions(dev, bars); +out: + return ret; +} + +/** + * ide_pci_setup_ports - configure ports/devices on PCI IDE + * @dev: PCI device + * @d: IDE port info + * @hw: struct ide_hw instances corresponding to this PCI IDE device + * @hws: struct ide_hw pointers table 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, const struct ide_port_info *d, + struct ide_hw *hw, struct ide_hw **hws) +{ + int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port; + u8 tmp; + + /* + * Set up the IDE ports + */ + + for (port = 0; port < channels; ++port) { + const struct ide_pci_enablebit *e = &d->enablebits[port]; + + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || + (tmp & e->mask) != e->val)) { + printk(KERN_INFO "%s %s: IDE port disabled\n", + d->name, pci_name(dev)); + continue; /* port not enabled */ + } + + if (ide_hw_configure(dev, d, port, hw + port)) + continue; + + *(hws + port) = hw + port; + } +} +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 struct ide_port_info; + * for all other chipsets, we just assume both interfaces are enabled. + */ +static int do_ide_setup_pci_device(struct pci_dev *dev, + const struct ide_port_info *d, + u8 noisy) +{ + int pciirq, ret; + + /* + * Can we trust the reported IRQ? + */ + pciirq = dev->irq; + + /* + * 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) : 0; + if (ret < 0) + goto out; + + if (ide_pci_is_in_compatibility_mode(dev)) { + if (noisy) + printk(KERN_INFO "%s %s: not 100%% native mode: will " + "probe irqs later\n", d->name, pci_name(dev)); + pciirq = 0; + } else if (!pciirq && noisy) { + printk(KERN_WARNING "%s %s: bad irq (%d): will probe later\n", + d->name, pci_name(dev), pciirq); + } else if (noisy) { + printk(KERN_INFO "%s %s: 100%% native mode on irq %d\n", + d->name, pci_name(dev), pciirq); + } + + ret = pciirq; +out: + return ret; +} + +int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2, + const struct ide_port_info *d, void *priv) +{ + struct pci_dev *pdev[] = { dev1, dev2 }; + struct ide_host *host; + int ret, i, n_ports = dev2 ? 4 : 2, bars; + struct ide_hw hw[4], *hws[] = { NULL, NULL, NULL, NULL }; + + if (d->host_flags & IDE_HFLAG_SINGLE) + bars = (1 << 2) - 1; + else + bars = (1 << 4) - 1; + + if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) { + if (d->host_flags & IDE_HFLAG_CS5520) + bars |= (1 << 2); + else + bars |= (1 << 4); + } + + for (i = 0; i < n_ports / 2; i++) { + ret = ide_setup_pci_controller(pdev[i], bars, d, !i); + if (ret < 0) { + if (i == 1) + pci_release_selected_regions(pdev[0], bars); + goto out; + } + + ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]); + } + + host = ide_host_alloc(d, hws, n_ports); + if (host == NULL) { + ret = -ENOMEM; + goto out_free_bars; + } + + host->dev[0] = &dev1->dev; + if (dev2) + host->dev[1] = &dev2->dev; + + host->host_priv = priv; + host->irq_flags = IRQF_SHARED; + + pci_set_drvdata(pdev[0], host); + if (dev2) + pci_set_drvdata(pdev[1], host); + + for (i = 0; i < n_ports / 2; i++) { + ret = do_ide_setup_pci_device(pdev[i], d, !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_free_bars; + + /* fixup IRQ */ + if (ide_pci_is_in_compatibility_mode(pdev[i])) { + hw[i*2].irq = pci_get_legacy_ide_irq(pdev[i], 0); + hw[i*2 + 1].irq = pci_get_legacy_ide_irq(pdev[i], 1); + } else + hw[i*2 + 1].irq = hw[i*2].irq = ret; + } + + ret = ide_host_register(host, d, hws); + if (ret) + ide_host_free(host); + else + goto out; + +out_free_bars: + i = n_ports / 2; + while (i--) + pci_release_selected_regions(pdev[i], bars); +out: + return ret; +} +EXPORT_SYMBOL_GPL(ide_pci_init_two); + +int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d, + void *priv) +{ + return ide_pci_init_two(dev, NULL, d, priv); +} +EXPORT_SYMBOL_GPL(ide_pci_init_one); + +void ide_pci_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL; + int bars; + + if (host->host_flags & IDE_HFLAG_SINGLE) + bars = (1 << 2) - 1; + else + bars = (1 << 4) - 1; + + if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) { + if (host->host_flags & IDE_HFLAG_CS5520) + bars |= (1 << 2); + else + bars |= (1 << 4); + } + + ide_host_remove(host); + + if (dev2) + pci_release_selected_regions(dev2, bars); + pci_release_selected_regions(dev, bars); + + if (dev2) + pci_disable_device(dev2); + pci_disable_device(dev); +} +EXPORT_SYMBOL_GPL(ide_pci_remove); + +#ifdef CONFIG_PM +int ide_pci_suspend(struct pci_dev *dev, pm_message_t state) +{ + pci_save_state(dev); + pci_disable_device(dev); + pci_set_power_state(dev, pci_choose_state(dev, state)); + + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_suspend); + +int ide_pci_resume(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + int rc; + + pci_set_power_state(dev, PCI_D0); + + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_restore_state(dev); + pci_set_master(dev); + + if (host->init_chipset) + host->init_chipset(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(ide_pci_resume); +#endif diff --git a/drivers/ide/siimage.c b/drivers/ide/siimage.c new file mode 100644 index 000000000..c4b20f350 --- /dev/null +++ b/drivers/ide/siimage.c @@ -0,0 +1,843 @@ +/* + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat + * Copyright (C) 2007-2008 MontaVista Software, Inc. + * Copyright (C) 2007-2008 Bartlomiej Zolnierkiewicz + * + * May be copied or modified under the terms of the GNU General Public License + * + * Documentation for CMD680: + * http://gkernel.sourceforge.net/specs/sii/sii-0680a-v1.31.pdf.bz2 + * + * Documentation for SiI 3112: + * http://gkernel.sourceforge.net/specs/sii/3112A_SiI-DS-0095-B2.pdf.bz2 + * + * Errata and other documentation only available under NDA. + * + * + * 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 necessary + * + * The Dell DRAC4 has some interesting features including effectively hot + * unplugging/replugging the virtual CD interface when the DRAC is reset. + * This often causes drivers/ide/siimage to panic but is ok with the rather + * smarter code in libata. + * + * TODO: + * - VDMA support + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/io.h> + +#define DRV_NAME "siimage" + +/** + * 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) +{ +#ifdef CONFIG_BLK_DEV_IDE_SATA + 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(); +#endif + 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(to_pci_dev(hwif->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 isn't performance + * critical. + */ + +static unsigned long siimage_selreg(ide_hwif_t *hwif, int r) +{ + unsigned long base = (unsigned long)hwif->hwif_data; + + base += 0xA0 + r; + if (hwif->host_flags & IDE_HFLAG_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 = drive->hwif; + unsigned long base = (unsigned long)hwif->hwif_data; + u8 unit = drive->dn & 1; + + base += 0xA0 + r; + if (hwif->host_flags & IDE_HFLAG_MMIO) + base += hwif->channel << 6; + else + base += hwif->channel << 4; + base |= unit << unit; + return base; +} + +static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + u8 tmp = 0; + + if (host->host_priv) + tmp = readb((void __iomem *)addr); + else + pci_read_config_byte(dev, addr, &tmp); + + return tmp; +} + +static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + u16 tmp = 0; + + if (host->host_priv) + tmp = readw((void __iomem *)addr); + else + pci_read_config_word(dev, addr, &tmp); + + return tmp; +} + +static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) + writeb(val, (void __iomem *)addr); + else + pci_write_config_byte(dev, addr, val); +} + +static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) + writew(val, (void __iomem *)addr); + else + pci_write_config_word(dev, addr, val); +} + +static void sil_iowrite32(struct pci_dev *dev, u32 val, unsigned long addr) +{ + struct ide_host *host = pci_get_drvdata(dev); + + if (host->host_priv) + writel(val, (void __iomem *)addr); + else + pci_write_config_dword(dev, addr, val); +} + +/** + * sil_udma_filter - compute UDMA mask + * @drive: IDE device + * + * Compute the available UDMA speeds for the device on the interface. + * + * For the CMD680 this depends on the clocking mode (scsc), for the + * SI3112 SATA controller life is a bit simpler. + */ + +static u8 sil_pata_udma_filter(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long base = (unsigned long)hwif->hwif_data; + u8 scsc, mask = 0; + + base += (hwif->host_flags & IDE_HFLAG_MMIO) ? 0x4A : 0x8A; + + scsc = sil_ioread8(dev, base); + + switch (scsc & 0x30) { + case 0x10: /* 133 */ + mask = ATA_UDMA6; + break; + case 0x20: /* 2xPCI */ + mask = ATA_UDMA6; + break; + case 0x00: /* 100 */ + mask = ATA_UDMA5; + break; + default: /* Disabled ? */ + BUG(); + } + + return mask; +} + +static u8 sil_sata_udma_filter(ide_drive_t *drive) +{ + char *m = (char *)&drive->id[ATA_ID_PROD]; + + return strstr(m, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6; +} + +/** + * sil_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * Load the timing settings for this device mode into the + * controller. + */ + +static void sil_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u16 tf_speed[] = { 0x328a, 0x2283, 0x1281, 0x10c3, 0x10c1 }; + static const u16 data_speed[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 }; + + struct pci_dev *dev = to_pci_dev(hwif->dev); + ide_drive_t *pair = ide_get_pair_dev(drive); + u32 speedt = 0; + u16 speedp = 0; + unsigned long addr = siimage_seldev(drive, 0x04); + unsigned long tfaddr = siimage_selreg(hwif, 0x02); + unsigned long base = (unsigned long)hwif->hwif_data; + const u8 pio = drive->pio_mode - XFER_PIO_0; + u8 tf_pio = pio; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84) + : (mmio ? 0xB4 : 0x80); + u8 mode = 0; + u8 unit = drive->dn & 1; + + /* trim *taskfile* PIO to the slowest of the master/slave */ + if (pair) { + u8 pair_pio = pair->pio_mode - XFER_PIO_0; + + if (pair_pio < tf_pio) + tf_pio = pair_pio; + } + + /* cheat for now and use the docs */ + speedp = data_speed[pio]; + speedt = tf_speed[tf_pio]; + + sil_iowrite16(dev, speedp, addr); + sil_iowrite16(dev, speedt, tfaddr); + + /* now set up IORDY */ + speedp = sil_ioread16(dev, tfaddr - 2); + speedp &= ~0x200; + + mode = sil_ioread8(dev, base + addr_mask); + mode &= ~(unit ? 0x30 : 0x03); + + if (ide_pio_need_iordy(drive, pio)) { + speedp |= 0x200; + mode |= unit ? 0x10 : 0x01; + } + + sil_iowrite16(dev, speedp, tfaddr - 2); + sil_iowrite8(dev, mode, base + addr_mask); +} + +/** + * sil_set_dma_mode - set host controller for DMA mode + * @hwif: port + * @drive: drive + * + * Tune the SiI chipset for the desired DMA mode. + */ + +static void sil_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static const u8 ultra6[] = { 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 }; + static const u8 ultra5[] = { 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01 }; + static const u16 dma[] = { 0x2208, 0x10C2, 0x10C1 }; + + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long base = (unsigned long)hwif->hwif_data; + u16 ultra = 0, multi = 0; + u8 mode = 0, unit = drive->dn & 1; + u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0; + u8 scsc = 0, addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84) + : (mmio ? 0xB4 : 0x80); + unsigned long ma = siimage_seldev(drive, 0x08); + unsigned long ua = siimage_seldev(drive, 0x0C); + const u8 speed = drive->dma_mode; + + scsc = sil_ioread8 (dev, base + (mmio ? 0x4A : 0x8A)); + mode = sil_ioread8 (dev, base + addr_mask); + multi = sil_ioread16(dev, ma); + ultra = sil_ioread16(dev, ua); + + mode &= ~(unit ? 0x30 : 0x03); + ultra &= ~0x3F; + scsc = ((scsc & 0x30) == 0x00) ? 0 : 1; + + scsc = is_sata(hwif) ? 1 : scsc; + + if (speed >= XFER_UDMA_0) { + multi = dma[2]; + ultra |= scsc ? ultra6[speed - XFER_UDMA_0] : + ultra5[speed - XFER_UDMA_0]; + mode |= unit ? 0x30 : 0x03; + } else { + multi = dma[speed - XFER_MW_DMA_0]; + mode |= unit ? 0x20 : 0x02; + } + + sil_iowrite8 (dev, mode, base + addr_mask); + sil_iowrite16(dev, multi, ma); + sil_iowrite16(dev, ultra, ua); +} + +static int sil_test_irq(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long addr = siimage_selreg(hwif, 1); + u8 val = sil_ioread8(dev, addr); + + /* Return 1 if INTRQ asserted */ + return (val & 8) ? 1 : 0; +} + +/** + * siimage_mmio_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_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + void __iomem *sata_error_addr + = (void __iomem *)hwif->sata_scr[SATA_ERROR_OFFSET]; + + if (sata_error_addr) { + unsigned long base = (unsigned long)hwif->hwif_data; + u32 ext_stat = readl((void __iomem *)(base + 0x10)); + u8 watchdog = 0; + + if (ext_stat & ((hwif->channel) ? 0x40 : 0x10)) { + u32 sata_error = readl(sata_error_addr); + + writel(sata_error, sata_error_addr); + watchdog = (sata_error & 0x00680000) ? 1 : 0; + printk(KERN_WARNING "%s: sata_error = 0x%08x, " + "watchdog = %d, %s\n", + drive->name, sata_error, watchdog, __func__); + } else + watchdog = (ext_stat & 0x8000) ? 1 : 0; + + ext_stat >>= 16; + if (!(ext_stat & 0x0404) && !watchdog) + return 0; + } + + /* return 1 if INTR asserted */ + if (readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)) & 4) + return 1; + + return 0; +} + +static int siimage_dma_test_irq(ide_drive_t *drive) +{ + if (drive->hwif->host_flags & IDE_HFLAG_MMIO) + return siimage_mmio_dma_test_irq(drive); + else + return ide_dma_test_irq(drive); +} + +/** + * sil_sata_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 blk_status_t sil_sata_reset_poll(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + void __iomem *sata_status_addr + = (void __iomem *)hwif->sata_scr[SATA_STATUS_OFFSET]; + + if (sata_status_addr) { + /* SATA Status is available only when in MMIO mode */ + u32 sata_stat = readl(sata_status_addr); + + if ((sata_stat & 0x03) != 0x03) { + printk(KERN_WARNING "%s: reset phy dead, status=0x%08x\n", + hwif->name, sata_stat); + return BLK_STS_IOERR; + } + } + + return BLK_STS_OK; +} + +/** + * sil_sata_pre_reset - reset hook + * @drive: IDE device being reset + * + * For the SATA devices we need to handle recalibration/geometry + * differently + */ + +static void sil_sata_pre_reset(ide_drive_t *drive) +{ + if (drive->media == ide_disk) { + drive->special_flags &= + ~(IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE); + } +} + +/** + * init_chipset_siimage - set up an SI device + * @dev: PCI device + * + * Perform the initial PCI set up for this device. Attempt to switch + * to 133 MHz clocking if the system isn't already set up to do it. + */ + +static int init_chipset_siimage(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + void __iomem *ioaddr = host->host_priv; + unsigned long base, scsc_addr; + u8 rev = dev->revision, tmp; + + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, rev ? 1 : 255); + + if (ioaddr) + pci_set_master(dev); + + base = (unsigned long)ioaddr; + + if (ioaddr && pdev_is_sata(dev)) { + u32 tmp32, irq_mask; + + /* make sure IDE0/1 interrupts are not masked */ + irq_mask = (1 << 22) | (1 << 23); + tmp32 = readl(ioaddr + 0x48); + if (tmp32 & irq_mask) { + tmp32 &= ~irq_mask; + writel(tmp32, ioaddr + 0x48); + readl(ioaddr + 0x48); /* flush */ + } + writel(0, ioaddr + 0x148); + writel(0, ioaddr + 0x1C8); + } + + sil_iowrite8(dev, 0, base ? (base + 0xB4) : 0x80); + sil_iowrite8(dev, 0, base ? (base + 0xF4) : 0x84); + + scsc_addr = base ? (base + 0x4A) : 0x8A; + tmp = sil_ioread8(dev, scsc_addr); + + switch (tmp & 0x30) { + case 0x00: + /* On 100 MHz clocking, try and switch to 133 MHz */ + sil_iowrite8(dev, tmp | 0x10, scsc_addr); + break; + case 0x30: + /* Clocking is disabled, attempt to force 133MHz clocking. */ + sil_iowrite8(dev, tmp & ~0x20, scsc_addr); + case 0x10: + /* On 133Mhz clocking. */ + break; + case 0x20: + /* On PCIx2 clocking. */ + break; + } + + tmp = sil_ioread8(dev, scsc_addr); + + sil_iowrite8 (dev, 0x72, base + 0xA1); + sil_iowrite16(dev, 0x328A, base + 0xA2); + sil_iowrite32(dev, 0x62DD62DD, base + 0xA4); + sil_iowrite32(dev, 0x43924392, base + 0xA8); + sil_iowrite32(dev, 0x40094009, base + 0xAC); + sil_iowrite8 (dev, 0x72, base ? (base + 0xE1) : 0xB1); + sil_iowrite16(dev, 0x328A, base ? (base + 0xE2) : 0xB2); + sil_iowrite32(dev, 0x62DD62DD, base ? (base + 0xE4) : 0xB4); + sil_iowrite32(dev, 0x43924392, base ? (base + 0xE8) : 0xB8); + sil_iowrite32(dev, 0x40094009, base ? (base + 0xEC) : 0xBC); + + if (base && pdev_is_sata(dev)) { + writel(0xFFFF0000, ioaddr + 0x108); + writel(0xFFFF0000, ioaddr + 0x188); + writel(0x00680000, ioaddr + 0x148); + writel(0x00680000, ioaddr + 0x1C8); + } + + /* report the clocking mode of the controller */ + if (!pdev_is_sata(dev)) { + static const char *clk_str[] = + { "== 100", "== 133", "== 2X PCI", "DISABLED!" }; + + tmp >>= 4; + printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n", + pci_name(dev), clk_str[tmp & 3]); + } + + 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 isn't a standard defined layout for them this time. + * + * The hardware supports buffered taskfiles and also some rather nice + * extended PRD tables. For better SI3112 support use the libata driver + */ + +static void init_mmio_iops_siimage(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + void *addr = host->host_priv; + u8 ch = hwif->channel; + struct ide_io_ports *io_ports = &hwif->io_ports; + unsigned long base; + + /* + * Fill in the basic hwif bits + */ + hwif->host_flags |= IDE_HFLAG_MMIO; + + hwif->hwif_data = addr; + + /* + * Now set up the hw. We have to do this ourselves as the + * MMIO layout isn't the same as the standard port based I/O. + */ + memset(io_ports, 0, sizeof(*io_ports)); + + 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. + */ + io_ports->data_addr = base; + io_ports->error_addr = base + 1; + io_ports->nsect_addr = base + 2; + io_ports->lbal_addr = base + 3; + io_ports->lbam_addr = base + 4; + io_ports->lbah_addr = base + 5; + io_ports->device_addr = base + 6; + io_ports->status_addr = base + 7; + io_ports->ctl_addr = base + 10; + + 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->irq = dev->irq; + + hwif->dma_base = (unsigned long)addr + (ch ? 0x08 : 0x00); +} + +static int is_dev_seagate_sata(ide_drive_t *drive) +{ + const char *s = (const char *)&drive->id[ATA_ID_PROD]; + unsigned len = strnlen(s, ATA_ID_PROD_LEN); + + 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; +} + +/** + * sil_quirkproc - post probe fixups + * @drive: drive + * + * 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 sil_quirkproc(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + + /* Try and rise the rqsize */ + if (!is_sata(hwif) || !is_dev_seagate_sata(drive)) + 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 init_iops_siimage(ide_hwif_t *hwif) +{ + struct ide_host *host = dev_get_drvdata(hwif->dev); + + hwif->hwif_data = NULL; + + /* Pessimal until we finish probing */ + hwif->rqsize = 15; + + if (host->host_priv) + init_mmio_iops_siimage(hwif); +} + +/** + * sil_cable_detect - cable detection + * @hwif: interface to check + * + * Check for the presence of an ATA66 capable cable on the interface. + */ + +static u8 sil_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long addr = siimage_selreg(hwif, 0); + u8 ata66 = sil_ioread8(dev, addr); + + return (ata66 & 0x01) ? ATA_CBL_PATA80 : ATA_CBL_PATA40; +} + +static const struct ide_port_ops sil_pata_port_ops = { + .set_pio_mode = sil_set_pio_mode, + .set_dma_mode = sil_set_dma_mode, + .quirkproc = sil_quirkproc, + .test_irq = sil_test_irq, + .udma_filter = sil_pata_udma_filter, + .cable_detect = sil_cable_detect, +}; + +static const struct ide_port_ops sil_sata_port_ops = { + .set_pio_mode = sil_set_pio_mode, + .set_dma_mode = sil_set_dma_mode, + .reset_poll = sil_sata_reset_poll, + .pre_reset = sil_sata_pre_reset, + .quirkproc = sil_quirkproc, + .test_irq = sil_test_irq, + .udma_filter = sil_sata_udma_filter, + .cable_detect = sil_cable_detect, +}; + +static const struct ide_dma_ops sil_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = siimage_dma_test_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_lost_irq = ide_dma_lost_irq, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +#define DECLARE_SII_DEV(p_ops) \ + { \ + .name = DRV_NAME, \ + .init_chipset = init_chipset_siimage, \ + .init_iops = init_iops_siimage, \ + .port_ops = p_ops, \ + .dma_ops = &sil_dma_ops, \ + .pio_mask = ATA_PIO4, \ + .mwdma_mask = ATA_MWDMA2, \ + .udma_mask = ATA_UDMA6, \ + } + +static const struct ide_port_info siimage_chipsets[] = { + /* 0: SiI680 */ DECLARE_SII_DEV(&sil_pata_port_ops), + /* 1: SiI3112 */ DECLARE_SII_DEV(&sil_sata_port_ops) +}; + +/** + * siimage_init_one - PCI layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an SiI680 or SiI3112 controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int siimage_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + void __iomem *ioaddr = NULL; + resource_size_t bar5 = pci_resource_start(dev, 5); + unsigned long barsize = pci_resource_len(dev, 5); + int rc; + struct ide_port_info d; + u8 idx = id->driver_data; + u8 BA5_EN; + + d = siimage_chipsets[idx]; + + if (idx) { + static int first = 1; + + if (first) { + printk(KERN_INFO DRV_NAME ": For full SATA support you " + "should use the libata sata_sil module.\n"); + first = 0; + } + + d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA; + } + + rc = pci_enable_device(dev); + if (rc) + return rc; + + pci_read_config_byte(dev, 0x8A, &BA5_EN); + if ((BA5_EN & 0x01) || bar5) { + /* + * 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, d.name)) { + printk(KERN_WARNING DRV_NAME " %s: MMIO ports not " + "available\n", pci_name(dev)); + } else { + ioaddr = pci_ioremap_bar(dev, 5); + if (ioaddr == NULL) + release_mem_region(bar5, barsize); + } + } + + rc = ide_pci_init_one(dev, &d, ioaddr); + if (rc) { + if (ioaddr) { + iounmap(ioaddr); + release_mem_region(bar5, barsize); + } + pci_disable_device(dev); + } + + return rc; +} + +static void siimage_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + void __iomem *ioaddr = host->host_priv; + + ide_pci_remove(dev); + + if (ioaddr) { + resource_size_t bar5 = pci_resource_start(dev, 5); + unsigned long barsize = pci_resource_len(dev, 5); + + iounmap(ioaddr); + release_mem_region(bar5, barsize); + } + + pci_disable_device(dev); +} + +static const struct pci_device_id siimage_pci_tbl[] = { + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), 0 }, +#ifdef CONFIG_BLK_DEV_IDE_SATA + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_3112), 1 }, + { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 1 }, +#endif + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, siimage_pci_tbl); + +static struct pci_driver siimage_pci_driver = { + .name = "SiI_IDE", + .id_table = siimage_pci_tbl, + .probe = siimage_init_one, + .remove = siimage_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init siimage_ide_init(void) +{ + return ide_pci_register_driver(&siimage_pci_driver); +} + +static void __exit siimage_ide_exit(void) +{ + pci_unregister_driver(&siimage_pci_driver); +} + +module_init(siimage_ide_init); +module_exit(siimage_ide_exit); + +MODULE_AUTHOR("Andre Hedrick, Alan Cox"); +MODULE_DESCRIPTION("PCI driver module for SiI IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c new file mode 100644 index 000000000..1a700bef6 --- /dev/null +++ b/drivers/ide/sis5513.c @@ -0,0 +1,637 @@ +/* + * 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> + * Copyright (C) 2007-2009 Bartlomiej Zolnierkiewicz + * + * 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/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> + +#define DRV_NAME "sis5513" + +/* registers layout and init values are chipset family dependent */ +#undef ATA_16 +#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[] = { + { "SiS968", PCI_DEVICE_ID_SI_968, ATA_133 }, + { "SiS966", PCI_DEVICE_ID_SI_966, ATA_133 }, + { "SiS965", PCI_DEVICE_ID_SI_965, ATA_133 }, + { "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 }, + { "SiS5517", PCI_DEVICE_ID_SI_5517, 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), + different 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)" +}; + +/* + * Configuration functions + */ + +static u8 sis_ata133_get_base(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u32 reg54 = 0; + + pci_read_config_dword(dev, 0x54, ®54); + + return ((reg54 & 0x40000000) ? 0x70 : 0x40) + drive->dn * 4; +} + +static void sis_ata16_program_timings(ide_drive_t *drive, const u8 mode) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u16 t1 = 0; + u8 drive_pci = 0x40 + drive->dn * 2; + + const u16 pio_timings[] = { 0x000, 0x607, 0x404, 0x303, 0x301 }; + const u16 mwdma_timings[] = { 0x008, 0x302, 0x301 }; + + pci_read_config_word(dev, drive_pci, &t1); + + /* clear active/recovery timings */ + t1 &= ~0x070f; + if (mode >= XFER_MW_DMA_0) { + if (chipset_family > ATA_16) + t1 &= ~0x8000; /* disable UDMA */ + t1 |= mwdma_timings[mode - XFER_MW_DMA_0]; + } else + t1 |= pio_timings[mode - XFER_PIO_0]; + + pci_write_config_word(dev, drive_pci, t1); +} + +static void sis_ata100_program_timings(ide_drive_t *drive, const u8 mode) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u8 t1, drive_pci = 0x40 + drive->dn * 2; + + /* timing bits: 7:4 active 3:0 recovery */ + const u8 pio_timings[] = { 0x00, 0x67, 0x44, 0x33, 0x31 }; + const u8 mwdma_timings[] = { 0x08, 0x32, 0x31 }; + + if (mode >= XFER_MW_DMA_0) { + u8 t2 = 0; + + pci_read_config_byte(dev, drive_pci, &t2); + t2 &= ~0x80; /* disable UDMA */ + pci_write_config_byte(dev, drive_pci, t2); + + t1 = mwdma_timings[mode - XFER_MW_DMA_0]; + } else + t1 = pio_timings[mode - XFER_PIO_0]; + + pci_write_config_byte(dev, drive_pci + 1, t1); +} + +static void sis_ata133_program_timings(ide_drive_t *drive, const u8 mode) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u32 t1 = 0; + u8 drive_pci = sis_ata133_get_base(drive), clk, idx; + + pci_read_config_dword(dev, drive_pci, &t1); + + t1 &= 0xc0c00fff; + clk = (t1 & 0x08) ? ATA_133 : ATA_100; + if (mode >= XFER_MW_DMA_0) { + t1 &= ~0x04; /* disable UDMA */ + idx = mode - XFER_MW_DMA_0 + 5; + } else + idx = mode - XFER_PIO_0; + t1 |= ini_time_value[clk][idx] << 12; + t1 |= act_time_value[clk][idx] << 16; + t1 |= rco_time_value[clk][idx] << 24; + + pci_write_config_dword(dev, drive_pci, t1); +} + +static void sis_program_timings(ide_drive_t *drive, const u8 mode) +{ + if (chipset_family < ATA_100) /* ATA_16/33/66/100a */ + sis_ata16_program_timings(drive, mode); + else if (chipset_family < ATA_133) /* ATA_100/133a */ + sis_ata100_program_timings(drive, mode); + else /* ATA_133 */ + sis_ata133_program_timings(drive, mode); +} + +static void config_drive_art_rwp(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 reg4bh = 0; + u8 rw_prefetch = 0; + + pci_read_config_byte(dev, 0x4b, ®4bh); + + rw_prefetch = reg4bh & ~(0x11 << drive->dn); + + if (drive->media == ide_disk) + rw_prefetch |= 0x11 << drive->dn; + + if (reg4bh != rw_prefetch) + pci_write_config_byte(dev, 0x4b, rw_prefetch); +} + +static void sis_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + config_drive_art_rwp(drive); + sis_program_timings(drive, drive->pio_mode); +} + +static void sis_ata133_program_udma_timings(ide_drive_t *drive, const u8 mode) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u32 regdw = 0; + u8 drive_pci = sis_ata133_get_base(drive), clk, idx; + + pci_read_config_dword(dev, drive_pci, ®dw); + + regdw |= 0x04; + regdw &= 0xfffff00f; + /* check if ATA133 enable */ + clk = (regdw & 0x08) ? ATA_133 : ATA_100; + idx = mode - XFER_UDMA_0; + regdw |= cycle_time_value[clk][idx] << 4; + regdw |= cvs_time_value[clk][idx] << 8; + + pci_write_config_dword(dev, drive_pci, regdw); +} + +static void sis_ata33_program_udma_timings(ide_drive_t *drive, const u8 mode) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u8 drive_pci = 0x40 + drive->dn * 2, reg = 0, i = chipset_family; + + pci_read_config_byte(dev, drive_pci + 1, ®); + + /* force the UDMA bit on if we want to use UDMA */ + reg |= 0x80; + /* clean reg cycle time bits */ + reg &= ~((0xff >> (8 - cycle_time_range[i])) << cycle_time_offset[i]); + /* set reg cycle time bits */ + reg |= cycle_time_value[i][mode - XFER_UDMA_0] << cycle_time_offset[i]; + + pci_write_config_byte(dev, drive_pci + 1, reg); +} + +static void sis_program_udma_timings(ide_drive_t *drive, const u8 mode) +{ + if (chipset_family >= ATA_133) /* ATA_133 */ + sis_ata133_program_udma_timings(drive, mode); + else /* ATA_33/66/100a/100/133a */ + sis_ata33_program_udma_timings(drive, mode); +} + +static void sis_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + const u8 speed = drive->dma_mode; + + if (speed >= XFER_UDMA_0) + sis_program_udma_timings(drive, speed); + else + sis_program_timings(drive, speed); +} + +static u8 sis_ata133_udma_filter(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u32 regdw = 0; + u8 drive_pci = sis_ata133_get_base(drive); + + pci_read_config_dword(dev, drive_pci, ®dw); + + /* if ATA133 disable, we should not set speed above UDMA5 */ + return (regdw & 0x08) ? ATA_UDMA6 : ATA_UDMA5; +} + +static int sis_find_family(struct pci_dev *dev) +{ + struct pci_dev *host; + int i = 0; + + chipset_family = 0; + + for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !chipset_family; i++) { + + host = pci_get_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) { + if (host->revision >= 0x30) + chipset_family = ATA_100a; + } + pci_dev_put(host); + + printk(KERN_INFO DRV_NAME " %s: %s %s controller\n", + pci_name(dev), 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 DRV_NAME " %s: SiS 962/963 MuTIOL IDE UDMA133 controller\n", + pci_name(dev)); + chipset_family = ATA_133; + + /* Check for 5513 compatibility 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 DRV_NAME " %s: Switching to 5513 register mapping\n", + pci_name(dev)); + } + } + } + + if (!chipset_family) { /* Belongs to pci-quirks */ + + struct pci_dev *lpc_bridge; + u16 trueid; + u8 prefctl; + u8 idecfg; + + 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_get_slot(dev->bus, 0x10); /* Bus 0, Dev 2, Fn 0 */ + pci_read_config_byte(dev, 0x49, &prefctl); + pci_dev_put(lpc_bridge); + + if (lpc_bridge->revision == 0x10 && (prefctl & 0x80)) { + printk(KERN_INFO DRV_NAME " %s: SiS 961B MuTIOL IDE UDMA133 controller\n", + pci_name(dev)); + chipset_family = ATA_133a; + } else { + printk(KERN_INFO DRV_NAME " %s: SiS 961 MuTIOL IDE UDMA100 controller\n", + pci_name(dev)); + chipset_family = ATA_100; + } + } + } + + return chipset_family; +} + +static int init_chipset_sis5513(struct pci_dev *dev) +{ + /* 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); + fallthrough; + 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; + } + + return 0; +} + +struct sis_laptop { + u16 device; + u16 subvendor; + u16 subdevice; +}; + +static const struct sis_laptop sis_laptop[] = { + /* devid, subvendor, subdev */ + { 0x5513, 0x1043, 0x1107 }, /* ASUS A6K */ + { 0x5513, 0x1734, 0x105f }, /* FSC Amilo A1630 */ + { 0x5513, 0x1071, 0x8640 }, /* EasyNote K5305 */ + /* end marker */ + { 0, } +}; + +static u8 sis_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + const struct sis_laptop *lap = &sis_laptop[0]; + u8 ata66 = 0; + + while (lap->device) { + if (lap->device == pdev->device && + lap->subvendor == pdev->subsystem_vendor && + lap->subdevice == pdev->subsystem_device) + return ATA_CBL_PATA40_SHORT; + lap++; + } + + if (chipset_family >= ATA_133) { + u16 regw = 0; + u16 reg_addr = hwif->channel ? 0x52: 0x50; + pci_read_config_word(pdev, 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(pdev, 0x48, ®48h); + ata66 = (reg48h & mask) ? 0 : 1; + } + + return ata66 ? ATA_CBL_PATA80 : ATA_CBL_PATA40; +} + +static const struct ide_port_ops sis_port_ops = { + .set_pio_mode = sis_set_pio_mode, + .set_dma_mode = sis_set_dma_mode, + .cable_detect = sis_cable_detect, +}; + +static const struct ide_port_ops sis_ata133_port_ops = { + .set_pio_mode = sis_set_pio_mode, + .set_dma_mode = sis_set_dma_mode, + .udma_filter = sis_ata133_udma_filter, + .cable_detect = sis_cable_detect, +}; + +static const struct ide_port_info sis5513_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_sis5513, + .enablebits = { {0x4a, 0x02, 0x02}, {0x4a, 0x04, 0x04} }, + .host_flags = IDE_HFLAG_NO_AUTODMA, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, +}; + +static int sis5513_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d = sis5513_chipset; + u8 udma_rates[] = { 0x00, 0x00, 0x07, 0x1f, 0x3f, 0x3f, 0x7f, 0x7f }; + int rc; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + if (sis_find_family(dev) == 0) + return -ENOTSUPP; + + if (chipset_family >= ATA_133) + d.port_ops = &sis_ata133_port_ops; + else + d.port_ops = &sis_port_ops; + + d.udma_mask = udma_rates[chipset_family]; + + return ide_pci_init_one(dev, &d, NULL); +} + +static void sis5513_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_disable_device(dev); +} + +static const struct pci_device_id sis5513_pci_tbl[] = { + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_5513), 0 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_5518), 0 }, + { PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_1180), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sis5513_pci_tbl); + +static struct pci_driver sis5513_pci_driver = { + .name = "SIS_IDE", + .id_table = sis5513_pci_tbl, + .probe = sis5513_init_one, + .remove = sis5513_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init sis5513_ide_init(void) +{ + return ide_pci_register_driver(&sis5513_pci_driver); +} + +static void __exit sis5513_ide_exit(void) +{ + pci_unregister_driver(&sis5513_pci_driver); +} + +module_init(sis5513_ide_init); +module_exit(sis5513_ide_exit); + +MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik"); +MODULE_DESCRIPTION("PCI driver module for SIS IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c new file mode 100644 index 000000000..5c24c420c --- /dev/null +++ b/drivers/ide/sl82c105.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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 + * + * Copyright (C) 2006-2007,2009 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> + +#include <asm/io.h> + +#define DRV_NAME "sl82c105" + +/* + * 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 runaway timings. + */ +static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio) +{ + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + unsigned int cmd_on, cmd_off; + u8 iordy = 0; + + cmd_on = (t->active + 29) / 30; + cmd_off = (ide_pio_cycle_time(drive, pio) - 30 * cmd_on + 29) / 30; + + if (cmd_on == 0) + cmd_on = 1; + + if (cmd_off == 0) + cmd_off = 1; + + if (ide_pio_need_iordy(drive, pio)) + iordy = 0x40; + + return (cmd_on - 1) << 8 | (cmd_off - 1) | iordy; +} + +/* + * Configure the chipset for PIO mode. + */ +static void sl82c105_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + int reg = 0x44 + drive->dn * 4; + u16 drv_ctrl; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + drv_ctrl = get_pio_timings(drive, pio); + + /* + * Store the PIO timings so that we can restore them + * in case DMA will be turned off... + */ + timings &= 0xffff0000; + timings |= drv_ctrl; + ide_set_drivedata(drive, (void *)timings); + + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word (dev, reg, &drv_ctrl); + + printk(KERN_DEBUG "%s: selected %s (%dns) (%04X)\n", drive->name, + ide_xfer_verbose(pio + XFER_PIO_0), + ide_pio_cycle_time(drive, pio), drv_ctrl); +} + +/* + * Configure the chipset for DMA mode. + */ +static void sl82c105_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200}; + unsigned long timings = (unsigned long)ide_get_drivedata(drive); + u16 drv_ctrl; + const u8 speed = drive->dma_mode; + + drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0]; + + /* + * Store the DMA timings so that we can actually program + * them when DMA will be turned on... + */ + timings &= 0x0000ffff; + timings |= (unsigned long)drv_ctrl << 16; + ide_set_drivedata(drive, (void *)timings); +} + +static int sl82c105_test_irq(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; + + pci_read_config_dword(dev, 0x40, &val); + + return (val & mask) ? 1 : 0; +} + +/* + * 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 void sl82c105_dma_lost_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA; + u8 dma_cmd; + + printk(KERN_WARNING "sl82c105: lost IRQ, resetting host\n"); + + /* + * Check the raw interrupt from the drive. + */ + pci_read_config_dword(dev, 0x40, &val); + if (val & mask) + printk(KERN_INFO "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. + */ + dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + if (dma_cmd & 1) { + outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD); + printk(KERN_INFO "sl82c105: DMA was enabled\n"); + } + + sl82c105_reset_host(dev); +} + +/* + * 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_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct pci_dev *dev = to_pci_dev(hwif->dev); + int reg = 0x44 + drive->dn * 4; + + pci_write_config_word(dev, reg, + (unsigned long)ide_get_drivedata(drive) >> 16); + + sl82c105_reset_host(dev); + ide_dma_start(drive); +} + +static void sl82c105_dma_clear(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + + sl82c105_reset_host(dev); +} + +static int sl82c105_dma_end(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + int reg = 0x44 + drive->dn * 4; + int ret = ide_dma_end(drive); + + pci_write_config_word(dev, reg, + (unsigned long)ide_get_drivedata(drive)); + + return ret; +} + +/* + * ATA reset will clear the 16 bits mode in the control + * register, we need to reprogram it + */ +static void sl82c105_resetproc(ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(drive->hwif->dev); + u32 val; + + pci_read_config_dword(dev, 0x40, &val); + val |= (CTRL_P1F16 | CTRL_P0F16); + pci_write_config_dword(dev, 0x40, val); +} + +/* + * Return the revision of the Winbond bridge + * which this function is part of. + */ +static u8 sl82c105_bridge_revision(struct pci_dev *dev) +{ + struct pci_dev *bridge; + + /* + * The bridge should be part of the same device, but function 0. + */ + bridge = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), + 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) { + pci_dev_put(bridge); + return -1; + } + /* + * We need to find function 0's revision, not function 1 + */ + pci_dev_put(bridge); + + return bridge->revision; +} + +/* + * 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 int init_chipset_sl82c105(struct pci_dev *dev) +{ + u32 val; + + pci_read_config_dword(dev, 0x40, &val); + val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16; + pci_write_config_dword(dev, 0x40, val); + + return 0; +} + +static const struct ide_port_ops sl82c105_port_ops = { + .set_pio_mode = sl82c105_set_pio_mode, + .set_dma_mode = sl82c105_set_dma_mode, + .resetproc = sl82c105_resetproc, + .test_irq = sl82c105_test_irq, +}; + +static const struct ide_dma_ops sl82c105_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = sl82c105_dma_start, + .dma_end = sl82c105_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = sl82c105_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_clear = sl82c105_dma_clear, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info sl82c105_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_sl82c105, + .enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, + .port_ops = &sl82c105_port_ops, + .dma_ops = &sl82c105_dma_ops, + .host_flags = IDE_HFLAG_IO_32BIT | + IDE_HFLAG_UNMASK_IRQS | + IDE_HFLAG_SERIALIZE_DMA | + IDE_HFLAG_NO_AUTODMA, + .pio_mask = ATA_PIO5, + .mwdma_mask = ATA_MWDMA2, +}; + +static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct ide_port_info d = sl82c105_chipset; + u8 rev = sl82c105_bridge_revision(dev); + + if (rev <= 5) { + /* + * Never ever EVER under any circumstances enable + * DMA when the bridge is this old. + */ + printk(KERN_INFO DRV_NAME ": Winbond W83C553 bridge " + "revision %d, BM-DMA disabled\n", rev); + d.dma_ops = NULL; + d.mwdma_mask = 0; + d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA; + } + + return ide_pci_init_one(dev, &d, NULL); +} + +static const struct pci_device_id sl82c105_pci_tbl[] = { + { PCI_VDEVICE(WINBOND, PCI_DEVICE_ID_WINBOND_82C105), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl); + +static struct pci_driver sl82c105_pci_driver = { + .name = "W82C105_IDE", + .id_table = sl82c105_pci_tbl, + .probe = sl82c105_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init sl82c105_ide_init(void) +{ + return ide_pci_register_driver(&sl82c105_pci_driver); +} + +static void __exit sl82c105_ide_exit(void) +{ + pci_unregister_driver(&sl82c105_pci_driver); +} + +module_init(sl82c105_ide_init); +module_exit(sl82c105_ide_exit); + +MODULE_DESCRIPTION("PCI driver module for W82C105 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c new file mode 100644 index 000000000..f521d5ebf --- /dev/null +++ b/drivers/ide/slc90e66.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com> + * + * This is a look-alike variation of the ICH0 PIIX4 Ultra-66, + * but this keeps the ISA-Bridge and slots alive. + * + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "slc90e66" + +static DEFINE_SPINLOCK(slc90e66_lock); + +static void slc90e66_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + int is_slave = drive->dn & 1; + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + u8 slave_data; + int control = 0; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + /* ISP RTC */ + static const u8 timings[][2] = { + { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + spin_lock_irqsave(&slc90e66_lock, flags); + pci_read_config_word(dev, master_port, &master_data); + + if (pio > 1) + control |= 1; /* Programmable timing on */ + if (drive->media == ide_disk) + control |= 4; /* Prefetch, post write */ + if (ide_pio_need_iordy(drive, pio)) + control |= 2; /* IORDY */ + if (is_slave) { + master_data |= 0x4000; + master_data &= ~0x0070; + if (pio > 1) { + /* Set PPE, IE and TIME */ + master_data |= control << 4; + } + pci_read_config_byte(dev, slave_port, &slave_data); + slave_data &= hwif->channel ? 0x0f : 0xf0; + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << + (hwif->channel ? 4 : 0); + } else { + master_data &= ~0x3307; + if (pio > 1) { + /* enable PPE, IE and TIME */ + master_data |= control; + } + 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(&slc90e66_lock, flags); +} + +static void slc90e66_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 maslave = hwif->channel ? 0x42 : 0x40; + int sitre = 0, a_speed = 7 << (drive->dn * 4); + int u_speed = 0, u_flag = 1 << drive->dn; + u16 reg4042, reg44, reg48, reg4a; + const u8 speed = drive->dma_mode; + + 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); + + if (speed >= XFER_UDMA_0) { + u_speed = (speed - XFER_UDMA_0) << (drive->dn * 4); + + if (!(reg48 & u_flag)) + pci_write_config_word(dev, 0x48, reg48|u_flag); + if ((reg4a & a_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 { + const u8 mwdma_to_pio[] = { 0, 3, 4 }; + + if (reg48 & u_flag) + pci_write_config_word(dev, 0x48, reg48 & ~u_flag); + if (reg4a & a_speed) + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + + if (speed >= XFER_MW_DMA_0) + drive->pio_mode = + mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0; + else + drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */ + + slc90e66_set_pio_mode(hwif, drive); + } +} + +static u8 slc90e66_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u8 reg47 = 0, mask = hwif->channel ? 0x01 : 0x02; + + pci_read_config_byte(dev, 0x47, ®47); + + /* bit[0(1)]: 0:80, 1:40 */ + return (reg47 & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +static const struct ide_port_ops slc90e66_port_ops = { + .set_pio_mode = slc90e66_set_pio_mode, + .set_dma_mode = slc90e66_set_dma_mode, + .cable_detect = slc90e66_cable_detect, +}; + +static const struct ide_port_info slc90e66_chipset = { + .name = DRV_NAME, + .enablebits = { {0x41, 0x80, 0x80}, {0x43, 0x80, 0x80} }, + .port_ops = &slc90e66_port_ops, + .pio_mask = ATA_PIO4, + .swdma_mask = ATA_SWDMA2_ONLY, + .mwdma_mask = ATA_MWDMA12_ONLY, + .udma_mask = ATA_UDMA4, +}; + +static int slc90e66_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &slc90e66_chipset, NULL); +} + +static const struct pci_device_id slc90e66_pci_tbl[] = { + { PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl); + +static struct pci_driver slc90e66_pci_driver = { + .name = "SLC90e66_IDE", + .id_table = slc90e66_pci_tbl, + .probe = slc90e66_init_one, + .remove = ide_pci_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init slc90e66_ide_init(void) +{ + return ide_pci_register_driver(&slc90e66_pci_driver); +} + +static void __exit slc90e66_ide_exit(void) +{ + pci_unregister_driver(&slc90e66_pci_driver); +} + +module_init(slc90e66_ide_init); +module_exit(slc90e66_ide_exit); + +MODULE_AUTHOR("Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/tc86c001.c b/drivers/ide/tc86c001.c new file mode 100644 index 000000000..17e6132b9 --- /dev/null +++ b/drivers/ide/tc86c001.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2002 Toshiba Corporation + * Copyright (C) 2005-2006 MontaVista Software, Inc. <source@mvista.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/module.h> + +#define DRV_NAME "tc86c001" + +static void tc86c001_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + unsigned long scr_port = hwif->config_data + (drive->dn ? 0x02 : 0x00); + u16 mode, scr = inw(scr_port); + const u8 speed = drive->dma_mode; + + switch (speed) { + case XFER_UDMA_4: mode = 0x00c0; break; + case XFER_UDMA_3: mode = 0x00b0; break; + case XFER_UDMA_2: mode = 0x00a0; break; + case XFER_UDMA_1: mode = 0x0090; break; + case XFER_UDMA_0: mode = 0x0080; break; + case XFER_MW_DMA_2: mode = 0x0070; break; + case XFER_MW_DMA_1: mode = 0x0060; break; + case XFER_MW_DMA_0: mode = 0x0050; break; + case XFER_PIO_4: mode = 0x0400; break; + case XFER_PIO_3: mode = 0x0300; break; + case XFER_PIO_2: mode = 0x0200; break; + case XFER_PIO_1: mode = 0x0100; break; + case XFER_PIO_0: + default: mode = 0x0000; break; + } + + scr &= (speed < XFER_MW_DMA_0) ? 0xf8ff : 0xff0f; + scr |= mode; + outw(scr, scr_port); +} + +static void tc86c001_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + tc86c001_set_mode(hwif, drive); +} + +/* + * HACKITY HACK + * + * This is a workaround for the limitation 5 of the TC86C001 IDE controller: + * if a DMA transfer terminates prematurely, the controller leaves the device's + * interrupt request (INTRQ) pending and does not generate a PCI interrupt (or + * set the interrupt bit in the DMA status register), thus no PCI interrupt + * will occur until a DMA transfer has been successfully completed. + * + * We work around this by initiating dummy, zero-length DMA transfer on + * a DMA timeout expiration. I found no better way to do this with the current + * IDE core than to temporarily replace a higher level driver's timer expiry + * handler with our own backing up to that handler in case our recovery fails. + */ +static int tc86c001_timer_expiry(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + ide_expiry_t *expiry = ide_get_hwifdata(hwif); + u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS); + + /* Restore a higher level driver's expiry handler first. */ + hwif->expiry = expiry; + + if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */ + unsigned long sc_base = hwif->config_data; + unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); + u8 dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD); + + printk(KERN_WARNING "%s: DMA interrupt possibly stuck, " + "attempting recovery...\n", drive->name); + + /* Stop DMA */ + outb(dma_cmd & ~0x01, hwif->dma_base + ATA_DMA_CMD); + + /* Setup the dummy DMA transfer */ + outw(0, sc_base + 0x0a); /* Sector Count */ + outw(0, twcr_port); /* Transfer Word Count 1 or 2 */ + + /* Start the dummy DMA transfer */ + + /* clear R_OR_WCTR for write */ + outb(0x00, hwif->dma_base + ATA_DMA_CMD); + /* set START_STOPBM */ + outb(0x01, hwif->dma_base + ATA_DMA_CMD); + + /* + * If an interrupt was pending, it should come thru shortly. + * If not, a higher level driver's expiry handler should + * eventually cause some kind of recovery from the DMA stall. + */ + return WAIT_MIN_SLEEP; + } + + /* Chain to the restored expiry handler if DMA wasn't active. */ + if (likely(expiry != NULL)) + return expiry(drive); + + /* If there was no handler, "emulate" that for ide_timer_expiry()... */ + return -1; +} + +static void tc86c001_dma_start(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned long sc_base = hwif->config_data; + unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04); + unsigned long nsectors = blk_rq_sectors(hwif->rq); + + /* + * We have to manually load the sector count and size into + * the appropriate system control registers for DMA to work + * with LBA48 and ATAPI devices... + */ + outw(nsectors, sc_base + 0x0a); /* Sector Count */ + outw(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */ + + /* Install our timeout expiry hook, saving the current handler... */ + ide_set_hwifdata(hwif, hwif->expiry); + hwif->expiry = &tc86c001_timer_expiry; + + ide_dma_start(drive); +} + +static u8 tc86c001_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long sc_base = pci_resource_start(dev, 5); + u16 scr1 = inw(sc_base + 0x00); + + /* + * System Control 1 Register bit 13 (PDIAGN): + * 0=80-pin cable, 1=40-pin cable + */ + return (scr1 & 0x2000) ? ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +static void init_hwif_tc86c001(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned long sc_base = pci_resource_start(dev, 5); + u16 scr1 = inw(sc_base + 0x00); + + /* System Control 1 Register bit 15 (Soft Reset) set */ + outw(scr1 | 0x8000, sc_base + 0x00); + + /* System Control 1 Register bit 14 (FIFO Reset) set */ + outw(scr1 | 0x4000, sc_base + 0x00); + + /* System Control 1 Register: reset clear */ + outw(scr1 & ~0xc000, sc_base + 0x00); + + /* Store the system control register base for convenience... */ + hwif->config_data = sc_base; + + if (!hwif->dma_base) + return; + + /* + * Sector Count Control Register bits 0 and 1 set: + * software sets Sector Count Register for master and slave device + */ + outw(0x0003, sc_base + 0x0c); + + /* Sector Count Register limit */ + hwif->rqsize = 0xffff; +} + +static const struct ide_port_ops tc86c001_port_ops = { + .set_pio_mode = tc86c001_set_pio_mode, + .set_dma_mode = tc86c001_set_mode, + .cable_detect = tc86c001_cable_detect, +}; + +static const struct ide_dma_ops tc86c001_dma_ops = { + .dma_host_set = ide_dma_host_set, + .dma_setup = ide_dma_setup, + .dma_start = tc86c001_dma_start, + .dma_end = ide_dma_end, + .dma_test_irq = ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = ide_dma_sff_read_status, +}; + +static const struct ide_port_info tc86c001_chipset = { + .name = DRV_NAME, + .init_hwif = init_hwif_tc86c001, + .port_ops = &tc86c001_port_ops, + .dma_ops = &tc86c001_dma_ops, + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA4, +}; + +static int tc86c001_init_one(struct pci_dev *dev, + const struct pci_device_id *id) +{ + int rc; + + rc = pci_enable_device(dev); + if (rc) + goto out; + + rc = pci_request_region(dev, 5, DRV_NAME); + if (rc) { + printk(KERN_ERR DRV_NAME ": system control regs already in use"); + goto out_disable; + } + + rc = ide_pci_init_one(dev, &tc86c001_chipset, NULL); + if (rc) + goto out_release; + + goto out; + +out_release: + pci_release_region(dev, 5); +out_disable: + pci_disable_device(dev); +out: + return rc; +} + +static void tc86c001_remove(struct pci_dev *dev) +{ + ide_pci_remove(dev); + pci_release_region(dev, 5); + pci_disable_device(dev); +} + +static const struct pci_device_id tc86c001_pci_tbl[] = { + { PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE), 0 }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl); + +static struct pci_driver tc86c001_pci_driver = { + .name = "TC86C001", + .id_table = tc86c001_pci_tbl, + .probe = tc86c001_init_one, + .remove = tc86c001_remove, +}; + +static int __init tc86c001_ide_init(void) +{ + return ide_pci_register_driver(&tc86c001_pci_driver); +} + +static void __exit tc86c001_ide_exit(void) +{ + pci_unregister_driver(&tc86c001_pci_driver); +} + +module_init(tc86c001_ide_init); +module_exit(tc86c001_ide_exit); + +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); +MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/triflex.c b/drivers/ide/triflex.c new file mode 100644 index 000000000..16ddd0956 --- /dev/null +++ b/drivers/ide/triflex.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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> + * + * Loosely based on the piix & svwks drivers. + * + * Documentation: + * Not publicly available. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define DRV_NAME "triflex" + +static void triflex_set_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + u32 triflex_timings = 0; + u16 timing = 0; + u8 channel_offset = hwif->channel ? 0x74 : 0x70, unit = drive->dn & 1; + + pci_read_config_dword(dev, channel_offset, &triflex_timings); + + switch (drive->dma_mode) { + 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; + } + + triflex_timings &= ~(0xFFFF << (16 * unit)); + triflex_timings |= (timing << (16 * unit)); + + pci_write_config_dword(dev, channel_offset, triflex_timings); +} + +static void triflex_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + triflex_set_mode(hwif, drive); +} + +static const struct ide_port_ops triflex_port_ops = { + .set_pio_mode = triflex_set_pio_mode, + .set_dma_mode = triflex_set_mode, +}; + +static const struct ide_port_info triflex_device = { + .name = DRV_NAME, + .enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}}, + .port_ops = &triflex_port_ops, + .pio_mask = ATA_PIO4, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, +}; + +static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &triflex_device, NULL); +} + +static const struct pci_device_id triflex_pci_tbl[] = { + { PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, triflex_pci_tbl); + +#ifdef CONFIG_PM +static int triflex_ide_pci_suspend(struct pci_dev *dev, pm_message_t state) +{ + /* + * We must not disable or powerdown the device. + * APM bios refuses to suspend if IDE is not accessible. + */ + pci_save_state(dev); + return 0; +} +#else +#define triflex_ide_pci_suspend NULL +#endif + +static struct pci_driver triflex_pci_driver = { + .name = "TRIFLEX_IDE", + .id_table = triflex_pci_tbl, + .probe = triflex_init_one, + .remove = ide_pci_remove, + .suspend = triflex_ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init triflex_ide_init(void) +{ + return ide_pci_register_driver(&triflex_pci_driver); +} + +static void __exit triflex_ide_exit(void) +{ + pci_unregister_driver(&triflex_pci_driver); +} + +module_init(triflex_ide_init); +module_exit(triflex_ide_exit); + +MODULE_AUTHOR("Torben Mathiasen"); +MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/ide/trm290.c b/drivers/ide/trm290.c new file mode 100644 index 000000000..d550b379b --- /dev/null +++ b/drivers/ide/trm290.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 1997-1998 Mark Lord + * Copyright (c) 2007 MontaVista Software, Inc. <source@mvista.com> + * + * May be copied or modified under the terms of the GNU General Public License + * + * June 22, 2004 - get rid of check_region + * - Jesper Juhl + * + */ + +/* + * This module provides support for the bus-master IDE DMA function + * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards, + * including a "Precision Instruments" board. The TRM290 pre-dates + * the sff-8038 standard (ide-dma.c) by a few months, and differs + * significantly enough to warrant separate routines for some functions, + * while re-using others from ide-dma.c. + * + * EXPERIMENTAL! It works for me (a sample of one). + * + * Works reliably for me in DMA mode (READs only), + * DMA WRITEs are disabled by default (see #define below); + * + * DMA is not enabled automatically for this chipset, + * but can be turned on manually (with "hdparm -d1") at run time. + * + * I need volunteers with "spare" drives for further testing + * and development, and maybe to help figure out the peculiarities. + * Even knowing the registers (below), some things behave strangely. + */ + +#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */ + +/* + * TRM-290 PCI-IDE2 Bus Master Chip + * ================================ + * The configuration registers are addressed in normal I/O port space + * and are used as follows: + * + * trm290_base depends on jumper settings, and is probed for by ide-dma.c + * + * trm290_base+2 when WRITTEN: chiptest register (byte, write-only) + * bit7 must always be written as "1" + * bits6-2 undefined + * bit1 1=legacy_compatible_mode, 0=native_pci_mode + * bit0 1=test_mode, 0=normal(default) + * + * trm290_base+2 when READ: status register (byte, read-only) + * bits7-2 undefined + * bit1 channel0 busmaster interrupt status 0=none, 1=asserted + * bit0 channel0 interrupt status 0=none, 1=asserted + * + * trm290_base+3 Interrupt mask register + * bits7-5 undefined + * bit4 legacy_header: 1=present, 0=absent + * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only) + * bit2 channel1 interrupt status 0=none, 1=asserted (read only) + * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default) + * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default) + * + * trm290_base+1 "CPR" Config Pointer Register (byte) + * bit7 1=autoincrement CPR bits 2-0 after each access of CDR + * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state + * bit5 0=enabled master burst access (default), 1=disable (write only) + * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast + * bit3 0=primary IDE channel, 1=secondary IDE channel + * bits2-0 register index for accesses through CDR port + * + * trm290_base+0 "CDR" Config Data Register (word) + * two sets of seven config registers, + * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6), + * each index defined below: + * + * Index-0 Base address register for command block (word) + * defaults: 0x1f0 for primary, 0x170 for secondary + * + * Index-1 general config register (byte) + * bit7 1=DMA enable, 0=DMA disable + * bit6 1=activate IDE_RESET, 0=no action (default) + * bit5 1=enable IORDY, 0=disable IORDY (default) + * bit4 0=16-bit data port(default), 1=8-bit (XT) data port + * bit3 interrupt polarity: 1=active_low, 0=active_high(default) + * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only) + * bit1 bus_master_mode(?): 1=enable, 0=disable(default) + * bit0 enable_io_ports: 1=enable(default), 0=disable + * + * Index-2 read-ahead counter preload bits 0-7 (byte, write only) + * bits7-0 bits7-0 of readahead count + * + * Index-3 read-ahead config register (byte, write only) + * bit7 1=enable_readahead, 0=disable_readahead(default) + * bit6 1=clear_FIFO, 0=no_action + * bit5 undefined + * bit4 mode4 timing control: 1=enable, 0=disable(default) + * bit3 undefined + * bit2 undefined + * bits1-0 bits9-8 of read-ahead count + * + * Index-4 base address register for control block (word) + * defaults: 0x3f6 for primary, 0x376 for secondary + * + * Index-5 data port timings (shared by both drives) (byte) + * standard PCI "clk" (clock) counts, default value = 0xf5 + * + * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk + * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk, + * 011=4clk, 100=5clk, 101=6clk, + * 110=8clk, 111=12clk + * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk, + * 011=5clk, 100=6clk, 101=8clk, + * 110=12clk, 111=16clk + * + * Index-6 command/control port timings (shared by both drives) (byte) + * same layout as Index-5, default value = 0xde + * + * Suggested CDR programming for PIO mode0 (600ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary + * + * Suggested CDR programming for PIO mode3 (180ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary + * + * Suggested CDR programming for PIO mode4 (120ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary + * + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ide.h> + +#include <asm/io.h> + +#define DRV_NAME "trm290" + +static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) +{ + ide_hwif_t *hwif = drive->hwif; + 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 */ + outb(0x51 | (hwif->channel << 3), hwif->config_data + 1); + outw(reg & 0xff, hwif->config_data); + } + + /* enable IRQ if not probing */ + if (drive->dev_flags & IDE_DFLAG_PRESENT) { + reg = inw(hwif->config_data + 3); + reg &= 0x13; + reg &= ~(1 << hwif->channel); + outw(reg, hwif->config_data + 3); + } + + local_irq_restore(flags); +} + +static void trm290_dev_select(ide_drive_t *drive) +{ + trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA)); + + outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr); +} + +static int trm290_dma_check(ide_drive_t *drive, struct ide_cmd *cmd) +{ + if (cmd->tf_flags & IDE_TFLAG_WRITE) { +#ifdef TRM290_NO_DMA_WRITES + /* always use PIO for writes */ + return 1; +#endif + } + return 0; +} + +static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + unsigned int count, rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 1 : 2; + + count = ide_build_dmatable(drive, cmd); + if (count == 0) + /* try PIO instead of DMA */ + return 1; + + outl(hwif->dmatable_dma | rw, hwif->dma_base); + /* start DMA */ + outw(count * 2 - 1, hwif->dma_base + 2); + + return 0; +} + +static void trm290_dma_start(ide_drive_t *drive) +{ + trm290_prepare_drive(drive, 1); +} + +static int trm290_dma_end(ide_drive_t *drive) +{ + u16 status = inw(drive->hwif->dma_base + 2); + + trm290_prepare_drive(drive, 0); + + return status != 0x00ff; +} + +static int trm290_dma_test_irq(ide_drive_t *drive) +{ + u16 status = inw(drive->hwif->dma_base + 2); + + return status == 0x00ff; +} + +static void trm290_dma_host_set(ide_drive_t *drive, int on) +{ +} + +static void init_hwif_trm290(ide_hwif_t *hwif) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + unsigned int cfg_base = pci_resource_start(dev, 4); + unsigned long flags; + u8 reg = 0; + + if ((dev->class & 5) && cfg_base) + printk(KERN_INFO DRV_NAME " %s: chip", pci_name(dev)); + else { + cfg_base = 0x3df0; + printk(KERN_INFO DRV_NAME " %s: using default", pci_name(dev)); + } + printk(KERN_CONT " config base at 0x%04x\n", cfg_base); + hwif->config_data = cfg_base; + hwif->dma_base = (cfg_base + 4) ^ (hwif->channel ? 0x80 : 0); + + printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n", + hwif->name, hwif->dma_base, hwif->dma_base + 3); + + if (ide_allocate_dma_engine(hwif)) + return; + + local_irq_save(flags); + /* put config reg into first byte of hwif->select_data */ + outb(0x51 | (hwif->channel << 3), hwif->config_data + 1); + /* select PIO as default */ + hwif->select_data = 0x21; + outb(hwif->select_data, hwif->config_data); + /* get IRQ info */ + reg = inb(hwif->config_data + 3); + /* mask IRQs for both ports */ + reg = (reg & 0x10) | 0x03; + outb(reg, hwif->config_data + 3); + local_irq_restore(flags); + + if (reg & 0x10) + /* legacy mode */ + hwif->irq = hwif->channel ? 15 : 14; + +#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; + + outb(0x54 | (hwif->channel << 3), hwif->config_data + 1); + old = inw(hwif->config_data); + old &= ~1; + old_mask = inb(old + 2); + if (old != compat && old_mask == 0xff) { + /* leave lower 10 bits untouched */ + compat += (next_offset += 0x400); + hwif->io_ports.ctl_addr = compat + 2; + outw(compat | 1, hwif->config_data); + new = 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 const struct ide_tp_ops trm290_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = trm290_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +static const struct ide_dma_ops trm290_dma_ops = { + .dma_host_set = trm290_dma_host_set, + .dma_setup = trm290_dma_setup, + .dma_start = trm290_dma_start, + .dma_end = trm290_dma_end, + .dma_test_irq = trm290_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_check = trm290_dma_check, +}; + +static const struct ide_port_info trm290_chipset = { + .name = DRV_NAME, + .init_hwif = init_hwif_trm290, + .tp_ops = &trm290_tp_ops, + .dma_ops = &trm290_dma_ops, + .host_flags = IDE_HFLAG_TRM290 | + IDE_HFLAG_NO_ATAPI_DMA | +#if 0 /* play it safe for now */ + IDE_HFLAG_TRUST_BIOS_FOR_DMA | +#endif + IDE_HFLAG_NO_AUTODMA | + IDE_HFLAG_NO_LBA48, +}; + +static int trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + return ide_pci_init_one(dev, &trm290_chipset, NULL); +} + +static const struct pci_device_id trm290_pci_tbl[] = { + { PCI_VDEVICE(TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290), 0 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, trm290_pci_tbl); + +static struct pci_driver trm290_pci_driver = { + .name = "TRM290_IDE", + .id_table = trm290_pci_tbl, + .probe = trm290_init_one, + .remove = ide_pci_remove, +}; + +static int __init trm290_ide_init(void) +{ + return ide_pci_register_driver(&trm290_pci_driver); +} + +static void __exit trm290_ide_exit(void) +{ + pci_unregister_driver(&trm290_pci_driver); +} + +module_init(trm290_ide_init); +module_exit(trm290_ide_exit); + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/tx4938ide.c b/drivers/ide/tx4938ide.c new file mode 100644 index 000000000..962eb9250 --- /dev/null +++ b/drivers/ide/tx4938ide.c @@ -0,0 +1,209 @@ +/* + * TX4938 internal IDE driver + * Based on tx4939ide.c. + * + * 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. + * + * (C) Copyright TOSHIBA CORPORATION 2005-2007 + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +#include <asm/ide.h> +#include <asm/txx9/tx4938.h> + +static void tx4938ide_tune_ebusc(unsigned int ebus_ch, + unsigned int gbus_clock, + u8 pio) +{ + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio); + u64 cr = __raw_readq(&tx4938_ebuscptr->cr[ebus_ch]); + unsigned int sp = (cr >> 4) & 3; + unsigned int clock = gbus_clock / (4 - sp); + unsigned int cycle = 1000000000 / clock; + unsigned int shwt; + int wt; + + /* Minimum DIOx- active time */ + wt = DIV_ROUND_UP(t->act8b, cycle) - 2; + /* IORDY setup time: 35ns */ + wt = max_t(int, wt, DIV_ROUND_UP(35, cycle)); + /* actual wait-cycle is max(wt & ~1, 1) */ + if (wt > 2 && (wt & 1)) + wt++; + wt &= ~1; + /* Address-valid to DIOR/DIOW setup */ + shwt = DIV_ROUND_UP(t->setup, cycle); + + /* -DIOx recovery time (SHWT * 4) and cycle time requirement */ + while ((shwt * 4 + wt + (wt ? 2 : 3)) * cycle < t->cycle) + shwt++; + if (shwt > 7) { + pr_warn("tx4938ide: SHWT violation (%d)\n", shwt); + shwt = 7; + } + pr_debug("tx4938ide: ebus %d, bus cycle %dns, WT %d, SHWT %d\n", + ebus_ch, cycle, wt, shwt); + + __raw_writeq((cr & ~0x3f007ull) | (wt << 12) | shwt, + &tx4938_ebuscptr->cr[ebus_ch]); +} + +static void tx4938ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct tx4938ide_platform_info *pdata = dev_get_platdata(hwif->dev); + u8 safe = drive->pio_mode - XFER_PIO_0; + ide_drive_t *pair; + + pair = ide_get_pair_dev(drive); + if (pair) + safe = min_t(u8, safe, pair->pio_mode - XFER_PIO_0); + tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, safe); +} + +#ifdef __BIG_ENDIAN + +/* custom iops (independent from SWAP_IO_SPACE) */ +static void tx4938ide_input_data_swap(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long port = drive->hwif->io_ports.data_addr; + unsigned short *ptr = buf; + unsigned int count = (len + 1) / 2; + + while (count--) + *ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port)); + __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2)); +} + +static void tx4938ide_output_data_swap(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long port = drive->hwif->io_ports.data_addr; + unsigned short *ptr = buf; + unsigned int count = (len + 1) / 2; + + while (count--) { + __raw_writew(le16_to_cpu(*ptr), (void __iomem *)port); + ptr++; + } + __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2)); +} + +static const struct ide_tp_ops tx4938ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = tx4938ide_input_data_swap, + .output_data = tx4938ide_output_data_swap, +}; + +#endif /* __BIG_ENDIAN */ + +static const struct ide_port_ops tx4938ide_port_ops = { + .set_pio_mode = tx4938ide_set_pio_mode, +}; + +static const struct ide_port_info tx4938ide_port_info __initconst = { + .port_ops = &tx4938ide_port_ops, +#ifdef __BIG_ENDIAN + .tp_ops = &tx4938ide_tp_ops, +#endif + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO5, + .chipset = ide_generic, +}; + +static int __init tx4938ide_probe(struct platform_device *pdev) +{ + struct ide_hw hw, *hws[] = { &hw }; + struct ide_host *host; + struct resource *res; + struct tx4938ide_platform_info *pdata = dev_get_platdata(&pdev->dev); + int irq, ret, i; + unsigned long mapbase, mapctl; + struct ide_port_info d = tx4938ide_port_info; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), "tx4938ide")) + return -EBUSY; + mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start, + 8 << pdata->ioport_shift); + mapctl = (unsigned long)devm_ioremap(&pdev->dev, + res->start + 0x10000 + + (6 << pdata->ioport_shift), + 1 << pdata->ioport_shift); + if (!mapbase || !mapctl) + return -EBUSY; + + memset(&hw, 0, sizeof(hw)); + if (pdata->ioport_shift) { + unsigned long port = mapbase; + unsigned long ctl = mapctl; + + hw.io_ports_array[0] = port; +#ifdef __BIG_ENDIAN + port++; + ctl++; +#endif + for (i = 1; i <= 7; i++) + hw.io_ports_array[i] = + port + (i << pdata->ioport_shift); + hw.io_ports.ctl_addr = ctl; + } else + ide_std_init_ports(&hw, mapbase, mapctl); + hw.irq = irq; + hw.dev = &pdev->dev; + + pr_info("TX4938 IDE interface (base %#lx, ctl %#lx, irq %d)\n", + mapbase, mapctl, hw.irq); + if (pdata->gbus_clock) + tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, 0); + else + d.port_ops = NULL; + ret = ide_host_add(&d, hws, 1, &host); + if (!ret) + platform_set_drvdata(pdev, host); + return ret; +} + +static int __exit tx4938ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = platform_get_drvdata(pdev); + + ide_host_remove(host); + return 0; +} + +static struct platform_driver tx4938ide_driver = { + .driver = { + .name = "tx4938ide", + }, + .remove = __exit_p(tx4938ide_remove), +}; + +module_platform_driver_probe(tx4938ide_driver, tx4938ide_probe); + +MODULE_DESCRIPTION("TX4938 internal IDE driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tx4938ide"); diff --git a/drivers/ide/tx4939ide.c b/drivers/ide/tx4939ide.c new file mode 100644 index 000000000..b1bbf807b --- /dev/null +++ b/drivers/ide/tx4939ide.c @@ -0,0 +1,628 @@ +/* + * TX4939 internal IDE driver + * Based on RBTX49xx patch from CELF patch archive. + * + * 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. + * + * (C) Copyright TOSHIBA CORPORATION 2005-2007 + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/scatterlist.h> + +#include <asm/ide.h> + +#define MODNAME "tx4939ide" + +/* ATA Shadow Registers (8-bit except for Data which is 16-bit) */ +#define TX4939IDE_Data 0x000 +#define TX4939IDE_Error_Feature 0x001 +#define TX4939IDE_Sec 0x002 +#define TX4939IDE_LBA0 0x003 +#define TX4939IDE_LBA1 0x004 +#define TX4939IDE_LBA2 0x005 +#define TX4939IDE_DevHead 0x006 +#define TX4939IDE_Stat_Cmd 0x007 +#define TX4939IDE_AltStat_DevCtl 0x402 +/* H/W DMA Registers */ +#define TX4939IDE_DMA_Cmd 0x800 /* 8-bit */ +#define TX4939IDE_DMA_Stat 0x802 /* 8-bit */ +#define TX4939IDE_PRD_Ptr 0x804 /* 32-bit */ +/* ATA100 CORE Registers (16-bit) */ +#define TX4939IDE_Sys_Ctl 0xc00 +#define TX4939IDE_Xfer_Cnt_1 0xc08 +#define TX4939IDE_Xfer_Cnt_2 0xc0a +#define TX4939IDE_Sec_Cnt 0xc10 +#define TX4939IDE_Start_Lo_Addr 0xc18 +#define TX4939IDE_Start_Up_Addr 0xc20 +#define TX4939IDE_Add_Ctl 0xc28 +#define TX4939IDE_Lo_Burst_Cnt 0xc30 +#define TX4939IDE_Up_Burst_Cnt 0xc38 +#define TX4939IDE_PIO_Addr 0xc88 +#define TX4939IDE_H_Rst_Tim 0xc90 +#define TX4939IDE_Int_Ctl 0xc98 +#define TX4939IDE_Pkt_Cmd 0xcb8 +#define TX4939IDE_Bxfer_Cnt_Hi 0xcc0 +#define TX4939IDE_Bxfer_Cnt_Lo 0xcc8 +#define TX4939IDE_Dev_TErr 0xcd0 +#define TX4939IDE_Pkt_Xfer_Ctl 0xcd8 +#define TX4939IDE_Start_TAddr 0xce0 + +/* bits for Int_Ctl */ +#define TX4939IDE_INT_ADDRERR 0x80 +#define TX4939IDE_INT_REACHMUL 0x40 +#define TX4939IDE_INT_DEVTIMING 0x20 +#define TX4939IDE_INT_UDMATERM 0x10 +#define TX4939IDE_INT_TIMER 0x08 +#define TX4939IDE_INT_BUSERR 0x04 +#define TX4939IDE_INT_XFEREND 0x02 +#define TX4939IDE_INT_HOST 0x01 + +#define TX4939IDE_IGNORE_INTS \ + (TX4939IDE_INT_ADDRERR | TX4939IDE_INT_REACHMUL | \ + TX4939IDE_INT_DEVTIMING | TX4939IDE_INT_UDMATERM | \ + TX4939IDE_INT_TIMER | TX4939IDE_INT_XFEREND) + +#ifdef __BIG_ENDIAN +#define tx4939ide_swizzlel(a) ((a) ^ 4) +#define tx4939ide_swizzlew(a) ((a) ^ 6) +#define tx4939ide_swizzleb(a) ((a) ^ 7) +#else +#define tx4939ide_swizzlel(a) (a) +#define tx4939ide_swizzlew(a) (a) +#define tx4939ide_swizzleb(a) (a) +#endif + +static u16 tx4939ide_readw(void __iomem *base, u32 reg) +{ + return __raw_readw(base + tx4939ide_swizzlew(reg)); +} +static u8 tx4939ide_readb(void __iomem *base, u32 reg) +{ + return __raw_readb(base + tx4939ide_swizzleb(reg)); +} +static void tx4939ide_writel(u32 val, void __iomem *base, u32 reg) +{ + __raw_writel(val, base + tx4939ide_swizzlel(reg)); +} +static void tx4939ide_writew(u16 val, void __iomem *base, u32 reg) +{ + __raw_writew(val, base + tx4939ide_swizzlew(reg)); +} +static void tx4939ide_writeb(u8 val, void __iomem *base, u32 reg) +{ + __raw_writeb(val, base + tx4939ide_swizzleb(reg)); +} + +#define TX4939IDE_BASE(hwif) ((void __iomem *)(hwif)->extra_base) + +static void tx4939ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + int is_slave = drive->dn; + u32 mask, val; + const u8 pio = drive->pio_mode - XFER_PIO_0; + u8 safe = pio; + ide_drive_t *pair; + + pair = ide_get_pair_dev(drive); + if (pair) + safe = min_t(u8, safe, pair->pio_mode - XFER_PIO_0); + /* + * Update Command Transfer Mode for master/slave and Data + * Transfer Mode for this drive. + */ + mask = is_slave ? 0x07f00000 : 0x000007f0; + val = ((safe << 8) | (pio << 4)) << (is_slave ? 16 : 0); + hwif->select_data = (hwif->select_data & ~mask) | val; + /* tx4939ide_tf_load_fixup() will set the Sys_Ctl register */ +} + +static void tx4939ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + u32 mask, val; + const u8 mode = drive->dma_mode; + + /* Update Data Transfer Mode for this drive. */ + if (mode >= XFER_UDMA_0) + val = mode - XFER_UDMA_0 + 8; + else + val = mode - XFER_MW_DMA_0 + 5; + if (drive->dn) { + mask = 0x00f00000; + val <<= 20; + } else { + mask = 0x000000f0; + val <<= 4; + } + hwif->select_data = (hwif->select_data & ~mask) | val; + /* tx4939ide_tf_load_fixup() will set the Sys_Ctl register */ +} + +static u16 tx4939ide_check_error_ints(ide_hwif_t *hwif) +{ + void __iomem *base = TX4939IDE_BASE(hwif); + u16 ctl = tx4939ide_readw(base, TX4939IDE_Int_Ctl); + + if (ctl & TX4939IDE_INT_BUSERR) { + /* reset FIFO */ + u16 sysctl = tx4939ide_readw(base, TX4939IDE_Sys_Ctl); + + tx4939ide_writew(sysctl | 0x4000, base, TX4939IDE_Sys_Ctl); + /* wait 12GBUSCLK (typ. 60ns @ GBUS200MHz, max 270ns) */ + ndelay(270); + tx4939ide_writew(sysctl, base, TX4939IDE_Sys_Ctl); + } + if (ctl & (TX4939IDE_INT_ADDRERR | + TX4939IDE_INT_DEVTIMING | TX4939IDE_INT_BUSERR)) + pr_err("%s: Error interrupt %#x (%s%s%s )\n", + hwif->name, ctl, + ctl & TX4939IDE_INT_ADDRERR ? " Address-Error" : "", + ctl & TX4939IDE_INT_DEVTIMING ? " DEV-Timing" : "", + ctl & TX4939IDE_INT_BUSERR ? " Bus-Error" : ""); + return ctl; +} + +static void tx4939ide_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif; + void __iomem *base; + u16 ctl; + + /* + * tx4939ide_dma_test_irq() and tx4939ide_dma_end() do all job + * for DMA case. + */ + if (drive->waiting_for_dma) + return; + hwif = drive->hwif; + base = TX4939IDE_BASE(hwif); + ctl = tx4939ide_check_error_ints(hwif); + tx4939ide_writew(ctl, base, TX4939IDE_Int_Ctl); +} + +static u8 tx4939ide_cable_detect(ide_hwif_t *hwif) +{ + void __iomem *base = TX4939IDE_BASE(hwif); + + return tx4939ide_readw(base, TX4939IDE_Sys_Ctl) & 0x2000 ? + ATA_CBL_PATA40 : ATA_CBL_PATA80; +} + +#ifdef __BIG_ENDIAN +static void tx4939ide_dma_host_set(ide_drive_t *drive, int on) +{ + ide_hwif_t *hwif = drive->hwif; + u8 unit = drive->dn; + void __iomem *base = TX4939IDE_BASE(hwif); + u8 dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat); + + if (on) + dma_stat |= (1 << (5 + unit)); + else + dma_stat &= ~(1 << (5 + unit)); + + tx4939ide_writeb(dma_stat, base, TX4939IDE_DMA_Stat); +} +#else +#define tx4939ide_dma_host_set ide_dma_host_set +#endif + +static u8 tx4939ide_clear_dma_status(void __iomem *base) +{ + u8 dma_stat; + + /* read DMA status for INTR & ERROR flags */ + dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat); + /* clear INTR & ERROR flags */ + tx4939ide_writeb(dma_stat | ATA_DMA_INTR | ATA_DMA_ERR, base, + TX4939IDE_DMA_Stat); + /* recover intmask cleared by writing to bit2 of DMA_Stat */ + tx4939ide_writew(TX4939IDE_IGNORE_INTS << 8, base, TX4939IDE_Int_Ctl); + return dma_stat; +} + +#ifdef __BIG_ENDIAN +/* custom ide_build_dmatable to handle swapped layout */ +static int tx4939ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + u32 *table = (u32 *)hwif->dmatable_cpu; + unsigned int count = 0; + int i; + struct scatterlist *sg; + + for_each_sg(hwif->sg_table, sg, cmd->sg_nents, i) { + u32 cur_addr, cur_len, bcount; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the DMA table, without crossing any 64kB boundaries. + */ + + while (cur_len) { + if (count++ >= PRD_ENTRIES) + goto use_pio_instead; + + bcount = 0x10000 - (cur_addr & 0xffff); + if (bcount > cur_len) + bcount = cur_len; + /* + * This workaround for zero count seems required. + * (standard ide_build_dmatable does it too) + */ + if (bcount == 0x10000) + bcount = 0x8000; + *table++ = bcount & 0xffff; + *table++ = cur_addr; + cur_addr += bcount; + cur_len -= bcount; + } + } + + if (count) { + *(table - 2) |= 0x80000000; + return count; + } + +use_pio_instead: + printk(KERN_ERR "%s: %s\n", drive->name, + count ? "DMA table too small" : "empty DMA table?"); + + return 0; /* revert to PIO for this request */ +} +#else +#define tx4939ide_build_dmatable ide_build_dmatable +#endif + +static int tx4939ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd) +{ + ide_hwif_t *hwif = drive->hwif; + void __iomem *base = TX4939IDE_BASE(hwif); + u8 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR; + + /* fall back to PIO! */ + if (tx4939ide_build_dmatable(drive, cmd) == 0) + return 1; + + /* PRD table */ + tx4939ide_writel(hwif->dmatable_dma, base, TX4939IDE_PRD_Ptr); + + /* specify r/w */ + tx4939ide_writeb(rw, base, TX4939IDE_DMA_Cmd); + + /* clear INTR & ERROR flags */ + tx4939ide_clear_dma_status(base); + + tx4939ide_writew(SECTOR_SIZE / 2, base, drive->dn ? + TX4939IDE_Xfer_Cnt_2 : TX4939IDE_Xfer_Cnt_1); + + tx4939ide_writew(blk_rq_sectors(cmd->rq), base, TX4939IDE_Sec_Cnt); + + return 0; +} + +static int tx4939ide_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + u8 dma_stat, dma_cmd; + void __iomem *base = TX4939IDE_BASE(hwif); + u16 ctl = tx4939ide_readw(base, TX4939IDE_Int_Ctl); + + /* get DMA command mode */ + dma_cmd = tx4939ide_readb(base, TX4939IDE_DMA_Cmd); + /* stop DMA */ + tx4939ide_writeb(dma_cmd & ~ATA_DMA_START, base, TX4939IDE_DMA_Cmd); + + /* read and clear the INTR & ERROR bits */ + dma_stat = tx4939ide_clear_dma_status(base); + +#define CHECK_DMA_MASK (ATA_DMA_ACTIVE | ATA_DMA_ERR | ATA_DMA_INTR) + + /* verify good DMA status */ + if ((dma_stat & CHECK_DMA_MASK) == 0 && + (ctl & (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST)) == + (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST)) + /* INT_IDE lost... bug? */ + return 0; + return ((dma_stat & CHECK_DMA_MASK) != + ATA_DMA_INTR) ? 0x10 | dma_stat : 0; +} + +/* returns 1 if DMA IRQ issued, 0 otherwise */ +static int tx4939ide_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + void __iomem *base = TX4939IDE_BASE(hwif); + u16 ctl, ide_int; + u8 dma_stat, stat; + int found = 0; + + ctl = tx4939ide_check_error_ints(hwif); + ide_int = ctl & (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST); + switch (ide_int) { + case TX4939IDE_INT_HOST: + /* On error, XFEREND might not be asserted. */ + stat = tx4939ide_readb(base, TX4939IDE_AltStat_DevCtl); + if ((stat & (ATA_BUSY | ATA_DRQ | ATA_ERR)) == ATA_ERR) + found = 1; + else + /* Wait for XFEREND (Mask HOST and unmask XFEREND) */ + ctl &= ~TX4939IDE_INT_XFEREND << 8; + ctl |= ide_int << 8; + break; + case TX4939IDE_INT_HOST | TX4939IDE_INT_XFEREND: + dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat); + if (!(dma_stat & ATA_DMA_INTR)) + pr_warn("%s: weird interrupt status. " + "DMA_Stat %#02x int_ctl %#04x\n", + hwif->name, dma_stat, ctl); + found = 1; + break; + } + /* + * Do not clear XFEREND, HOST now. They will be cleared by + * clearing bit2 of DMA_Stat. + */ + ctl &= ~ide_int; + tx4939ide_writew(ctl, base, TX4939IDE_Int_Ctl); + return found; +} + +#ifdef __BIG_ENDIAN +static u8 tx4939ide_dma_sff_read_status(ide_hwif_t *hwif) +{ + void __iomem *base = TX4939IDE_BASE(hwif); + + return tx4939ide_readb(base, TX4939IDE_DMA_Stat); +} +#else +#define tx4939ide_dma_sff_read_status ide_dma_sff_read_status +#endif + +static void tx4939ide_init_hwif(ide_hwif_t *hwif) +{ + void __iomem *base = TX4939IDE_BASE(hwif); + + /* Soft Reset */ + tx4939ide_writew(0x8000, base, TX4939IDE_Sys_Ctl); + /* at least 20 GBUSCLK (typ. 100ns @ GBUS200MHz, max 450ns) */ + ndelay(450); + tx4939ide_writew(0x0000, base, TX4939IDE_Sys_Ctl); + /* mask some interrupts and clear all interrupts */ + tx4939ide_writew((TX4939IDE_IGNORE_INTS << 8) | 0xff, base, + TX4939IDE_Int_Ctl); + + tx4939ide_writew(0x0008, base, TX4939IDE_Lo_Burst_Cnt); + tx4939ide_writew(0, base, TX4939IDE_Up_Burst_Cnt); +} + +static int tx4939ide_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d) +{ + hwif->dma_base = + hwif->extra_base + tx4939ide_swizzleb(TX4939IDE_DMA_Cmd); + /* + * Note that we cannot use ATA_DMA_TABLE_OFS, ATA_DMA_STATUS + * for big endian. + */ + return ide_allocate_dma_engine(hwif); +} + +static void tx4939ide_tf_load_fixup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + void __iomem *base = TX4939IDE_BASE(hwif); + u16 sysctl = hwif->select_data >> (drive->dn ? 16 : 0); + + /* + * Fix ATA100 CORE System Control Register. (The write to the + * Device/Head register may write wrong data to the System + * Control Register) + * While Sys_Ctl is written here, dev_select() is not needed. + */ + tx4939ide_writew(sysctl, base, TX4939IDE_Sys_Ctl); +} + +static void tx4939ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, + u8 valid) +{ + ide_tf_load(drive, tf, valid); + + if (valid & IDE_VALID_DEVICE) + tx4939ide_tf_load_fixup(drive); +} + +#ifdef __BIG_ENDIAN + +/* custom iops (independent from SWAP_IO_SPACE) */ +static void tx4939ide_input_data_swap(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long port = drive->hwif->io_ports.data_addr; + unsigned short *ptr = buf; + unsigned int count = (len + 1) / 2; + + while (count--) + *ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port)); + __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2)); +} + +static void tx4939ide_output_data_swap(ide_drive_t *drive, struct ide_cmd *cmd, + void *buf, unsigned int len) +{ + unsigned long port = drive->hwif->io_ports.data_addr; + unsigned short *ptr = buf; + unsigned int count = (len + 1) / 2; + + while (count--) { + __raw_writew(le16_to_cpu(*ptr), (void __iomem *)port); + ptr++; + } + __ide_flush_dcache_range((unsigned long)buf, roundup(len, 2)); +} + +static const struct ide_tp_ops tx4939ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = tx4939ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = tx4939ide_input_data_swap, + .output_data = tx4939ide_output_data_swap, +}; + +#else /* __LITTLE_ENDIAN */ + +static const struct ide_tp_ops tx4939ide_tp_ops = { + .exec_command = ide_exec_command, + .read_status = ide_read_status, + .read_altstatus = ide_read_altstatus, + .write_devctl = ide_write_devctl, + + .dev_select = ide_dev_select, + .tf_load = tx4939ide_tf_load, + .tf_read = ide_tf_read, + + .input_data = ide_input_data, + .output_data = ide_output_data, +}; + +#endif /* __LITTLE_ENDIAN */ + +static const struct ide_port_ops tx4939ide_port_ops = { + .set_pio_mode = tx4939ide_set_pio_mode, + .set_dma_mode = tx4939ide_set_dma_mode, + .clear_irq = tx4939ide_clear_irq, + .cable_detect = tx4939ide_cable_detect, +}; + +static const struct ide_dma_ops tx4939ide_dma_ops = { + .dma_host_set = tx4939ide_dma_host_set, + .dma_setup = tx4939ide_dma_setup, + .dma_start = ide_dma_start, + .dma_end = tx4939ide_dma_end, + .dma_test_irq = tx4939ide_dma_test_irq, + .dma_lost_irq = ide_dma_lost_irq, + .dma_timer_expiry = ide_dma_sff_timer_expiry, + .dma_sff_read_status = tx4939ide_dma_sff_read_status, +}; + +static const struct ide_port_info tx4939ide_port_info __initconst = { + .init_hwif = tx4939ide_init_hwif, + .init_dma = tx4939ide_init_dma, + .port_ops = &tx4939ide_port_ops, + .dma_ops = &tx4939ide_dma_ops, + .tp_ops = &tx4939ide_tp_ops, + .host_flags = IDE_HFLAG_MMIO, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, + .udma_mask = ATA_UDMA5, + .chipset = ide_generic, +}; + +static int __init tx4939ide_probe(struct platform_device *pdev) +{ + struct ide_hw hw, *hws[] = { &hw }; + struct ide_host *host; + struct resource *res; + int irq, ret; + unsigned long mapbase; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + if (!devm_request_mem_region(&pdev->dev, res->start, + resource_size(res), MODNAME)) + return -EBUSY; + mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!mapbase) + return -EBUSY; + memset(&hw, 0, sizeof(hw)); + hw.io_ports.data_addr = + mapbase + tx4939ide_swizzlew(TX4939IDE_Data); + hw.io_ports.error_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_Error_Feature); + hw.io_ports.nsect_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_Sec); + hw.io_ports.lbal_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_LBA0); + hw.io_ports.lbam_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_LBA1); + hw.io_ports.lbah_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_LBA2); + hw.io_ports.device_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_DevHead); + hw.io_ports.command_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_Stat_Cmd); + hw.io_ports.ctl_addr = + mapbase + tx4939ide_swizzleb(TX4939IDE_AltStat_DevCtl); + hw.irq = irq; + hw.dev = &pdev->dev; + + pr_info("TX4939 IDE interface (base %#lx, irq %d)\n", mapbase, irq); + host = ide_host_alloc(&tx4939ide_port_info, hws, 1); + if (!host) + return -ENOMEM; + /* use extra_base for base address of the all registers */ + host->ports[0]->extra_base = mapbase; + ret = ide_host_register(host, &tx4939ide_port_info, hws); + if (ret) { + ide_host_free(host); + return ret; + } + platform_set_drvdata(pdev, host); + return 0; +} + +static int __exit tx4939ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = platform_get_drvdata(pdev); + + ide_host_remove(host); + return 0; +} + +#ifdef CONFIG_PM +static int tx4939ide_resume(struct platform_device *dev) +{ + struct ide_host *host = platform_get_drvdata(dev); + ide_hwif_t *hwif = host->ports[0]; + + tx4939ide_init_hwif(hwif); + return 0; +} +#else +#define tx4939ide_resume NULL +#endif + +static struct platform_driver tx4939ide_driver = { + .driver = { + .name = MODNAME, + }, + .remove = __exit_p(tx4939ide_remove), + .resume = tx4939ide_resume, +}; + +module_platform_driver_probe(tx4939ide_driver, tx4939ide_probe); + +MODULE_DESCRIPTION("TX4939 internal IDE driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tx4939ide"); diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c new file mode 100644 index 000000000..cf996f788 --- /dev/null +++ b/drivers/ide/umc8672.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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/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/ide.h> +#include <linux/init.h> + +#include <asm/io.h> + +#define DRV_NAME "umc8672" + +/* + * 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] = { + {0x0f, 0x0b, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1}, + {0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 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 umc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_hwif_t *mate = hwif->mate; + unsigned long flags; + const u8 pio = drive->pio_mode - XFER_PIO_0; + + printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", + drive->name, pio, pio_to_umc[pio]); + if (mate) + spin_lock_irqsave(&mate->lock, flags); + if (mate && mate->handler) { + 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); + } + if (mate) + spin_unlock_irqrestore(&mate->lock, flags); +} + +static const struct ide_port_ops umc8672_port_ops = { + .set_pio_mode = umc_set_pio_mode, +}; + +static const struct ide_port_info umc8672_port_info __initconst = { + .name = DRV_NAME, + .chipset = ide_umc8672, + .port_ops = &umc8672_port_ops, + .host_flags = IDE_HFLAG_NO_DMA, + .pio_mask = ATA_PIO4, +}; + +static int __init umc8672_probe(void) +{ + unsigned long flags; + + 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); + + return ide_legacy_device_add(&umc8672_port_info, 0); +} + +static bool probe_umc8672; + +module_param_named(probe, probe_umc8672, bool, 0); +MODULE_PARM_DESC(probe, "probe for UMC8672 chipset"); + +static int __init umc8672_init(void) +{ + if (probe_umc8672 == 0) + goto out; + + if (umc8672_probe() == 0) + return 0; +out: + return -ENODEV; +} + +module_init(umc8672_init); + +MODULE_AUTHOR("Wolfram Podien"); +MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c new file mode 100644 index 000000000..63a3aca50 --- /dev/null +++ b/drivers/ide/via82cxxx.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VIA IDE driver for Linux. Supported southbridges: + * + * vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b, + * vt82c686, vt82c686a, vt82c686b, vt8231, vt8233, vt8233c, vt8233a, + * vt8235, vt8237, vt8237a + * + * Copyright (c) 2000-2002 Vojtech Pavlik + * Copyright (c) 2007-2010 Bartlomiej Zolnierkiewicz + * + * Based on the work of: + * Michel Aubry + * Jeff Garzik + * Andre Hedrick + * + * Documentation: + * Obsolete device documentation publicly available from via.com.tw + * Current device documentation available under NDA only + */ + + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/dmi.h> + +#ifdef CONFIG_PPC_CHRP +#include <asm/processor.h> +#endif + +#define DRV_NAME "via82cxxx" + +#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_BAD_PREQ 0x01 /* Crashes if PREQ# till DDACK# set */ +#define VIA_BAD_CLK66 0x02 /* 66 MHz clock doesn't work correctly */ +#define VIA_SET_FIFO 0x04 /* Needs to have FIFO split set */ +#define VIA_NO_UNMASK 0x08 /* Doesn't work with IRQ unmasking on */ +#define VIA_BAD_ID 0x10 /* Has wrong vendor ID (0x1107) */ +#define VIA_BAD_AST 0x20 /* Don't touch Address Setup Timing */ +#define VIA_SATA_PATA 0x80 /* SATA/PATA combined configuration */ + +enum { + VIA_IDFLAG_SINGLE = (1 << 1), /* single channel controller */ +}; + +/* + * VIA SouthBridge chips. + */ + +static struct via_isa_bridge { + char *name; + u16 id; + u8 rev_min; + u8 rev_max; + u8 udma_mask; + u8 flags; +} via_isa_bridges[] = { + { "vx855", PCI_DEVICE_ID_VIA_VX855, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA }, + { "vx800", PCI_DEVICE_ID_VIA_VX800, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA }, + { "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA }, + { "vt8261", PCI_DEVICE_ID_VIA_8261, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0xff, ATA_UDMA6, VIA_BAD_AST }, + { "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8233a", PCI_DEVICE_ID_VIA_8233A, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { "vt8233c", PCI_DEVICE_ID_VIA_8233C_0, 0x00, 0x2f, ATA_UDMA5, }, + { "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, ATA_UDMA5, }, + { "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, ATA_UDMA5, }, + { "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, ATA_UDMA5, }, + { "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, ATA_UDMA4, }, + { "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, ATA_UDMA2, VIA_BAD_CLK66 }, + { "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, ATA_UDMA4, }, + { "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, ATA_UDMA2, VIA_BAD_CLK66 }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, ATA_UDMA2, VIA_SET_FIFO }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, ATA_UDMA2, VIA_SET_FIFO | VIA_BAD_PREQ }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, ATA_UDMA2, VIA_SET_FIFO }, + { "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, ATA_UDMA2, VIA_SET_FIFO }, + { "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, 0x00, VIA_SET_FIFO }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, 0x00, VIA_SET_FIFO | VIA_NO_UNMASK }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, 0x00, VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID }, + { "vtxxxx", PCI_DEVICE_ID_VIA_ANON, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST }, + { NULL } +}; + +static unsigned int via_clock; +static char *via_dma[] = { "16", "25", "33", "44", "66", "100", "133" }; + +struct via82cxxx_dev +{ + struct via_isa_bridge *via_config; + unsigned int via_80w; +}; + +/** + * 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(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing) +{ + struct pci_dev *dev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; + u8 t; + + if (~vdev->via_config->flags & VIA_BAD_AST) { + pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((clamp_val(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)), + ((clamp_val(timing->act8b, 1, 16) - 1) << 4) | (clamp_val(timing->rec8b, 1, 16) - 1)); + + pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn), + ((clamp_val(timing->active, 1, 16) - 1) << 4) | (clamp_val(timing->recover, 1, 16) - 1)); + + switch (vdev->via_config->udma_mask) { + case ATA_UDMA2: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 5) - 2)) : 0x03; break; + case ATA_UDMA4: t = timing->udma ? (0xe8 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x0f; break; + case ATA_UDMA5: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break; + case ATA_UDMA6: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break; + } + + /* Set UDMA unless device is not UDMA capable */ + if (vdev->via_config->udma_mask) { + u8 udma_etc; + + pci_read_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, &udma_etc); + + /* clear transfer mode bit */ + udma_etc &= ~0x20; + + if (timing->udma) { + /* preserve 80-wire cable detection bit */ + udma_etc &= 0x10; + udma_etc |= t; + } + + pci_write_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, udma_etc); + } +} + +/** + * via_set_drive - configure transfer mode + * @hwif: port + * @drive: Drive to set up + * + * via_set_drive() computes timing values configures the chipset to + * a desired transfer mode. It also can be called by upper layers. + */ + +static void via_set_drive(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_drive_t *peer = ide_get_pair_dev(drive); + struct ide_host *host = dev_get_drvdata(hwif->dev); + struct via82cxxx_dev *vdev = host->host_priv; + struct ide_timing t, p; + unsigned int T, UT; + const u8 speed = drive->dma_mode; + + T = 1000000000 / via_clock; + + switch (vdev->via_config->udma_mask) { + case ATA_UDMA2: UT = T; break; + case ATA_UDMA4: UT = T/2; break; + case ATA_UDMA5: UT = T/3; break; + case ATA_UDMA6: UT = T/4; break; + default: UT = T; + } + + ide_timing_compute(drive, speed, &t, T, UT); + + if (peer) { + ide_timing_compute(peer, peer->pio_mode, &p, T, UT); + ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); + } + + via_set_speed(hwif, drive->dn, &t); +} + +/** + * via_set_pio_mode - set host controller for PIO mode + * @hwif: port + * @drive: drive + * + * A callback from the upper layers for PIO-only tuning. + */ + +static void via_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive) +{ + drive->dma_mode = drive->pio_mode; + via_set_drive(hwif, drive); +} + +static struct via_isa_bridge *via_config_find(struct pci_dev **isa) +{ + struct via_isa_bridge *via_config; + + for (via_config = via_isa_bridges; + via_config->id != PCI_DEVICE_ID_VIA_ANON; via_config++) + if ((*isa = pci_get_device(PCI_VENDOR_ID_VIA + + !!(via_config->flags & VIA_BAD_ID), + via_config->id, NULL))) { + + if ((*isa)->revision >= via_config->rev_min && + (*isa)->revision <= via_config->rev_max) + break; + pci_dev_put(*isa); + } + + return via_config; +} + +/* + * Check and handle 80-wire cable presence + */ +static void via_cable_detect(struct via82cxxx_dev *vdev, u32 u) +{ + int i; + + switch (vdev->via_config->udma_mask) { + case ATA_UDMA4: + 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 + */ + vdev->via_80w |= (1 << (1 - (i >> 4))); + } + break; + + case ATA_UDMA5: + 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 + */ + vdev->via_80w |= (1 << (1 - (i >> 4))); + } + break; + + case ATA_UDMA6: + 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 + */ + vdev->via_80w |= (1 << (1 - (i >> 4))); + } + break; + } +} + +/** + * init_chipset_via82cxxx - initialization handler + * @dev: PCI device + * + * The initialization callback. Here we determine the IDE chip type + * and initialize its drive independent registers. + */ + +static int init_chipset_via82cxxx(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; + struct via_isa_bridge *via_config = vdev->via_config; + u8 t, v; + u32 u; + + /* + * Detect cable and configure Clk66 + */ + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + + via_cable_detect(vdev, u); + + if (via_config->udma_mask == ATA_UDMA4) { + /* Enable Clk66 */ + pci_write_config_dword(dev, VIA_UDMA_TIMING, u|0x80008); + } else if (via_config->flags & VIA_BAD_CLK66) { + /* Would cause trouble on 596a and 686 */ + 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); + + return 0; +} + +/* + * Cable special cases + */ + +static const struct dmi_system_id cable_dmi_table[] = { + { + .ident = "Acer Ferrari 3400", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer,Inc."), + DMI_MATCH(DMI_BOARD_NAME, "Ferrari 3400"), + }, + }, + { } +}; + +static int via_cable_override(struct pci_dev *pdev) +{ + /* Systems by DMI */ + if (dmi_check_system(cable_dmi_table)) + return 1; + + /* Arima W730-K8/Targa Visionary 811/... */ + if (pdev->subsystem_vendor == 0x161F && + pdev->subsystem_device == 0x2032) + return 1; + + return 0; +} + +static u8 via82cxxx_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *pdev = to_pci_dev(hwif->dev); + struct ide_host *host = pci_get_drvdata(pdev); + struct via82cxxx_dev *vdev = host->host_priv; + + if (via_cable_override(pdev)) + return ATA_CBL_PATA40_SHORT; + + if ((vdev->via_config->flags & VIA_SATA_PATA) && hwif->channel == 0) + return ATA_CBL_SATA; + + if ((vdev->via_80w >> hwif->channel) & 1) + return ATA_CBL_PATA80; + else + return ATA_CBL_PATA40; +} + +static const struct ide_port_ops via_port_ops = { + .set_pio_mode = via_set_pio_mode, + .set_dma_mode = via_set_drive, + .cable_detect = via82cxxx_cable_detect, +}; + +static const struct ide_port_info via82cxxx_chipset = { + .name = DRV_NAME, + .init_chipset = init_chipset_via82cxxx, + .enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } }, + .port_ops = &via_port_ops, + .host_flags = IDE_HFLAG_PIO_NO_BLACKLIST | + IDE_HFLAG_POST_SET_MODE | + IDE_HFLAG_IO_32BIT, + .pio_mask = ATA_PIO5, + .swdma_mask = ATA_SWDMA2, + .mwdma_mask = ATA_MWDMA2, +}; + +static int via_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct pci_dev *isa = NULL; + struct via_isa_bridge *via_config; + struct via82cxxx_dev *vdev; + int rc; + u8 idx = id->driver_data; + struct ide_port_info d; + + d = via82cxxx_chipset; + + /* + * Find the ISA bridge and check we know what it is. + */ + via_config = via_config_find(&isa); + + /* + * Print the boot message. + */ + printk(KERN_INFO DRV_NAME " %s: VIA %s (rev %02x) IDE %sDMA%s\n", + pci_name(dev), via_config->name, isa->revision, + via_config->udma_mask ? "U" : "MW", + via_dma[via_config->udma_mask ? + (fls(via_config->udma_mask) - 1) : 0]); + + pci_dev_put(isa); + + /* + * Determine system bus clock. + */ + via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 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 DRV_NAME ": User given PCI clock speed " + "impossible (%d), using 33 MHz instead.\n", via_clock); + via_clock = 33333; + } + + if (idx == 1) + d.enablebits[1].reg = d.enablebits[0].reg = 0; + else + d.host_flags |= IDE_HFLAG_NO_AUTODMA; + + if (idx == VIA_IDFLAG_SINGLE) + d.host_flags |= IDE_HFLAG_SINGLE; + + if ((via_config->flags & VIA_NO_UNMASK) == 0) + d.host_flags |= IDE_HFLAG_UNMASK_IRQS; + + d.udma_mask = via_config->udma_mask; + + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + printk(KERN_ERR DRV_NAME " %s: out of memory :(\n", + pci_name(dev)); + return -ENOMEM; + } + + vdev->via_config = via_config; + + rc = ide_pci_init_one(dev, &d, vdev); + if (rc) + kfree(vdev); + + return rc; +} + +static void via_remove(struct pci_dev *dev) +{ + struct ide_host *host = pci_get_drvdata(dev); + struct via82cxxx_dev *vdev = host->host_priv; + + ide_pci_remove(dev); + kfree(vdev); +} + +static const struct pci_device_id via_pci_tbl[] = { + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), 0 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1), 0 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_CX700_IDE), 0 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_VX855_IDE), VIA_IDFLAG_SINGLE }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410), 1 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6415), 1 }, + { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), 1 }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, via_pci_tbl); + +static struct pci_driver via_pci_driver = { + .name = "VIA_IDE", + .id_table = via_pci_tbl, + .probe = via_init_one, + .remove = via_remove, + .suspend = ide_pci_suspend, + .resume = ide_pci_resume, +}; + +static int __init via_ide_init(void) +{ + return ide_pci_register_driver(&via_pci_driver); +} + +static void __exit via_ide_exit(void) +{ + pci_unregister_driver(&via_pci_driver); +} + +module_init(via_ide_init); +module_exit(via_ide_exit); + +MODULE_AUTHOR("Vojtech Pavlik, Bartlomiej Zolnierkiewicz, Michel Aubry, Jeff Garzik, Andre Hedrick"); +MODULE_DESCRIPTION("PCI driver module for VIA IDE"); +MODULE_LICENSE("GPL"); |