for-5.14/libata-2021-06-27
-----BEGIN PGP SIGNATURE----- iQJEBAABCAAuFiEEwPw5LcreJtl1+l5K99NY+ylx4KYFAmDYxaEQHGF4Ym9lQGtl cm5lbC5kawAKCRD301j7KXHgppWsEAC6yhQf7PlxqrjNaGvk6jLEZNfvg7R+LnEB Otf+oMehaPYVojVDS+4USOvs7P6uv/locJRMBVQTBs0Y6Fo2foXdjbt8jMYiaF7f 7ax9gZq5KymYxT06EY2Y3s88sKe+bC07+BT/Lka0TMTcorDx0qUloBvoPTj5ix6U EzgGWP1bPeajZdXTHg4didErHxwimmdj7J18Bptw+7+kIlJCmTp47YEc3JDzfWG/ +UPBW8brxFhj5qlDZPrTIf5Crs2ATeprEWNrHMZxv4m3qvaR9kYWrA6saT6+GEZP ZaOc4Vwr/bYNdWcLevIa8lwzEe7AvVdjG6zmZjZst/WcwX02Yn1Kdjpgr+6AwGyo 36chRijiCKexHTBmOe3kaLMJDHfA9ssl3li4FDb2baOtENQAdSfvyDolARXHuh89 Tt07ypXAOHtRP9O4TpZYu1LM0FYfnUV675cKCzQgWBBALUKmsyTcN9AAD/iFVgRk zBQPByNIbG2gJj79QR21uLYPBkWXmo40Jg+BBh2GwwWQPY1Zz91jCfSotpsHvbjv FzajhoCxcySSsmDtBVIkzZ6kVbocLYNw8dQ2+tgpNhJA0wgOFY4S5aIJnLxL0CfK n2MOw8KWaM8w9qJwkqVYsLU+CJU0yWU9Bz08cCyDZ+Pt+Y+ZReG5jlamg9M6xZ/t GNujjas1UA== =79iZ -----END PGP SIGNATURE----- Merge tag 'for-5.14/libata-2021-06-27' of git://git.kernel.dk/linux-block Pull libata updates from Jens Axboe: "The big change in this round is that we're finally in a position where we can sanely remove the old drivers/ide/ code, as libata covers everything we need by now. This is exciting for two reasons: 1) we delete a lot of legacy code that doesn't really meet the standards we have today, and 2) it enables us to clean up various bits in the block layer that exist only because of the old IDE code. Outside of that, just a few minor fixes here, fixups for warnings, etc" * tag 'for-5.14/libata-2021-06-27' of git://git.kernel.dk/linux-block: (29 commits) ata: rb532_cf: remove redundant codes ide: remove the legacy ide driver m68k: use libata instead of the legacy ide driver ARM: disable CONFIG_IDE in pxa_defconfig ARM: disable CONFIG_IDE in footbridge_defconfig alpha: use libata instead of the legacy ide driver pata_cypress: add a module option to disable BM-DMA ata: pata_macio: Avoid overwriting initialised field in 'pata_macio_sht' ata: pata_serverworks: Avoid overwriting initialised field in 'serverworks_osb4_sht ata: pata_sc1200: sc1200_sht'Avoid overwriting initialised field in ' ata: pata_cs5530: Avoid overwriting initialised field in 'cs5530_sht' ata: pata_cs5520: Avoid overwriting initialised field in 'cs5520_sht' ata: pata_atiixp: Avoid overwriting initialised field in 'atiixp_sht' ata: sata_nv: Do not over-write initialise fields in 'nv_adma_sht' and 'nv_swncq_sht' ata: sata_mv: Do not over-write initialise fields in 'mv6_sht' ata: sata_sil24: Do not over-write initialise fields in 'sil24_sht' ata: ahci: Ensure initialised fields are not overwritten in AHCI_SHT() ata: include: libata: Move fields commonly over-written to separate MACRO ahci: Add support for Dell S140 and later controllers ata: ahci_sunxi: Disable DIPM ...
This commit is contained in:
Коммит
43bd8a67cd
|
@ -7,8 +7,8 @@ Summary of `HDIO_` ioctl calls
|
|||
November, 2004
|
||||
|
||||
This document attempts to describe the ioctl(2) calls supported by
|
||||
the HD/IDE layer. These are by-and-large implemented (as of Linux 2.6)
|
||||
in drivers/ide/ide.c and drivers/block/scsi_ioctl.c
|
||||
the HD/IDE layer. These are by-and-large implemented (as of Linux 5.11)
|
||||
drivers/ata/libata-scsi.c.
|
||||
|
||||
ioctl values are listed in <linux/hdreg.h>. As of this writing, they
|
||||
are as follows:
|
||||
|
@ -17,50 +17,17 @@ are as follows:
|
|||
|
||||
======================= =======================================
|
||||
HDIO_GETGEO get device geometry
|
||||
HDIO_GET_UNMASKINTR get current unmask setting
|
||||
HDIO_GET_MULTCOUNT get current IDE blockmode setting
|
||||
HDIO_GET_QDMA get use-qdma flag
|
||||
HDIO_SET_XFER set transfer rate via proc
|
||||
HDIO_OBSOLETE_IDENTITY OBSOLETE, DO NOT USE
|
||||
HDIO_GET_KEEPSETTINGS get keep-settings-on-reset flag
|
||||
HDIO_GET_32BIT get current io_32bit setting
|
||||
HDIO_GET_NOWERR get ignore-write-error flag
|
||||
HDIO_GET_DMA get use-dma flag
|
||||
HDIO_GET_NICE get nice flags
|
||||
HDIO_GET_IDENTITY get IDE identification info
|
||||
HDIO_GET_WCACHE get write cache mode on|off
|
||||
HDIO_GET_ACOUSTIC get acoustic value
|
||||
HDIO_GET_ADDRESS get sector addressing mode
|
||||
HDIO_GET_BUSSTATE get the bus state of the hwif
|
||||
HDIO_TRISTATE_HWIF execute a channel tristate
|
||||
HDIO_DRIVE_RESET execute a device reset
|
||||
HDIO_DRIVE_TASKFILE execute raw taskfile
|
||||
HDIO_DRIVE_TASK execute task and special drive command
|
||||
HDIO_DRIVE_CMD execute a special drive command
|
||||
HDIO_DRIVE_CMD_AEB HDIO_DRIVE_TASK
|
||||
======================= =======================================
|
||||
|
||||
ioctls that pass non-pointer values:
|
||||
|
||||
======================= =======================================
|
||||
HDIO_SET_MULTCOUNT change IDE blockmode
|
||||
HDIO_SET_UNMASKINTR permit other irqs during I/O
|
||||
HDIO_SET_KEEPSETTINGS keep ioctl settings on reset
|
||||
HDIO_SET_32BIT change io_32bit flags
|
||||
HDIO_SET_NOWERR change ignore-write-error flag
|
||||
HDIO_SET_DMA change use-dma flag
|
||||
HDIO_SET_PIO_MODE reconfig interface to new speed
|
||||
HDIO_SCAN_HWIF register and (re)scan interface
|
||||
HDIO_SET_NICE set nice flags
|
||||
HDIO_UNREGISTER_HWIF unregister interface
|
||||
HDIO_SET_WCACHE change write cache enable-disable
|
||||
HDIO_SET_ACOUSTIC change acoustic behavior
|
||||
HDIO_SET_BUSSTATE set the bus state of the hwif
|
||||
HDIO_SET_QDMA change use-qdma flag
|
||||
HDIO_SET_ADDRESS change lba addressing modes
|
||||
|
||||
HDIO_SET_IDE_SCSI Set scsi emulation mode on/off
|
||||
HDIO_SET_SCSI_IDE not implemented yet
|
||||
======================= =======================================
|
||||
|
||||
|
||||
|
@ -137,143 +104,6 @@ HDIO_GETGEO
|
|||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_UNMASKINTR
|
||||
get current unmask setting
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_UNMASKINTR, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the drive's current unmask setting
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_SET_UNMASKINTR
|
||||
permit other irqs during I/O
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
unsigned long val;
|
||||
|
||||
ioctl(fd, HDIO_SET_UNMASKINTR, val);
|
||||
|
||||
inputs:
|
||||
New value for unmask flag
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_MULTCOUNT
|
||||
get current IDE blockmode setting
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_MULTCOUNT, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current IDE block mode setting. This
|
||||
controls how many sectors the drive will transfer per
|
||||
interrupt.
|
||||
|
||||
|
||||
|
||||
HDIO_SET_MULTCOUNT
|
||||
change IDE blockmode
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int val;
|
||||
|
||||
ioctl(fd, HDIO_SET_MULTCOUNT, val);
|
||||
|
||||
inputs:
|
||||
New value for IDE block mode setting. This controls how many
|
||||
sectors the drive will transfer per interrupt.
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range supported by disk.
|
||||
- EBUSY Controller busy or blockmode already set.
|
||||
- EIO Drive did not accept new block mode.
|
||||
|
||||
notes:
|
||||
Source code comments read::
|
||||
|
||||
This is tightly woven into the driver->do_special cannot
|
||||
touch. DON'T do it again until a total personality rewrite
|
||||
is committed.
|
||||
|
||||
If blockmode has already been set, this ioctl will fail with
|
||||
-EBUSY
|
||||
|
||||
|
||||
|
||||
HDIO_GET_QDMA
|
||||
get use-qdma flag
|
||||
|
||||
|
||||
Not implemented, as of 2.6.8.1
|
||||
|
||||
|
||||
|
||||
HDIO_SET_XFER
|
||||
set transfer rate via proc
|
||||
|
||||
|
||||
Not implemented, as of 2.6.8.1
|
||||
|
||||
|
||||
|
||||
HDIO_OBSOLETE_IDENTITY
|
||||
OBSOLETE, DO NOT USE
|
||||
|
||||
|
||||
Same as HDIO_GET_IDENTITY (see below), except that it only
|
||||
returns the first 142 bytes of drive identity information.
|
||||
|
||||
|
||||
|
||||
HDIO_GET_IDENTITY
|
||||
get IDE identification info
|
||||
|
||||
|
@ -308,60 +138,6 @@ HDIO_GET_IDENTITY
|
|||
|
||||
|
||||
|
||||
HDIO_GET_KEEPSETTINGS
|
||||
get keep-settings-on-reset flag
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_KEEPSETTINGS, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current "keep settings" flag
|
||||
|
||||
|
||||
|
||||
notes:
|
||||
When set, indicates that kernel should restore settings
|
||||
after a drive reset.
|
||||
|
||||
|
||||
|
||||
HDIO_SET_KEEPSETTINGS
|
||||
keep ioctl settings on reset
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_SET_KEEPSETTINGS, val);
|
||||
|
||||
inputs:
|
||||
New value for keep_settings flag
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_GET_32BIT
|
||||
get current io_32bit setting
|
||||
|
||||
|
@ -387,288 +163,6 @@ HDIO_GET_32BIT
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_NOWERR
|
||||
get ignore-write-error flag
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_NOWERR, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current ignore-write-error flag
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_DMA
|
||||
get use-dma flag
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_DMA, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current use-dma flag
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_NICE
|
||||
get nice flags
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long nice;
|
||||
|
||||
ioctl(fd, HDIO_GET_NICE, &nice);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The drive's "nice" values.
|
||||
|
||||
|
||||
|
||||
notes:
|
||||
Per-drive flags which determine when the system will give more
|
||||
bandwidth to other devices sharing the same IDE bus.
|
||||
|
||||
See <linux/hdreg.h>, near symbol IDE_NICE_DSC_OVERLAP.
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_SET_NICE
|
||||
set nice flags
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
unsigned long nice;
|
||||
|
||||
...
|
||||
ioctl(fd, HDIO_SET_NICE, nice);
|
||||
|
||||
inputs:
|
||||
bitmask of nice flags.
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EPERM Flags other than DSC_OVERLAP and NICE_1 set.
|
||||
- EPERM DSC_OVERLAP specified but not supported by drive
|
||||
|
||||
notes:
|
||||
This ioctl sets the DSC_OVERLAP and NICE_1 flags from values
|
||||
provided by the user.
|
||||
|
||||
Nice flags are listed in <linux/hdreg.h>, starting with
|
||||
IDE_NICE_DSC_OVERLAP. These values represent shifts.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_WCACHE
|
||||
get write cache mode on|off
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_WCACHE, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current write cache mode
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_ACOUSTIC
|
||||
get acoustic value
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_ACOUSTIC, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current acoustic settings
|
||||
|
||||
|
||||
|
||||
notes:
|
||||
See HDIO_SET_ACOUSTIC
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_GET_ADDRESS
|
||||
usage::
|
||||
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_GET_ADDRESS, &val);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
The value of the current addressing mode:
|
||||
|
||||
= ===================
|
||||
0 28-bit
|
||||
1 48-bit
|
||||
2 48-bit doing 28-bit
|
||||
3 64-bit
|
||||
= ===================
|
||||
|
||||
|
||||
|
||||
HDIO_GET_BUSSTATE
|
||||
get the bus state of the hwif
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long state;
|
||||
|
||||
ioctl(fd, HDIO_SCAN_HWIF, &state);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
Current power state of the IDE bus. One of BUSSTATE_OFF,
|
||||
BUSSTATE_ON, or BUSSTATE_TRISTATE
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_SET_BUSSTATE
|
||||
set the bus state of the hwif
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int state;
|
||||
|
||||
...
|
||||
ioctl(fd, HDIO_SCAN_HWIF, state);
|
||||
|
||||
inputs:
|
||||
Desired IDE power state. One of BUSSTATE_OFF, BUSSTATE_ON,
|
||||
or BUSSTATE_TRISTATE
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_RAWIO
|
||||
- EOPNOTSUPP Hardware interface does not support bus power control
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_TRISTATE_HWIF
|
||||
execute a channel tristate
|
||||
|
||||
|
||||
Not implemented, as of 2.6.8.1. See HDIO_SET_BUSSTATE
|
||||
|
||||
|
||||
|
||||
HDIO_DRIVE_RESET
|
||||
execute a device reset
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int args[3]
|
||||
|
||||
...
|
||||
ioctl(fd, HDIO_DRIVE_RESET, args);
|
||||
|
||||
inputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- ENXIO No such device: phy dead or ctl_addr == 0
|
||||
- EIO I/O error: reset timed out or hardware error
|
||||
|
||||
notes:
|
||||
|
||||
- Execute a reset on the device as soon as the current IO
|
||||
operation has completed.
|
||||
|
||||
- Executes an ATAPI soft reset if applicable, otherwise
|
||||
executes an ATA soft reset on the controller.
|
||||
|
||||
|
||||
|
||||
HDIO_DRIVE_TASKFILE
|
||||
execute raw taskfile
|
||||
|
||||
|
@ -1026,14 +520,6 @@ HDIO_DRIVE_TASK
|
|||
|
||||
|
||||
|
||||
HDIO_DRIVE_CMD_AEB
|
||||
HDIO_DRIVE_TASK
|
||||
|
||||
|
||||
Not implemented, as of 2.6.8.1
|
||||
|
||||
|
||||
|
||||
HDIO_SET_32BIT
|
||||
change io_32bit flags
|
||||
|
||||
|
@ -1059,284 +545,3 @@ HDIO_SET_32BIT
|
|||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 3]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
|
||||
HDIO_SET_NOWERR
|
||||
change ignore-write-error flag
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int val;
|
||||
|
||||
ioctl(fd, HDIO_SET_NOWERR, val);
|
||||
|
||||
inputs:
|
||||
New value for ignore-write-error flag. Used for ignoring
|
||||
|
||||
|
||||
WRERR_STAT
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SET_DMA
|
||||
change use-dma flag
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_SET_DMA, val);
|
||||
|
||||
inputs:
|
||||
New value for use-dma flag
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SET_PIO_MODE
|
||||
reconfig interface to new speed
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_SET_PIO_MODE, val);
|
||||
|
||||
inputs:
|
||||
New interface speed.
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 255]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SCAN_HWIF
|
||||
register and (re)scan interface
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int args[3]
|
||||
|
||||
...
|
||||
ioctl(fd, HDIO_SCAN_HWIF, args);
|
||||
|
||||
inputs:
|
||||
|
||||
======= =========================
|
||||
args[0] io address to probe
|
||||
|
||||
|
||||
args[1] control address to probe
|
||||
args[2] irq number
|
||||
======= =========================
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_RAWIO
|
||||
- EIO Probe failed.
|
||||
|
||||
notes:
|
||||
This ioctl initializes the addresses and irq for a disk
|
||||
controller, probes for drives, and creates /proc/ide
|
||||
interfaces as appropriate.
|
||||
|
||||
|
||||
|
||||
HDIO_UNREGISTER_HWIF
|
||||
unregister interface
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int index;
|
||||
|
||||
ioctl(fd, HDIO_UNREGISTER_HWIF, index);
|
||||
|
||||
inputs:
|
||||
index index of hardware interface to unregister
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error returns:
|
||||
- EACCES Access denied: requires CAP_SYS_RAWIO
|
||||
|
||||
notes:
|
||||
This ioctl removes a hardware interface from the kernel.
|
||||
|
||||
Currently (2.6.8) this ioctl silently fails if any drive on
|
||||
the interface is busy.
|
||||
|
||||
|
||||
|
||||
HDIO_SET_WCACHE
|
||||
change write cache enable-disable
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int val;
|
||||
|
||||
ioctl(fd, HDIO_SET_WCACHE, val);
|
||||
|
||||
inputs:
|
||||
New value for write cache enable
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SET_ACOUSTIC
|
||||
change acoustic behavior
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int val;
|
||||
|
||||
ioctl(fd, HDIO_SET_ACOUSTIC, val);
|
||||
|
||||
inputs:
|
||||
New value for drive acoustic settings
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 254]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SET_QDMA
|
||||
change use-qdma flag
|
||||
|
||||
|
||||
Not implemented, as of 2.6.8.1
|
||||
|
||||
|
||||
|
||||
HDIO_SET_ADDRESS
|
||||
change lba addressing modes
|
||||
|
||||
|
||||
usage::
|
||||
|
||||
int val;
|
||||
|
||||
ioctl(fd, HDIO_SET_ADDRESS, val);
|
||||
|
||||
inputs:
|
||||
New value for addressing mode
|
||||
|
||||
= ===================
|
||||
0 28-bit
|
||||
1 48-bit
|
||||
2 48-bit doing 28-bit
|
||||
= ===================
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 2]
|
||||
- EBUSY Controller busy
|
||||
- EIO Drive does not support lba48 mode.
|
||||
|
||||
|
||||
HDIO_SET_IDE_SCSI
|
||||
usage::
|
||||
|
||||
|
||||
long val;
|
||||
|
||||
ioctl(fd, HDIO_SET_IDE_SCSI, val);
|
||||
|
||||
inputs:
|
||||
New value for scsi emulation mode (?)
|
||||
|
||||
|
||||
|
||||
outputs:
|
||||
none
|
||||
|
||||
|
||||
|
||||
error return:
|
||||
- EINVAL Called on a partition instead of the whole disk device
|
||||
- EACCES Access denied: requires CAP_SYS_ADMIN
|
||||
- EINVAL value out of range [0 1]
|
||||
- EBUSY Controller busy
|
||||
|
||||
|
||||
|
||||
HDIO_SET_SCSI_IDE
|
||||
Not implemented, as of 2.6.8.1
|
||||
|
|
16
MAINTAINERS
16
MAINTAINERS
|
@ -8771,22 +8771,6 @@ L: linux-i2c@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-icy.c
|
||||
|
||||
IDE SUBSYSTEM
|
||||
M: "David S. Miller" <davem@davemloft.net>
|
||||
L: linux-ide@vger.kernel.org
|
||||
S: Maintained
|
||||
Q: http://patchwork.ozlabs.org/project/linux-ide/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/davem/ide.git
|
||||
F: Documentation/ide/
|
||||
F: drivers/ide/
|
||||
F: include/linux/ide.h
|
||||
|
||||
IDE/ATAPI DRIVERS
|
||||
L: linux-ide@vger.kernel.org
|
||||
S: Orphan
|
||||
F: Documentation/cdrom/ide-cd.rst
|
||||
F: drivers/ide/ide-cd*
|
||||
|
||||
IDEAPAD LAPTOP EXTRAS DRIVER
|
||||
M: Ike Panhc <ike.pan@canonical.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
|
|
@ -25,19 +25,18 @@ CONFIG_PNP=y
|
|||
CONFIG_ISAPNP=y
|
||||
CONFIG_BLK_DEV_FD=y
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_IDE_GENERIC=y
|
||||
CONFIG_BLK_DEV_GENERIC=y
|
||||
CONFIG_BLK_DEV_ALI15X3=y
|
||||
CONFIG_BLK_DEV_CMD64X=y
|
||||
CONFIG_BLK_DEV_CY82C693=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_BLK_DEV_SR=y
|
||||
CONFIG_SCSI_AIC7XXX=m
|
||||
CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
|
||||
# CONFIG_AIC7XXX_DEBUG_ENABLE is not set
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_SATA_PMP is not set
|
||||
CONFIG_PATA_ALI=y
|
||||
CONFIG_PATA_CMD64X=y
|
||||
CONFIG_PATA_CYPRESS=y
|
||||
CONFIG_ATA_GENERIC=y
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_DUMMY=m
|
||||
CONFIG_NET_ETHERNET=y
|
||||
|
|
|
@ -64,7 +64,6 @@ CONFIG_PARIDE_ON26=m
|
|||
CONFIG_BLK_DEV_LOOP=m
|
||||
CONFIG_BLK_DEV_NBD=m
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_IDE=y
|
||||
CONFIG_NETDEVICES=y
|
||||
CONFIG_NET_ETHERNET=y
|
||||
CONFIG_NET_VENDOR_3COM=y
|
||||
|
|
|
@ -215,8 +215,6 @@ CONFIG_IIO=m
|
|||
CONFIG_AD5446=m
|
||||
CONFIG_EEPROM_AT24=m
|
||||
CONFIG_SENSORS_LIS3_SPI=m
|
||||
CONFIG_IDE=m
|
||||
CONFIG_BLK_DEV_IDECS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=m
|
||||
CONFIG_CHR_DEV_ST=m
|
||||
|
|
|
@ -875,16 +875,8 @@ static const struct resource atari_scsi_tt_rsrc[] __initconst = {
|
|||
#define FALCON_IDE_BASE 0xfff00000
|
||||
|
||||
static const struct resource atari_falconide_rsrc[] __initconst = {
|
||||
{
|
||||
.flags = IORESOURCE_MEM,
|
||||
.start = FALCON_IDE_BASE,
|
||||
.end = FALCON_IDE_BASE + 0x39,
|
||||
},
|
||||
{
|
||||
.flags = IORESOURCE_IRQ,
|
||||
.start = IRQ_MFP_FSCSI,
|
||||
.end = IRQ_MFP_FSCSI,
|
||||
},
|
||||
DEFINE_RES_MEM(FALCON_IDE_BASE, 0x38),
|
||||
DEFINE_RES_MEM(FALCON_IDE_BASE + 0x38, 2),
|
||||
};
|
||||
|
||||
int __init atari_platform_init(void)
|
||||
|
|
|
@ -323,11 +323,6 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_DUMMY_IRQ=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_IDE_GD_ATAPI=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_GAYLE=y
|
||||
CONFIG_BLK_DEV_BUDDHA=y
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -344,6 +339,11 @@ CONFIG_GVP11_SCSI=y
|
|||
CONFIG_SCSI_A4000T=y
|
||||
CONFIG_SCSI_ZORRO7XX=y
|
||||
CONFIG_SCSI_ZORRO_ESP=y
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_ATA_VERBOSE_ERROR is not set
|
||||
# CONFIG_ATA_BMDMA is not set
|
||||
CONFIG_PATA_GAYLE=y
|
||||
CONFIG_PATA_BUDDHA=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_BLK_DEV_DM=m
|
||||
|
|
|
@ -324,10 +324,6 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_DUMMY_IRQ=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_IDE_GD_ATAPI=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_FALCON_IDE=y
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -339,6 +335,10 @@ CONFIG_SCSI_SAS_ATTRS=m
|
|||
CONFIG_ISCSI_TCP=m
|
||||
CONFIG_ISCSI_BOOT_SYSFS=m
|
||||
CONFIG_ATARI_SCSI=y
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_ATA_VERBOSE_ERROR is not set
|
||||
# CONFIG_ATA_BMDMA is not set
|
||||
CONFIG_PATA_FALCON=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_BLK_DEV_DM=m
|
||||
|
|
|
@ -315,11 +315,6 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_DUMMY_IRQ=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_IDE_GD_ATAPI=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_PLATFORM=y
|
||||
CONFIG_BLK_DEV_MAC_IDE=y
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -332,6 +327,10 @@ CONFIG_ISCSI_TCP=m
|
|||
CONFIG_ISCSI_BOOT_SYSFS=m
|
||||
CONFIG_MAC_SCSI=y
|
||||
CONFIG_SCSI_MAC_ESP=y
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_ATA_VERBOSE_ERROR is not set
|
||||
# CONFIG_ATA_BMDMA is not set
|
||||
CONFIG_PATA_PLATFORM=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_BLK_DEV_DM=m
|
||||
|
|
|
@ -344,15 +344,6 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_DUMMY_IRQ=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_IDE_GD_ATAPI=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_PLATFORM=y
|
||||
CONFIG_BLK_DEV_GAYLE=y
|
||||
CONFIG_BLK_DEV_BUDDHA=y
|
||||
CONFIG_BLK_DEV_FALCON_IDE=y
|
||||
CONFIG_BLK_DEV_MAC_IDE=y
|
||||
CONFIG_BLK_DEV_Q40IDE=y
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -376,6 +367,13 @@ CONFIG_MVME147_SCSI=y
|
|||
CONFIG_MVME16x_SCSI=y
|
||||
CONFIG_BVME6000_SCSI=y
|
||||
CONFIG_SUN3X_ESP=y
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_ATA_VERBOSE_ERROR is not set
|
||||
# CONFIG_ATA_BMDMA is not set
|
||||
CONFIG_PATA_FALCON=y
|
||||
CONFIG_PATA_GAYLE=y
|
||||
CONFIG_PATA_BUDDHA=y
|
||||
CONFIG_PATA_PLATFORM=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_BLK_DEV_DM=m
|
||||
|
|
|
@ -314,10 +314,6 @@ CONFIG_BLK_DEV_RAM=y
|
|||
CONFIG_CDROM_PKTCDVD=m
|
||||
CONFIG_ATA_OVER_ETH=m
|
||||
CONFIG_DUMMY_IRQ=m
|
||||
CONFIG_IDE=y
|
||||
CONFIG_IDE_GD_ATAPI=y
|
||||
CONFIG_BLK_DEV_IDECD=y
|
||||
CONFIG_BLK_DEV_Q40IDE=y
|
||||
CONFIG_RAID_ATTRS=m
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
|
@ -328,6 +324,10 @@ CONFIG_SCSI_CONSTANTS=y
|
|||
CONFIG_SCSI_SAS_ATTRS=m
|
||||
CONFIG_ISCSI_TCP=m
|
||||
CONFIG_ISCSI_BOOT_SYSFS=m
|
||||
CONFIG_ATA=y
|
||||
# CONFIG_ATA_VERBOSE_ERROR is not set
|
||||
# CONFIG_ATA_BMDMA is not set
|
||||
CONFIG_PATA_FALCON=y
|
||||
CONFIG_MD=y
|
||||
CONFIG_MD_LINEAR=m
|
||||
CONFIG_BLK_DEV_DM=m
|
||||
|
|
|
@ -933,13 +933,15 @@ static const struct resource mac_scsi_ccl_rsrc[] __initconst = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct resource mac_ide_quadra_rsrc[] __initconst = {
|
||||
DEFINE_RES_MEM(0x50F1A000, 0x104),
|
||||
static const struct resource mac_pata_quadra_rsrc[] __initconst = {
|
||||
DEFINE_RES_MEM(0x50F1A000, 0x38),
|
||||
DEFINE_RES_MEM(0x50F1A038, 0x04),
|
||||
DEFINE_RES_IRQ(IRQ_NUBUS_F),
|
||||
};
|
||||
|
||||
static const struct resource mac_ide_pb_rsrc[] __initconst = {
|
||||
DEFINE_RES_MEM(0x50F1A000, 0x104),
|
||||
static const struct resource mac_pata_pb_rsrc[] __initconst = {
|
||||
DEFINE_RES_MEM(0x50F1A000, 0x38),
|
||||
DEFINE_RES_MEM(0x50F1A038, 0x04),
|
||||
DEFINE_RES_IRQ(IRQ_NUBUS_C),
|
||||
};
|
||||
|
||||
|
@ -949,7 +951,7 @@ static const struct resource mac_pata_baboon_rsrc[] __initconst = {
|
|||
DEFINE_RES_IRQ(IRQ_BABOON_1),
|
||||
};
|
||||
|
||||
static const struct pata_platform_info mac_pata_baboon_data __initconst = {
|
||||
static const struct pata_platform_info mac_pata_data __initconst = {
|
||||
.ioport_shift = 2,
|
||||
};
|
||||
|
||||
|
@ -1067,17 +1069,19 @@ int __init mac_platform_init(void)
|
|||
|
||||
switch (macintosh_config->ide_type) {
|
||||
case MAC_IDE_QUADRA:
|
||||
platform_device_register_simple("mac_ide", -1,
|
||||
mac_ide_quadra_rsrc, ARRAY_SIZE(mac_ide_quadra_rsrc));
|
||||
platform_device_register_resndata(NULL, "pata_platform", -1,
|
||||
mac_pata_quadra_rsrc, ARRAY_SIZE(mac_pata_quadra_rsrc),
|
||||
&mac_pata_data, sizeof(mac_pata_data));
|
||||
break;
|
||||
case MAC_IDE_PB:
|
||||
platform_device_register_simple("mac_ide", -1,
|
||||
mac_ide_pb_rsrc, ARRAY_SIZE(mac_ide_pb_rsrc));
|
||||
platform_device_register_resndata(NULL, "pata_platform", -1,
|
||||
mac_pata_pb_rsrc, ARRAY_SIZE(mac_pata_pb_rsrc),
|
||||
&mac_pata_data, sizeof(mac_pata_data));
|
||||
break;
|
||||
case MAC_IDE_BABOON:
|
||||
platform_device_register_resndata(NULL, "pata_platform", -1,
|
||||
mac_pata_baboon_rsrc, ARRAY_SIZE(mac_pata_baboon_rsrc),
|
||||
&mac_pata_baboon_data, sizeof(mac_pata_baboon_data));
|
||||
&mac_pata_data, sizeof(mac_pata_data));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -286,14 +286,39 @@ static int q40_set_rtc_pll(struct rtc_pll_info *pll)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static __init int q40_add_kbd_device(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
#define PCIDE_BASE1 0x1f0
|
||||
#define PCIDE_BASE2 0x170
|
||||
#define PCIDE_CTL 0x206
|
||||
|
||||
static const struct resource q40_pata_rsrc_0[] __initconst = {
|
||||
DEFINE_RES_MEM(q40_isa_io_base + PCIDE_BASE1 * 4, 0x38),
|
||||
DEFINE_RES_MEM(q40_isa_io_base + (PCIDE_BASE1 + PCIDE_CTL) * 4, 2),
|
||||
DEFINE_RES_IO(PCIDE_BASE1, 8),
|
||||
DEFINE_RES_IO(PCIDE_BASE1 + PCIDE_CTL, 1),
|
||||
DEFINE_RES_IRQ(14),
|
||||
};
|
||||
|
||||
static const struct resource q40_pata_rsrc_1[] __initconst = {
|
||||
DEFINE_RES_MEM(q40_isa_io_base + PCIDE_BASE2 * 4, 0x38),
|
||||
DEFINE_RES_MEM(q40_isa_io_base + (PCIDE_BASE2 + PCIDE_CTL) * 4, 2),
|
||||
DEFINE_RES_IO(PCIDE_BASE2, 8),
|
||||
DEFINE_RES_IO(PCIDE_BASE2 + PCIDE_CTL, 1),
|
||||
DEFINE_RES_IRQ(15),
|
||||
};
|
||||
|
||||
static __init int q40_platform_init(void)
|
||||
{
|
||||
if (!MACH_IS_Q40)
|
||||
return -ENODEV;
|
||||
|
||||
pdev = platform_device_register_simple("q40kbd", -1, NULL, 0);
|
||||
return PTR_ERR_OR_ZERO(pdev);
|
||||
platform_device_register_simple("q40kbd", -1, NULL, 0);
|
||||
|
||||
platform_device_register_simple("atari-falcon-ide", 0, q40_pata_rsrc_0,
|
||||
ARRAY_SIZE(q40_pata_rsrc_0));
|
||||
|
||||
platform_device_register_simple("atari-falcon-ide", 1, q40_pata_rsrc_1,
|
||||
ARRAY_SIZE(q40_pata_rsrc_1));
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(q40_add_kbd_device);
|
||||
arch_initcall(q40_platform_init);
|
||||
|
|
|
@ -33,8 +33,6 @@ source "drivers/nvme/Kconfig"
|
|||
|
||||
source "drivers/misc/Kconfig"
|
||||
|
||||
source "drivers/ide/Kconfig"
|
||||
|
||||
source "drivers/scsi/Kconfig"
|
||||
|
||||
source "drivers/ata/Kconfig"
|
||||
|
|
|
@ -78,7 +78,6 @@ obj-$(CONFIG_CXL_BUS) += cxl/
|
|||
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
|
||||
obj-$(CONFIG_NUBUS) += nubus/
|
||||
obj-y += macintosh/
|
||||
obj-$(CONFIG_IDE) += ide/
|
||||
obj-y += scsi/
|
||||
obj-y += nvme/
|
||||
obj-$(CONFIG_ATA) += ata/
|
||||
|
|
|
@ -1015,11 +1015,11 @@ config PATA_CMD640_PCI
|
|||
If unsure, say N.
|
||||
|
||||
config PATA_FALCON
|
||||
tristate "Atari Falcon PATA support"
|
||||
depends on M68K && ATARI
|
||||
tristate "Atari Falcon and Q40/Q60 PATA support"
|
||||
depends on M68K && (ATARI || Q40)
|
||||
help
|
||||
This option enables support for the on-board IDE
|
||||
interface on the Atari Falcon.
|
||||
interface on the Atari Falcon and Q40/Q60.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
|
|
@ -446,6 +446,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
|
|||
{ PCI_VENDOR_ID_AMD, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
|
||||
|
||||
/* Dell S140/S150 */
|
||||
{ PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_SUBVENDOR_ID_DELL, PCI_ANY_ID,
|
||||
PCI_CLASS_STORAGE_RAID << 8, 0xffffff, board_ahci },
|
||||
|
||||
/* VIA */
|
||||
{ PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */
|
||||
{ PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */
|
||||
|
|
|
@ -384,12 +384,15 @@ extern struct device_attribute *ahci_sdev_attrs[];
|
|||
* for ATA_BASE_SHT
|
||||
*/
|
||||
#define AHCI_SHT(drv_name) \
|
||||
ATA_NCQ_SHT(drv_name), \
|
||||
__ATA_BASE_SHT(drv_name), \
|
||||
.can_queue = AHCI_MAX_CMDS, \
|
||||
.sg_tablesize = AHCI_MAX_SG, \
|
||||
.dma_boundary = AHCI_DMA_BOUNDARY, \
|
||||
.shost_attrs = ahci_shost_attrs, \
|
||||
.sdev_attrs = ahci_sdev_attrs
|
||||
.sdev_attrs = ahci_sdev_attrs, \
|
||||
.change_queue_depth = ata_scsi_change_queue_depth, \
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_RR, \
|
||||
.slave_configure = ata_scsi_slave_config
|
||||
|
||||
extern struct ata_port_operations ahci_ops;
|
||||
extern struct ata_port_operations ahci_platform_ops;
|
||||
|
|
|
@ -200,7 +200,7 @@ static void ahci_sunxi_start_engine(struct ata_port *ap)
|
|||
}
|
||||
|
||||
static const struct ata_port_info ahci_sunxi_port_info = {
|
||||
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ,
|
||||
.flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ | ATA_FLAG_NO_DIPM,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.udma_mask = ATA_UDMA6,
|
||||
.port_ops = &ahci_platform_ops,
|
||||
|
|
|
@ -252,8 +252,9 @@ static void atiixp_bmdma_stop(struct ata_queued_cmd *qc)
|
|||
}
|
||||
|
||||
static struct scsi_host_template atiixp_sht = {
|
||||
ATA_BMDMA_SHT(DRV_NAME),
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
};
|
||||
|
||||
static struct ata_port_operations atiixp_port_ops = {
|
||||
|
|
|
@ -95,8 +95,9 @@ static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev)
|
|||
}
|
||||
|
||||
static struct scsi_host_template cs5520_sht = {
|
||||
ATA_BMDMA_SHT(DRV_NAME),
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
};
|
||||
|
||||
static struct ata_port_operations cs5520_port_ops = {
|
||||
|
|
|
@ -147,8 +147,9 @@ static unsigned int cs5530_qc_issue(struct ata_queued_cmd *qc)
|
|||
}
|
||||
|
||||
static struct scsi_host_template cs5530_sht = {
|
||||
ATA_BMDMA_SHT(DRV_NAME),
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
};
|
||||
|
||||
static struct ata_port_operations cs5530_port_ops = {
|
||||
|
|
|
@ -41,6 +41,10 @@ enum {
|
|||
CY82_INDEX_TIMEOUT = 0x32
|
||||
};
|
||||
|
||||
static bool enable_dma = true;
|
||||
module_param(enable_dma, bool, 0);
|
||||
MODULE_PARM_DESC(enable_dma, "Enable bus master DMA operations");
|
||||
|
||||
/**
|
||||
* cy82c693_set_piomode - set initial PIO mode data
|
||||
* @ap: ATA interface
|
||||
|
@ -124,14 +128,16 @@ static struct ata_port_operations cy82c693_port_ops = {
|
|||
|
||||
static int cy82c693_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
static const struct ata_port_info info = {
|
||||
static struct ata_port_info info = {
|
||||
.flags = ATA_FLAG_SLAVE_POSS,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.port_ops = &cy82c693_port_ops
|
||||
};
|
||||
const struct ata_port_info *ppi[] = { &info, &ata_dummy_port_info };
|
||||
|
||||
if (enable_dma)
|
||||
info.mwdma_mask = ATA_MWDMA2;
|
||||
|
||||
/* Devfn 1 is the ATA primary. The secondary is magic and on devfn2.
|
||||
For the moment we don't handle the secondary. FIXME */
|
||||
|
||||
|
|
|
@ -928,7 +928,7 @@ static int ep93xx_pata_probe(struct platform_device *pdev)
|
|||
/* INT[3] (IRQ_EP93XX_EXT3) line connected as pull down */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
err = -ENXIO;
|
||||
err = irq;
|
||||
goto err_rel_gpio;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,8 +33,6 @@
|
|||
#define DRV_NAME "pata_falcon"
|
||||
#define DRV_VERSION "0.1.0"
|
||||
|
||||
#define ATA_HD_CONTROL 0x39
|
||||
|
||||
static struct scsi_host_template pata_falcon_sht = {
|
||||
ATA_PIO_SHT(DRV_NAME),
|
||||
};
|
||||
|
@ -121,23 +119,42 @@ static struct ata_port_operations pata_falcon_ops = {
|
|||
|
||||
static int __init pata_falcon_init_one(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct resource *base_mem_res, *ctl_mem_res;
|
||||
struct resource *base_res, *ctl_res, *irq_res;
|
||||
struct ata_host *host;
|
||||
struct ata_port *ap;
|
||||
void __iomem *base;
|
||||
int irq = 0;
|
||||
|
||||
dev_info(&pdev->dev, "Atari Falcon PATA controller\n");
|
||||
dev_info(&pdev->dev, "Atari Falcon and Q40/Q60 PATA 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)) {
|
||||
base_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (base_res && !devm_request_region(&pdev->dev, base_res->start,
|
||||
resource_size(base_res), DRV_NAME)) {
|
||||
dev_err(&pdev->dev, "resources busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ctl_res = platform_get_resource(pdev, IORESOURCE_IO, 1);
|
||||
if (ctl_res && !devm_request_region(&pdev->dev, ctl_res->start,
|
||||
resource_size(ctl_res), DRV_NAME)) {
|
||||
dev_err(&pdev->dev, "resources busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
base_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!base_mem_res)
|
||||
return -ENODEV;
|
||||
if (!devm_request_mem_region(&pdev->dev, base_mem_res->start,
|
||||
resource_size(base_mem_res), DRV_NAME)) {
|
||||
dev_err(&pdev->dev, "resources busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
ctl_mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!ctl_mem_res)
|
||||
return -ENODEV;
|
||||
|
||||
/* allocate host */
|
||||
host = ata_host_alloc(&pdev->dev, 1);
|
||||
if (!host)
|
||||
|
@ -147,10 +164,10 @@ static int __init pata_falcon_init_one(struct platform_device *pdev)
|
|||
ap->ops = &pata_falcon_ops;
|
||||
ap->pio_mask = ATA_PIO4;
|
||||
ap->flags |= ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY;
|
||||
ap->flags |= ATA_FLAG_PIO_POLLING;
|
||||
|
||||
base = (void __iomem *)res->start;
|
||||
ap->ioaddr.data_addr = base;
|
||||
base = (void __iomem *)base_mem_res->start;
|
||||
/* N.B. this assumes data_addr will be used for word-sized I/O only */
|
||||
ap->ioaddr.data_addr = base + 0 + 0 * 4;
|
||||
ap->ioaddr.error_addr = base + 1 + 1 * 4;
|
||||
ap->ioaddr.feature_addr = base + 1 + 1 * 4;
|
||||
ap->ioaddr.nsect_addr = base + 1 + 2 * 4;
|
||||
|
@ -161,14 +178,25 @@ static int __init pata_falcon_init_one(struct platform_device *pdev)
|
|||
ap->ioaddr.status_addr = base + 1 + 7 * 4;
|
||||
ap->ioaddr.command_addr = base + 1 + 7 * 4;
|
||||
|
||||
ap->ioaddr.altstatus_addr = base + ATA_HD_CONTROL;
|
||||
ap->ioaddr.ctl_addr = base + ATA_HD_CONTROL;
|
||||
base = (void __iomem *)ctl_mem_res->start;
|
||||
ap->ioaddr.altstatus_addr = base + 1;
|
||||
ap->ioaddr.ctl_addr = base + 1;
|
||||
|
||||
ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", (unsigned long)base,
|
||||
(unsigned long)base + ATA_HD_CONTROL);
|
||||
ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx",
|
||||
(unsigned long)base_mem_res->start,
|
||||
(unsigned long)ctl_mem_res->start);
|
||||
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (irq_res && irq_res->start > 0) {
|
||||
irq = irq_res->start;
|
||||
} else {
|
||||
ap->flags |= ATA_FLAG_PIO_POLLING;
|
||||
ata_port_desc(ap, "no IRQ, using PIO polling");
|
||||
}
|
||||
|
||||
/* activate */
|
||||
return ata_host_activate(host, 0, NULL, 0, &pata_falcon_sht);
|
||||
return ata_host_activate(host, irq, irq ? ata_sff_interrupt : NULL,
|
||||
IRQF_SHARED, &pata_falcon_sht);
|
||||
}
|
||||
|
||||
static int __exit pata_falcon_remove_one(struct platform_device *pdev)
|
||||
|
|
|
@ -914,7 +914,7 @@ static int pata_macio_do_resume(struct pata_macio_priv *priv)
|
|||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct scsi_host_template pata_macio_sht = {
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
__ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = MAX_DCMDS,
|
||||
/* We may not need that strict one */
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
|
@ -923,6 +923,9 @@ static struct scsi_host_template pata_macio_sht = {
|
|||
*/
|
||||
.max_segment_size = MAX_DBDMA_SEG,
|
||||
.slave_configure = pata_macio_slave_config,
|
||||
.sdev_attrs = ata_common_sdev_attrs,
|
||||
.can_queue = ATA_DEF_QUEUE,
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_RR,
|
||||
};
|
||||
|
||||
static struct ata_port_operations pata_macio_ops = {
|
||||
|
|
|
@ -898,10 +898,11 @@ static int octeon_cf_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_handler = octeon_cf_interrupt;
|
||||
i = platform_get_irq(dma_dev, 0);
|
||||
if (i > 0)
|
||||
if (i > 0) {
|
||||
irq = i;
|
||||
irq_handler = octeon_cf_interrupt;
|
||||
}
|
||||
}
|
||||
of_node_put(dma_node);
|
||||
}
|
||||
|
|
|
@ -115,10 +115,10 @@ static int rb532_pata_driver_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no IRQ resource found\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN);
|
||||
if (IS_ERR(gpiod)) {
|
||||
|
|
|
@ -193,8 +193,9 @@ static int sc1200_qc_defer(struct ata_queued_cmd *qc)
|
|||
}
|
||||
|
||||
static struct scsi_host_template sc1200_sht = {
|
||||
ATA_BMDMA_SHT(DRV_NAME),
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
};
|
||||
|
||||
static struct ata_port_operations sc1200_port_ops = {
|
||||
|
|
|
@ -253,8 +253,9 @@ static void serverworks_set_dmamode(struct ata_port *ap, struct ata_device *adev
|
|||
}
|
||||
|
||||
static struct scsi_host_template serverworks_osb4_sht = {
|
||||
ATA_BMDMA_SHT(DRV_NAME),
|
||||
ATA_BASE_SHT(DRV_NAME),
|
||||
.sg_tablesize = LIBATA_DUMB_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
};
|
||||
|
||||
static struct scsi_host_template serverworks_csb_sht = {
|
||||
|
|
|
@ -313,7 +313,7 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host,
|
|||
|
||||
DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n",
|
||||
intr_coalescing_count, intr_coalescing_ticks);
|
||||
DPRINTK("ICC register status: (hcr base: 0x%x) = 0x%x\n",
|
||||
DPRINTK("ICC register status: (hcr base: %p) = 0x%x\n",
|
||||
hcr_base, ioread32(hcr_base + ICC));
|
||||
}
|
||||
|
||||
|
|
|
@ -469,10 +469,12 @@ static int ahci_highbank_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no irq\n");
|
||||
return -EINVAL;
|
||||
return irq;
|
||||
}
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL);
|
||||
if (!hpriv) {
|
||||
|
|
|
@ -666,10 +666,14 @@ static struct scsi_host_template mv5_sht = {
|
|||
};
|
||||
#endif
|
||||
static struct scsi_host_template mv6_sht = {
|
||||
ATA_NCQ_SHT(DRV_NAME),
|
||||
__ATA_BASE_SHT(DRV_NAME),
|
||||
.can_queue = MV_MAX_Q_DEPTH - 1,
|
||||
.sg_tablesize = MV_MAX_SG_CT / 2,
|
||||
.dma_boundary = MV_DMA_BOUNDARY,
|
||||
.sdev_attrs = ata_ncq_sdev_attrs,
|
||||
.change_queue_depth = ata_scsi_change_queue_depth,
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_RR,
|
||||
.slave_configure = ata_scsi_slave_config
|
||||
};
|
||||
|
||||
static struct ata_port_operations mv5_ops = {
|
||||
|
|
|
@ -375,19 +375,25 @@ static struct scsi_host_template nv_sht = {
|
|||
};
|
||||
|
||||
static struct scsi_host_template nv_adma_sht = {
|
||||
ATA_NCQ_SHT(DRV_NAME),
|
||||
__ATA_BASE_SHT(DRV_NAME),
|
||||
.can_queue = NV_ADMA_MAX_CPBS,
|
||||
.sg_tablesize = NV_ADMA_SGTBL_TOTAL_LEN,
|
||||
.dma_boundary = NV_ADMA_DMA_BOUNDARY,
|
||||
.slave_configure = nv_adma_slave_config,
|
||||
.sdev_attrs = ata_ncq_sdev_attrs,
|
||||
.change_queue_depth = ata_scsi_change_queue_depth,
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_RR,
|
||||
};
|
||||
|
||||
static struct scsi_host_template nv_swncq_sht = {
|
||||
ATA_NCQ_SHT(DRV_NAME),
|
||||
__ATA_BASE_SHT(DRV_NAME),
|
||||
.can_queue = ATA_MAX_QUEUE - 1,
|
||||
.sg_tablesize = LIBATA_MAX_PRD,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
.slave_configure = nv_swncq_slave_config,
|
||||
.sdev_attrs = ata_ncq_sdev_attrs,
|
||||
.change_queue_depth = ata_scsi_change_queue_depth,
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_RR,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -2118,7 +2124,7 @@ static int nv_swncq_sdbfis(struct ata_port *ap)
|
|||
*/
|
||||
lack_dhfis = 1;
|
||||
|
||||
DPRINTK("id 0x%x QC: qc_active 0x%x,"
|
||||
DPRINTK("id 0x%x QC: qc_active 0x%llx,"
|
||||
"SWNCQ:qc_active 0x%X defer_bits %X "
|
||||
"dhfis 0x%X dmafis 0x%X last_issue_tag %x\n",
|
||||
ap->print_id, ap->qc_active, pp->qc_active,
|
||||
|
|
|
@ -374,11 +374,14 @@ static struct pci_driver sil24_pci_driver = {
|
|||
};
|
||||
|
||||
static struct scsi_host_template sil24_sht = {
|
||||
ATA_NCQ_SHT(DRV_NAME),
|
||||
__ATA_BASE_SHT(DRV_NAME),
|
||||
.can_queue = SIL24_MAX_CMDS,
|
||||
.sg_tablesize = SIL24_MAX_SGE,
|
||||
.dma_boundary = ATA_DMA_BOUNDARY,
|
||||
.tag_alloc_policy = BLK_TAG_ALLOC_FIFO,
|
||||
.sdev_attrs = ata_ncq_sdev_attrs,
|
||||
.change_queue_depth = ata_scsi_change_queue_depth,
|
||||
.slave_configure = ata_scsi_slave_config
|
||||
};
|
||||
|
||||
static struct ata_port_operations sil24_ops = {
|
||||
|
|
|
@ -1,849 +0,0 @@
|
|||
# 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
|
|
@ -1,111 +0,0 @@
|
|||
# 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
|
|
@ -1,331 +0,0 @@
|
|||
// 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");
|
|
@ -1,250 +0,0 @@
|
|||
// 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");
|
|
@ -1,602 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,343 +0,0 @@
|
|||
// 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");
|
|
@ -1,212 +0,0 @@
|
|||
// 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");
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,848 +0,0 @@
|
|||
// 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");
|
|
@ -1,452 +0,0 @@
|
|||
// 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");
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,295 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,216 +0,0 @@
|
|||
// 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");
|
|
@ -1,294 +0,0 @@
|
|||
// 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)");
|
|
@ -1,234 +0,0 @@
|
|||
// 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");
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
* 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");
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
// 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");
|
|
@ -1,197 +0,0 @@
|
|||
/*
|
||||
* 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) {
|
||||
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");
|
|
@ -1,188 +0,0 @@
|
|||
/*
|
||||
* 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");
|
1545
drivers/ide/hpt366.c
1545
drivers/ide/hpt366.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,383 +0,0 @@
|
|||
// 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");
|
|
@ -1,692 +0,0 @@
|
|||
// 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);
|
|
@ -1,65 +0,0 @@
|
|||
// 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");
|
|
@ -1,622 +0,0 @@
|
|||
// 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);
|
||||
}
|
||||
}
|
|
@ -1,756 +0,0 @@
|
|||
// 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(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);
|
1858
drivers/ide/ide-cd.c
1858
drivers/ide/ide-cd.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,123 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,468 +0,0 @@
|
|||
// 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(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;
|
||||
}
|
|
@ -1,362 +0,0 @@
|
|||
// 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
|
|
@ -1,364 +0,0 @@
|
|||
/*======================================================================
|
||||
|
||||
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);
|
|
@ -1,192 +0,0 @@
|
|||
// 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(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;
|
||||
}
|
|
@ -1,795 +0,0 @@
|
|||
// 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(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,
|
||||
};
|
|
@ -1,30 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,33 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
// 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 },
|
||||
};
|
|
@ -1,336 +0,0 @@
|
|||
// 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);
|
|
@ -1,551 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
|
@ -1,443 +0,0 @@
|
|||
// 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);
|
|
@ -1,551 +0,0 @@
|
|||
// 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
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,339 +0,0 @@
|
|||
// 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
|
|
@ -1,34 +0,0 @@
|
|||
// 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 },
|
||||
};
|
|
@ -1,432 +0,0 @@
|
|||
// 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");
|
|
@ -1,43 +0,0 @@
|
|||
/* 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 */
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,262 +0,0 @@
|
|||
// 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,
|
||||
};
|
|
@ -1,904 +0,0 @@
|
|||
/*
|
||||
* 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);
|
|
@ -1,306 +0,0 @@
|
|||
// 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(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(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);
|
|
@ -1,536 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// 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);
|
|
@ -1,146 +0,0 @@
|
|||
// 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);
|
|
@ -1,155 +0,0 @@
|
|||
// 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(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;
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* 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");
|
|
@ -1,96 +0,0 @@
|
|||
// 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;
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
// 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(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(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);
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
// 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");
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,633 +0,0 @@
|
|||
// 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);
|
||||
}
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* 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);
|
|
@ -1,143 +0,0 @@
|
|||
// 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;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,668 +0,0 @@
|
|||
// 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(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
|
|
@ -1,198 +0,0 @@
|
|||
// 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);
|
|
@ -1,267 +0,0 @@
|
|||
// 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);
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
// 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");
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче