mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 09:16:33 +00:00
ide: remove the legacy ide driver
The legay ide driver has been replace with libata starting in 2003 and has been scheduled for removal for a while. Finally kill it off so that we can start cleaning up various bits of cruft it forced on the block layer. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
b90257bfdd
commit
b7fb14d3ac
@ -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
@ -8763,22 +8763,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
|
||||
|
@ -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/
|
||||
|
@ -1,827 +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 and Q40/Q60 IDE interface support"
|
||||
depends on ATARI || Q40
|
||||
help
|
||||
This is the IDE driver for the on-board IDE interface on the Atari
|
||||
Falcon and Q40/Q60. Say Y if you have such a machine and want to use
|
||||
IDE devices (hard disks, CD-ROM drives, etc.) that are connected to
|
||||
the on-board IDE interface.
|
||||
|
||||
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,109 +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_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,226 +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"
|
||||
|
||||
#ifdef CONFIG_ATARI
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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 = {
|
||||
#ifdef CONFIG_ATARI
|
||||
.get_lock = falconide_get_lock,
|
||||
.release_lock = falconide_release_lock,
|
||||
#endif
|
||||
.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,
|
||||
unsigned long ctl, int irq)
|
||||
{
|
||||
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 = ctl + 1;
|
||||
|
||||
hw->irq = irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for a Falcon IDE interface
|
||||
*/
|
||||
|
||||
static int __init falconide_init(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *base_mem_res, *ctl_mem_res;
|
||||
struct resource *base_res, *ctl_res, *irq_res;
|
||||
struct ide_host *host;
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
int rc;
|
||||
int irq;
|
||||
|
||||
dev_info(&pdev->dev, "Atari Falcon and Q40/Q60 IDE controller\n");
|
||||
|
||||
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, 0);
|
||||
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;
|
||||
|
||||
if (MACH_IS_ATARI) {
|
||||
irq = IRQ_MFP_IDE;
|
||||
} else {
|
||||
irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (irq_res && irq_res->start > 0)
|
||||
irq = irq_res->start;
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
falconide_setup_ports(&hw, base_mem_res->start, ctl_mem_res->start, irq);
|
||||
|
||||
host = ide_host_alloc(&falconide_port_info, hws, 1);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!MACH_IS_ATARI) {
|
||||
host->get_lock = NULL;
|
||||
host->release_lock = NULL;
|
||||
}
|
||||
|
||||
if (host->get_lock)
|
||||
host->get_lock(NULL, NULL);
|
||||
rc = ide_host_register(host, &falconide_port_info, hws);
|
||||
if (host->release_lock)
|
||||
host->release_lock();
|
||||
|
||||
if (rc)
|
||||
goto err_free;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
return 0;
|
||||
err_free:
|
||||
ide_host_free(host);
|
||||
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
File diff suppressed because it is too large
Load Diff
@ -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
File diff suppressed because it is too large
Load Diff
@ -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");
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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");
|
@ -1,133 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Platform IDE driver
|
||||
*
|
||||
* Copyright (C) 2007 MontaVista Software
|
||||
*
|
||||
* Maintainer: Kumar Gala <galak@kernel.crashing.org>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/ata_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
static void plat_ide_setup_ports(struct ide_hw *hw, void __iomem *base,
|
||||
void __iomem *ctrl,
|
||||
struct pata_platform_info *pdata, int irq)
|
||||
{
|
||||
unsigned long port = (unsigned long)base;
|
||||
int i;
|
||||
|
||||
hw->io_ports.data_addr = port;
|
||||
|
||||
port += (1 << pdata->ioport_shift);
|
||||
for (i = 1; i <= 7;
|
||||
i++, port += (1 << pdata->ioport_shift))
|
||||
hw->io_ports_array[i] = port;
|
||||
|
||||
hw->io_ports.ctl_addr = (unsigned long)ctrl;
|
||||
|
||||
hw->irq = irq;
|
||||
}
|
||||
|
||||
static const struct ide_port_info platform_ide_port_info = {
|
||||
.host_flags = IDE_HFLAG_NO_DMA,
|
||||
.chipset = ide_generic,
|
||||
};
|
||||
|
||||
static int plat_ide_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res_base, *res_alt, *res_irq;
|
||||
void __iomem *base, *alt_base;
|
||||
struct pata_platform_info *pdata;
|
||||
struct ide_host *host;
|
||||
int ret = 0, mmio = 0;
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
struct ide_port_info d = platform_ide_port_info;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
/* get a pointer to the register memory */
|
||||
res_base = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
res_alt = platform_get_resource(pdev, IORESOURCE_IO, 1);
|
||||
|
||||
if (!res_base || !res_alt) {
|
||||
res_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res_alt = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res_base || !res_alt) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
mmio = 1;
|
||||
}
|
||||
|
||||
res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res_irq) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mmio) {
|
||||
base = devm_ioremap(&pdev->dev,
|
||||
res_base->start, resource_size(res_base));
|
||||
alt_base = devm_ioremap(&pdev->dev,
|
||||
res_alt->start, resource_size(res_alt));
|
||||
} else {
|
||||
base = devm_ioport_map(&pdev->dev,
|
||||
res_base->start, resource_size(res_base));
|
||||
alt_base = devm_ioport_map(&pdev->dev,
|
||||
res_alt->start, resource_size(res_alt));
|
||||
}
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
plat_ide_setup_ports(&hw, base, alt_base, pdata, res_irq->start);
|
||||
hw.dev = &pdev->dev;
|
||||
|
||||
d.irq_flags = res_irq->flags & IRQF_TRIGGER_MASK;
|
||||
if (res_irq->flags & IORESOURCE_IRQ_SHAREABLE)
|
||||
d.irq_flags |= IRQF_SHARED;
|
||||
|
||||
if (mmio)
|
||||
d.host_flags |= IDE_HFLAG_MMIO;
|
||||
|
||||
ret = ide_host_add(&d, hws, 1, &host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int plat_ide_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ide_host *host = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
ide_host_remove(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver platform_ide_driver = {
|
||||
.driver = {
|
||||
.name = "pata_platform",
|
||||
},
|
||||
.probe = plat_ide_probe,
|
||||
.remove = plat_ide_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(platform_ide_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Platform IDE driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:pata_platform");
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* BRIEF MODULE DESCRIPTION
|
||||
* IT8172 IDE controller support
|
||||
*
|
||||
* Copyright (C) 2000 MontaVista Software Inc.
|
||||
* Copyright (C) 2008 Shane McDonald
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "IT8172"
|
||||
|
||||
static void it8172_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u16 drive_enables;
|
||||
u32 drive_timing;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
/*
|
||||
* The highest value of DIOR/DIOW pulse width and recovery time
|
||||
* that can be set in the IT8172 is 8 PCI clock cycles. As a result,
|
||||
* it cannot be configured for PIO mode 0. This table sets these
|
||||
* parameters to the maximum supported by the IT8172.
|
||||
*/
|
||||
static const u8 timings[] = { 0x3f, 0x3c, 0x1b, 0x12, 0x0a };
|
||||
|
||||
pci_read_config_word(dev, 0x40, &drive_enables);
|
||||
pci_read_config_dword(dev, 0x44, &drive_timing);
|
||||
|
||||
/*
|
||||
* Enable port 0x44. The IT8172 spec is confused; it calls
|
||||
* this register the "Slave IDE Timing Register", but in fact,
|
||||
* it controls timing for both master and slave drives.
|
||||
*/
|
||||
drive_enables |= 0x4000;
|
||||
|
||||
drive_enables &= drive->dn ? 0xc006 : 0xc060;
|
||||
if (drive->media == ide_disk)
|
||||
/* enable prefetch */
|
||||
drive_enables |= 0x0004 << (drive->dn * 4);
|
||||
if (ide_pio_need_iordy(drive, pio))
|
||||
/* enable IORDY sample-point */
|
||||
drive_enables |= 0x0002 << (drive->dn * 4);
|
||||
|
||||
drive_timing &= drive->dn ? 0x00003f00 : 0x000fc000;
|
||||
drive_timing |= timings[pio] << (drive->dn * 6 + 8);
|
||||
|
||||
pci_write_config_word(dev, 0x40, drive_enables);
|
||||
pci_write_config_dword(dev, 0x44, drive_timing);
|
||||
}
|
||||
|
||||
static void it8172_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
int a_speed = 3 << (drive->dn * 4);
|
||||
int u_flag = 1 << drive->dn;
|
||||
int u_speed = 0;
|
||||
u8 reg48, reg4a;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
pci_read_config_byte(dev, 0x48, ®48);
|
||||
pci_read_config_byte(dev, 0x4a, ®4a);
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
u8 udma = speed - XFER_UDMA_0;
|
||||
u_speed = udma << (drive->dn * 4);
|
||||
|
||||
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
|
||||
reg4a &= ~a_speed;
|
||||
pci_write_config_byte(dev, 0x4a, reg4a | u_speed);
|
||||
} else {
|
||||
const u8 mwdma_to_pio[] = { 0, 3, 4 };
|
||||
|
||||
pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
|
||||
pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed);
|
||||
|
||||
drive->pio_mode =
|
||||
mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
|
||||
|
||||
it8172_set_pio_mode(hwif, drive);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const struct ide_port_ops it8172_port_ops = {
|
||||
.set_pio_mode = it8172_set_pio_mode,
|
||||
.set_dma_mode = it8172_set_dma_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info it8172_port_info = {
|
||||
.name = DRV_NAME,
|
||||
.port_ops = &it8172_port_ops,
|
||||
.enablebits = { {0x41, 0x80, 0x80}, {0x00, 0x00, 0x00} },
|
||||
.host_flags = IDE_HFLAG_SINGLE,
|
||||
.pio_mask = ATA_PIO4 & ~ATA_PIO0,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA2,
|
||||
};
|
||||
|
||||
static int it8172_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
|
||||
return -ENODEV; /* IT8172 is more than an IDE controller */
|
||||
return ide_pci_init_one(dev, &it8172_port_info, NULL);
|
||||
}
|
||||
|
||||
static struct pci_device_id it8172_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8172), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, it8172_pci_tbl);
|
||||
|
||||
static struct pci_driver it8172_pci_driver = {
|
||||
.name = "IT8172_IDE",
|
||||
.id_table = it8172_pci_tbl,
|
||||
.probe = it8172_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init it8172_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&it8172_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit it8172_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&it8172_pci_driver);
|
||||
}
|
||||
|
||||
module_init(it8172_ide_init);
|
||||
module_exit(it8172_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Steve Longerbeam");
|
||||
MODULE_DESCRIPTION("PCI driver module for ITE 8172 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,217 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* ITE 8213 IDE driver
|
||||
*
|
||||
* Copyright (C) 2006 Jack Lee
|
||||
* Copyright (C) 2006 Alan Cox
|
||||
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "it8213"
|
||||
|
||||
/**
|
||||
* it8213_set_pio_mode - set host controller for PIO mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Set the interface PIO mode.
|
||||
*/
|
||||
|
||||
static void it8213_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
static DEFINE_SPINLOCK(tune_lock);
|
||||
int control = 0;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
static const u8 timings[][2] = {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
spin_lock_irqsave(&tune_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media != ide_disk)
|
||||
control |= 4; /* ATAPI */
|
||||
if (ide_pio_need_iordy(drive, pio))
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1)
|
||||
master_data = master_data | (control << 4);
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data = slave_data & 0xf0;
|
||||
slave_data = slave_data | (timings[pio][0] << 2) | timings[pio][1];
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1)
|
||||
master_data = master_data | control;
|
||||
master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&tune_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* it8213_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Tune the ITE chipset for the DMA mode.
|
||||
*/
|
||||
|
||||
static void it8213_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 maslave = 0x40;
|
||||
int a_speed = 3 << (drive->dn * 4);
|
||||
int u_flag = 1 << drive->dn;
|
||||
int v_flag = 0x01 << drive->dn;
|
||||
int w_flag = 0x10 << drive->dn;
|
||||
int u_speed = 0;
|
||||
u16 reg4042, reg4a;
|
||||
u8 reg48, reg54, reg55;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
pci_read_config_byte(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_read_config_byte(dev, 0x54, ®54);
|
||||
pci_read_config_byte(dev, 0x55, ®55);
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
u8 udma = speed - XFER_UDMA_0;
|
||||
|
||||
u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4);
|
||||
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
|
||||
if (speed >= XFER_UDMA_5)
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
|
||||
else
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
|
||||
if ((reg4a & a_speed) != u_speed)
|
||||
pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
|
||||
if (speed > XFER_UDMA_2) {
|
||||
if (!(reg54 & v_flag))
|
||||
pci_write_config_byte(dev, 0x54, reg54 | v_flag);
|
||||
} else
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
} else {
|
||||
const u8 mwdma_to_pio[] = { 0, 3, 4 };
|
||||
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
if (reg54 & v_flag)
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
if (reg55 & w_flag)
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
|
||||
if (speed >= XFER_MW_DMA_0)
|
||||
drive->pio_mode =
|
||||
mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
|
||||
else
|
||||
drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
|
||||
|
||||
it8213_set_pio_mode(hwif, drive);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 it8213_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 reg42h = 0;
|
||||
|
||||
pci_read_config_byte(dev, 0x42, ®42h);
|
||||
|
||||
return (reg42h & 0x02) ? ATA_CBL_PATA40 : ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops it8213_port_ops = {
|
||||
.set_pio_mode = it8213_set_pio_mode,
|
||||
.set_dma_mode = it8213_set_dma_mode,
|
||||
.cable_detect = it8213_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info it8213_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.enablebits = { {0x41, 0x80, 0x80} },
|
||||
.port_ops = &it8213_port_ops,
|
||||
.host_flags = IDE_HFLAG_SINGLE,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.swdma_mask = ATA_SWDMA2_ONLY,
|
||||
.mwdma_mask = ATA_MWDMA12_ONLY,
|
||||
.udma_mask = ATA_UDMA6,
|
||||
};
|
||||
|
||||
/**
|
||||
* it8213_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an ITE8213 controller. As
|
||||
* this device follows the standard interfaces we can use the
|
||||
* standard helper functions to do almost all the work for us.
|
||||
*/
|
||||
|
||||
static int it8213_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &it8213_chipset, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id it8213_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8213), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, it8213_pci_tbl);
|
||||
|
||||
static struct pci_driver it8213_pci_driver = {
|
||||
.name = "ITE8213_IDE",
|
||||
.id_table = it8213_pci_tbl,
|
||||
.probe = it8213_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init it8213_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&it8213_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit it8213_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&it8213_pci_driver);
|
||||
}
|
||||
|
||||
module_init(it8213_ide_init);
|
||||
module_exit(it8213_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Jack Lee, Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the ITE 8213");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,715 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2004 Red Hat
|
||||
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
* Based in part on the ITE vendor provided SCSI driver.
|
||||
*
|
||||
* Documentation:
|
||||
* Datasheet is freely available, some other documents under NDA.
|
||||
*
|
||||
* The ITE8212 isn't exactly a standard IDE controller. It has two
|
||||
* modes. In pass through mode then it is an IDE controller. In its smart
|
||||
* mode its actually quite a capable hardware raid controller disguised
|
||||
* as an IDE controller. Smart mode only understands DMA read/write and
|
||||
* identify, none of the fancier commands apply. The IT8211 is identical
|
||||
* in other respects but lacks the raid mode.
|
||||
*
|
||||
* Errata:
|
||||
* o Rev 0x10 also requires master/slave hold the same DMA timings and
|
||||
* cannot do ATAPI MWDMA.
|
||||
* o The identify data for raid volumes lacks CHS info (technically ok)
|
||||
* but also fails to set the LBA28 and other bits. We fix these in
|
||||
* the IDE probe quirk code.
|
||||
* o If you write LBA48 sized I/O's (ie > 256 sector) in smart mode
|
||||
* raid then the controller firmware dies
|
||||
* o Smart mode without RAID doesn't clear all the necessary identify
|
||||
* bits to reduce the command set to the one used
|
||||
*
|
||||
* This has a few impacts on the driver
|
||||
* - In pass through mode we do all the work you would expect
|
||||
* - In smart mode the clocking set up is done by the controller generally
|
||||
* but we must watch the other limits and filter.
|
||||
* - There are a few extra vendor commands that actually talk to the
|
||||
* controller but only work PIO with no IRQ.
|
||||
*
|
||||
* Vendor areas of the identify block in smart mode are used for the
|
||||
* timing and policy set up. Each HDD in raid mode also has a serial
|
||||
* block on the disk. The hardware extra commands are get/set chip status,
|
||||
* rebuild, get rebuild status.
|
||||
*
|
||||
* In Linux the driver supports pass through mode as if the device was
|
||||
* just another IDE controller. If the smart mode is running then
|
||||
* volumes are managed by the controller firmware and each IDE "disk"
|
||||
* is a raid volume. Even more cute - the controller can do automated
|
||||
* hotplug and rebuild.
|
||||
*
|
||||
* The pass through controller itself is a little demented. It has a
|
||||
* flaw that it has a single set of PIO/MWDMA timings per channel so
|
||||
* non UDMA devices restrict each others performance. It also has a
|
||||
* single clock source per channel so mixed UDMA100/133 performance
|
||||
* isn't perfect and we have to pick a clock. Thankfully none of this
|
||||
* matters in smart mode. ATAPI DMA is not currently supported.
|
||||
*
|
||||
* It seems the smart mode is a win for RAID1/RAID10 but otherwise not.
|
||||
*
|
||||
* TODO
|
||||
* - ATAPI UDMA is ok but not MWDMA it seems
|
||||
* - RAID configuration ioctls
|
||||
* - Move to libata once it grows up
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "it821x"
|
||||
|
||||
#define QUIRK_VORTEX86 1
|
||||
|
||||
struct it821x_dev
|
||||
{
|
||||
unsigned int smart:1, /* Are we in smart raid mode */
|
||||
timing10:1; /* Rev 0x10 */
|
||||
u8 clock_mode; /* 0, ATA_50 or ATA_66 */
|
||||
u8 want[2][2]; /* Mode/Pri log for master slave */
|
||||
/* We need these for switching the clock when DMA goes on/off
|
||||
The high byte is the 66Mhz timing */
|
||||
u16 pio[2]; /* Cached PIO values */
|
||||
u16 mwdma[2]; /* Cached MWDMA values */
|
||||
u16 udma[2]; /* Cached UDMA values (per drive) */
|
||||
u16 quirks;
|
||||
};
|
||||
|
||||
#define ATA_66 0
|
||||
#define ATA_50 1
|
||||
#define ATA_ANY 2
|
||||
|
||||
#define UDMA_OFF 0
|
||||
#define MWDMA_OFF 0
|
||||
|
||||
/*
|
||||
* We allow users to force the card into non raid mode without
|
||||
* flashing the alternative BIOS. This is also necessary right now
|
||||
* for embedded platforms that cannot run a PC BIOS but are using this
|
||||
* device.
|
||||
*/
|
||||
|
||||
static int it8212_noraid;
|
||||
|
||||
/**
|
||||
* it821x_program - program the PIO/MWDMA registers
|
||||
* @drive: drive to tune
|
||||
* @timing: timing info
|
||||
*
|
||||
* Program the PIO/MWDMA timing for this channel according to the
|
||||
* current clock.
|
||||
*/
|
||||
|
||||
static void it821x_program(ide_drive_t *drive, u16 timing)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int channel = hwif->channel;
|
||||
u8 conf;
|
||||
|
||||
/* Program PIO/MWDMA timing bits */
|
||||
if(itdev->clock_mode == ATA_66)
|
||||
conf = timing >> 8;
|
||||
else
|
||||
conf = timing & 0xFF;
|
||||
|
||||
pci_write_config_byte(dev, 0x54 + 4 * channel, conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_program_udma - program the UDMA registers
|
||||
* @drive: drive to tune
|
||||
* @timing: timing info
|
||||
*
|
||||
* Program the UDMA timing for this drive according to the
|
||||
* current clock.
|
||||
*/
|
||||
|
||||
static void it821x_program_udma(ide_drive_t *drive, u16 timing)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int channel = hwif->channel;
|
||||
u8 unit = drive->dn & 1, conf;
|
||||
|
||||
/* Program UDMA timing bits */
|
||||
if(itdev->clock_mode == ATA_66)
|
||||
conf = timing >> 8;
|
||||
else
|
||||
conf = timing & 0xFF;
|
||||
|
||||
if (itdev->timing10 == 0)
|
||||
pci_write_config_byte(dev, 0x56 + 4 * channel + unit, conf);
|
||||
else {
|
||||
pci_write_config_byte(dev, 0x56 + 4 * channel, conf);
|
||||
pci_write_config_byte(dev, 0x56 + 4 * channel + 1, conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_clock_strategy
|
||||
* @drive: drive to set up
|
||||
*
|
||||
* Select between the 50 and 66Mhz base clocks to get the best
|
||||
* results for this interface.
|
||||
*/
|
||||
|
||||
static void it821x_clock_strategy(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
ide_drive_t *pair = ide_get_pair_dev(drive);
|
||||
int clock, altclock, sel = 0;
|
||||
u8 unit = drive->dn & 1, v;
|
||||
|
||||
if(itdev->want[0][0] > itdev->want[1][0]) {
|
||||
clock = itdev->want[0][1];
|
||||
altclock = itdev->want[1][1];
|
||||
} else {
|
||||
clock = itdev->want[1][1];
|
||||
altclock = itdev->want[0][1];
|
||||
}
|
||||
|
||||
/*
|
||||
* if both clocks can be used for the mode with the higher priority
|
||||
* use the clock needed by the mode with the lower priority
|
||||
*/
|
||||
if (clock == ATA_ANY)
|
||||
clock = altclock;
|
||||
|
||||
/* Nobody cares - keep the same clock */
|
||||
if(clock == ATA_ANY)
|
||||
return;
|
||||
/* No change */
|
||||
if(clock == itdev->clock_mode)
|
||||
return;
|
||||
|
||||
/* Load this into the controller ? */
|
||||
if(clock == ATA_66)
|
||||
itdev->clock_mode = ATA_66;
|
||||
else {
|
||||
itdev->clock_mode = ATA_50;
|
||||
sel = 1;
|
||||
}
|
||||
|
||||
pci_read_config_byte(dev, 0x50, &v);
|
||||
v &= ~(1 << (1 + hwif->channel));
|
||||
v |= sel << (1 + hwif->channel);
|
||||
pci_write_config_byte(dev, 0x50, v);
|
||||
|
||||
/*
|
||||
* Reprogram the UDMA/PIO of the pair drive for the switch
|
||||
* MWDMA will be dealt with by the dma switcher
|
||||
*/
|
||||
if(pair && itdev->udma[1-unit] != UDMA_OFF) {
|
||||
it821x_program_udma(pair, itdev->udma[1-unit]);
|
||||
it821x_program(pair, itdev->pio[1-unit]);
|
||||
}
|
||||
/*
|
||||
* Reprogram the UDMA/PIO of our drive for the switch.
|
||||
* MWDMA will be dealt with by the dma switcher
|
||||
*/
|
||||
if(itdev->udma[unit] != UDMA_OFF) {
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_set_pio_mode - set host controller for PIO mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Tune the host to the desired PIO mode taking into the consideration
|
||||
* the maximum PIO mode supported by the other device on the cable.
|
||||
*/
|
||||
|
||||
static void it821x_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
ide_drive_t *pair = ide_get_pair_dev(drive);
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
u8 unit = drive->dn & 1, set_pio = pio;
|
||||
|
||||
/* Spec says 89 ref driver uses 88 */
|
||||
static u16 pio_timings[]= { 0xAA88, 0xA382, 0xA181, 0x3332, 0x3121 };
|
||||
static u8 pio_want[] = { ATA_66, ATA_66, ATA_66, ATA_66, ATA_ANY };
|
||||
|
||||
/*
|
||||
* Compute the best PIO mode we can for a given device. We must
|
||||
* pick a speed that does not cause problems with the other device
|
||||
* on the cable.
|
||||
*/
|
||||
if (pair) {
|
||||
u8 pair_pio = pair->pio_mode - XFER_PIO_0;
|
||||
/* trim PIO to the slowest of the master/slave */
|
||||
if (pair_pio < set_pio)
|
||||
set_pio = pair_pio;
|
||||
}
|
||||
|
||||
/* We prefer 66Mhz clock for PIO 0-3, don't care for PIO4 */
|
||||
itdev->want[unit][1] = pio_want[set_pio];
|
||||
itdev->want[unit][0] = 1; /* PIO is lowest priority */
|
||||
itdev->pio[unit] = pio_timings[set_pio];
|
||||
it821x_clock_strategy(drive);
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_tune_mwdma - tune a channel for MWDMA
|
||||
* @drive: drive to set up
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller when doing MWDMA in pass through mode. The caller
|
||||
* must manage the whole lack of per device MWDMA/PIO timings and
|
||||
* the shared MWDMA/PIO timing register.
|
||||
*/
|
||||
|
||||
static void it821x_tune_mwdma(ide_drive_t *drive, u8 mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct it821x_dev *itdev = (void *)ide_get_hwifdata(hwif);
|
||||
u8 unit = drive->dn & 1, channel = hwif->channel, conf;
|
||||
|
||||
static u16 dma[] = { 0x8866, 0x3222, 0x3121 };
|
||||
static u8 mwdma_want[] = { ATA_ANY, ATA_66, ATA_ANY };
|
||||
|
||||
itdev->want[unit][1] = mwdma_want[mode_wanted];
|
||||
itdev->want[unit][0] = 2; /* MWDMA is low priority */
|
||||
itdev->mwdma[unit] = dma[mode_wanted];
|
||||
itdev->udma[unit] = UDMA_OFF;
|
||||
|
||||
/* UDMA bits off - Revision 0x10 do them in pairs */
|
||||
pci_read_config_byte(dev, 0x50, &conf);
|
||||
if (itdev->timing10)
|
||||
conf |= channel ? 0x60: 0x18;
|
||||
else
|
||||
conf |= 1 << (3 + 2 * channel + unit);
|
||||
pci_write_config_byte(dev, 0x50, conf);
|
||||
|
||||
it821x_clock_strategy(drive);
|
||||
/* FIXME: do we need to program this ? */
|
||||
/* it821x_program(drive, itdev->mwdma[unit]); */
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_tune_udma - tune a channel for UDMA
|
||||
* @drive: drive to set up
|
||||
* @mode_wanted: the target operating mode
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller when doing UDMA modes in pass through.
|
||||
*/
|
||||
|
||||
static void it821x_tune_udma(ide_drive_t *drive, u8 mode_wanted)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
u8 unit = drive->dn & 1, channel = hwif->channel, conf;
|
||||
|
||||
static u16 udma[] = { 0x4433, 0x4231, 0x3121, 0x2121, 0x1111, 0x2211, 0x1111 };
|
||||
static u8 udma_want[] = { ATA_ANY, ATA_50, ATA_ANY, ATA_66, ATA_66, ATA_50, ATA_66 };
|
||||
|
||||
itdev->want[unit][1] = udma_want[mode_wanted];
|
||||
itdev->want[unit][0] = 3; /* UDMA is high priority */
|
||||
itdev->mwdma[unit] = MWDMA_OFF;
|
||||
itdev->udma[unit] = udma[mode_wanted];
|
||||
if(mode_wanted >= 5)
|
||||
itdev->udma[unit] |= 0x8080; /* UDMA 5/6 select on */
|
||||
|
||||
/* UDMA on. Again revision 0x10 must do the pair */
|
||||
pci_read_config_byte(dev, 0x50, &conf);
|
||||
if (itdev->timing10)
|
||||
conf &= channel ? 0x9F: 0xE7;
|
||||
else
|
||||
conf &= ~ (1 << (3 + 2 * channel + unit));
|
||||
pci_write_config_byte(dev, 0x50, conf);
|
||||
|
||||
it821x_clock_strategy(drive);
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_dma_read - DMA hook
|
||||
* @drive: drive for DMA
|
||||
*
|
||||
* The IT821x has a single timing register for MWDMA and for PIO
|
||||
* operations. As we flip back and forth we have to reload the
|
||||
* clock. In addition the rev 0x10 device only works if the same
|
||||
* timing value is loaded into the master and slave UDMA clock
|
||||
* so we must also reload that.
|
||||
*
|
||||
* FIXME: we could figure out in advance if we need to do reloads
|
||||
*/
|
||||
|
||||
static void it821x_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
u8 unit = drive->dn & 1;
|
||||
|
||||
if(itdev->mwdma[unit] != MWDMA_OFF)
|
||||
it821x_program(drive, itdev->mwdma[unit]);
|
||||
else if(itdev->udma[unit] != UDMA_OFF && itdev->timing10)
|
||||
it821x_program_udma(drive, itdev->udma[unit]);
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_dma_write - DMA hook
|
||||
* @drive: drive for DMA stop
|
||||
*
|
||||
* The IT821x has a single timing register for MWDMA and for PIO
|
||||
* operations. As we flip back and forth we have to reload the
|
||||
* clock.
|
||||
*/
|
||||
|
||||
static int it821x_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(hwif);
|
||||
int ret = ide_dma_end(drive);
|
||||
u8 unit = drive->dn & 1;
|
||||
|
||||
if(itdev->mwdma[unit] != MWDMA_OFF)
|
||||
it821x_program(drive, itdev->pio[unit]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Tune the ITE chipset for the desired DMA mode.
|
||||
*/
|
||||
|
||||
static void it821x_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
/*
|
||||
* MWDMA tuning is really hard because our MWDMA and PIO
|
||||
* timings are kept in the same place. We can switch in the
|
||||
* host dma on/off callbacks.
|
||||
*/
|
||||
if (speed >= XFER_UDMA_0 && speed <= XFER_UDMA_6)
|
||||
it821x_tune_udma(drive, speed - XFER_UDMA_0);
|
||||
else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
|
||||
it821x_tune_mwdma(drive, speed - XFER_MW_DMA_0);
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_cable_detect - cable detection
|
||||
* @hwif: interface to check
|
||||
*
|
||||
* Check for the presence of an ATA66 capable cable on the
|
||||
* interface. Problematic as it seems some cards don't have
|
||||
* the needed logic onboard.
|
||||
*/
|
||||
|
||||
static u8 it821x_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
/* The reference driver also only does disk side */
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
/**
|
||||
* it821x_quirkproc - post init callback
|
||||
* @drive: drive
|
||||
*
|
||||
* This callback is run after the drive has been probed but
|
||||
* before anything gets attached. It allows drivers to do any
|
||||
* final tuning that is needed, or fixups to work around bugs.
|
||||
*/
|
||||
|
||||
static void it821x_quirkproc(ide_drive_t *drive)
|
||||
{
|
||||
struct it821x_dev *itdev = ide_get_hwifdata(drive->hwif);
|
||||
u16 *id = drive->id;
|
||||
|
||||
if (!itdev->smart) {
|
||||
/*
|
||||
* If we are in pass through mode then not much
|
||||
* needs to be done, but we do bother to clear the
|
||||
* IRQ mask as we may well be in PIO (eg rev 0x10)
|
||||
* for now and we know unmasking is safe on this chipset.
|
||||
*/
|
||||
drive->dev_flags |= IDE_DFLAG_UNMASK;
|
||||
} else {
|
||||
/*
|
||||
* Perform fixups on smart mode. We need to "lose" some
|
||||
* capabilities the firmware lacks but does not filter, and
|
||||
* also patch up some capability bits that it forgets to set
|
||||
* in RAID mode.
|
||||
*/
|
||||
|
||||
/* Check for RAID v native */
|
||||
if (strstr((char *)&id[ATA_ID_PROD],
|
||||
"Integrated Technology Express")) {
|
||||
/* In raid mode the ident block is slightly buggy
|
||||
We need to set the bits so that the IDE layer knows
|
||||
LBA28. LBA48 and DMA ar valid */
|
||||
id[ATA_ID_CAPABILITY] |= (3 << 8); /* LBA28, DMA */
|
||||
id[ATA_ID_COMMAND_SET_2] |= 0x0400; /* LBA48 valid */
|
||||
id[ATA_ID_CFS_ENABLE_2] |= 0x0400; /* LBA48 on */
|
||||
/* Reporting logic */
|
||||
printk(KERN_INFO "%s: IT8212 %sRAID %d volume",
|
||||
drive->name, id[147] ? "Bootable " : "",
|
||||
id[ATA_ID_CSFO]);
|
||||
if (id[ATA_ID_CSFO] != 1)
|
||||
printk(KERN_CONT "(%dK stripe)", id[146]);
|
||||
printk(KERN_CONT ".\n");
|
||||
} else {
|
||||
/* Non RAID volume. Fixups to stop the core code
|
||||
doing unsupported things */
|
||||
id[ATA_ID_FIELD_VALID] &= 3;
|
||||
id[ATA_ID_QUEUE_DEPTH] = 0;
|
||||
id[ATA_ID_COMMAND_SET_1] = 0;
|
||||
id[ATA_ID_COMMAND_SET_2] &= 0xC400;
|
||||
id[ATA_ID_CFSSE] &= 0xC000;
|
||||
id[ATA_ID_CFS_ENABLE_1] = 0;
|
||||
id[ATA_ID_CFS_ENABLE_2] &= 0xC400;
|
||||
id[ATA_ID_CSF_DEFAULT] &= 0xC000;
|
||||
id[127] = 0;
|
||||
id[ATA_ID_DLF] = 0;
|
||||
id[ATA_ID_CSFO] = 0;
|
||||
id[ATA_ID_CFA_POWER] = 0;
|
||||
printk(KERN_INFO "%s: Performing identify fixups.\n",
|
||||
drive->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set MWDMA0 mode as enabled/support - just to tell
|
||||
* IDE core that DMA is supported (it821x hardware
|
||||
* takes care of DMA mode programming).
|
||||
*/
|
||||
if (ata_id_has_dma(id)) {
|
||||
id[ATA_ID_MWDMA_MODES] |= 0x0101;
|
||||
drive->current_speed = XFER_MW_DMA_0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const struct ide_dma_ops it821x_pass_through_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = it821x_dma_start,
|
||||
.dma_end = it821x_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
/**
|
||||
* init_hwif_it821x - set up hwif structs
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* We do the basic set up of the interface structure. The IT8212
|
||||
* requires several custom handlers so we override the default
|
||||
* ide DMA handlers appropriately
|
||||
*/
|
||||
|
||||
static void init_hwif_it821x(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct it821x_dev *itdevs = host->host_priv;
|
||||
struct it821x_dev *idev = itdevs + hwif->channel;
|
||||
u8 conf;
|
||||
|
||||
ide_set_hwifdata(hwif, idev);
|
||||
|
||||
pci_read_config_byte(dev, 0x50, &conf);
|
||||
if (conf & 1) {
|
||||
idev->smart = 1;
|
||||
hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA;
|
||||
/* Long I/O's although allowed in LBA48 space cause the
|
||||
onboard firmware to enter the twighlight zone */
|
||||
hwif->rqsize = 256;
|
||||
}
|
||||
|
||||
/* Pull the current clocks from 0x50 also */
|
||||
if (conf & (1 << (1 + hwif->channel)))
|
||||
idev->clock_mode = ATA_50;
|
||||
else
|
||||
idev->clock_mode = ATA_66;
|
||||
|
||||
idev->want[0][1] = ATA_ANY;
|
||||
idev->want[1][1] = ATA_ANY;
|
||||
|
||||
/*
|
||||
* Not in the docs but according to the reference driver
|
||||
* this is necessary.
|
||||
*/
|
||||
|
||||
if (dev->revision == 0x10) {
|
||||
idev->timing10 = 1;
|
||||
hwif->host_flags |= IDE_HFLAG_NO_ATAPI_DMA;
|
||||
if (idev->smart == 0)
|
||||
printk(KERN_WARNING DRV_NAME " %s: revision 0x10, "
|
||||
"workarounds activated\n", pci_name(dev));
|
||||
}
|
||||
|
||||
if (idev->smart == 0) {
|
||||
/* MWDMA/PIO clock switching for pass through mode */
|
||||
hwif->dma_ops = &it821x_pass_through_dma_ops;
|
||||
} else
|
||||
hwif->host_flags |= IDE_HFLAG_NO_SET_MODE;
|
||||
|
||||
if (hwif->dma_base == 0)
|
||||
return;
|
||||
|
||||
hwif->ultra_mask = ATA_UDMA6;
|
||||
hwif->mwdma_mask = ATA_MWDMA2;
|
||||
|
||||
/* Vortex86SX quirk: prevent Ultra-DMA mode to fix BadCRC issue */
|
||||
if (idev->quirks & QUIRK_VORTEX86) {
|
||||
if (dev->revision == 0x11)
|
||||
hwif->ultra_mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void it8212_disable_raid(struct pci_dev *dev)
|
||||
{
|
||||
/* Reset local CPU, and set BIOS not ready */
|
||||
pci_write_config_byte(dev, 0x5E, 0x01);
|
||||
|
||||
/* Set to bypass mode, and reset PCI bus */
|
||||
pci_write_config_byte(dev, 0x50, 0x00);
|
||||
pci_write_config_word(dev, PCI_COMMAND,
|
||||
PCI_COMMAND_PARITY | PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
pci_write_config_word(dev, 0x40, 0xA0F3);
|
||||
|
||||
pci_write_config_dword(dev,0x4C, 0x02040204);
|
||||
pci_write_config_byte(dev, 0x42, 0x36);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20);
|
||||
}
|
||||
|
||||
static int init_chipset_it821x(struct pci_dev *dev)
|
||||
{
|
||||
u8 conf;
|
||||
static char *mode[2] = { "pass through", "smart" };
|
||||
|
||||
/* Force the card into bypass mode if so requested */
|
||||
if (it8212_noraid) {
|
||||
printk(KERN_INFO DRV_NAME " %s: forcing bypass mode\n",
|
||||
pci_name(dev));
|
||||
it8212_disable_raid(dev);
|
||||
}
|
||||
pci_read_config_byte(dev, 0x50, &conf);
|
||||
printk(KERN_INFO DRV_NAME " %s: controller in %s mode\n",
|
||||
pci_name(dev), mode[conf & 1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops it821x_port_ops = {
|
||||
/* it821x_set_{pio,dma}_mode() are only used in pass-through mode */
|
||||
.set_pio_mode = it821x_set_pio_mode,
|
||||
.set_dma_mode = it821x_set_dma_mode,
|
||||
.quirkproc = it821x_quirkproc,
|
||||
.cable_detect = it821x_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info it821x_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_it821x,
|
||||
.init_hwif = init_hwif_it821x,
|
||||
.port_ops = &it821x_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
};
|
||||
|
||||
/**
|
||||
* it821x_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an ITE821x controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int it821x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct it821x_dev *itdevs;
|
||||
int rc;
|
||||
|
||||
itdevs = kcalloc(2, sizeof(*itdevs), GFP_KERNEL);
|
||||
if (itdevs == NULL) {
|
||||
printk(KERN_ERR DRV_NAME " %s: out of memory\n", pci_name(dev));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
itdevs->quirks = id->driver_data;
|
||||
|
||||
rc = ide_pci_init_one(dev, &it821x_chipset, itdevs);
|
||||
if (rc)
|
||||
kfree(itdevs);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void it821x_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct it821x_dev *itdevs = host->host_priv;
|
||||
|
||||
ide_pci_remove(dev);
|
||||
kfree(itdevs);
|
||||
}
|
||||
|
||||
static const struct pci_device_id it821x_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), 0 },
|
||||
{ PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8212), 0 },
|
||||
{ PCI_VDEVICE(RDC, PCI_DEVICE_ID_RDC_D1010), QUIRK_VORTEX86 },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, it821x_pci_tbl);
|
||||
|
||||
static struct pci_driver it821x_pci_driver = {
|
||||
.name = "ITE821x IDE",
|
||||
.id_table = it821x_pci_tbl,
|
||||
.probe = it821x_init_one,
|
||||
.remove = it821x_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init it821x_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&it821x_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit it821x_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&it821x_pci_driver);
|
||||
}
|
||||
|
||||
module_init(it821x_ide_init);
|
||||
module_exit(it821x_ide_exit);
|
||||
|
||||
module_param_named(noraid, it8212_noraid, int, S_IRUGO);
|
||||
MODULE_PARM_DESC(noraid, "Force card into bypass mode");
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the ITE 821x");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,176 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (C) 2006 Red Hat
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "jmicron"
|
||||
|
||||
typedef enum {
|
||||
PORT_PATA0 = 0,
|
||||
PORT_PATA1 = 1,
|
||||
PORT_SATA = 2,
|
||||
} port_type;
|
||||
|
||||
/**
|
||||
* jmicron_cable_detect - cable detection
|
||||
* @hwif: IDE port
|
||||
*
|
||||
* Returns the cable type.
|
||||
*/
|
||||
|
||||
static u8 jmicron_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
|
||||
u32 control;
|
||||
u32 control5;
|
||||
|
||||
int port = hwif->channel;
|
||||
port_type port_map[2];
|
||||
|
||||
pci_read_config_dword(pdev, 0x40, &control);
|
||||
|
||||
/* There are two basic mappings. One has the two SATA ports merged
|
||||
as master/slave and the secondary as PATA, the other has only the
|
||||
SATA port mapped */
|
||||
if (control & (1 << 23)) {
|
||||
port_map[0] = PORT_SATA;
|
||||
port_map[1] = PORT_PATA0;
|
||||
} else {
|
||||
port_map[0] = PORT_SATA;
|
||||
port_map[1] = PORT_SATA;
|
||||
}
|
||||
|
||||
/* The 365/366 may have this bit set to map the second PATA port
|
||||
as the internal primary channel */
|
||||
pci_read_config_dword(pdev, 0x80, &control5);
|
||||
if (control5 & (1<<24))
|
||||
port_map[0] = PORT_PATA1;
|
||||
|
||||
/* The two ports may then be logically swapped by the firmware */
|
||||
if (control & (1 << 22))
|
||||
port = port ^ 1;
|
||||
|
||||
/*
|
||||
* Now we know which physical port we are talking about we can
|
||||
* actually do our cable checking etc. Thankfully we don't need
|
||||
* to do the plumbing for other cases.
|
||||
*/
|
||||
switch (port_map[port]) {
|
||||
case PORT_PATA0:
|
||||
if (control & (1 << 3)) /* 40/80 pin primary */
|
||||
return ATA_CBL_PATA40;
|
||||
return ATA_CBL_PATA80;
|
||||
case PORT_PATA1:
|
||||
if (control5 & (1 << 19)) /* 40/80 pin secondary */
|
||||
return ATA_CBL_PATA40;
|
||||
return ATA_CBL_PATA80;
|
||||
case PORT_SATA:
|
||||
break;
|
||||
}
|
||||
/* Avoid bogus "control reaches end of non-void function" */
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static void jmicron_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* jmicron_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* As the JMicron snoops for timings we don't need to do anything here.
|
||||
*/
|
||||
|
||||
static void jmicron_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct ide_port_ops jmicron_port_ops = {
|
||||
.set_pio_mode = jmicron_set_pio_mode,
|
||||
.set_dma_mode = jmicron_set_dma_mode,
|
||||
.cable_detect = jmicron_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info jmicron_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.enablebits = { { 0x40, 0x01, 0x01 }, { 0x40, 0x10, 0x10 } },
|
||||
.port_ops = &jmicron_port_ops,
|
||||
.pio_mask = ATA_PIO5,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA6,
|
||||
};
|
||||
|
||||
/**
|
||||
* jmicron_init_one - pci layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds a Jmicron controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &jmicron_chipset, NULL);
|
||||
}
|
||||
|
||||
/* All JMB PATA controllers have and will continue to have the same
|
||||
* interface. Matching vendor and device class is enough for all
|
||||
* current and future controllers if the controller is programmed
|
||||
* properly.
|
||||
*
|
||||
* If libata is configured, jmicron PCI quirk programs the controller
|
||||
* into the correct mode. If libata isn't configured, match known
|
||||
* device IDs too to maintain backward compatibility.
|
||||
*/
|
||||
static struct pci_device_id jmicron_pci_tbl[] = {
|
||||
#if !defined(CONFIG_ATA) && !defined(CONFIG_ATA_MODULE)
|
||||
{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB361) },
|
||||
{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB363) },
|
||||
{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB365) },
|
||||
{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB366) },
|
||||
{ PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB368) },
|
||||
#endif
|
||||
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
|
||||
PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 0 },
|
||||
{ 0, },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, jmicron_pci_tbl);
|
||||
|
||||
static struct pci_driver jmicron_pci_driver = {
|
||||
.name = "JMicron IDE",
|
||||
.id_table = jmicron_pci_tbl,
|
||||
.probe = jmicron_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init jmicron_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&jmicron_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit jmicron_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&jmicron_pci_driver);
|
||||
}
|
||||
|
||||
module_init(jmicron_ide_init);
|
||||
module_exit(jmicron_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,350 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1997-1998 Mark Lord <mlord@pobox.com>
|
||||
* Copyright (C) 1998 Eddie C. Dost <ecd@skynet.be>
|
||||
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2004 Grant Grundler <grundler at parisc-linux.org>
|
||||
*
|
||||
* Inspired by an earlier effort from David S. Miller <davem@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "ns87415"
|
||||
|
||||
#ifdef CONFIG_SUPERIO
|
||||
/* SUPERIO 87560 is a PoS chip that NatSem denies exists.
|
||||
* Unfortunately, it's built-in on all Astro-based PA-RISC workstations
|
||||
* which use the integrated NS87514 cell for CD-ROM support.
|
||||
* i.e we have to support for CD-ROM installs.
|
||||
* See drivers/parisc/superio.c for more gory details.
|
||||
*/
|
||||
#include <asm/superio.h>
|
||||
|
||||
#define SUPERIO_IDE_MAX_RETRIES 25
|
||||
|
||||
/* Because of a defect in Super I/O, all reads of the PCI DMA status
|
||||
* registers, IDE status register and the IDE select register need to be
|
||||
* retried
|
||||
*/
|
||||
static u8 superio_ide_inb (unsigned long port)
|
||||
{
|
||||
u8 tmp;
|
||||
int retries = SUPERIO_IDE_MAX_RETRIES;
|
||||
|
||||
/* printk(" [ reading port 0x%x with retry ] ", port); */
|
||||
|
||||
do {
|
||||
tmp = inb(port);
|
||||
if (tmp == 0)
|
||||
udelay(50);
|
||||
} while (tmp == 0 && retries-- > 0);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static u8 superio_read_status(ide_hwif_t *hwif)
|
||||
{
|
||||
return superio_ide_inb(hwif->io_ports.status_addr);
|
||||
}
|
||||
|
||||
static u8 superio_dma_sff_read_status(ide_hwif_t *hwif)
|
||||
{
|
||||
return superio_ide_inb(hwif->dma_base + ATA_DMA_STATUS);
|
||||
}
|
||||
|
||||
static void superio_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
|
||||
u8 valid)
|
||||
{
|
||||
struct ide_io_ports *io_ports = &drive->hwif->io_ports;
|
||||
|
||||
if (valid & IDE_VALID_ERROR)
|
||||
tf->error = inb(io_ports->feature_addr);
|
||||
if (valid & IDE_VALID_NSECT)
|
||||
tf->nsect = inb(io_ports->nsect_addr);
|
||||
if (valid & IDE_VALID_LBAL)
|
||||
tf->lbal = inb(io_ports->lbal_addr);
|
||||
if (valid & IDE_VALID_LBAM)
|
||||
tf->lbam = inb(io_ports->lbam_addr);
|
||||
if (valid & IDE_VALID_LBAH)
|
||||
tf->lbah = inb(io_ports->lbah_addr);
|
||||
if (valid & IDE_VALID_DEVICE)
|
||||
tf->device = superio_ide_inb(io_ports->device_addr);
|
||||
}
|
||||
|
||||
static void ns87415_dev_select(ide_drive_t *drive);
|
||||
|
||||
static const struct ide_tp_ops superio_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = superio_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = ns87415_dev_select,
|
||||
.tf_load = ide_tf_load,
|
||||
.tf_read = superio_tf_read,
|
||||
|
||||
.input_data = ide_input_data,
|
||||
.output_data = ide_output_data,
|
||||
};
|
||||
|
||||
static void superio_init_iops(struct hwif_s *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
u32 dma_stat;
|
||||
u8 port = hwif->channel, tmp;
|
||||
|
||||
dma_stat = (pci_resource_start(pdev, 4) & ~3) + (!port ? 2 : 0xa);
|
||||
|
||||
/* Clear error/interrupt, enable dma */
|
||||
tmp = superio_ide_inb(dma_stat);
|
||||
outb(tmp | 0x66, dma_stat);
|
||||
}
|
||||
#else
|
||||
#define superio_dma_sff_read_status ide_dma_sff_read_status
|
||||
#endif
|
||||
|
||||
static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 };
|
||||
|
||||
/*
|
||||
* This routine either enables/disables (according to IDE_DFLAG_PRESENT)
|
||||
* the IRQ associated with the port,
|
||||
* and selects either PIO or DMA handshaking for the next I/O operation.
|
||||
*/
|
||||
static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data;
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
new = *old;
|
||||
|
||||
/* Adjust IRQ enable bit */
|
||||
bit = 1 << (8 + hwif->channel);
|
||||
|
||||
if (drive->dev_flags & IDE_DFLAG_PRESENT)
|
||||
new &= ~bit;
|
||||
else
|
||||
new |= bit;
|
||||
|
||||
/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
|
||||
bit = 1 << (20 + (drive->dn & 1) + (hwif->channel << 1));
|
||||
other = 1 << (20 + (1 - (drive->dn & 1)) + (hwif->channel << 1));
|
||||
new = use_dma ? ((new & ~other) | bit) : (new & ~bit);
|
||||
|
||||
if (new != *old) {
|
||||
unsigned char stat;
|
||||
|
||||
/*
|
||||
* Don't change DMA engine settings while Write Buffers
|
||||
* are busy.
|
||||
*/
|
||||
(void) pci_read_config_byte(dev, 0x43, &stat);
|
||||
while (stat & 0x03) {
|
||||
udelay(1);
|
||||
(void) pci_read_config_byte(dev, 0x43, &stat);
|
||||
}
|
||||
|
||||
*old = new;
|
||||
(void) pci_write_config_dword(dev, 0x40, new);
|
||||
|
||||
/*
|
||||
* And let things settle...
|
||||
*/
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void ns87415_dev_select(ide_drive_t *drive)
|
||||
{
|
||||
ns87415_prepare_drive(drive,
|
||||
!!(drive->dev_flags & IDE_DFLAG_USING_DMA));
|
||||
|
||||
outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr);
|
||||
}
|
||||
|
||||
static void ns87415_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ns87415_prepare_drive(drive, 1);
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static int ns87415_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 dma_stat = 0, dma_cmd = 0;
|
||||
|
||||
dma_stat = hwif->dma_ops->dma_sff_read_status(hwif);
|
||||
/* get DMA command mode */
|
||||
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
|
||||
/* stop DMA */
|
||||
outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
|
||||
/* from ERRATA: clear the INTR & ERROR bits */
|
||||
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
|
||||
outb(dma_cmd | 6, hwif->dma_base + ATA_DMA_CMD);
|
||||
|
||||
ns87415_prepare_drive(drive, 0);
|
||||
|
||||
/* verify good DMA status */
|
||||
return (dma_stat & 7) != 4;
|
||||
}
|
||||
|
||||
static void init_hwif_ns87415 (ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned int ctrl, using_inta;
|
||||
u8 progif;
|
||||
#ifdef __sparc_v9__
|
||||
int timeout;
|
||||
u8 stat;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We cannot probe for IRQ: both ports share common IRQ on INTA.
|
||||
* Also, leave IRQ masked during drive probing, to prevent infinite
|
||||
* interrupts from a potentially floating INTA..
|
||||
*
|
||||
* IRQs get unmasked in dev_select() when drive is first used.
|
||||
*/
|
||||
(void) pci_read_config_dword(dev, 0x40, &ctrl);
|
||||
(void) pci_read_config_byte(dev, 0x09, &progif);
|
||||
/* is irq in "native" mode? */
|
||||
using_inta = progif & (1 << (hwif->channel << 1));
|
||||
if (!using_inta)
|
||||
using_inta = ctrl & (1 << (4 + hwif->channel));
|
||||
if (hwif->mate) {
|
||||
hwif->select_data = hwif->mate->select_data;
|
||||
} else {
|
||||
hwif->select_data = (unsigned long)
|
||||
&ns87415_control[ns87415_count++];
|
||||
ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */
|
||||
if (using_inta)
|
||||
ctrl &= ~(1 << 6); /* unmask INTA */
|
||||
*((unsigned int *)hwif->select_data) = ctrl;
|
||||
(void) pci_write_config_dword(dev, 0x40, ctrl);
|
||||
|
||||
/*
|
||||
* Set prefetch size to 512 bytes for both ports,
|
||||
* but don't turn on/off prefetching here.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x55, 0xee);
|
||||
|
||||
#ifdef __sparc_v9__
|
||||
/*
|
||||
* XXX: Reset the device, if we don't it will not respond to
|
||||
* dev_select() properly during first ide_probe_port().
|
||||
*/
|
||||
timeout = 10000;
|
||||
outb(12, hwif->io_ports.ctl_addr);
|
||||
udelay(10);
|
||||
outb(8, hwif->io_ports.ctl_addr);
|
||||
do {
|
||||
udelay(50);
|
||||
stat = hwif->tp_ops->read_status(hwif);
|
||||
if (stat == 0xff)
|
||||
break;
|
||||
} while ((stat & ATA_BUSY) && --timeout);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!using_inta)
|
||||
hwif->irq = pci_get_legacy_ide_irq(dev, hwif->channel);
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
outb(0x60, hwif->dma_base + ATA_DMA_STATUS);
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops ns87415_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = ns87415_dev_select,
|
||||
.tf_load = ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = ide_input_data,
|
||||
.output_data = ide_output_data,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops ns87415_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = ns87415_dma_start,
|
||||
.dma_end = ns87415_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = superio_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info ns87415_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_hwif = init_hwif_ns87415,
|
||||
.tp_ops = &ns87415_tp_ops,
|
||||
.dma_ops = &ns87415_dma_ops,
|
||||
.host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA |
|
||||
IDE_HFLAG_NO_ATAPI_DMA,
|
||||
};
|
||||
|
||||
static int ns87415_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ide_port_info d = ns87415_chipset;
|
||||
|
||||
#ifdef CONFIG_SUPERIO
|
||||
if (PCI_SLOT(dev->devfn) == 0xE) {
|
||||
/* Built-in - assume it's under superio. */
|
||||
d.init_iops = superio_init_iops;
|
||||
d.tp_ops = &superio_tp_ops;
|
||||
}
|
||||
#endif
|
||||
return ide_pci_init_one(dev, &d, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id ns87415_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_87415), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ns87415_pci_tbl);
|
||||
|
||||
static struct pci_driver ns87415_pci_driver = {
|
||||
.name = "NS87415_IDE",
|
||||
.id_table = ns87415_pci_tbl,
|
||||
.probe = ns87415_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init ns87415_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&ns87415_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit ns87415_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&ns87415_pci_driver);
|
||||
}
|
||||
|
||||
module_init(ns87415_ide_init);
|
||||
module_exit(ns87415_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord, Eddie Dost, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for NS87415 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,179 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1996-1998 Linus Torvalds & authors (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors:
|
||||
* Jaromir Koutek <miri@punknet.cz>,
|
||||
* Jan Harkes <jaharkes@cwi.nl>,
|
||||
* Mark Lord <mlord@pobox.com>
|
||||
* Some parts of code are from ali14xx.c and from rz1000.c.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "opti621"
|
||||
|
||||
#define READ_REG 0 /* index of Read cycle timing register */
|
||||
#define WRITE_REG 1 /* index of Write cycle timing register */
|
||||
#define CNTRL_REG 3 /* index of Control register */
|
||||
#define STRAP_REG 5 /* index of Strap register */
|
||||
#define MISC_REG 6 /* index of Miscellaneous register */
|
||||
|
||||
static int reg_base;
|
||||
|
||||
static DEFINE_SPINLOCK(opti621_lock);
|
||||
|
||||
/* Write value to register reg, base of register
|
||||
* is at reg_base (0x1f0 primary, 0x170 secondary,
|
||||
* if not changed by PCI configuration).
|
||||
* This is from setupvic.exe program.
|
||||
*/
|
||||
static void write_reg(u8 value, int reg)
|
||||
{
|
||||
inw(reg_base + 1);
|
||||
inw(reg_base + 1);
|
||||
outb(3, reg_base + 2);
|
||||
outb(value, reg_base + reg);
|
||||
outb(0x83, reg_base + 2);
|
||||
}
|
||||
|
||||
/* Read value from register reg, base of register
|
||||
* is at reg_base (0x1f0 primary, 0x170 secondary,
|
||||
* if not changed by PCI configuration).
|
||||
* This is from setupvic.exe program.
|
||||
*/
|
||||
static u8 read_reg(int reg)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
||||
inw(reg_base + 1);
|
||||
inw(reg_base + 1);
|
||||
outb(3, reg_base + 2);
|
||||
ret = inb(reg_base + reg);
|
||||
outb(0x83, reg_base + 2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void opti621_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
ide_drive_t *pair = ide_get_pair_dev(drive);
|
||||
unsigned long flags;
|
||||
unsigned long mode = drive->pio_mode, pair_mode;
|
||||
const u8 pio = mode - XFER_PIO_0;
|
||||
u8 tim, misc, addr_pio = pio, clk;
|
||||
|
||||
/* DRDY is default 2 (by OPTi Databook) */
|
||||
static const u8 addr_timings[2][5] = {
|
||||
{ 0x20, 0x10, 0x00, 0x00, 0x00 }, /* 33 MHz */
|
||||
{ 0x10, 0x10, 0x00, 0x00, 0x00 }, /* 25 MHz */
|
||||
};
|
||||
static const u8 data_rec_timings[2][5] = {
|
||||
{ 0x5b, 0x45, 0x32, 0x21, 0x20 }, /* 33 MHz */
|
||||
{ 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
|
||||
};
|
||||
|
||||
ide_set_drivedata(drive, (void *)mode);
|
||||
|
||||
if (pair) {
|
||||
pair_mode = (unsigned long)ide_get_drivedata(pair);
|
||||
if (pair_mode && pair_mode < mode)
|
||||
addr_pio = pair_mode - XFER_PIO_0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&opti621_lock, flags);
|
||||
|
||||
reg_base = hwif->io_ports.data_addr;
|
||||
|
||||
/* allow Register-B */
|
||||
outb(0xc0, reg_base + CNTRL_REG);
|
||||
/* hmm, setupvic.exe does this ;-) */
|
||||
outb(0xff, reg_base + 5);
|
||||
/* if reads 0xff, adapter not exist? */
|
||||
(void)inb(reg_base + CNTRL_REG);
|
||||
/* if reads 0xc0, no interface exist? */
|
||||
read_reg(CNTRL_REG);
|
||||
|
||||
/* check CLK speed */
|
||||
clk = read_reg(STRAP_REG) & 1;
|
||||
|
||||
printk(KERN_INFO "%s: CLK = %d MHz\n", hwif->name, clk ? 25 : 33);
|
||||
|
||||
tim = data_rec_timings[clk][pio];
|
||||
misc = addr_timings[clk][addr_pio];
|
||||
|
||||
/* select Index-0/1 for Register-A/B */
|
||||
write_reg(drive->dn & 1, MISC_REG);
|
||||
/* set read cycle timings */
|
||||
write_reg(tim, READ_REG);
|
||||
/* set write cycle timings */
|
||||
write_reg(tim, WRITE_REG);
|
||||
|
||||
/* use Register-A for drive 0 */
|
||||
/* use Register-B for drive 1 */
|
||||
write_reg(0x85, CNTRL_REG);
|
||||
|
||||
/* set address setup, DRDY timings, */
|
||||
/* and read prefetch for both drives */
|
||||
write_reg(misc, MISC_REG);
|
||||
|
||||
spin_unlock_irqrestore(&opti621_lock, flags);
|
||||
}
|
||||
|
||||
static const struct ide_port_ops opti621_port_ops = {
|
||||
.set_pio_mode = opti621_set_pio_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info opti621_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.enablebits = { {0x45, 0x80, 0x00}, {0x40, 0x08, 0x00} },
|
||||
.port_ops = &opti621_port_ops,
|
||||
.host_flags = IDE_HFLAG_NO_DMA,
|
||||
.pio_mask = ATA_PIO4,
|
||||
};
|
||||
|
||||
static int opti621_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &opti621_chipset, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id opti621_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C621), 0 },
|
||||
{ PCI_VDEVICE(OPTI, PCI_DEVICE_ID_OPTI_82C825), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, opti621_pci_tbl);
|
||||
|
||||
static struct pci_driver opti621_pci_driver = {
|
||||
.name = "Opti621_IDE",
|
||||
.id_table = opti621_pci_tbl,
|
||||
.probe = opti621_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init opti621_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&opti621_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit opti621_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&opti621_pci_driver);
|
||||
}
|
||||
|
||||
module_init(opti621_ide_init);
|
||||
module_exit(opti621_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Jaromir Koutek, Jan Harkes, Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for Opti621 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,387 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Palmchip bk3710 IDE controller
|
||||
*
|
||||
* Copyright (C) 2006 Texas Instruments.
|
||||
* Copyright (C) 2007 MontaVista Software, Inc., <source@mvista.com>
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* Offset of the primary interface registers */
|
||||
#define IDE_PALM_ATA_PRI_REG_OFFSET 0x1F0
|
||||
|
||||
/* Primary Control Offset */
|
||||
#define IDE_PALM_ATA_PRI_CTL_OFFSET 0x3F6
|
||||
|
||||
#define BK3710_BMICP 0x00
|
||||
#define BK3710_BMISP 0x02
|
||||
#define BK3710_BMIDTP 0x04
|
||||
#define BK3710_IDETIMP 0x40
|
||||
#define BK3710_IDESTATUS 0x47
|
||||
#define BK3710_UDMACTL 0x48
|
||||
#define BK3710_MISCCTL 0x50
|
||||
#define BK3710_REGSTB 0x54
|
||||
#define BK3710_REGRCVR 0x58
|
||||
#define BK3710_DATSTB 0x5C
|
||||
#define BK3710_DATRCVR 0x60
|
||||
#define BK3710_DMASTB 0x64
|
||||
#define BK3710_DMARCVR 0x68
|
||||
#define BK3710_UDMASTB 0x6C
|
||||
#define BK3710_UDMATRP 0x70
|
||||
#define BK3710_UDMAENV 0x74
|
||||
#define BK3710_IORDYTMP 0x78
|
||||
|
||||
static unsigned ideclk_period; /* in nanoseconds */
|
||||
|
||||
struct palm_bk3710_udmatiming {
|
||||
unsigned int rptime; /* tRP -- Ready to pause time (nsec) */
|
||||
unsigned int cycletime; /* tCYCTYP2/2 -- avg Cycle Time (nsec) */
|
||||
/* tENV is always a minimum of 20 nsec */
|
||||
};
|
||||
|
||||
static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = {
|
||||
{ 160, 240 / 2 }, /* UDMA Mode 0 */
|
||||
{ 125, 160 / 2 }, /* UDMA Mode 1 */
|
||||
{ 100, 120 / 2 }, /* UDMA Mode 2 */
|
||||
{ 100, 90 / 2 }, /* UDMA Mode 3 */
|
||||
{ 100, 60 / 2 }, /* UDMA Mode 4 */
|
||||
{ 85, 40 / 2 }, /* UDMA Mode 5 */
|
||||
};
|
||||
|
||||
static void palm_bk3710_setudmamode(void __iomem *base, unsigned int dev,
|
||||
unsigned int mode)
|
||||
{
|
||||
u8 tenv, trp, t0;
|
||||
u32 val32;
|
||||
u16 val16;
|
||||
|
||||
/* DMA Data Setup */
|
||||
t0 = DIV_ROUND_UP(palm_bk3710_udmatimings[mode].cycletime,
|
||||
ideclk_period) - 1;
|
||||
tenv = DIV_ROUND_UP(20, ideclk_period) - 1;
|
||||
trp = DIV_ROUND_UP(palm_bk3710_udmatimings[mode].rptime,
|
||||
ideclk_period) - 1;
|
||||
|
||||
/* udmastb Ultra DMA Access Strobe Width */
|
||||
val32 = readl(base + BK3710_UDMASTB) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (t0 << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_UDMASTB);
|
||||
|
||||
/* udmatrp Ultra DMA Ready to Pause Time */
|
||||
val32 = readl(base + BK3710_UDMATRP) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (trp << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_UDMATRP);
|
||||
|
||||
/* udmaenv Ultra DMA envelop Time */
|
||||
val32 = readl(base + BK3710_UDMAENV) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (tenv << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_UDMAENV);
|
||||
|
||||
/* Enable UDMA for Device */
|
||||
val16 = readw(base + BK3710_UDMACTL) | (1 << dev);
|
||||
writew(val16, base + BK3710_UDMACTL);
|
||||
}
|
||||
|
||||
static void palm_bk3710_setdmamode(void __iomem *base, unsigned int dev,
|
||||
unsigned short min_cycle,
|
||||
unsigned int mode)
|
||||
{
|
||||
u8 td, tkw, t0;
|
||||
u32 val32;
|
||||
u16 val16;
|
||||
struct ide_timing *t;
|
||||
int cycletime;
|
||||
|
||||
t = ide_timing_find_mode(mode);
|
||||
cycletime = max_t(int, t->cycle, min_cycle);
|
||||
|
||||
/* DMA Data Setup */
|
||||
t0 = DIV_ROUND_UP(cycletime, ideclk_period);
|
||||
td = DIV_ROUND_UP(t->active, ideclk_period);
|
||||
tkw = t0 - td - 1;
|
||||
td -= 1;
|
||||
|
||||
val32 = readl(base + BK3710_DMASTB) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (td << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_DMASTB);
|
||||
|
||||
val32 = readl(base + BK3710_DMARCVR) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (tkw << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_DMARCVR);
|
||||
|
||||
/* Disable UDMA for Device */
|
||||
val16 = readw(base + BK3710_UDMACTL) & ~(1 << dev);
|
||||
writew(val16, base + BK3710_UDMACTL);
|
||||
}
|
||||
|
||||
static void palm_bk3710_setpiomode(void __iomem *base, ide_drive_t *mate,
|
||||
unsigned int dev, unsigned int cycletime,
|
||||
unsigned int mode)
|
||||
{
|
||||
u8 t2, t2i, t0;
|
||||
u32 val32;
|
||||
struct ide_timing *t;
|
||||
|
||||
t = ide_timing_find_mode(XFER_PIO_0 + mode);
|
||||
|
||||
/* PIO Data Setup */
|
||||
t0 = DIV_ROUND_UP(cycletime, ideclk_period);
|
||||
t2 = DIV_ROUND_UP(t->active, ideclk_period);
|
||||
|
||||
t2i = t0 - t2 - 1;
|
||||
t2 -= 1;
|
||||
|
||||
val32 = readl(base + BK3710_DATSTB) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (t2 << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_DATSTB);
|
||||
|
||||
val32 = readl(base + BK3710_DATRCVR) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (t2i << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_DATRCVR);
|
||||
|
||||
if (mate) {
|
||||
u8 mode2 = mate->pio_mode - XFER_PIO_0;
|
||||
|
||||
if (mode2 < mode)
|
||||
mode = mode2;
|
||||
}
|
||||
|
||||
/* TASKFILE Setup */
|
||||
t0 = DIV_ROUND_UP(t->cyc8b, ideclk_period);
|
||||
t2 = DIV_ROUND_UP(t->act8b, ideclk_period);
|
||||
|
||||
t2i = t0 - t2 - 1;
|
||||
t2 -= 1;
|
||||
|
||||
val32 = readl(base + BK3710_REGSTB) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (t2 << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_REGSTB);
|
||||
|
||||
val32 = readl(base + BK3710_REGRCVR) & (0xFF << (dev ? 0 : 8));
|
||||
val32 |= (t2i << (dev ? 8 : 0));
|
||||
writel(val32, base + BK3710_REGRCVR);
|
||||
}
|
||||
|
||||
static void palm_bk3710_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
int is_slave = drive->dn & 1;
|
||||
void __iomem *base = (void __iomem *)hwif->dma_base;
|
||||
const u8 xferspeed = drive->dma_mode;
|
||||
|
||||
if (xferspeed >= XFER_UDMA_0) {
|
||||
palm_bk3710_setudmamode(base, is_slave,
|
||||
xferspeed - XFER_UDMA_0);
|
||||
} else {
|
||||
palm_bk3710_setdmamode(base, is_slave,
|
||||
drive->id[ATA_ID_EIDE_DMA_MIN],
|
||||
xferspeed);
|
||||
}
|
||||
}
|
||||
|
||||
static void palm_bk3710_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
unsigned int cycle_time;
|
||||
int is_slave = drive->dn & 1;
|
||||
ide_drive_t *mate;
|
||||
void __iomem *base = (void __iomem *)hwif->dma_base;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
/*
|
||||
* Obtain the drive PIO data for tuning the Palm Chip registers
|
||||
*/
|
||||
cycle_time = ide_pio_cycle_time(drive, pio);
|
||||
mate = ide_get_pair_dev(drive);
|
||||
palm_bk3710_setpiomode(base, mate, is_slave, cycle_time, pio);
|
||||
}
|
||||
|
||||
static void palm_bk3710_chipinit(void __iomem *base)
|
||||
{
|
||||
/*
|
||||
* REVISIT: the ATA reset signal needs to be managed through a
|
||||
* GPIO, which means it should come from platform_data. Until
|
||||
* we get and use such information, we have to trust that things
|
||||
* have been reset before we get here.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Program the IDETIMP Register Value based on the following assumptions
|
||||
*
|
||||
* (ATA_IDETIMP_IDEEN , ENABLE ) |
|
||||
* (ATA_IDETIMP_PREPOST1 , DISABLE) |
|
||||
* (ATA_IDETIMP_PREPOST0 , DISABLE) |
|
||||
*
|
||||
* DM6446 silicon rev 2.1 and earlier have no observed net benefit
|
||||
* from enabling prefetch/postwrite.
|
||||
*/
|
||||
writew(BIT(15), base + BK3710_IDETIMP);
|
||||
|
||||
/*
|
||||
* UDMACTL Ultra-ATA DMA Control
|
||||
* (ATA_UDMACTL_UDMAP1 , 0 ) |
|
||||
* (ATA_UDMACTL_UDMAP0 , 0 )
|
||||
*
|
||||
*/
|
||||
writew(0, base + BK3710_UDMACTL);
|
||||
|
||||
/*
|
||||
* MISCCTL Miscellaneous Conrol Register
|
||||
* (ATA_MISCCTL_HWNHLD1P , 1 cycle)
|
||||
* (ATA_MISCCTL_HWNHLD0P , 1 cycle)
|
||||
* (ATA_MISCCTL_TIMORIDE , 1)
|
||||
*/
|
||||
writel(0x001, base + BK3710_MISCCTL);
|
||||
|
||||
/*
|
||||
* IORDYTMP IORDY Timer for Primary Register
|
||||
* (ATA_IORDYTMP_IORDYTMP , 0xffff )
|
||||
*/
|
||||
writel(0xFFFF, base + BK3710_IORDYTMP);
|
||||
|
||||
/*
|
||||
* Configure BMISP Register
|
||||
* (ATA_BMISP_DMAEN1 , DISABLE ) |
|
||||
* (ATA_BMISP_DMAEN0 , DISABLE ) |
|
||||
* (ATA_BMISP_IORDYINT , CLEAR) |
|
||||
* (ATA_BMISP_INTRSTAT , CLEAR) |
|
||||
* (ATA_BMISP_DMAERROR , CLEAR)
|
||||
*/
|
||||
writew(0, base + BK3710_BMISP);
|
||||
|
||||
palm_bk3710_setpiomode(base, NULL, 0, 600, 0);
|
||||
palm_bk3710_setpiomode(base, NULL, 1, 600, 0);
|
||||
}
|
||||
|
||||
static u8 palm_bk3710_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static int palm_bk3710_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name);
|
||||
|
||||
if (ide_allocate_dma_engine(hwif))
|
||||
return -1;
|
||||
|
||||
hwif->dma_base = hwif->io_ports.data_addr - IDE_PALM_ATA_PRI_REG_OFFSET;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops palm_bk3710_ports_ops = {
|
||||
.set_pio_mode = palm_bk3710_set_pio_mode,
|
||||
.set_dma_mode = palm_bk3710_set_dma_mode,
|
||||
.cable_detect = palm_bk3710_cable_detect,
|
||||
};
|
||||
|
||||
static struct ide_port_info palm_bk3710_port_info __initdata = {
|
||||
.init_dma = palm_bk3710_init_dma,
|
||||
.port_ops = &palm_bk3710_ports_ops,
|
||||
.dma_ops = &sff_dma_ops,
|
||||
.host_flags = IDE_HFLAG_MMIO,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.chipset = ide_palm3710,
|
||||
};
|
||||
|
||||
static int __init palm_bk3710_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct resource *mem, *irq;
|
||||
void __iomem *base;
|
||||
unsigned long rate, mem_size;
|
||||
int i, rc;
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
|
||||
clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return -ENODEV;
|
||||
|
||||
clk_enable(clk);
|
||||
rate = clk_get_rate(clk);
|
||||
if (!rate)
|
||||
return -EINVAL;
|
||||
|
||||
/* NOTE: round *down* to meet minimum timings; we count in clocks */
|
||||
ideclk_period = 1000000000UL / rate;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (mem == NULL) {
|
||||
printk(KERN_ERR "failed to get memory region resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (irq == NULL) {
|
||||
printk(KERN_ERR "failed to get IRQ resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
mem_size = resource_size(mem);
|
||||
if (request_mem_region(mem->start, mem_size, "palm_bk3710") == NULL) {
|
||||
printk(KERN_ERR "failed to request memory region\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
base = ioremap(mem->start, mem_size);
|
||||
if (!base) {
|
||||
printk(KERN_ERR "failed to map IO memory\n");
|
||||
release_mem_region(mem->start, mem_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Configure the Palm Chip controller */
|
||||
palm_bk3710_chipinit(base);
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
for (i = 0; i < IDE_NR_PORTS - 2; i++)
|
||||
hw.io_ports_array[i] = (unsigned long)
|
||||
(base + IDE_PALM_ATA_PRI_REG_OFFSET + i);
|
||||
hw.io_ports.ctl_addr = (unsigned long)
|
||||
(base + IDE_PALM_ATA_PRI_CTL_OFFSET);
|
||||
hw.irq = irq->start;
|
||||
hw.dev = &pdev->dev;
|
||||
|
||||
palm_bk3710_port_info.udma_mask = rate < 100000000 ? ATA_UDMA4 :
|
||||
ATA_UDMA5;
|
||||
|
||||
/* Register the IDE interface with Linux */
|
||||
rc = ide_host_add(&palm_bk3710_port_info, hws, 1, NULL);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
out:
|
||||
printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:palm_bk3710");
|
||||
|
||||
static struct platform_driver platform_bk_driver = {
|
||||
.driver = {
|
||||
.name = "palm_bk3710",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init palm_bk3710_init(void)
|
||||
{
|
||||
return platform_driver_probe(&platform_bk_driver, palm_bk3710_probe);
|
||||
}
|
||||
|
||||
module_init(palm_bk3710_init);
|
||||
MODULE_LICENSE("GPL");
|
@ -1,557 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Promise TX2/TX4/TX2000/133 IDE driver
|
||||
*
|
||||
* Split from:
|
||||
* linux/drivers/ide/pdc202xx.c Version 0.35 Mar. 30, 2002
|
||||
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2005-2007 MontaVista Software, Inc.
|
||||
* Portions Copyright (C) 1999 Promise Technology, Inc.
|
||||
* Author: Frank Tiernan (frankt@promise.com)
|
||||
* Released under terms of General Public License
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
#include <asm/prom.h>
|
||||
#endif
|
||||
|
||||
#define DRV_NAME "pdc202xx_new"
|
||||
|
||||
#undef DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(fmt, args...) printk("%s: " fmt, __func__, ## args)
|
||||
#else
|
||||
#define DBG(fmt, args...)
|
||||
#endif
|
||||
|
||||
static u8 max_dma_rate(struct pci_dev *pdev)
|
||||
{
|
||||
u8 mode;
|
||||
|
||||
switch(pdev->device) {
|
||||
case PCI_DEVICE_ID_PROMISE_20277:
|
||||
case PCI_DEVICE_ID_PROMISE_20276:
|
||||
case PCI_DEVICE_ID_PROMISE_20275:
|
||||
case PCI_DEVICE_ID_PROMISE_20271:
|
||||
case PCI_DEVICE_ID_PROMISE_20269:
|
||||
mode = 4;
|
||||
break;
|
||||
case PCI_DEVICE_ID_PROMISE_20270:
|
||||
case PCI_DEVICE_ID_PROMISE_20268:
|
||||
mode = 3;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_indexed_reg - Get indexed register
|
||||
* @hwif: for the port address
|
||||
* @index: index of the indexed register
|
||||
*/
|
||||
static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index)
|
||||
{
|
||||
u8 value;
|
||||
|
||||
outb(index, hwif->dma_base + 1);
|
||||
value = inb(hwif->dma_base + 3);
|
||||
|
||||
DBG("index[%02X] value[%02X]\n", index, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* set_indexed_reg - Set indexed register
|
||||
* @hwif: for the port address
|
||||
* @index: index of the indexed register
|
||||
*/
|
||||
static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value)
|
||||
{
|
||||
outb(index, hwif->dma_base + 1);
|
||||
outb(value, hwif->dma_base + 3);
|
||||
DBG("index[%02X] value[%02X]\n", index, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* ATA Timing Tables based on 133 MHz PLL output clock.
|
||||
*
|
||||
* If the PLL outputs 100 MHz clock, the ASIC hardware will set
|
||||
* the timing registers automatically when "set features" command is
|
||||
* issued to the device. However, if the PLL output clock is 133 MHz,
|
||||
* the following tables must be used.
|
||||
*/
|
||||
static struct pio_timing {
|
||||
u8 reg0c, reg0d, reg13;
|
||||
} pio_timings [] = {
|
||||
{ 0xfb, 0x2b, 0xac }, /* PIO mode 0, IORDY off, Prefetch off */
|
||||
{ 0x46, 0x29, 0xa4 }, /* PIO mode 1, IORDY off, Prefetch off */
|
||||
{ 0x23, 0x26, 0x64 }, /* PIO mode 2, IORDY off, Prefetch off */
|
||||
{ 0x27, 0x0d, 0x35 }, /* PIO mode 3, IORDY on, Prefetch off */
|
||||
{ 0x23, 0x09, 0x25 }, /* PIO mode 4, IORDY on, Prefetch off */
|
||||
};
|
||||
|
||||
static struct mwdma_timing {
|
||||
u8 reg0e, reg0f;
|
||||
} mwdma_timings [] = {
|
||||
{ 0xdf, 0x5f }, /* MWDMA mode 0 */
|
||||
{ 0x6b, 0x27 }, /* MWDMA mode 1 */
|
||||
{ 0x69, 0x25 }, /* MWDMA mode 2 */
|
||||
};
|
||||
|
||||
static struct udma_timing {
|
||||
u8 reg10, reg11, reg12;
|
||||
} udma_timings [] = {
|
||||
{ 0x4a, 0x0f, 0xd5 }, /* UDMA mode 0 */
|
||||
{ 0x3a, 0x0a, 0xd0 }, /* UDMA mode 1 */
|
||||
{ 0x2a, 0x07, 0xcd }, /* UDMA mode 2 */
|
||||
{ 0x1a, 0x05, 0xcd }, /* UDMA mode 3 */
|
||||
{ 0x1a, 0x03, 0xcd }, /* UDMA mode 4 */
|
||||
{ 0x1a, 0x02, 0xcb }, /* UDMA mode 5 */
|
||||
{ 0x1a, 0x01, 0xcb }, /* UDMA mode 6 */
|
||||
};
|
||||
|
||||
static void pdcnew_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
/*
|
||||
* IDE core issues SETFEATURES_XFER to the drive first (thanks to
|
||||
* IDE_HFLAG_POST_SET_MODE in ->host_flags). PDC202xx hardware will
|
||||
* automatically set the timing registers based on 100 MHz PLL output.
|
||||
*
|
||||
* As we set up the PLL to output 133 MHz for UltraDMA/133 capable
|
||||
* chips, we must override the default register settings...
|
||||
*/
|
||||
if (max_dma_rate(dev) == 4) {
|
||||
u8 mode = speed & 0x07;
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
set_indexed_reg(hwif, 0x10 + adj,
|
||||
udma_timings[mode].reg10);
|
||||
set_indexed_reg(hwif, 0x11 + adj,
|
||||
udma_timings[mode].reg11);
|
||||
set_indexed_reg(hwif, 0x12 + adj,
|
||||
udma_timings[mode].reg12);
|
||||
} else {
|
||||
set_indexed_reg(hwif, 0x0e + adj,
|
||||
mwdma_timings[mode].reg0e);
|
||||
set_indexed_reg(hwif, 0x0f + adj,
|
||||
mwdma_timings[mode].reg0f);
|
||||
}
|
||||
} else if (speed == XFER_UDMA_2) {
|
||||
/* Set tHOLD bit to 0 if using UDMA mode 2 */
|
||||
u8 tmp = get_indexed_reg(hwif, 0x10 + adj);
|
||||
|
||||
set_indexed_reg(hwif, 0x10 + adj, tmp & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
static void pdcnew_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 adj = (drive->dn & 1) ? 0x08 : 0x00;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
if (max_dma_rate(dev) == 4) {
|
||||
set_indexed_reg(hwif, 0x0c + adj, pio_timings[pio].reg0c);
|
||||
set_indexed_reg(hwif, 0x0d + adj, pio_timings[pio].reg0d);
|
||||
set_indexed_reg(hwif, 0x13 + adj, pio_timings[pio].reg13);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 pdcnew_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
if (get_indexed_reg(hwif, 0x0b) & 0x04)
|
||||
return ATA_CBL_PATA40;
|
||||
else
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static void pdcnew_reset(ide_drive_t *drive)
|
||||
{
|
||||
/*
|
||||
* Deleted this because it is redundant from the caller.
|
||||
*/
|
||||
printk(KERN_WARNING "pdc202xx_new: %s channel reset.\n",
|
||||
drive->hwif->channel ? "Secondary" : "Primary");
|
||||
}
|
||||
|
||||
/**
|
||||
* read_counter - Read the byte count registers
|
||||
* @dma_base: for the port address
|
||||
*/
|
||||
static long read_counter(u32 dma_base)
|
||||
{
|
||||
u32 pri_dma_base = dma_base, sec_dma_base = dma_base + 0x08;
|
||||
u8 cnt0, cnt1, cnt2, cnt3;
|
||||
long count = 0, last;
|
||||
int retry = 3;
|
||||
|
||||
do {
|
||||
last = count;
|
||||
|
||||
/* Read the current count */
|
||||
outb(0x20, pri_dma_base + 0x01);
|
||||
cnt0 = inb(pri_dma_base + 0x03);
|
||||
outb(0x21, pri_dma_base + 0x01);
|
||||
cnt1 = inb(pri_dma_base + 0x03);
|
||||
outb(0x20, sec_dma_base + 0x01);
|
||||
cnt2 = inb(sec_dma_base + 0x03);
|
||||
outb(0x21, sec_dma_base + 0x01);
|
||||
cnt3 = inb(sec_dma_base + 0x03);
|
||||
|
||||
count = (cnt3 << 23) | (cnt2 << 15) | (cnt1 << 8) | cnt0;
|
||||
|
||||
/*
|
||||
* The 30-bit decrementing counter is read in 4 pieces.
|
||||
* Incorrect value may be read when the most significant bytes
|
||||
* are changing...
|
||||
*/
|
||||
} while (retry-- && (((last ^ count) & 0x3fff8000) || last < count));
|
||||
|
||||
DBG("cnt0[%02X] cnt1[%02X] cnt2[%02X] cnt3[%02X]\n",
|
||||
cnt0, cnt1, cnt2, cnt3);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* detect_pll_input_clock - Detect the PLL input clock in Hz.
|
||||
* @dma_base: for the port address
|
||||
* E.g. 16949000 on 33 MHz PCI bus, i.e. half of the PCI clock.
|
||||
*/
|
||||
static long detect_pll_input_clock(unsigned long dma_base)
|
||||
{
|
||||
ktime_t start_time, end_time;
|
||||
long start_count, end_count;
|
||||
long pll_input, usec_elapsed;
|
||||
u8 scr1;
|
||||
|
||||
start_count = read_counter(dma_base);
|
||||
start_time = ktime_get();
|
||||
|
||||
/* Start the test mode */
|
||||
outb(0x01, dma_base + 0x01);
|
||||
scr1 = inb(dma_base + 0x03);
|
||||
DBG("scr1[%02X]\n", scr1);
|
||||
outb(scr1 | 0x40, dma_base + 0x03);
|
||||
|
||||
/* Let the counter run for 10 ms. */
|
||||
mdelay(10);
|
||||
|
||||
end_count = read_counter(dma_base);
|
||||
end_time = ktime_get();
|
||||
|
||||
/* Stop the test mode */
|
||||
outb(0x01, dma_base + 0x01);
|
||||
scr1 = inb(dma_base + 0x03);
|
||||
DBG("scr1[%02X]\n", scr1);
|
||||
outb(scr1 & ~0x40, dma_base + 0x03);
|
||||
|
||||
/*
|
||||
* Calculate the input clock in Hz
|
||||
* (the clock counter is 30 bit wide and counts down)
|
||||
*/
|
||||
usec_elapsed = ktime_us_delta(end_time, start_time);
|
||||
pll_input = ((start_count - end_count) & 0x3fffffff) / 10 *
|
||||
(10000000 / usec_elapsed);
|
||||
|
||||
DBG("start[%ld] end[%ld]\n", start_count, end_count);
|
||||
|
||||
return pll_input;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
static void apple_kiwi_init(struct pci_dev *pdev)
|
||||
{
|
||||
struct device_node *np = pci_device_to_OF_node(pdev);
|
||||
u8 conf;
|
||||
|
||||
if (np == NULL || !of_device_is_compatible(np, "kiwi-root"))
|
||||
return;
|
||||
|
||||
if (pdev->revision >= 0x03) {
|
||||
/* Setup chip magic config stuff (from darwin) */
|
||||
pci_read_config_byte (pdev, 0x40, &conf);
|
||||
pci_write_config_byte(pdev, 0x40, (conf | 0x01));
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PPC_PMAC */
|
||||
|
||||
static int init_chipset_pdcnew(struct pci_dev *dev)
|
||||
{
|
||||
const char *name = DRV_NAME;
|
||||
unsigned long dma_base = pci_resource_start(dev, 4);
|
||||
unsigned long sec_dma_base = dma_base + 0x08;
|
||||
long pll_input, pll_output, ratio;
|
||||
int f, r;
|
||||
u8 pll_ctl0, pll_ctl1;
|
||||
|
||||
if (dma_base == 0)
|
||||
return -EFAULT;
|
||||
|
||||
#ifdef CONFIG_PPC_PMAC
|
||||
apple_kiwi_init(dev);
|
||||
#endif
|
||||
|
||||
/* Calculate the required PLL output frequency */
|
||||
switch(max_dma_rate(dev)) {
|
||||
case 4: /* it's 133 MHz for Ultra133 chips */
|
||||
pll_output = 133333333;
|
||||
break;
|
||||
case 3: /* and 100 MHz for Ultra100 chips */
|
||||
default:
|
||||
pll_output = 100000000;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect PLL input clock.
|
||||
* On some systems, where PCI bus is running at non-standard clock rate
|
||||
* (e.g. 25 or 40 MHz), we have to adjust the cycle time.
|
||||
* PDC20268 and newer chips employ PLL circuit to help correct timing
|
||||
* registers setting.
|
||||
*/
|
||||
pll_input = detect_pll_input_clock(dma_base);
|
||||
printk(KERN_INFO "%s %s: PLL input clock is %ld kHz\n",
|
||||
name, pci_name(dev), pll_input / 1000);
|
||||
|
||||
/* Sanity check */
|
||||
if (unlikely(pll_input < 5000000L || pll_input > 70000000L)) {
|
||||
printk(KERN_ERR "%s %s: Bad PLL input clock %ld Hz, giving up!"
|
||||
"\n", name, pci_name(dev), pll_input);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
DBG("pll_output is %ld Hz\n", pll_output);
|
||||
|
||||
/* Show the current clock value of PLL control register
|
||||
* (maybe already configured by the BIOS)
|
||||
*/
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
pll_ctl0 = inb(sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
pll_ctl1 = inb(sec_dma_base + 0x03);
|
||||
|
||||
DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Calculate the ratio of F, R and NO
|
||||
* POUT = (F + 2) / (( R + 2) * NO)
|
||||
*/
|
||||
ratio = pll_output / (pll_input / 1000);
|
||||
if (ratio < 8600L) { /* 8.6x */
|
||||
/* Using NO = 0x01, R = 0x0d */
|
||||
r = 0x0d;
|
||||
} else if (ratio < 12900L) { /* 12.9x */
|
||||
/* Using NO = 0x01, R = 0x08 */
|
||||
r = 0x08;
|
||||
} else if (ratio < 16100L) { /* 16.1x */
|
||||
/* Using NO = 0x01, R = 0x06 */
|
||||
r = 0x06;
|
||||
} else if (ratio < 64000L) { /* 64x */
|
||||
r = 0x00;
|
||||
} else {
|
||||
/* Invalid ratio */
|
||||
printk(KERN_ERR "%s %s: Bad ratio %ld, giving up!\n",
|
||||
name, pci_name(dev), ratio);
|
||||
goto out;
|
||||
}
|
||||
|
||||
f = (ratio * (r + 2)) / 1000 - 2;
|
||||
|
||||
DBG("F[%d] R[%d] ratio*1000[%ld]\n", f, r, ratio);
|
||||
|
||||
if (unlikely(f < 0 || f > 127)) {
|
||||
/* Invalid F */
|
||||
printk(KERN_ERR "%s %s: F[%d] invalid!\n",
|
||||
name, pci_name(dev), f);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pll_ctl0 = (u8) f;
|
||||
pll_ctl1 = (u8) r;
|
||||
|
||||
DBG("Writing pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
outb(pll_ctl0, sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
outb(pll_ctl1, sec_dma_base + 0x03);
|
||||
|
||||
/* Wait the PLL circuit to be stable */
|
||||
mdelay(30);
|
||||
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* Show the current clock value of PLL control register
|
||||
*/
|
||||
outb(0x02, sec_dma_base + 0x01);
|
||||
pll_ctl0 = inb(sec_dma_base + 0x03);
|
||||
outb(0x03, sec_dma_base + 0x01);
|
||||
pll_ctl1 = inb(sec_dma_base + 0x03);
|
||||
|
||||
DBG("pll_ctl[%02X][%02X]\n", pll_ctl0, pll_ctl1);
|
||||
#endif
|
||||
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_dev *pdc20270_get_dev2(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *dev2;
|
||||
|
||||
dev2 = pci_get_slot(dev->bus, PCI_DEVFN(PCI_SLOT(dev->devfn) + 1,
|
||||
PCI_FUNC(dev->devfn)));
|
||||
|
||||
if (dev2 &&
|
||||
dev2->vendor == dev->vendor &&
|
||||
dev2->device == dev->device) {
|
||||
|
||||
if (dev2->irq != dev->irq) {
|
||||
dev2->irq = dev->irq;
|
||||
printk(KERN_INFO DRV_NAME " %s: PCI config space "
|
||||
"interrupt fixed\n", pci_name(dev));
|
||||
}
|
||||
|
||||
return dev2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops pdcnew_port_ops = {
|
||||
.set_pio_mode = pdcnew_set_pio_mode,
|
||||
.set_dma_mode = pdcnew_set_dma_mode,
|
||||
.resetproc = pdcnew_reset,
|
||||
.cable_detect = pdcnew_cable_detect,
|
||||
};
|
||||
|
||||
#define DECLARE_PDCNEW_DEV(udma) \
|
||||
{ \
|
||||
.name = DRV_NAME, \
|
||||
.init_chipset = init_chipset_pdcnew, \
|
||||
.port_ops = &pdcnew_port_ops, \
|
||||
.host_flags = IDE_HFLAG_POST_SET_MODE | \
|
||||
IDE_HFLAG_ERROR_STOPS_FIFO | \
|
||||
IDE_HFLAG_OFF_BOARD, \
|
||||
.pio_mask = ATA_PIO4, \
|
||||
.mwdma_mask = ATA_MWDMA2, \
|
||||
.udma_mask = udma, \
|
||||
}
|
||||
|
||||
static const struct ide_port_info pdcnew_chipsets[] = {
|
||||
/* 0: PDC202{68,70} */ DECLARE_PDCNEW_DEV(ATA_UDMA5),
|
||||
/* 1: PDC202{69,71,75,76,77} */ DECLARE_PDCNEW_DEV(ATA_UDMA6),
|
||||
};
|
||||
|
||||
/**
|
||||
* pdc202new_init_one - called when a pdc202xx is found
|
||||
* @dev: the pdc202new device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int pdc202new_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
const struct ide_port_info *d = &pdcnew_chipsets[id->driver_data];
|
||||
struct pci_dev *bridge = dev->bus->self;
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_PROMISE_20270 && bridge &&
|
||||
bridge->vendor == PCI_VENDOR_ID_DEC &&
|
||||
bridge->device == PCI_DEVICE_ID_DEC_21150) {
|
||||
struct pci_dev *dev2;
|
||||
|
||||
if (PCI_SLOT(dev->devfn) & 2)
|
||||
return -ENODEV;
|
||||
|
||||
dev2 = pdc20270_get_dev2(dev);
|
||||
|
||||
if (dev2) {
|
||||
int ret = ide_pci_init_two(dev, dev2, d, NULL);
|
||||
if (ret < 0)
|
||||
pci_dev_put(dev2);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_PROMISE_20276 && bridge &&
|
||||
bridge->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
(bridge->device == PCI_DEVICE_ID_INTEL_I960 ||
|
||||
bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) {
|
||||
printk(KERN_INFO DRV_NAME " %s: attached to I2O RAID controller,"
|
||||
" skipping\n", pci_name(dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ide_pci_init_one(dev, d, NULL);
|
||||
}
|
||||
|
||||
static void pdc202new_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
|
||||
|
||||
ide_pci_remove(dev);
|
||||
pci_dev_put(dev2);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pdc202new_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20268), 0 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20269), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20270), 0 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20271), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20275), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20276), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20277), 1 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pdc202new_pci_tbl);
|
||||
|
||||
static struct pci_driver pdc202new_pci_driver = {
|
||||
.name = "Promise_IDE",
|
||||
.id_table = pdc202new_pci_tbl,
|
||||
.probe = pdc202new_init_one,
|
||||
.remove = pdc202new_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init pdc202new_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&pdc202new_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit pdc202new_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pdc202new_pci_driver);
|
||||
}
|
||||
|
||||
module_init(pdc202new_ide_init);
|
||||
module_exit(pdc202new_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan");
|
||||
MODULE_DESCRIPTION("PCI driver module for Promise PDC20268 and higher");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,362 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2006-2007, 2009 MontaVista Software, Inc.
|
||||
* Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* Portions Copyright (C) 1999 Promise Technology, Inc.
|
||||
* Author: Frank Tiernan (frankt@promise.com)
|
||||
* Released under terms of General Public License
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "pdc202xx_old"
|
||||
|
||||
static void pdc202xx_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 drive_pci = 0x60 + (drive->dn << 2);
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
u8 AP = 0, BP = 0, CP = 0;
|
||||
u8 TA = 0, TB = 0, TC = 0;
|
||||
|
||||
pci_read_config_byte(dev, drive_pci, &AP);
|
||||
pci_read_config_byte(dev, drive_pci + 1, &BP);
|
||||
pci_read_config_byte(dev, drive_pci + 2, &CP);
|
||||
|
||||
switch(speed) {
|
||||
case XFER_UDMA_5:
|
||||
case XFER_UDMA_4: TB = 0x20; TC = 0x01; break;
|
||||
case XFER_UDMA_2: TB = 0x20; TC = 0x01; break;
|
||||
case XFER_UDMA_3:
|
||||
case XFER_UDMA_1: TB = 0x40; TC = 0x02; break;
|
||||
case XFER_UDMA_0:
|
||||
case XFER_MW_DMA_2: TB = 0x60; TC = 0x03; break;
|
||||
case XFER_MW_DMA_1: TB = 0x60; TC = 0x04; break;
|
||||
case XFER_MW_DMA_0: TB = 0xE0; TC = 0x0F; break;
|
||||
case XFER_PIO_4: TA = 0x01; TB = 0x04; break;
|
||||
case XFER_PIO_3: TA = 0x02; TB = 0x06; break;
|
||||
case XFER_PIO_2: TA = 0x03; TB = 0x08; break;
|
||||
case XFER_PIO_1: TA = 0x05; TB = 0x0C; break;
|
||||
case XFER_PIO_0:
|
||||
default: TA = 0x09; TB = 0x13; break;
|
||||
}
|
||||
|
||||
if (speed < XFER_SW_DMA_0) {
|
||||
/*
|
||||
* preserve SYNC_INT / ERDDY_EN bits while clearing
|
||||
* Prefetch_EN / IORDY_EN / PA[3:0] bits of register A
|
||||
*/
|
||||
AP &= ~0x3f;
|
||||
if (ide_pio_need_iordy(drive, speed - XFER_PIO_0))
|
||||
AP |= 0x20; /* set IORDY_EN bit */
|
||||
if (drive->media == ide_disk)
|
||||
AP |= 0x10; /* set Prefetch_EN bit */
|
||||
/* clear PB[4:0] bits of register B */
|
||||
BP &= ~0x1f;
|
||||
pci_write_config_byte(dev, drive_pci, AP | TA);
|
||||
pci_write_config_byte(dev, drive_pci + 1, BP | TB);
|
||||
} else {
|
||||
/* clear MB[2:0] bits of register B */
|
||||
BP &= ~0xe0;
|
||||
/* clear MC[3:0] bits of register C */
|
||||
CP &= ~0x0f;
|
||||
pci_write_config_byte(dev, drive_pci + 1, BP | TB);
|
||||
pci_write_config_byte(dev, drive_pci + 2, CP | TC);
|
||||
}
|
||||
}
|
||||
|
||||
static void pdc202xx_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
drive->dma_mode = drive->pio_mode;
|
||||
pdc202xx_set_mode(hwif, drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_test_irq(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long high_16 = pci_resource_start(dev, 4);
|
||||
u8 sc1d = inb(high_16 + 0x1d);
|
||||
|
||||
if (hwif->channel) {
|
||||
/*
|
||||
* bit 7: error, bit 6: interrupting,
|
||||
* bit 5: FIFO full, bit 4: FIFO empty
|
||||
*/
|
||||
return (sc1d & 0x40) ? 1 : 0;
|
||||
} else {
|
||||
/*
|
||||
* bit 3: error, bit 2: interrupting,
|
||||
* bit 1: FIFO full, bit 0: FIFO empty
|
||||
*/
|
||||
return (sc1d & 0x04) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 pdc2026x_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u16 CIS, mask = hwif->channel ? (1 << 11) : (1 << 10);
|
||||
|
||||
pci_read_config_word(dev, 0x50, &CIS);
|
||||
|
||||
return (CIS & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the control register to use the 66MHz system
|
||||
* clock for UDMA 3/4/5 mode operation when necessary.
|
||||
*
|
||||
* FIXME: this register is shared by both channels, some locking is needed
|
||||
*
|
||||
* It may also be possible to leave the 66MHz clock on
|
||||
* and readjust the timing parameters.
|
||||
*/
|
||||
static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long clock_reg = hwif->extra_base + 0x01;
|
||||
u8 clock = inb(clock_reg);
|
||||
|
||||
outb(clock | (hwif->channel ? 0x08 : 0x02), clock_reg);
|
||||
}
|
||||
|
||||
static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif)
|
||||
{
|
||||
unsigned long clock_reg = hwif->extra_base + 0x01;
|
||||
u8 clock = inb(clock_reg);
|
||||
|
||||
outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg);
|
||||
}
|
||||
|
||||
static void pdc2026x_init_hwif(ide_hwif_t *hwif)
|
||||
{
|
||||
pdc_old_disable_66MHz_clock(hwif);
|
||||
}
|
||||
|
||||
static void pdc202xx_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->current_speed > XFER_UDMA_2)
|
||||
pdc_old_enable_66MHz_clock(drive->hwif);
|
||||
if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) {
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct request *rq = hwif->rq;
|
||||
unsigned long high_16 = hwif->extra_base - 16;
|
||||
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
|
||||
u32 word_count = 0;
|
||||
u8 clock = inb(high_16 + 0x11);
|
||||
|
||||
outb(clock | (hwif->channel ? 0x08 : 0x02), high_16 + 0x11);
|
||||
word_count = (blk_rq_sectors(rq) << 8);
|
||||
word_count = (rq_data_dir(rq) == READ) ?
|
||||
word_count | 0x05000000 :
|
||||
word_count | 0x06000000;
|
||||
outl(word_count, atapi_reg);
|
||||
}
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static int pdc202xx_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->media != ide_disk || (drive->dev_flags & IDE_DFLAG_LBA48)) {
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned long high_16 = hwif->extra_base - 16;
|
||||
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
|
||||
u8 clock = 0;
|
||||
|
||||
outl(0, atapi_reg); /* zero out extra */
|
||||
clock = inb(high_16 + 0x11);
|
||||
outb(clock & ~(hwif->channel ? 0x08:0x02), high_16 + 0x11);
|
||||
}
|
||||
if (drive->current_speed > XFER_UDMA_2)
|
||||
pdc_old_disable_66MHz_clock(drive->hwif);
|
||||
return ide_dma_end(drive);
|
||||
}
|
||||
|
||||
static int init_chipset_pdc202xx(struct pci_dev *dev)
|
||||
{
|
||||
unsigned long dmabase = pci_resource_start(dev, 4);
|
||||
u8 udma_speed_flag = 0, primary_mode = 0, secondary_mode = 0;
|
||||
|
||||
if (dmabase == 0)
|
||||
goto out;
|
||||
|
||||
udma_speed_flag = inb(dmabase | 0x1f);
|
||||
primary_mode = inb(dmabase | 0x1a);
|
||||
secondary_mode = inb(dmabase | 0x1b);
|
||||
printk(KERN_INFO "%s: (U)DMA Burst Bit %sABLED " \
|
||||
"Primary %s Mode " \
|
||||
"Secondary %s Mode.\n", pci_name(dev),
|
||||
(udma_speed_flag & 1) ? "EN" : "DIS",
|
||||
(primary_mode & 1) ? "MASTER" : "PCI",
|
||||
(secondary_mode & 1) ? "MASTER" : "PCI" );
|
||||
|
||||
if (!(udma_speed_flag & 1)) {
|
||||
printk(KERN_INFO "%s: FORCING BURST BIT 0x%02x->0x%02x ",
|
||||
pci_name(dev), udma_speed_flag,
|
||||
(udma_speed_flag|1));
|
||||
outb(udma_speed_flag | 1, dmabase | 0x1f);
|
||||
printk("%sACTIVE\n", (inb(dmabase | 0x1f) & 1) ? "" : "IN");
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pdc202ata4_fixup_irq(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
|
||||
u8 irq = 0, irq2 = 0;
|
||||
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq);
|
||||
/* 0xbc */
|
||||
pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2);
|
||||
if (irq != irq2) {
|
||||
pci_write_config_byte(dev,
|
||||
(PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */
|
||||
printk(KERN_INFO "%s %s: PCI config space interrupt "
|
||||
"mirror fixed\n", name, pci_name(dev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define IDE_HFLAGS_PDC202XX \
|
||||
(IDE_HFLAG_ERROR_STOPS_FIFO | \
|
||||
IDE_HFLAG_OFF_BOARD)
|
||||
|
||||
static const struct ide_port_ops pdc20246_port_ops = {
|
||||
.set_pio_mode = pdc202xx_set_pio_mode,
|
||||
.set_dma_mode = pdc202xx_set_mode,
|
||||
.test_irq = pdc202xx_test_irq,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops pdc2026x_port_ops = {
|
||||
.set_pio_mode = pdc202xx_set_pio_mode,
|
||||
.set_dma_mode = pdc202xx_set_mode,
|
||||
.test_irq = pdc202xx_test_irq,
|
||||
.cable_detect = pdc2026x_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops pdc2026x_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = pdc202xx_dma_start,
|
||||
.dma_end = pdc202xx_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
#define DECLARE_PDC2026X_DEV(udma, sectors) \
|
||||
{ \
|
||||
.name = DRV_NAME, \
|
||||
.init_chipset = init_chipset_pdc202xx, \
|
||||
.init_hwif = pdc2026x_init_hwif, \
|
||||
.port_ops = &pdc2026x_port_ops, \
|
||||
.dma_ops = &pdc2026x_dma_ops, \
|
||||
.host_flags = IDE_HFLAGS_PDC202XX, \
|
||||
.pio_mask = ATA_PIO4, \
|
||||
.mwdma_mask = ATA_MWDMA2, \
|
||||
.udma_mask = udma, \
|
||||
.max_sectors = sectors, \
|
||||
}
|
||||
|
||||
static const struct ide_port_info pdc202xx_chipsets[] = {
|
||||
{ /* 0: PDC20246 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_pdc202xx,
|
||||
.port_ops = &pdc20246_port_ops,
|
||||
.dma_ops = &sff_dma_ops,
|
||||
.host_flags = IDE_HFLAGS_PDC202XX,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA2,
|
||||
},
|
||||
|
||||
/* 1: PDC2026{2,3} */
|
||||
DECLARE_PDC2026X_DEV(ATA_UDMA4, 0),
|
||||
/* 2: PDC2026{5,7}: UDMA5, limit LBA48 requests to 256 sectors */
|
||||
DECLARE_PDC2026X_DEV(ATA_UDMA5, 256),
|
||||
};
|
||||
|
||||
/**
|
||||
* pdc202xx_init_one - called when a PDC202xx is found
|
||||
* @dev: the pdc202xx device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int pdc202xx_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
const struct ide_port_info *d;
|
||||
u8 idx = id->driver_data;
|
||||
|
||||
d = &pdc202xx_chipsets[idx];
|
||||
|
||||
if (idx < 2)
|
||||
pdc202ata4_fixup_irq(dev, d->name);
|
||||
|
||||
if (dev->vendor == PCI_DEVICE_ID_PROMISE_20265) {
|
||||
struct pci_dev *bridge = dev->bus->self;
|
||||
|
||||
if (bridge &&
|
||||
bridge->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
(bridge->device == PCI_DEVICE_ID_INTEL_I960 ||
|
||||
bridge->device == PCI_DEVICE_ID_INTEL_I960RM)) {
|
||||
printk(KERN_INFO DRV_NAME " %s: skipping Promise "
|
||||
"PDC20265 attached to I2O RAID controller\n",
|
||||
pci_name(dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
return ide_pci_init_one(dev, d, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id pdc202xx_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20246), 0 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20262), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20263), 1 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20265), 2 },
|
||||
{ PCI_VDEVICE(PROMISE, PCI_DEVICE_ID_PROMISE_20267), 2 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pdc202xx_pci_tbl);
|
||||
|
||||
static struct pci_driver pdc202xx_pci_driver = {
|
||||
.name = "Promise_Old_IDE",
|
||||
.id_table = pdc202xx_pci_tbl,
|
||||
.probe = pdc202xx_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init pdc202xx_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&pdc202xx_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit pdc202xx_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pdc202xx_pci_driver);
|
||||
}
|
||||
|
||||
module_init(pdc202xx_ide_init);
|
||||
module_exit(pdc202xx_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Frank Tiernan, Bartlomiej Zolnierkiewicz");
|
||||
MODULE_DESCRIPTION("PCI driver module for older Promise IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,476 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2003 Red Hat
|
||||
* Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Documentation:
|
||||
*
|
||||
* Publicly available from Intel web site. Errata documentation
|
||||
* is also publicly available. As an aide to anyone hacking on this
|
||||
* driver the list of errata that are relevant is below.going back to
|
||||
* PIIX4. Older device documentation is now a bit tricky to find.
|
||||
*
|
||||
* Errata of note:
|
||||
*
|
||||
* Unfixable
|
||||
* PIIX4 errata #9 - Only on ultra obscure hw
|
||||
* ICH3 errata #13 - Not observed to affect real hw
|
||||
* by Intel
|
||||
*
|
||||
* Things we must deal with
|
||||
* PIIX4 errata #10 - BM IDE hang with non UDMA
|
||||
* (must stop/start dma to recover)
|
||||
* 440MX errata #15 - As PIIX4 errata #10
|
||||
* PIIX4 errata #15 - Must not read control registers
|
||||
* during a PIO transfer
|
||||
* 440MX errata #13 - As PIIX4 errata #15
|
||||
* ICH2 errata #21 - DMA mode 0 doesn't work right
|
||||
* ICH0/1 errata #55 - As ICH2 errata #21
|
||||
* ICH2 spec c #9 - Extra operations needed to handle
|
||||
* drive hotswap [NOT YET SUPPORTED]
|
||||
* ICH2 spec c #20 - IDE PRD must not cross a 64K boundary
|
||||
* and must be dword aligned
|
||||
* ICH2 spec c #24 - UDMA mode 4,5 t85/86 should be 6ns not 3.3
|
||||
*
|
||||
* Should have been BIOS fixed:
|
||||
* 450NX: errata #19 - DMA hangs on old 450NX
|
||||
* 450NX: errata #20 - DMA hangs on old 450NX
|
||||
* 450NX: errata #25 - Corruption with DMA on old 450NX
|
||||
* ICH3 errata #15 - IDE deadlock under high load
|
||||
* (BIOS must set dev 31 fn 0 bit 23)
|
||||
* ICH3 errata #18 - Don't use native mode
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "piix"
|
||||
|
||||
static int no_piix_dma;
|
||||
|
||||
/**
|
||||
* piix_set_pio_mode - set host controller for PIO mode
|
||||
* @port: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Set the interface PIO mode based upon the settings done by AMI BIOS.
|
||||
*/
|
||||
|
||||
static void piix_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = hwif->channel ? 0x42 : 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
static DEFINE_SPINLOCK(tune_lock);
|
||||
int control = 0;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
/* ISP RTC */
|
||||
static const u8 timings[][2]= {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
/*
|
||||
* Master vs slave is synchronized above us but the slave register is
|
||||
* shared by the two hwifs so the corner case of two slave timeouts in
|
||||
* parallel must be locked.
|
||||
*/
|
||||
spin_lock_irqsave(&tune_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media == ide_disk)
|
||||
control |= 4; /* Prefetch, post write */
|
||||
if (ide_pio_need_iordy(drive, pio))
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1) {
|
||||
/* Set PPE, IE and TIME */
|
||||
master_data |= control << 4;
|
||||
}
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data &= hwif->channel ? 0x0f : 0xf0;
|
||||
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) <<
|
||||
(hwif->channel ? 4 : 0);
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1) {
|
||||
/* enable PPE, IE and TIME */
|
||||
master_data |= control;
|
||||
}
|
||||
master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&tune_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Set a PIIX host controller to the desired DMA mode. This involves
|
||||
* programming the right timing data into the PCI configuration space.
|
||||
*/
|
||||
|
||||
static void piix_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 maslave = hwif->channel ? 0x42 : 0x40;
|
||||
int a_speed = 3 << (drive->dn * 4);
|
||||
int u_flag = 1 << drive->dn;
|
||||
int v_flag = 0x01 << drive->dn;
|
||||
int w_flag = 0x10 << drive->dn;
|
||||
int u_speed = 0;
|
||||
int sitre;
|
||||
u16 reg4042, reg4a;
|
||||
u8 reg48, reg54, reg55;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
sitre = (reg4042 & 0x4000) ? 1 : 0;
|
||||
pci_read_config_byte(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_read_config_byte(dev, 0x54, ®54);
|
||||
pci_read_config_byte(dev, 0x55, ®55);
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
u8 udma = speed - XFER_UDMA_0;
|
||||
|
||||
u_speed = min_t(u8, 2 - (udma & 1), udma) << (drive->dn * 4);
|
||||
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_byte(dev, 0x48, reg48 | u_flag);
|
||||
if (speed == XFER_UDMA_5) {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55|w_flag);
|
||||
} else {
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
}
|
||||
if ((reg4a & a_speed) != u_speed)
|
||||
pci_write_config_word(dev, 0x4a, (reg4a & ~a_speed) | u_speed);
|
||||
if (speed > XFER_UDMA_2) {
|
||||
if (!(reg54 & v_flag))
|
||||
pci_write_config_byte(dev, 0x54, reg54 | v_flag);
|
||||
} else
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
} else {
|
||||
const u8 mwdma_to_pio[] = { 0, 3, 4 };
|
||||
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_byte(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
if (reg54 & v_flag)
|
||||
pci_write_config_byte(dev, 0x54, reg54 & ~v_flag);
|
||||
if (reg55 & w_flag)
|
||||
pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag);
|
||||
|
||||
if (speed >= XFER_MW_DMA_0)
|
||||
drive->pio_mode =
|
||||
mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
|
||||
else
|
||||
drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
|
||||
|
||||
piix_set_pio_mode(hwif, drive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_ich - set up the ICH chipset
|
||||
* @dev: PCI device to set up
|
||||
*
|
||||
* Initialize the PCI device as required. For the ICH this turns
|
||||
* out to be nice and simple.
|
||||
*/
|
||||
|
||||
static int init_chipset_ich(struct pci_dev *dev)
|
||||
{
|
||||
u32 extra = 0;
|
||||
|
||||
pci_read_config_dword(dev, 0x54, &extra);
|
||||
pci_write_config_dword(dev, 0x54, extra | 0x400);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ich_clear_irq - clear BMDMA status
|
||||
* @drive: IDE drive
|
||||
*
|
||||
* ICHx contollers set DMA INTR no matter DMA or PIO.
|
||||
* BMDMA status might need to be cleared even for
|
||||
* PIO interrupts to prevent spurious/lost IRQ.
|
||||
*/
|
||||
static void ich_clear_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 dma_stat;
|
||||
|
||||
/*
|
||||
* ide_dma_end() needs BMDMA status for error checking.
|
||||
* So, skip clearing BMDMA status here and leave it
|
||||
* to ide_dma_end() if this is DMA interrupt.
|
||||
*/
|
||||
if (drive->waiting_for_dma || hwif->dma_base == 0)
|
||||
return;
|
||||
|
||||
/* clear the INTR & ERROR bits */
|
||||
dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS);
|
||||
/* Should we force the bit as well ? */
|
||||
outb(dma_stat, hwif->dma_base + ATA_DMA_STATUS);
|
||||
}
|
||||
|
||||
struct ich_laptop {
|
||||
u16 device;
|
||||
u16 subvendor;
|
||||
u16 subdevice;
|
||||
};
|
||||
|
||||
/*
|
||||
* List of laptops that use short cables rather than 80 wire
|
||||
*/
|
||||
|
||||
static const struct ich_laptop ich_laptop[] = {
|
||||
/* devid, subvendor, subdev */
|
||||
{ 0x27DF, 0x1025, 0x0102 }, /* ICH7 on Acer 5602aWLMi */
|
||||
{ 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */
|
||||
{ 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */
|
||||
{ 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */
|
||||
{ 0x27DF, 0x103C, 0x30A1 }, /* ICH7 on HP Compaq nc2400 */
|
||||
{ 0x27DF, 0x1071, 0xD221 }, /* ICH7 on Hercules EC-900 */
|
||||
{ 0x24CA, 0x1025, 0x0061 }, /* ICH4 on Acer Aspire 2023WLMi */
|
||||
{ 0x24CA, 0x1025, 0x003d }, /* ICH4 on ACER TM290 */
|
||||
{ 0x266F, 0x1025, 0x0066 }, /* ICH6 on ACER Aspire 1694WLMi */
|
||||
{ 0x2653, 0x1043, 0x82D8 }, /* ICH6M on Asus Eee 701 */
|
||||
{ 0x27df, 0x104d, 0x900e }, /* ICH7 on Sony TZ-90 */
|
||||
/* end marker */
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static u8 piix_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
const struct ich_laptop *lap = &ich_laptop[0];
|
||||
u8 reg54h = 0, mask = hwif->channel ? 0xc0 : 0x30;
|
||||
|
||||
/* check for specials */
|
||||
while (lap->device) {
|
||||
if (lap->device == pdev->device &&
|
||||
lap->subvendor == pdev->subsystem_vendor &&
|
||||
lap->subdevice == pdev->subsystem_device) {
|
||||
return ATA_CBL_PATA40_SHORT;
|
||||
}
|
||||
lap++;
|
||||
}
|
||||
|
||||
pci_read_config_byte(pdev, 0x54, ®54h);
|
||||
|
||||
return (reg54h & mask) ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_hwif_piix - fill in the hwif for the PIIX
|
||||
* @hwif: IDE interface
|
||||
*
|
||||
* Set up the ide_hwif_t for the PIIX interface according to the
|
||||
* capabilities of the hardware.
|
||||
*/
|
||||
|
||||
static void init_hwif_piix(ide_hwif_t *hwif)
|
||||
{
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
if (no_piix_dma)
|
||||
hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops piix_port_ops = {
|
||||
.set_pio_mode = piix_set_pio_mode,
|
||||
.set_dma_mode = piix_set_dma_mode,
|
||||
.cable_detect = piix_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops ich_port_ops = {
|
||||
.set_pio_mode = piix_set_pio_mode,
|
||||
.set_dma_mode = piix_set_dma_mode,
|
||||
.clear_irq = ich_clear_irq,
|
||||
.cable_detect = piix_cable_detect,
|
||||
};
|
||||
|
||||
#define DECLARE_PIIX_DEV(udma) \
|
||||
{ \
|
||||
.name = DRV_NAME, \
|
||||
.init_hwif = init_hwif_piix, \
|
||||
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
|
||||
.port_ops = &piix_port_ops, \
|
||||
.pio_mask = ATA_PIO4, \
|
||||
.swdma_mask = ATA_SWDMA2_ONLY, \
|
||||
.mwdma_mask = ATA_MWDMA12_ONLY, \
|
||||
.udma_mask = udma, \
|
||||
}
|
||||
|
||||
#define DECLARE_ICH_DEV(mwdma, udma) \
|
||||
{ \
|
||||
.name = DRV_NAME, \
|
||||
.init_chipset = init_chipset_ich, \
|
||||
.init_hwif = init_hwif_piix, \
|
||||
.enablebits = {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, \
|
||||
.port_ops = &ich_port_ops, \
|
||||
.pio_mask = ATA_PIO4, \
|
||||
.swdma_mask = ATA_SWDMA2_ONLY, \
|
||||
.mwdma_mask = mwdma, \
|
||||
.udma_mask = udma, \
|
||||
}
|
||||
|
||||
static const struct ide_port_info piix_pci_info[] = {
|
||||
/* 0: MPIIX */
|
||||
{ /*
|
||||
* MPIIX actually has only a single IDE channel mapped to
|
||||
* the primary or secondary ports depending on the value
|
||||
* of the bit 14 of the IDETIM register at offset 0x6c
|
||||
*/
|
||||
.name = DRV_NAME,
|
||||
.enablebits = {{0x6d,0xc0,0x80}, {0x6d,0xc0,0xc0}},
|
||||
.host_flags = IDE_HFLAG_ISA_PORTS | IDE_HFLAG_NO_DMA,
|
||||
.pio_mask = ATA_PIO4,
|
||||
/* This is a painful system best to let it self tune for now */
|
||||
},
|
||||
/* 1: PIIXa/PIIXb/PIIX3 */
|
||||
DECLARE_PIIX_DEV(0x00), /* no udma */
|
||||
/* 2: PIIX4 */
|
||||
DECLARE_PIIX_DEV(ATA_UDMA2),
|
||||
/* 3: ICH0 */
|
||||
DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA2),
|
||||
/* 4: ICH */
|
||||
DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA4),
|
||||
/* 5: PIIX4 */
|
||||
DECLARE_PIIX_DEV(ATA_UDMA4),
|
||||
/* 6: ICH[2-6]/ICH[2-3]M/C-ICH/ICH5-SATA/ESB2/ICH8M */
|
||||
DECLARE_ICH_DEV(ATA_MWDMA12_ONLY, ATA_UDMA5),
|
||||
/* 7: ICH7/7-R, no MWDMA1 */
|
||||
DECLARE_ICH_DEV(ATA_MWDMA2_ONLY, ATA_UDMA5),
|
||||
};
|
||||
|
||||
/**
|
||||
* piix_init_one - called when a PIIX is found
|
||||
* @dev: the piix device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int piix_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &piix_pci_info[id->driver_data], NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_check_450nx - Check for problem 450NX setup
|
||||
*
|
||||
* Check for the present of 450NX errata #19 and errata #25. If
|
||||
* they are found, disable use of DMA IDE
|
||||
*/
|
||||
|
||||
static void piix_check_450nx(void)
|
||||
{
|
||||
struct pci_dev *pdev = NULL;
|
||||
u16 cfg;
|
||||
while((pdev=pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev))!=NULL)
|
||||
{
|
||||
/* Look for 450NX PXB. Check for problem configurations
|
||||
A PCI quirk checks bit 6 already */
|
||||
pci_read_config_word(pdev, 0x41, &cfg);
|
||||
/* Only on the original revision: IDE DMA can hang */
|
||||
if (pdev->revision == 0x00)
|
||||
no_piix_dma = 1;
|
||||
/* On all revisions below 5 PXB bus lock must be disabled for IDE */
|
||||
else if (cfg & (1<<14) && pdev->revision < 5)
|
||||
no_piix_dma = 2;
|
||||
}
|
||||
if(no_piix_dma)
|
||||
printk(KERN_WARNING DRV_NAME ": 450NX errata present, disabling IDE DMA.\n");
|
||||
if(no_piix_dma == 2)
|
||||
printk(KERN_WARNING DRV_NAME ": A BIOS update may resolve this.\n");
|
||||
}
|
||||
|
||||
static const struct pci_device_id piix_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_0), 1 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371FB_1), 1 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371MX), 0 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371SB_1), 1 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82371AB), 2 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AB_1), 3 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82443MX_1), 2 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801AA_1), 4 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82372FB_1), 5 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82451NX), 2 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_9), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801BA_8), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_10), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801CA_11), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_11), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_11), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801E_11), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_10), 6 },
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801EB_1), 6 },
|
||||
#endif
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB_2), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH6_19), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH7_21), 7 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_82801DB_1), 6 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ESB2_18), 7 },
|
||||
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICH8_6), 6 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, piix_pci_tbl);
|
||||
|
||||
static struct pci_driver piix_pci_driver = {
|
||||
.name = "PIIX_IDE",
|
||||
.id_table = piix_pci_tbl,
|
||||
.probe = piix_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init piix_ide_init(void)
|
||||
{
|
||||
piix_check_450nx();
|
||||
return ide_pci_register_driver(&piix_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit piix_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&piix_pci_driver);
|
||||
}
|
||||
|
||||
module_init(piix_ide_init);
|
||||
module_exit(piix_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Andrzej Krzysztofowicz");
|
||||
MODULE_DESCRIPTION("PCI driver module for Intel PIIX IDE");
|
||||
MODULE_LICENSE("GPL");
|
1703
drivers/ide/pmac.c
1703
drivers/ide/pmac.c
File diff suppressed because it is too large
Load Diff
@ -1,446 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1996-2001 Linus Torvalds & author (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Version 0.03 Cleaned auto-tune, added probe
|
||||
* Version 0.04 Added second channel tuning
|
||||
* Version 0.05 Enhanced tuning ; added qd6500 support
|
||||
* Version 0.06 Added dos driver's list
|
||||
* Version 0.07 Second channel bug fix
|
||||
*
|
||||
* QDI QD6500/QD6580 EIDE controller fast support
|
||||
*
|
||||
* To activate controller support, use "ide0=qd65xx"
|
||||
*/
|
||||
|
||||
/*
|
||||
* Rewritten from the work of Colten Edwards <pje120@cs.usask.ca> by
|
||||
* Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "qd65xx"
|
||||
|
||||
#include "qd65xx.h"
|
||||
|
||||
/*
|
||||
* I/O ports are 0x30-0x31 (and 0x32-0x33 for qd6580)
|
||||
* or 0xb0-0xb1 (and 0xb2-0xb3 for qd6580)
|
||||
* -- qd6500 is a single IDE interface
|
||||
* -- qd6580 is a dual IDE interface
|
||||
*
|
||||
* More research on qd6580 being done by willmore@cig.mot.com (David)
|
||||
* More Information given by Petr Soucek (petr@ryston.cz)
|
||||
* http://www.ryston.cz/petr/vlb
|
||||
*/
|
||||
|
||||
/*
|
||||
* base: Timer1
|
||||
*
|
||||
*
|
||||
* base+0x01: Config (R/O)
|
||||
*
|
||||
* bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170 (only useful for qd6500)
|
||||
* bit 1: qd65xx baseport: 1 = 0xb0 ; 0 = 0x30
|
||||
* bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz
|
||||
* bit 3: qd6500: 1 = disabled, 0 = enabled
|
||||
* qd6580: 1
|
||||
* upper nibble:
|
||||
* qd6500: 1100
|
||||
* qd6580: either 1010 or 0101
|
||||
*
|
||||
*
|
||||
* base+0x02: Timer2 (qd6580 only)
|
||||
*
|
||||
*
|
||||
* base+0x03: Control (qd6580 only)
|
||||
*
|
||||
* bits 0-3 must always be set 1
|
||||
* bit 4 must be set 1, but is set 0 by dos driver while measuring vlb clock
|
||||
* bit 0 : 1 = Only primary port enabled : channel 0 for hda, channel 1 for hdb
|
||||
* 0 = Primary and Secondary ports enabled : channel 0 for hda & hdb
|
||||
* channel 1 for hdc & hdd
|
||||
* bit 1 : 1 = only disks on primary port
|
||||
* 0 = disks & ATAPI devices on primary port
|
||||
* bit 2-4 : always 0
|
||||
* bit 5 : status, but of what ?
|
||||
* bit 6 : always set 1 by dos driver
|
||||
* bit 7 : set 1 for non-ATAPI devices on primary port
|
||||
* (maybe read-ahead and post-write buffer ?)
|
||||
*/
|
||||
|
||||
static int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */
|
||||
|
||||
/*
|
||||
* qd65xx_select:
|
||||
*
|
||||
* This routine is invoked to prepare for access to a given drive.
|
||||
*/
|
||||
|
||||
static void qd65xx_dev_select(ide_drive_t *drive)
|
||||
{
|
||||
u8 index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) |
|
||||
(QD_TIMREG(drive) & 0x02);
|
||||
|
||||
if (timings[index] != QD_TIMING(drive))
|
||||
outb(timings[index] = QD_TIMING(drive), QD_TIMREG(drive));
|
||||
|
||||
outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr);
|
||||
}
|
||||
|
||||
/*
|
||||
* qd6500_compute_timing
|
||||
*
|
||||
* computes the timing value where
|
||||
* lower nibble represents active time, in count of VLB clocks
|
||||
* upper nibble represents recovery time, in count of VLB clocks
|
||||
*/
|
||||
|
||||
static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time)
|
||||
{
|
||||
int clk = ide_vlb_clk ? ide_vlb_clk : 50;
|
||||
u8 act_cyc, rec_cyc;
|
||||
|
||||
if (clk <= 33) {
|
||||
act_cyc = 9 - IDE_IN(active_time * clk / 1000 + 1, 2, 9);
|
||||
rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 0, 15);
|
||||
} else {
|
||||
act_cyc = 8 - IDE_IN(active_time * clk / 1000 + 1, 1, 8);
|
||||
rec_cyc = 18 - IDE_IN(recovery_time * clk / 1000 + 1, 3, 18);
|
||||
}
|
||||
|
||||
return (rec_cyc << 4) | 0x08 | act_cyc;
|
||||
}
|
||||
|
||||
/*
|
||||
* qd6580_compute_timing
|
||||
*
|
||||
* idem for qd6580
|
||||
*/
|
||||
|
||||
static u8 qd6580_compute_timing (int active_time, int recovery_time)
|
||||
{
|
||||
int clk = ide_vlb_clk ? ide_vlb_clk : 50;
|
||||
u8 act_cyc, rec_cyc;
|
||||
|
||||
act_cyc = 17 - IDE_IN(active_time * clk / 1000 + 1, 2, 17);
|
||||
rec_cyc = 15 - IDE_IN(recovery_time * clk / 1000 + 1, 2, 15);
|
||||
|
||||
return (rec_cyc << 4) | act_cyc;
|
||||
}
|
||||
|
||||
/*
|
||||
* qd_find_disk_type
|
||||
*
|
||||
* tries to find timing from dos driver's table
|
||||
*/
|
||||
|
||||
static int qd_find_disk_type (ide_drive_t *drive,
|
||||
int *active_time, int *recovery_time)
|
||||
{
|
||||
struct qd65xx_timing_s *p;
|
||||
char *m = (char *)&drive->id[ATA_ID_PROD];
|
||||
char model[ATA_ID_PROD_LEN];
|
||||
|
||||
if (*m == 0)
|
||||
return 0;
|
||||
|
||||
strncpy(model, m, ATA_ID_PROD_LEN);
|
||||
ide_fixstring(model, ATA_ID_PROD_LEN, 1); /* byte-swap */
|
||||
|
||||
for (p = qd65xx_timing ; p->offset != -1 ; p++) {
|
||||
if (!strncmp(p->model, model+p->offset, 4)) {
|
||||
printk(KERN_DEBUG "%s: listed !\n", drive->name);
|
||||
*active_time = p->active;
|
||||
*recovery_time = p->recovery;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* qd_set_timing:
|
||||
*
|
||||
* records the timing
|
||||
*/
|
||||
|
||||
static void qd_set_timing (ide_drive_t *drive, u8 timing)
|
||||
{
|
||||
unsigned long data = (unsigned long)ide_get_drivedata(drive);
|
||||
|
||||
data &= 0xff00;
|
||||
data |= timing;
|
||||
ide_set_drivedata(drive, (void *)data);
|
||||
|
||||
printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
|
||||
}
|
||||
|
||||
static void qd6500_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
u16 *id = drive->id;
|
||||
int active_time = 175;
|
||||
int recovery_time = 415; /* worst case values from the dos driver */
|
||||
|
||||
/* FIXME: use drive->pio_mode value */
|
||||
if (!qd_find_disk_type(drive, &active_time, &recovery_time) &&
|
||||
(id[ATA_ID_OLD_PIO_MODES] & 0xff) && (id[ATA_ID_FIELD_VALID] & 2) &&
|
||||
id[ATA_ID_EIDE_PIO] >= 240) {
|
||||
printk(KERN_INFO "%s: PIO mode%d\n", drive->name,
|
||||
id[ATA_ID_OLD_PIO_MODES] & 0xff);
|
||||
active_time = 110;
|
||||
recovery_time = drive->id[ATA_ID_EIDE_PIO] - 120;
|
||||
}
|
||||
|
||||
qd_set_timing(drive, qd6500_compute_timing(drive->hwif,
|
||||
active_time, recovery_time));
|
||||
}
|
||||
|
||||
static void qd6580_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
|
||||
unsigned int cycle_time;
|
||||
int active_time = 175;
|
||||
int recovery_time = 415; /* worst case values from the dos driver */
|
||||
u8 base = (hwif->config_data & 0xff00) >> 8;
|
||||
|
||||
if (drive->id && !qd_find_disk_type(drive, &active_time, &recovery_time)) {
|
||||
cycle_time = ide_pio_cycle_time(drive, pio);
|
||||
|
||||
switch (pio) {
|
||||
case 0: break;
|
||||
case 3:
|
||||
if (cycle_time >= 110) {
|
||||
active_time = 86;
|
||||
recovery_time = cycle_time - 102;
|
||||
} else
|
||||
printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name);
|
||||
break;
|
||||
case 4:
|
||||
if (cycle_time >= 69) {
|
||||
active_time = 70;
|
||||
recovery_time = cycle_time - 61;
|
||||
} else
|
||||
printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name);
|
||||
break;
|
||||
default:
|
||||
if (cycle_time >= 180) {
|
||||
active_time = 110;
|
||||
recovery_time = cycle_time - 120;
|
||||
} else {
|
||||
active_time = t->active;
|
||||
recovery_time = cycle_time - active_time;
|
||||
}
|
||||
}
|
||||
printk(KERN_INFO "%s: PIO mode%d\n", drive->name,pio);
|
||||
}
|
||||
|
||||
if (!hwif->channel && drive->media != ide_disk) {
|
||||
outb(0x5f, QD_CONTROL_PORT);
|
||||
printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO "
|
||||
"and post-write buffer on %s.\n",
|
||||
drive->name, hwif->name);
|
||||
}
|
||||
|
||||
qd_set_timing(drive, qd6580_compute_timing(active_time, recovery_time));
|
||||
}
|
||||
|
||||
/*
|
||||
* qd_testreg
|
||||
*
|
||||
* tests if the given port is a register
|
||||
*/
|
||||
|
||||
static int __init qd_testreg(int port)
|
||||
{
|
||||
unsigned long flags;
|
||||
u8 savereg, readreg;
|
||||
|
||||
local_irq_save(flags);
|
||||
savereg = inb_p(port);
|
||||
outb_p(QD_TESTVAL, port); /* safe value */
|
||||
readreg = inb_p(port);
|
||||
outb(savereg, port);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (savereg == QD_TESTVAL) {
|
||||
printk(KERN_ERR "Outch ! the probe for qd65xx isn't reliable !\n");
|
||||
printk(KERN_ERR "Please contact maintainers to tell about your hardware\n");
|
||||
printk(KERN_ERR "Assuming qd65xx is not present.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (readreg != QD_TESTVAL);
|
||||
}
|
||||
|
||||
static void __init qd6500_init_dev(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 base = (hwif->config_data & 0xff00) >> 8;
|
||||
u8 config = QD_CONFIG(hwif);
|
||||
|
||||
ide_set_drivedata(drive, (void *)QD6500_DEF_DATA);
|
||||
}
|
||||
|
||||
static void __init qd6580_init_dev(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned long t1, t2;
|
||||
u8 base = (hwif->config_data & 0xff00) >> 8;
|
||||
u8 config = QD_CONFIG(hwif);
|
||||
|
||||
if (hwif->host_flags & IDE_HFLAG_SINGLE) {
|
||||
t1 = QD6580_DEF_DATA;
|
||||
t2 = QD6580_DEF_DATA2;
|
||||
} else
|
||||
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
|
||||
|
||||
ide_set_drivedata(drive, (void *)((drive->dn & 1) ? t2 : t1));
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops qd65xx_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = qd65xx_dev_select,
|
||||
.tf_load = ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = ide_input_data,
|
||||
.output_data = ide_output_data,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops qd6500_port_ops = {
|
||||
.init_dev = qd6500_init_dev,
|
||||
.set_pio_mode = qd6500_set_pio_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops qd6580_port_ops = {
|
||||
.init_dev = qd6580_init_dev,
|
||||
.set_pio_mode = qd6580_set_pio_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info qd65xx_port_info __initconst = {
|
||||
.name = DRV_NAME,
|
||||
.tp_ops = &qd65xx_tp_ops,
|
||||
.chipset = ide_qd65xx,
|
||||
.host_flags = IDE_HFLAG_IO_32BIT |
|
||||
IDE_HFLAG_NO_DMA,
|
||||
.pio_mask = ATA_PIO4,
|
||||
};
|
||||
|
||||
/*
|
||||
* qd_probe:
|
||||
*
|
||||
* looks at the specified baseport, and if qd found, registers & initialises it
|
||||
* return 1 if another qd may be probed
|
||||
*/
|
||||
|
||||
static int __init qd_probe(int base)
|
||||
{
|
||||
int rc;
|
||||
u8 config, unit, control;
|
||||
struct ide_port_info d = qd65xx_port_info;
|
||||
|
||||
config = inb(QD_CONFIG_PORT);
|
||||
|
||||
if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) )
|
||||
return -ENODEV;
|
||||
|
||||
unit = ! (config & QD_CONFIG_IDE_BASEPORT);
|
||||
|
||||
if (unit)
|
||||
d.host_flags |= IDE_HFLAG_QD_2ND_PORT;
|
||||
|
||||
switch (config & 0xf0) {
|
||||
case QD_CONFIG_QD6500:
|
||||
if (qd_testreg(base))
|
||||
return -ENODEV; /* bad register */
|
||||
|
||||
if (config & QD_CONFIG_DISABLED) {
|
||||
printk(KERN_WARNING "qd6500 is disabled !\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
printk(KERN_NOTICE "qd6500 at %#x\n", base);
|
||||
printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n",
|
||||
config, QD_ID3);
|
||||
|
||||
d.port_ops = &qd6500_port_ops;
|
||||
d.host_flags |= IDE_HFLAG_SINGLE;
|
||||
break;
|
||||
case QD_CONFIG_QD6580_A:
|
||||
case QD_CONFIG_QD6580_B:
|
||||
if (qd_testreg(base) || qd_testreg(base + 0x02))
|
||||
return -ENODEV; /* bad registers */
|
||||
|
||||
control = inb(QD_CONTROL_PORT);
|
||||
|
||||
printk(KERN_NOTICE "qd6580 at %#x\n", base);
|
||||
printk(KERN_DEBUG "qd6580: config=%#x, control=%#x, ID3=%u\n",
|
||||
config, control, QD_ID3);
|
||||
|
||||
outb(QD_DEF_CONTR, QD_CONTROL_PORT);
|
||||
|
||||
d.port_ops = &qd6580_port_ops;
|
||||
if (control & QD_CONTR_SEC_DISABLED)
|
||||
d.host_flags |= IDE_HFLAG_SINGLE;
|
||||
|
||||
printk(KERN_INFO "qd6580: %s IDE board\n",
|
||||
(control & QD_CONTR_SEC_DISABLED) ? "single" : "dual");
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
rc = ide_legacy_device_add(&d, (base << 8) | config);
|
||||
|
||||
if (d.host_flags & IDE_HFLAG_SINGLE)
|
||||
return (rc == 0) ? 1 : rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool probe_qd65xx;
|
||||
|
||||
module_param_named(probe, probe_qd65xx, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for QD65xx chipsets");
|
||||
|
||||
static int __init qd65xx_init(void)
|
||||
{
|
||||
int rc1, rc2 = -ENODEV;
|
||||
|
||||
if (probe_qd65xx == 0)
|
||||
return -ENODEV;
|
||||
|
||||
rc1 = qd_probe(0x30);
|
||||
if (rc1)
|
||||
rc2 = qd_probe(0xb0);
|
||||
|
||||
if (rc1 < 0 && rc2 < 0)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(qd65xx_init);
|
||||
|
||||
MODULE_AUTHOR("Samuel Thibault");
|
||||
MODULE_DESCRIPTION("support of qd65xx vlb ide chipset");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,145 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2000 Linus Torvalds & authors
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors: Petr Soucek <petr@ryston.cz>
|
||||
* Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
*/
|
||||
|
||||
/* truncates a in [b,c] */
|
||||
#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) )
|
||||
|
||||
#define IDE_IMPLY(a,b) ((!(a)) || (b))
|
||||
|
||||
#define QD_TIM1_PORT (base)
|
||||
#define QD_CONFIG_PORT (base+0x01)
|
||||
#define QD_TIM2_PORT (base+0x02)
|
||||
#define QD_CONTROL_PORT (base+0x03)
|
||||
|
||||
#define QD_CONFIG_IDE_BASEPORT 0x01
|
||||
#define QD_CONFIG_BASEPORT 0x02
|
||||
#define QD_CONFIG_ID3 0x04
|
||||
#define QD_CONFIG_DISABLED 0x08
|
||||
#define QD_CONFIG_QD6500 0xc0
|
||||
#define QD_CONFIG_QD6580_A 0xa0
|
||||
#define QD_CONFIG_QD6580_B 0x50
|
||||
|
||||
#define QD_CONTR_SEC_DISABLED 0x01
|
||||
|
||||
#define QD_ID3 ((config & QD_CONFIG_ID3)!=0)
|
||||
|
||||
#define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff)
|
||||
|
||||
static inline u8 QD_TIMING(ide_drive_t *drive)
|
||||
{
|
||||
return (unsigned long)ide_get_drivedata(drive) & 0x00ff;
|
||||
}
|
||||
|
||||
static inline u8 QD_TIMREG(ide_drive_t *drive)
|
||||
{
|
||||
return ((unsigned long)ide_get_drivedata(drive) & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
#define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08))
|
||||
#define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00))
|
||||
#define QD6580_DEF_DATA2 ((QD_TIM2_PORT<<8) | (QD_ID3 ? 0x0a : 0x00))
|
||||
#define QD_DEF_CONTR (0x40 | ((control & 0x02) ? 0x9f : 0x1f))
|
||||
|
||||
#define QD_TESTVAL 0x19 /* safe value */
|
||||
|
||||
/* Drive specific timing taken from DOS driver v3.7 */
|
||||
|
||||
static struct qd65xx_timing_s {
|
||||
s8 offset; /* ofset from the beginning of Model Number" */
|
||||
char model[4]; /* 4 chars from Model number, no conversion */
|
||||
s16 active; /* active time */
|
||||
s16 recovery; /* recovery time */
|
||||
} qd65xx_timing [] = {
|
||||
{ 30, "2040", 110, 225 }, /* Conner CP30204 */
|
||||
{ 30, "2045", 135, 225 }, /* Conner CP30254 */
|
||||
{ 30, "1040", 155, 325 }, /* Conner CP30104 */
|
||||
{ 30, "1047", 135, 265 }, /* Conner CP30174 */
|
||||
{ 30, "5344", 135, 225 }, /* Conner CP3544 */
|
||||
{ 30, "01 4", 175, 405 }, /* Conner CP-3104 */
|
||||
{ 27, "C030", 175, 375 }, /* Conner CP3000 */
|
||||
{ 8, "PL42", 110, 295 }, /* Quantum LP240 */
|
||||
{ 8, "PL21", 110, 315 }, /* Quantum LP120 */
|
||||
{ 8, "PL25", 175, 385 }, /* Quantum LP52 */
|
||||
{ 4, "PA24", 110, 285 }, /* WD Piranha SP4200 */
|
||||
{ 6, "2200", 110, 260 }, /* WD Caviar AC2200 */
|
||||
{ 6, "3204", 110, 235 }, /* WD Caviar AC2340 */
|
||||
{ 6, "1202", 110, 265 }, /* WD Caviar AC2120 */
|
||||
{ 0, "DS3-", 135, 315 }, /* Teac SD340 */
|
||||
{ 8, "KM32", 175, 355 }, /* Toshiba MK234 */
|
||||
{ 2, "53A1", 175, 355 }, /* Seagate ST351A */
|
||||
{ 2, "4108", 175, 295 }, /* Seagate ST1480A */
|
||||
{ 2, "1344", 175, 335 }, /* Seagate ST3144A */
|
||||
{ 6, "7 12", 110, 225 }, /* Maxtor 7213A */
|
||||
{ 30, "02F4", 145, 295 }, /* Conner 3204F */
|
||||
{ 2, "1302", 175, 335 }, /* Seagate ST3120A */
|
||||
{ 2, "2334", 145, 265 }, /* Seagate ST3243A */
|
||||
{ 2, "2338", 145, 275 }, /* Seagate ST3283A */
|
||||
{ 2, "3309", 145, 275 }, /* Seagate ST3390A */
|
||||
{ 2, "5305", 145, 275 }, /* Seagate ST3550A */
|
||||
{ 2, "4100", 175, 295 }, /* Seagate ST1400A */
|
||||
{ 2, "4110", 175, 295 }, /* Seagate ST1401A */
|
||||
{ 2, "6300", 135, 265 }, /* Seagate ST3600A */
|
||||
{ 2, "5300", 135, 265 }, /* Seagate ST3500A */
|
||||
{ 6, "7 31", 135, 225 }, /* Maxtor 7131 AT */
|
||||
{ 6, "7 43", 115, 265 }, /* Maxtor 7345 AT */
|
||||
{ 6, "7 42", 110, 255 }, /* Maxtor 7245 AT */
|
||||
{ 6, "3 04", 135, 265 }, /* Maxtor 340 AT */
|
||||
{ 6, "61 0", 135, 285 }, /* WD AC160 */
|
||||
{ 6, "1107", 135, 235 }, /* WD AC1170 */
|
||||
{ 6, "2101", 110, 220 }, /* WD AC1210 */
|
||||
{ 6, "4202", 135, 245 }, /* WD AC2420 */
|
||||
{ 6, "41 0", 175, 355 }, /* WD Caviar 140 */
|
||||
{ 6, "82 0", 175, 355 }, /* WD Caviar 280 */
|
||||
{ 8, "PL01", 175, 375 }, /* Quantum LP105 */
|
||||
{ 8, "PL25", 110, 295 }, /* Quantum LP525 */
|
||||
{ 10, "4S 2", 175, 385 }, /* Quantum ELS42 */
|
||||
{ 10, "8S 5", 175, 385 }, /* Quantum ELS85 */
|
||||
{ 10, "1S72", 175, 385 }, /* Quantum ELS127 */
|
||||
{ 10, "1S07", 175, 385 }, /* Quantum ELS170 */
|
||||
{ 8, "ZE42", 135, 295 }, /* Quantum EZ240 */
|
||||
{ 8, "ZE21", 175, 385 }, /* Quantum EZ127 */
|
||||
{ 8, "ZE58", 175, 385 }, /* Quantum EZ85 */
|
||||
{ 8, "ZE24", 175, 385 }, /* Quantum EZ42 */
|
||||
{ 27, "C036", 155, 325 }, /* Conner CP30064 */
|
||||
{ 27, "C038", 155, 325 }, /* Conner CP30084 */
|
||||
{ 6, "2205", 110, 255 }, /* WDC AC2250 */
|
||||
{ 2, " CHA", 140, 415 }, /* WDC AH series; WDC AH260, WDC */
|
||||
{ 2, " CLA", 140, 415 }, /* WDC AL series: WDC AL2120, 2170, */
|
||||
{ 4, "UC41", 140, 415 }, /* WDC CU140 */
|
||||
{ 6, "1207", 130, 275 }, /* WDC AC2170 */
|
||||
{ 6, "2107", 130, 275 }, /* WDC AC1270 */
|
||||
{ 6, "5204", 130, 275 }, /* WDC AC2540 */
|
||||
{ 30, "3004", 110, 235 }, /* Conner CP30340 */
|
||||
{ 30, "0345", 135, 255 }, /* Conner CP30544 */
|
||||
{ 12, "12A3", 175, 320 }, /* MAXTOR LXT-213A */
|
||||
{ 12, "43A0", 145, 240 }, /* MAXTOR LXT-340A */
|
||||
{ 6, "7 21", 180, 290 }, /* Maxtor 7120 AT */
|
||||
{ 6, "7 71", 135, 240 }, /* Maxtor 7170 AT */
|
||||
{ 12, "45\0000", 110, 205 }, /* MAXTOR MXT-540 */
|
||||
{ 8, "PL11", 180, 290 }, /* QUANTUM LP110A */
|
||||
{ 8, "OG21", 150, 275 }, /* QUANTUM GO120 */
|
||||
{ 12, "42A5", 175, 320 }, /* MAXTOR LXT-245A */
|
||||
{ 2, "2309", 175, 295 }, /* ST3290A */
|
||||
{ 2, "3358", 180, 310 }, /* ST3385A */
|
||||
{ 2, "6355", 180, 310 }, /* ST3655A */
|
||||
{ 2, "1900", 175, 270 }, /* ST9100A */
|
||||
{ 2, "1954", 175, 270 }, /* ST9145A */
|
||||
{ 2, "1909", 175, 270 }, /* ST9190AG */
|
||||
{ 2, "2953", 175, 270 }, /* ST9235A */
|
||||
{ 2, "1359", 175, 270 }, /* ST3195A */
|
||||
{ 24, "3R11", 175, 290 }, /* ALPS ELECTRIC Co.,LTD, DR311C */
|
||||
{ 0, "2M26", 175, 215 }, /* M262XT-0Ah */
|
||||
{ 4, "2253", 175, 300 }, /* HP C2235A */
|
||||
{ 4, "-32A", 145, 245 }, /* H3133-A2 */
|
||||
{ 30, "0326", 150, 270 }, /* Samsung Electronics 120MB */
|
||||
{ 30, "3044", 110, 195 }, /* Conner CFA340A */
|
||||
{ 30, "43A0", 110, 195 }, /* Conner CFA340A */
|
||||
{ -1, " ", 175, 415 } /* unknown disk name */
|
||||
};
|
@ -1,106 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 1996-2002 Russell King.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/ecard.h>
|
||||
|
||||
static const struct ide_port_info rapide_port_info = {
|
||||
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
|
||||
.chipset = ide_generic,
|
||||
};
|
||||
|
||||
static void rapide_setup_ports(struct ide_hw *hw, void __iomem *base,
|
||||
void __iomem *ctrl, unsigned int sz, int irq)
|
||||
{
|
||||
unsigned long port = (unsigned long)base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= 7; i++) {
|
||||
hw->io_ports_array[i] = port;
|
||||
port += sz;
|
||||
}
|
||||
hw->io_ports.ctl_addr = (unsigned long)ctrl;
|
||||
hw->irq = irq;
|
||||
}
|
||||
|
||||
static int rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct ide_host *host;
|
||||
int ret;
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
|
||||
ret = ecard_request_resources(ec);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
|
||||
if (!base) {
|
||||
ret = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq);
|
||||
hw.dev = &ec->dev;
|
||||
|
||||
ret = ide_host_add(&rapide_port_info, hws, 1, &host);
|
||||
if (ret)
|
||||
goto release;
|
||||
|
||||
ecard_set_drvdata(ec, host);
|
||||
goto out;
|
||||
|
||||
release:
|
||||
ecard_release_resources(ec);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rapide_remove(struct expansion_card *ec)
|
||||
{
|
||||
struct ide_host *host = ecard_get_drvdata(ec);
|
||||
|
||||
ecard_set_drvdata(ec, NULL);
|
||||
|
||||
ide_host_remove(host);
|
||||
|
||||
ecard_release_resources(ec);
|
||||
}
|
||||
|
||||
static struct ecard_id rapide_ids[] = {
|
||||
{ MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 },
|
||||
{ 0xffff, 0xffff }
|
||||
};
|
||||
|
||||
static struct ecard_driver rapide_driver = {
|
||||
.probe = rapide_probe,
|
||||
.remove = rapide_remove,
|
||||
.id_table = rapide_ids,
|
||||
.drv = {
|
||||
.name = "rapide",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rapide_init(void)
|
||||
{
|
||||
return ecard_register_driver(&rapide_driver);
|
||||
}
|
||||
|
||||
static void __exit rapide_exit(void)
|
||||
{
|
||||
ecard_remove_driver(&rapide_driver);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Yellowstone RAPIDE driver");
|
||||
|
||||
module_init(rapide_init);
|
||||
module_exit(rapide_exit);
|
@ -1,100 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1995-1998 Linus Torvalds & author (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Principal Author: mlord@pobox.com (Mark Lord)
|
||||
*
|
||||
* See linux/MAINTAINERS for address of current maintainer.
|
||||
*
|
||||
* This file provides support for disabling the buggy read-ahead
|
||||
* mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
|
||||
*
|
||||
* Dunno if this fixes both ports, or only the primary port (?).
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "rz1000"
|
||||
|
||||
static int rz1000_disable_readahead(struct pci_dev *dev)
|
||||
{
|
||||
u16 reg;
|
||||
|
||||
if (!pci_read_config_word (dev, 0x40, ®) &&
|
||||
!pci_write_config_word(dev, 0x40, reg & 0xdfff)) {
|
||||
printk(KERN_INFO "%s: disabled chipset read-ahead "
|
||||
"(buggy RZ1000/RZ1001)\n", pci_name(dev));
|
||||
return 0;
|
||||
} else {
|
||||
printk(KERN_INFO "%s: serialized, disabled unmasking "
|
||||
"(buggy RZ1000/RZ1001)\n", pci_name(dev));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ide_port_info rz1000_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.host_flags = IDE_HFLAG_NO_DMA,
|
||||
};
|
||||
|
||||
static int rz1000_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ide_port_info d = rz1000_chipset;
|
||||
int rc;
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (rz1000_disable_readahead(dev)) {
|
||||
d.host_flags |= IDE_HFLAG_SERIALIZE;
|
||||
d.host_flags |= IDE_HFLAG_NO_UNMASK_IRQS;
|
||||
}
|
||||
|
||||
return ide_pci_init_one(dev, &d, NULL);
|
||||
}
|
||||
|
||||
static void rz1000_remove(struct pci_dev *dev)
|
||||
{
|
||||
ide_pci_remove(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id rz1000_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), 0 },
|
||||
{ PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, rz1000_pci_tbl);
|
||||
|
||||
static struct pci_driver rz1000_pci_driver = {
|
||||
.name = "RZ1000_IDE",
|
||||
.id_table = rz1000_pci_tbl,
|
||||
.probe = rz1000_init_one,
|
||||
.remove = rz1000_remove,
|
||||
};
|
||||
|
||||
static int __init rz1000_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&rz1000_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit rz1000_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&rz1000_pci_driver);
|
||||
}
|
||||
|
||||
module_init(rz1000_ide_init);
|
||||
module_exit(rz1000_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for RZ1000 IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,355 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2000-2002 Mark Lord <mlord@pobox.com>
|
||||
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Development of this chipset driver was funded
|
||||
* by the nice folks at National Semiconductor.
|
||||
*
|
||||
* Documentation:
|
||||
* Available from National Semiconductor
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "sc1200"
|
||||
|
||||
#define SC1200_REV_A 0x00
|
||||
#define SC1200_REV_B1 0x01
|
||||
#define SC1200_REV_B3 0x02
|
||||
#define SC1200_REV_C1 0x03
|
||||
#define SC1200_REV_D1 0x04
|
||||
|
||||
#define PCI_CLK_33 0x00
|
||||
#define PCI_CLK_48 0x01
|
||||
#define PCI_CLK_66 0x02
|
||||
#define PCI_CLK_33A 0x03
|
||||
|
||||
static unsigned short sc1200_get_pci_clock (void)
|
||||
{
|
||||
unsigned char chip_id, silicon_revision;
|
||||
unsigned int pci_clock;
|
||||
/*
|
||||
* Check the silicon revision, as not all versions of the chip
|
||||
* have the register with the fast PCI bus timings.
|
||||
*/
|
||||
chip_id = inb (0x903c);
|
||||
silicon_revision = inb (0x903d);
|
||||
|
||||
// Read the fast pci clock frequency
|
||||
if (chip_id == 0x04 && silicon_revision < SC1200_REV_B1) {
|
||||
pci_clock = PCI_CLK_33;
|
||||
} else {
|
||||
// check clock generator configuration (cfcc)
|
||||
// the clock is in bits 8 and 9 of this word
|
||||
|
||||
pci_clock = inw (0x901e);
|
||||
pci_clock >>= 8;
|
||||
pci_clock &= 0x03;
|
||||
if (pci_clock == PCI_CLK_33A)
|
||||
pci_clock = PCI_CLK_33;
|
||||
}
|
||||
return pci_clock;
|
||||
}
|
||||
|
||||
/*
|
||||
* Here are the standard PIO mode 0-4 timings for each "format".
|
||||
* Format-0 uses fast data reg timings, with slower command reg timings.
|
||||
* Format-1 uses fast timings for all registers, but won't work with all drives.
|
||||
*/
|
||||
static const unsigned int sc1200_pio_timings[4][5] =
|
||||
{{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, // format0 33Mhz
|
||||
{0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}, // format1, 33Mhz
|
||||
{0xfaa3f4f3, 0xc23232b2, 0x513101c1, 0x31213121, 0x10211021}, // format1, 48Mhz
|
||||
{0xfff4fff4, 0xf35353d3, 0x814102f1, 0x42314231, 0x11311131}}; // format1, 66Mhz
|
||||
|
||||
/*
|
||||
* After chip reset, the PIO timings are set to 0x00009172, which is not valid.
|
||||
*/
|
||||
//#define SC1200_BAD_PIO(timings) (((timings)&~0x80000000)==0x00009172)
|
||||
|
||||
static void sc1200_tunepio(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
unsigned int basereg = hwif->channel ? 0x50 : 0x40, format = 0;
|
||||
|
||||
pci_read_config_dword(pdev, basereg + 4, &format);
|
||||
format = (format >> 31) & 1;
|
||||
if (format)
|
||||
format += sc1200_get_pci_clock();
|
||||
pci_write_config_dword(pdev, basereg + ((drive->dn & 1) << 3),
|
||||
sc1200_pio_timings[format][pio]);
|
||||
}
|
||||
|
||||
/*
|
||||
* The SC1200 specifies that two drives sharing a cable cannot mix
|
||||
* UDMA/MDMA. It has to be one or the other, for the pair, though
|
||||
* different timings can still be chosen for each drive. We could
|
||||
* set the appropriate timing bits on the fly, but that might be
|
||||
* a bit confusing. So, for now we statically handle this requirement
|
||||
* by looking at our mate drive to see what it is capable of, before
|
||||
* choosing a mode for our own drive.
|
||||
*/
|
||||
static u8 sc1200_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
ide_drive_t *mate = ide_get_pair_dev(drive);
|
||||
u16 *mateid;
|
||||
u8 mask = hwif->ultra_mask;
|
||||
|
||||
if (mate == NULL)
|
||||
goto out;
|
||||
mateid = mate->id;
|
||||
|
||||
if (ata_id_has_dma(mateid) && __ide_dma_bad_drive(mate) == 0) {
|
||||
if ((mateid[ATA_ID_FIELD_VALID] & 4) &&
|
||||
(mateid[ATA_ID_UDMA_MODES] & 7))
|
||||
goto out;
|
||||
if (mateid[ATA_ID_MWDMA_MODES] & 7)
|
||||
mask = 0;
|
||||
}
|
||||
out:
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void sc1200_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned int reg, timings;
|
||||
unsigned short pci_clock;
|
||||
unsigned int basereg = hwif->channel ? 0x50 : 0x40;
|
||||
const u8 mode = drive->dma_mode;
|
||||
|
||||
static const u32 udma_timing[3][3] = {
|
||||
{ 0x00921250, 0x00911140, 0x00911030 },
|
||||
{ 0x00932470, 0x00922260, 0x00922140 },
|
||||
{ 0x009436a1, 0x00933481, 0x00923261 },
|
||||
};
|
||||
|
||||
static const u32 mwdma_timing[3][3] = {
|
||||
{ 0x00077771, 0x00012121, 0x00002020 },
|
||||
{ 0x000bbbb2, 0x00024241, 0x00013131 },
|
||||
{ 0x000ffff3, 0x00035352, 0x00015151 },
|
||||
};
|
||||
|
||||
pci_clock = sc1200_get_pci_clock();
|
||||
|
||||
/*
|
||||
* Note that each DMA mode has several timings associated with it.
|
||||
* The correct timing depends on the fast PCI clock freq.
|
||||
*/
|
||||
|
||||
if (mode >= XFER_UDMA_0)
|
||||
timings = udma_timing[pci_clock][mode - XFER_UDMA_0];
|
||||
else
|
||||
timings = mwdma_timing[pci_clock][mode - XFER_MW_DMA_0];
|
||||
|
||||
if ((drive->dn & 1) == 0) {
|
||||
pci_read_config_dword(dev, basereg + 4, ®);
|
||||
timings |= reg & 0x80000000; /* preserve PIO format bit */
|
||||
pci_write_config_dword(dev, basereg + 4, timings);
|
||||
} else
|
||||
pci_write_config_dword(dev, basereg + 12, timings);
|
||||
}
|
||||
|
||||
/* Replacement for the standard ide_dma_end action in
|
||||
* dma_proc.
|
||||
*
|
||||
* returns 1 on error, 0 otherwise
|
||||
*/
|
||||
static int sc1200_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned long dma_base = hwif->dma_base;
|
||||
u8 dma_stat;
|
||||
|
||||
dma_stat = inb(dma_base+2); /* get DMA status */
|
||||
|
||||
if (!(dma_stat & 4))
|
||||
printk(" ide_dma_end dma_stat=%0x err=%x newerr=%x\n",
|
||||
dma_stat, ((dma_stat&7)!=4), ((dma_stat&2)==2));
|
||||
|
||||
outb(dma_stat|0x1b, dma_base+2); /* clear the INTR & ERROR bits */
|
||||
outb(inb(dma_base)&~1, dma_base); /* !! DO THIS HERE !! stop DMA */
|
||||
|
||||
return (dma_stat & 7) != 4; /* verify good DMA status */
|
||||
}
|
||||
|
||||
/*
|
||||
* sc1200_set_pio_mode() handles setting of PIO modes
|
||||
* for both the chipset and drive.
|
||||
*
|
||||
* All existing BIOSs for this chipset guarantee that all drives
|
||||
* will have valid default PIO timings set up before we get here.
|
||||
*/
|
||||
|
||||
static void sc1200_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
int mode = -1;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
/*
|
||||
* bad abuse of ->set_pio_mode interface
|
||||
*/
|
||||
switch (pio) {
|
||||
case 200: mode = XFER_UDMA_0; break;
|
||||
case 201: mode = XFER_UDMA_1; break;
|
||||
case 202: mode = XFER_UDMA_2; break;
|
||||
case 100: mode = XFER_MW_DMA_0; break;
|
||||
case 101: mode = XFER_MW_DMA_1; break;
|
||||
case 102: mode = XFER_MW_DMA_2; break;
|
||||
}
|
||||
if (mode != -1) {
|
||||
printk("SC1200: %s: changing (U)DMA mode\n", drive->name);
|
||||
ide_dma_off_quietly(drive);
|
||||
if (ide_set_dma_mode(drive, mode) == 0 &&
|
||||
(drive->dev_flags & IDE_DFLAG_USING_DMA))
|
||||
hwif->dma_ops->dma_host_set(drive, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
sc1200_tunepio(drive, pio);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
struct sc1200_saved_state {
|
||||
u32 regs[8];
|
||||
};
|
||||
|
||||
static int sc1200_suspend (struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
printk("SC1200: suspend(%u)\n", state.event);
|
||||
|
||||
/*
|
||||
* we only save state when going from full power to less
|
||||
*/
|
||||
if (state.event == PM_EVENT_ON) {
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct sc1200_saved_state *ss = host->host_priv;
|
||||
unsigned int r;
|
||||
|
||||
/*
|
||||
* save timing registers
|
||||
* (this may be unnecessary if BIOS also does it)
|
||||
*/
|
||||
for (r = 0; r < 8; r++)
|
||||
pci_read_config_dword(dev, 0x40 + r * 4, &ss->regs[r]);
|
||||
}
|
||||
|
||||
pci_disable_device(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc1200_resume (struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct sc1200_saved_state *ss = host->host_priv;
|
||||
unsigned int r;
|
||||
int i;
|
||||
|
||||
i = pci_enable_device(dev);
|
||||
if (i)
|
||||
return i;
|
||||
|
||||
/*
|
||||
* restore timing registers
|
||||
* (this may be unnecessary if BIOS also does it)
|
||||
*/
|
||||
for (r = 0; r < 8; r++)
|
||||
pci_write_config_dword(dev, 0x40 + r * 4, ss->regs[r]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct ide_port_ops sc1200_port_ops = {
|
||||
.set_pio_mode = sc1200_set_pio_mode,
|
||||
.set_dma_mode = sc1200_set_dma_mode,
|
||||
.udma_filter = sc1200_udma_filter,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops sc1200_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = ide_dma_start,
|
||||
.dma_end = sc1200_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info sc1200_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.port_ops = &sc1200_port_ops,
|
||||
.dma_ops = &sc1200_dma_ops,
|
||||
.host_flags = IDE_HFLAG_SERIALIZE |
|
||||
IDE_HFLAG_POST_SET_MODE |
|
||||
IDE_HFLAG_ABUSE_DMA_MODES,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA2,
|
||||
};
|
||||
|
||||
static int sc1200_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct sc1200_saved_state *ss = NULL;
|
||||
int rc;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
ss = kmalloc(sizeof(*ss), GFP_KERNEL);
|
||||
if (ss == NULL)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
rc = ide_pci_init_one(dev, &sc1200_chipset, ss);
|
||||
if (rc)
|
||||
kfree(ss);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct pci_device_id sc1200_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(NS, PCI_DEVICE_ID_NS_SCx200_IDE), 0},
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl);
|
||||
|
||||
static struct pci_driver sc1200_pci_driver = {
|
||||
.name = "SC1200_IDE",
|
||||
.id_table = sc1200_pci_tbl,
|
||||
.probe = sc1200_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = sc1200_suspend,
|
||||
.resume = sc1200_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init sc1200_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&sc1200_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit sc1200_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&sc1200_pci_driver);
|
||||
}
|
||||
|
||||
module_init(sc1200_ide_init);
|
||||
module_exit(sc1200_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for NS SC1200 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,456 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1998-2000 Michel Aubry
|
||||
* Copyright (C) 1998-2000 Andrzej Krzysztofowicz
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2007-2010 Bartlomiej Zolnierkiewicz
|
||||
* Portions copyright (c) 2001 Sun Microsystems
|
||||
*
|
||||
*
|
||||
* RCC/ServerWorks IDE driver for Linux
|
||||
*
|
||||
* OSB4: `Open South Bridge' IDE Interface (fn 1)
|
||||
* supports UDMA mode 2 (33 MB/s)
|
||||
*
|
||||
* CSB5: `Champion South Bridge' IDE Interface (fn 1)
|
||||
* all revisions support UDMA mode 4 (66 MB/s)
|
||||
* revision A2.0 and up support UDMA mode 5 (100 MB/s)
|
||||
*
|
||||
* *** The CSB5 does not provide ANY register ***
|
||||
* *** to detect 80-conductor cable presence. ***
|
||||
*
|
||||
* CSB6: `Champion South Bridge' IDE Interface (optional: third channel)
|
||||
*
|
||||
* HT1000: AKA BCM5785 - Hypertransport Southbridge for Opteron systems. IDE
|
||||
* controller same as the CSB6. Single channel ATA100 only.
|
||||
*
|
||||
* Documentation:
|
||||
* Available under NDA only. Errata info very hard to get.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "serverworks"
|
||||
|
||||
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
|
||||
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
|
||||
|
||||
/* Seagate Barracuda ATA IV Family drives in UDMA mode 5
|
||||
* can overrun their FIFOs when used with the CSB5 */
|
||||
static const char *svwks_bad_ata100[] = {
|
||||
"ST320011A",
|
||||
"ST340016A",
|
||||
"ST360021A",
|
||||
"ST380021A",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int check_in_drive_lists (ide_drive_t *drive, const char **list)
|
||||
{
|
||||
char *m = (char *)&drive->id[ATA_ID_PROD];
|
||||
|
||||
while (*list)
|
||||
if (!strcmp(*list++, m))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 svwks_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
|
||||
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) {
|
||||
return 0x1f;
|
||||
} else if (dev->revision < SVWKS_CSB5_REVISION_NEW) {
|
||||
return 0x07;
|
||||
} else {
|
||||
u8 btr = 0, mode, mask;
|
||||
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
mode = btr & 0x3;
|
||||
|
||||
/* If someone decides to do UDMA133 on CSB5 the same
|
||||
issue will bite so be inclusive */
|
||||
if (mode > 2 && check_in_drive_lists(drive, svwks_bad_ata100))
|
||||
mode = 2;
|
||||
|
||||
switch(mode) {
|
||||
case 3: mask = 0x3f; break;
|
||||
case 2: mask = 0x1f; break;
|
||||
case 1: mask = 0x07; break;
|
||||
default: mask = 0x00; break;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 svwks_csb_check (struct pci_dev *dev)
|
||||
{
|
||||
switch (dev->device) {
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2:
|
||||
case PCI_DEVICE_ID_SERVERWORKS_HT1000IDE:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void svwks_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
static const u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
|
||||
static const u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 };
|
||||
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
if (drive->dn >= ARRAY_SIZE(drive_pci))
|
||||
return;
|
||||
|
||||
pci_write_config_byte(dev, drive_pci[drive->dn], pio_modes[pio]);
|
||||
|
||||
if (svwks_csb_check(dev)) {
|
||||
u16 csb_pio = 0;
|
||||
|
||||
pci_read_config_word(dev, 0x4a, &csb_pio);
|
||||
|
||||
csb_pio &= ~(0x0f << (4 * drive->dn));
|
||||
csb_pio |= (pio << (4 * drive->dn));
|
||||
|
||||
pci_write_config_word(dev, 0x4a, csb_pio);
|
||||
}
|
||||
}
|
||||
|
||||
static void svwks_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
static const u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||
static const u8 dma_modes[] = { 0x77, 0x21, 0x20 };
|
||||
static const u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 };
|
||||
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
const u8 speed = drive->dma_mode;
|
||||
u8 unit = drive->dn & 1;
|
||||
|
||||
u8 ultra_enable = 0, ultra_timing = 0, dma_timing = 0;
|
||||
|
||||
if (drive->dn >= ARRAY_SIZE(drive_pci2))
|
||||
return;
|
||||
|
||||
pci_read_config_byte(dev, (0x56|hwif->channel), &ultra_timing);
|
||||
pci_read_config_byte(dev, 0x54, &ultra_enable);
|
||||
|
||||
ultra_timing &= ~(0x0F << (4*unit));
|
||||
ultra_enable &= ~(0x01 << drive->dn);
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
dma_timing |= dma_modes[2];
|
||||
ultra_timing |= (udma_modes[speed - XFER_UDMA_0] << (4 * unit));
|
||||
ultra_enable |= (0x01 << drive->dn);
|
||||
} else if (speed >= XFER_MW_DMA_0)
|
||||
dma_timing |= dma_modes[speed - XFER_MW_DMA_0];
|
||||
|
||||
pci_write_config_byte(dev, drive_pci2[drive->dn], dma_timing);
|
||||
pci_write_config_byte(dev, (0x56|hwif->channel), ultra_timing);
|
||||
pci_write_config_byte(dev, 0x54, ultra_enable);
|
||||
}
|
||||
|
||||
static int init_chipset_svwks(struct pci_dev *dev)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 btr;
|
||||
|
||||
/* force Master Latency Timer value to 64 PCICLKs */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40);
|
||||
|
||||
/* OSB4 : South Bridge and IDE */
|
||||
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
|
||||
struct pci_dev *isa_dev =
|
||||
pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
|
||||
if (isa_dev) {
|
||||
pci_read_config_dword(isa_dev, 0x64, ®);
|
||||
reg &= ~0x00002000; /* disable 600ns interrupt mask */
|
||||
if(!(reg & 0x00004000))
|
||||
printk(KERN_DEBUG DRV_NAME " %s: UDMA not BIOS "
|
||||
"enabled.\n", pci_name(dev));
|
||||
reg |= 0x00004000; /* enable UDMA/33 support */
|
||||
pci_write_config_dword(isa_dev, 0x64, reg);
|
||||
pci_dev_put(isa_dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup CSB5/CSB6 : South Bridge and IDE option RAID */
|
||||
else if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) {
|
||||
|
||||
/* Third Channel Test */
|
||||
if (!(PCI_FUNC(dev->devfn) & 1)) {
|
||||
struct pci_dev * findev = NULL;
|
||||
u32 reg4c = 0;
|
||||
findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL);
|
||||
if (findev) {
|
||||
pci_read_config_dword(findev, 0x4C, ®4c);
|
||||
reg4c &= ~0x000007FF;
|
||||
reg4c |= 0x00000040;
|
||||
reg4c |= 0x00000020;
|
||||
pci_write_config_dword(findev, 0x4C, reg4c);
|
||||
pci_dev_put(findev);
|
||||
}
|
||||
outb_p(0x06, 0x0c00);
|
||||
dev->irq = inb_p(0x0c01);
|
||||
} else {
|
||||
struct pci_dev * findev = NULL;
|
||||
u8 reg41 = 0;
|
||||
|
||||
findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
|
||||
PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL);
|
||||
if (findev) {
|
||||
pci_read_config_byte(findev, 0x41, ®41);
|
||||
reg41 &= ~0x40;
|
||||
pci_write_config_byte(findev, 0x41, reg41);
|
||||
pci_dev_put(findev);
|
||||
}
|
||||
/*
|
||||
* This is a device pin issue on CSB6.
|
||||
* Since there will be a future raid mode,
|
||||
* early versions of the chipset require the
|
||||
* interrupt pin to be set, and it is a compatibility
|
||||
* mode issue.
|
||||
*/
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)
|
||||
dev->irq = 0;
|
||||
}
|
||||
// pci_read_config_dword(dev, 0x40, &pioreg)
|
||||
// pci_write_config_dword(dev, 0x40, 0x99999999);
|
||||
// pci_read_config_dword(dev, 0x44, &dmareg);
|
||||
// pci_write_config_dword(dev, 0x44, 0xFFFFFFFF);
|
||||
/* setup the UDMA Control register
|
||||
*
|
||||
* 1. clear bit 6 to enable DMA
|
||||
* 2. enable DMA modes with bits 0-1
|
||||
* 00 : legacy
|
||||
* 01 : udma2
|
||||
* 10 : udma2/udma4
|
||||
* 11 : udma2/udma4/udma5
|
||||
*/
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
btr &= ~0x40;
|
||||
if (!(PCI_FUNC(dev->devfn) & 1))
|
||||
btr |= 0x2;
|
||||
else
|
||||
btr |= (dev->revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2;
|
||||
pci_write_config_byte(dev, 0x5A, btr);
|
||||
}
|
||||
/* Setup HT1000 SouthBridge Controller - Single Channel Only */
|
||||
else if (dev->device == PCI_DEVICE_ID_SERVERWORKS_HT1000IDE) {
|
||||
pci_read_config_byte(dev, 0x5A, &btr);
|
||||
btr &= ~0x40;
|
||||
btr |= 0x3;
|
||||
pci_write_config_byte(dev, 0x5A, btr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 ata66_svwks_svwks(ide_hwif_t *hwif)
|
||||
{
|
||||
return ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
/* On Dell PowerEdge servers with a CSB5/CSB6, the top two bits
|
||||
* of the subsystem device ID indicate presence of an 80-pin cable.
|
||||
* Bit 15 clear = secondary IDE channel does not have 80-pin cable.
|
||||
* Bit 15 set = secondary IDE channel has 80-pin cable.
|
||||
* Bit 14 clear = primary IDE channel does not have 80-pin cable.
|
||||
* Bit 14 set = primary IDE channel has 80-pin cable.
|
||||
*/
|
||||
static u8 ata66_svwks_dell(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL &&
|
||||
dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE ||
|
||||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE))
|
||||
return ((1 << (hwif->channel + 14)) &
|
||||
dev->subsystem_device) ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
|
||||
return ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
/* Sun Cobalt Alpine hardware avoids the 80-pin cable
|
||||
* detect issue by attaching the drives directly to the board.
|
||||
* This check follows the Dell precedent (how scary is that?!)
|
||||
*
|
||||
* WARNING: this only works on Alpine hardware!
|
||||
*/
|
||||
static u8 ata66_svwks_cobalt(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN &&
|
||||
dev->vendor == PCI_VENDOR_ID_SERVERWORKS &&
|
||||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE)
|
||||
return ((1 << (hwif->channel + 14)) &
|
||||
dev->subsystem_device) ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
|
||||
return ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
static u8 svwks_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
|
||||
/* Server Works */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SERVERWORKS)
|
||||
return ata66_svwks_svwks (hwif);
|
||||
|
||||
/* Dell PowerEdge */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL)
|
||||
return ata66_svwks_dell (hwif);
|
||||
|
||||
/* Cobalt Alpine */
|
||||
if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN)
|
||||
return ata66_svwks_cobalt (hwif);
|
||||
|
||||
/* Per Specified Design by OEM, and ASIC Architect */
|
||||
if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
|
||||
(dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2))
|
||||
return ATA_CBL_PATA80;
|
||||
|
||||
return ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops osb4_port_ops = {
|
||||
.set_pio_mode = svwks_set_pio_mode,
|
||||
.set_dma_mode = svwks_set_dma_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops svwks_port_ops = {
|
||||
.set_pio_mode = svwks_set_pio_mode,
|
||||
.set_dma_mode = svwks_set_dma_mode,
|
||||
.udma_filter = svwks_udma_filter,
|
||||
.cable_detect = svwks_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info serverworks_chipsets[] = {
|
||||
{ /* 0: OSB4 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.port_ops = &osb4_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = 0x00, /* UDMA is problematic on OSB4 */
|
||||
},
|
||||
{ /* 1: CSB5 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.port_ops = &svwks_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA5,
|
||||
},
|
||||
{ /* 2: CSB6 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.port_ops = &svwks_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA5,
|
||||
},
|
||||
{ /* 3: CSB6-2 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.port_ops = &svwks_port_ops,
|
||||
.host_flags = IDE_HFLAG_SINGLE,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA5,
|
||||
},
|
||||
{ /* 4: HT1000 */
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_svwks,
|
||||
.port_ops = &svwks_port_ops,
|
||||
.host_flags = IDE_HFLAG_SINGLE,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA5,
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* svwks_init_one - called when a OSB/CSB is found
|
||||
* @dev: the svwks device
|
||||
* @id: the matching pci id
|
||||
*
|
||||
* Called when the PCI registration layer (or the IDE initialization)
|
||||
* finds a device matching our IDE device tables.
|
||||
*/
|
||||
|
||||
static int svwks_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ide_port_info d;
|
||||
u8 idx = id->driver_data;
|
||||
|
||||
d = serverworks_chipsets[idx];
|
||||
|
||||
if (idx == 1)
|
||||
d.host_flags |= IDE_HFLAG_CLEAR_SIMPLEX;
|
||||
else if (idx == 2 || idx == 3) {
|
||||
if ((PCI_FUNC(dev->devfn) & 1) == 0) {
|
||||
if (pci_resource_start(dev, 0) != 0x01f1)
|
||||
d.host_flags |= IDE_HFLAG_NON_BOOTABLE;
|
||||
d.host_flags |= IDE_HFLAG_SINGLE;
|
||||
} else
|
||||
d.host_flags &= ~IDE_HFLAG_SINGLE;
|
||||
}
|
||||
|
||||
return ide_pci_init_one(dev, &d, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id svwks_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0 },
|
||||
{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 1 },
|
||||
{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2 },
|
||||
{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 3 },
|
||||
{ PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 4 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, svwks_pci_tbl);
|
||||
|
||||
static struct pci_driver svwks_pci_driver = {
|
||||
.name = "Serverworks_IDE",
|
||||
.id_table = svwks_pci_tbl,
|
||||
.probe = svwks_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init svwks_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&svwks_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit svwks_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&svwks_pci_driver);
|
||||
}
|
||||
|
||||
module_init(svwks_ide_init);
|
||||
module_exit(svwks_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Aubry. Andrzej Krzysztofowicz, Andre Hedrick, Bartlomiej Zolnierkiewicz");
|
||||
MODULE_DESCRIPTION("PCI driver module for Serverworks OSB4/CSB5/CSB6 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,682 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 1995-1998 Mark Lord
|
||||
* Copyright (C) 2007-2009 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* ide_setup_pci_baseregs - place a PCI IDE controller native
|
||||
* @dev: PCI device of interface to switch native
|
||||
* @name: Name of interface
|
||||
*
|
||||
* We attempt to place the PCI interface into PCI native mode. If
|
||||
* we succeed the BARs are ok and the controller is in PCI mode.
|
||||
* Returns 0 on success or an errno code.
|
||||
*
|
||||
* FIXME: if we program the interface and then fail to set the BARS
|
||||
* we don't switch it back to legacy mode. Do we actually care ??
|
||||
*/
|
||||
|
||||
static int ide_setup_pci_baseregs(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
u8 progif = 0;
|
||||
|
||||
/*
|
||||
* Place both IDE interfaces into PCI "native" mode:
|
||||
*/
|
||||
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
|
||||
(progif & 5) != 5) {
|
||||
if ((progif & 0xa) != 0xa) {
|
||||
printk(KERN_INFO "%s %s: device not capable of full "
|
||||
"native PCI mode\n", name, pci_name(dev));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
printk(KERN_INFO "%s %s: placing both ports into native PCI "
|
||||
"mode\n", name, pci_name(dev));
|
||||
(void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5);
|
||||
if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) ||
|
||||
(progif & 5) != 5) {
|
||||
printk(KERN_ERR "%s %s: rewrite of PROGIF failed, "
|
||||
"wanted 0x%04x, got 0x%04x\n",
|
||||
name, pci_name(dev), progif | 5, progif);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
static int ide_pci_clear_simplex(unsigned long dma_base, const char *name)
|
||||
{
|
||||
u8 dma_stat = inb(dma_base + 2);
|
||||
|
||||
outb(dma_stat & 0x60, dma_base + 2);
|
||||
dma_stat = inb(dma_base + 2);
|
||||
|
||||
return (dma_stat & 0x80) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_dma_base - setup BMIBA
|
||||
* @hwif: IDE interface
|
||||
* @d: IDE port info
|
||||
*
|
||||
* Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space.
|
||||
*/
|
||||
|
||||
unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long dma_base = 0;
|
||||
|
||||
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
||||
return hwif->dma_base;
|
||||
|
||||
if (hwif->mate && hwif->mate->dma_base) {
|
||||
dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8);
|
||||
} else {
|
||||
u8 baridx = (d->host_flags & IDE_HFLAG_CS5520) ? 2 : 4;
|
||||
|
||||
dma_base = pci_resource_start(dev, baridx);
|
||||
|
||||
if (dma_base == 0) {
|
||||
printk(KERN_ERR "%s %s: DMA base is invalid\n",
|
||||
d->name, pci_name(dev));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (hwif->channel)
|
||||
dma_base += 8;
|
||||
|
||||
return dma_base;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_dma_base);
|
||||
|
||||
int ide_pci_check_simplex(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 dma_stat;
|
||||
|
||||
if (d->host_flags & (IDE_HFLAG_MMIO | IDE_HFLAG_CS5520))
|
||||
goto out;
|
||||
|
||||
if (d->host_flags & IDE_HFLAG_CLEAR_SIMPLEX) {
|
||||
if (ide_pci_clear_simplex(hwif->dma_base, d->name))
|
||||
printk(KERN_INFO "%s %s: simplex device: DMA forced\n",
|
||||
d->name, pci_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device claims "simplex" DMA, this means that only one of
|
||||
* the two interfaces can be trusted with DMA at any point in time
|
||||
* (so we should enable DMA only on one of the two interfaces).
|
||||
*
|
||||
* FIXME: At this point we haven't probed the drives so we can't make
|
||||
* the appropriate decision. Really we should defer this problem until
|
||||
* we tune the drive then try to grab DMA ownership if we want to be
|
||||
* the DMA end. This has to be become dynamic to handle hot-plug.
|
||||
*/
|
||||
dma_stat = hwif->dma_ops->dma_sff_read_status(hwif);
|
||||
if ((dma_stat & 0x80) && hwif->mate && hwif->mate->dma_base) {
|
||||
printk(KERN_INFO "%s %s: simplex device: DMA disabled\n",
|
||||
d->name, pci_name(dev));
|
||||
return -1;
|
||||
}
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_check_simplex);
|
||||
|
||||
/*
|
||||
* Set up BM-DMA capability (PnP BIOS should have done this)
|
||||
*/
|
||||
int ide_pci_set_master(struct pci_dev *dev, const char *name)
|
||||
{
|
||||
u16 pcicmd;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
|
||||
|
||||
if ((pcicmd & PCI_COMMAND_MASTER) == 0) {
|
||||
pci_set_master(dev);
|
||||
|
||||
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) ||
|
||||
(pcicmd & PCI_COMMAND_MASTER) == 0) {
|
||||
printk(KERN_ERR "%s %s: error updating PCICMD\n",
|
||||
name, pci_name(dev));
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_set_master);
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
|
||||
void ide_setup_pci_noise(struct pci_dev *dev, const struct ide_port_info *d)
|
||||
{
|
||||
printk(KERN_INFO "%s %s: IDE controller (0x%04x:0x%04x rev 0x%02x)\n",
|
||||
d->name, pci_name(dev),
|
||||
dev->vendor, dev->device, dev->revision);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_setup_pci_noise);
|
||||
|
||||
|
||||
/**
|
||||
* ide_pci_enable - do PCI enables
|
||||
* @dev: PCI device
|
||||
* @bars: PCI BARs mask
|
||||
* @d: IDE port info
|
||||
*
|
||||
* Enable the IDE PCI device. We attempt to enable the device in full
|
||||
* but if that fails then we only need IO space. The PCI code should
|
||||
* have setup the proper resources for us already for controllers in
|
||||
* legacy mode.
|
||||
*
|
||||
* Returns zero on success or an error code
|
||||
*/
|
||||
|
||||
static int ide_pci_enable(struct pci_dev *dev, int bars,
|
||||
const struct ide_port_info *d)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pci_enable_device(dev)) {
|
||||
ret = pci_enable_device_io(dev);
|
||||
if (ret < 0) {
|
||||
printk(KERN_WARNING "%s %s: couldn't enable device\n",
|
||||
d->name, pci_name(dev));
|
||||
goto out;
|
||||
}
|
||||
printk(KERN_WARNING "%s %s: BIOS configuration fixed\n",
|
||||
d->name, pci_name(dev));
|
||||
}
|
||||
|
||||
/*
|
||||
* assume all devices can do 32-bit DMA for now, we can add
|
||||
* a DMA mask field to the struct ide_port_info if we need it
|
||||
* (or let lower level driver set the DMA mask)
|
||||
*/
|
||||
ret = dma_set_mask(&dev->dev, DMA_BIT_MASK(32));
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s %s: can't set DMA mask\n",
|
||||
d->name, pci_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pci_request_selected_regions(dev, bars, d->name);
|
||||
if (ret < 0)
|
||||
printk(KERN_ERR "%s %s: can't reserve resources\n",
|
||||
d->name, pci_name(dev));
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_configure - configure an unconfigured device
|
||||
* @dev: PCI device
|
||||
* @d: IDE port info
|
||||
*
|
||||
* Enable and configure the PCI device we have been passed.
|
||||
* Returns zero on success or an error code.
|
||||
*/
|
||||
|
||||
static int ide_pci_configure(struct pci_dev *dev, const struct ide_port_info *d)
|
||||
{
|
||||
u16 pcicmd = 0;
|
||||
/*
|
||||
* PnP BIOS was *supposed* to have setup this device, but we
|
||||
* can do it ourselves, so long as the BIOS has assigned an IRQ
|
||||
* (or possibly the device is using a "legacy header" for IRQs).
|
||||
* Maybe the user deliberately *disabled* the device,
|
||||
* but we'll eventually ignore it again if no drives respond.
|
||||
*/
|
||||
if (ide_setup_pci_baseregs(dev, d->name) ||
|
||||
pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) {
|
||||
printk(KERN_INFO "%s %s: device disabled (BIOS)\n",
|
||||
d->name, pci_name(dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) {
|
||||
printk(KERN_ERR "%s %s: error accessing PCI regs\n",
|
||||
d->name, pci_name(dev));
|
||||
return -EIO;
|
||||
}
|
||||
if (!(pcicmd & PCI_COMMAND_IO)) {
|
||||
printk(KERN_ERR "%s %s: unable to enable IDE controller\n",
|
||||
d->name, pci_name(dev));
|
||||
return -ENXIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_check_iomem - check a register is I/O
|
||||
* @dev: PCI device
|
||||
* @d: IDE port info
|
||||
* @bar: BAR number
|
||||
*
|
||||
* Checks if a BAR is configured and points to MMIO space. If so,
|
||||
* return an error code. Otherwise return 0
|
||||
*/
|
||||
|
||||
static int ide_pci_check_iomem(struct pci_dev *dev, const struct ide_port_info *d,
|
||||
int bar)
|
||||
{
|
||||
ulong flags = pci_resource_flags(dev, bar);
|
||||
|
||||
/* Unconfigured ? */
|
||||
if (!flags || pci_resource_len(dev, bar) == 0)
|
||||
return 0;
|
||||
|
||||
/* I/O space */
|
||||
if (flags & IORESOURCE_IO)
|
||||
return 0;
|
||||
|
||||
/* Bad */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_hw_configure - configure a struct ide_hw instance
|
||||
* @dev: PCI device holding interface
|
||||
* @d: IDE port info
|
||||
* @port: port number
|
||||
* @hw: struct ide_hw instance corresponding to this port
|
||||
*
|
||||
* Perform the initial set up for the hardware interface structure. This
|
||||
* is done per interface port rather than per PCI device. There may be
|
||||
* more than one port per device.
|
||||
*
|
||||
* Returns zero on success or an error code.
|
||||
*/
|
||||
|
||||
static int ide_hw_configure(struct pci_dev *dev, const struct ide_port_info *d,
|
||||
unsigned int port, struct ide_hw *hw)
|
||||
{
|
||||
unsigned long ctl = 0, base = 0;
|
||||
|
||||
if ((d->host_flags & IDE_HFLAG_ISA_PORTS) == 0) {
|
||||
if (ide_pci_check_iomem(dev, d, 2 * port) ||
|
||||
ide_pci_check_iomem(dev, d, 2 * port + 1)) {
|
||||
printk(KERN_ERR "%s %s: I/O baseregs (BIOS) are "
|
||||
"reported as MEM for port %d!\n",
|
||||
d->name, pci_name(dev), port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctl = pci_resource_start(dev, 2*port+1);
|
||||
base = pci_resource_start(dev, 2*port);
|
||||
} else {
|
||||
/* Use default values */
|
||||
ctl = port ? 0x374 : 0x3f4;
|
||||
base = port ? 0x170 : 0x1f0;
|
||||
}
|
||||
|
||||
if (!base || !ctl) {
|
||||
printk(KERN_ERR "%s %s: bad PCI BARs for port %d, skipping\n",
|
||||
d->name, pci_name(dev), port);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(hw, 0, sizeof(*hw));
|
||||
hw->dev = &dev->dev;
|
||||
ide_std_init_ports(hw, base, ctl | 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_IDEDMA_PCI
|
||||
/**
|
||||
* ide_hwif_setup_dma - configure DMA interface
|
||||
* @hwif: IDE interface
|
||||
* @d: IDE port info
|
||||
*
|
||||
* Set up the DMA base for the interface. Enable the master bits as
|
||||
* necessary and attempt to bring the device DMA into a ready to use
|
||||
* state
|
||||
*/
|
||||
|
||||
int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
|
||||
if ((d->host_flags & IDE_HFLAG_NO_AUTODMA) == 0 ||
|
||||
((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
|
||||
(dev->class & 0x80))) {
|
||||
unsigned long base = ide_pci_dma_base(hwif, d);
|
||||
|
||||
if (base == 0)
|
||||
return -1;
|
||||
|
||||
hwif->dma_base = base;
|
||||
|
||||
if (hwif->dma_ops == NULL)
|
||||
hwif->dma_ops = &sff_dma_ops;
|
||||
|
||||
if (ide_pci_check_simplex(hwif, d) < 0)
|
||||
return -1;
|
||||
|
||||
if (ide_pci_set_master(dev, d->name) < 0)
|
||||
return -1;
|
||||
|
||||
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
||||
printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name);
|
||||
else
|
||||
printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n",
|
||||
hwif->name, base, base + 7);
|
||||
|
||||
hwif->extra_base = base + (hwif->channel ? 8 : 16);
|
||||
|
||||
if (ide_allocate_dma_engine(hwif))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_BLK_DEV_IDEDMA_PCI */
|
||||
|
||||
/**
|
||||
* ide_setup_pci_controller - set up IDE PCI
|
||||
* @dev: PCI device
|
||||
* @bars: PCI BARs mask
|
||||
* @d: IDE port info
|
||||
* @noisy: verbose flag
|
||||
*
|
||||
* Set up the PCI and controller side of the IDE interface. This brings
|
||||
* up the PCI side of the device, checks that the device is enabled
|
||||
* and enables it if need be
|
||||
*/
|
||||
|
||||
static int ide_setup_pci_controller(struct pci_dev *dev, int bars,
|
||||
const struct ide_port_info *d, int noisy)
|
||||
{
|
||||
int ret;
|
||||
u16 pcicmd;
|
||||
|
||||
if (noisy)
|
||||
ide_setup_pci_noise(dev, d);
|
||||
|
||||
ret = ide_pci_enable(dev, bars, d);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = pci_read_config_word(dev, PCI_COMMAND, &pcicmd);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s %s: error accessing PCI regs\n",
|
||||
d->name, pci_name(dev));
|
||||
goto out_free_bars;
|
||||
}
|
||||
if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */
|
||||
ret = ide_pci_configure(dev, d);
|
||||
if (ret < 0)
|
||||
goto out_free_bars;
|
||||
printk(KERN_INFO "%s %s: device enabled (Linux)\n",
|
||||
d->name, pci_name(dev));
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
out_free_bars:
|
||||
pci_release_selected_regions(dev, bars);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ide_pci_setup_ports - configure ports/devices on PCI IDE
|
||||
* @dev: PCI device
|
||||
* @d: IDE port info
|
||||
* @hw: struct ide_hw instances corresponding to this PCI IDE device
|
||||
* @hws: struct ide_hw pointers table to update
|
||||
*
|
||||
* Scan the interfaces attached to this device and do any
|
||||
* necessary per port setup. Attach the devices and ask the
|
||||
* generic DMA layer to do its work for us.
|
||||
*
|
||||
* Normally called automaticall from do_ide_pci_setup_device,
|
||||
* but is also used directly as a helper function by some controllers
|
||||
* where the chipset setup is not the default PCI IDE one.
|
||||
*/
|
||||
|
||||
void ide_pci_setup_ports(struct pci_dev *dev, const struct ide_port_info *d,
|
||||
struct ide_hw *hw, struct ide_hw **hws)
|
||||
{
|
||||
int channels = (d->host_flags & IDE_HFLAG_SINGLE) ? 1 : 2, port;
|
||||
u8 tmp;
|
||||
|
||||
/*
|
||||
* Set up the IDE ports
|
||||
*/
|
||||
|
||||
for (port = 0; port < channels; ++port) {
|
||||
const struct ide_pci_enablebit *e = &d->enablebits[port];
|
||||
|
||||
if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) ||
|
||||
(tmp & e->mask) != e->val)) {
|
||||
printk(KERN_INFO "%s %s: IDE port disabled\n",
|
||||
d->name, pci_name(dev));
|
||||
continue; /* port not enabled */
|
||||
}
|
||||
|
||||
if (ide_hw_configure(dev, d, port, hw + port))
|
||||
continue;
|
||||
|
||||
*(hws + port) = hw + port;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_setup_ports);
|
||||
|
||||
/*
|
||||
* ide_setup_pci_device() looks at the primary/secondary interfaces
|
||||
* on a PCI IDE device and, if they are enabled, prepares the IDE driver
|
||||
* for use with them. This generic code works for most PCI chipsets.
|
||||
*
|
||||
* One thing that is not standardized is the location of the
|
||||
* primary/secondary interface "enable/disable" bits. For chipsets that
|
||||
* we "know" about, this information is in the struct ide_port_info;
|
||||
* for all other chipsets, we just assume both interfaces are enabled.
|
||||
*/
|
||||
static int do_ide_setup_pci_device(struct pci_dev *dev,
|
||||
const struct ide_port_info *d,
|
||||
u8 noisy)
|
||||
{
|
||||
int pciirq, ret;
|
||||
|
||||
/*
|
||||
* Can we trust the reported IRQ?
|
||||
*/
|
||||
pciirq = dev->irq;
|
||||
|
||||
/*
|
||||
* This allows offboard ide-pci cards the enable a BIOS,
|
||||
* verify interrupt settings of split-mirror pci-config
|
||||
* space, place chipset into init-mode, and/or preserve
|
||||
* an interrupt if the card is not native ide support.
|
||||
*/
|
||||
ret = d->init_chipset ? d->init_chipset(dev) : 0;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (ide_pci_is_in_compatibility_mode(dev)) {
|
||||
if (noisy)
|
||||
printk(KERN_INFO "%s %s: not 100%% native mode: will "
|
||||
"probe irqs later\n", d->name, pci_name(dev));
|
||||
pciirq = 0;
|
||||
} else if (!pciirq && noisy) {
|
||||
printk(KERN_WARNING "%s %s: bad irq (%d): will probe later\n",
|
||||
d->name, pci_name(dev), pciirq);
|
||||
} else if (noisy) {
|
||||
printk(KERN_INFO "%s %s: 100%% native mode on irq %d\n",
|
||||
d->name, pci_name(dev), pciirq);
|
||||
}
|
||||
|
||||
ret = pciirq;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ide_pci_init_two(struct pci_dev *dev1, struct pci_dev *dev2,
|
||||
const struct ide_port_info *d, void *priv)
|
||||
{
|
||||
struct pci_dev *pdev[] = { dev1, dev2 };
|
||||
struct ide_host *host;
|
||||
int ret, i, n_ports = dev2 ? 4 : 2, bars;
|
||||
struct ide_hw hw[4], *hws[] = { NULL, NULL, NULL, NULL };
|
||||
|
||||
if (d->host_flags & IDE_HFLAG_SINGLE)
|
||||
bars = (1 << 2) - 1;
|
||||
else
|
||||
bars = (1 << 4) - 1;
|
||||
|
||||
if ((d->host_flags & IDE_HFLAG_NO_DMA) == 0) {
|
||||
if (d->host_flags & IDE_HFLAG_CS5520)
|
||||
bars |= (1 << 2);
|
||||
else
|
||||
bars |= (1 << 4);
|
||||
}
|
||||
|
||||
for (i = 0; i < n_ports / 2; i++) {
|
||||
ret = ide_setup_pci_controller(pdev[i], bars, d, !i);
|
||||
if (ret < 0) {
|
||||
if (i == 1)
|
||||
pci_release_selected_regions(pdev[0], bars);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ide_pci_setup_ports(pdev[i], d, &hw[i*2], &hws[i*2]);
|
||||
}
|
||||
|
||||
host = ide_host_alloc(d, hws, n_ports);
|
||||
if (host == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_bars;
|
||||
}
|
||||
|
||||
host->dev[0] = &dev1->dev;
|
||||
if (dev2)
|
||||
host->dev[1] = &dev2->dev;
|
||||
|
||||
host->host_priv = priv;
|
||||
host->irq_flags = IRQF_SHARED;
|
||||
|
||||
pci_set_drvdata(pdev[0], host);
|
||||
if (dev2)
|
||||
pci_set_drvdata(pdev[1], host);
|
||||
|
||||
for (i = 0; i < n_ports / 2; i++) {
|
||||
ret = do_ide_setup_pci_device(pdev[i], d, !i);
|
||||
|
||||
/*
|
||||
* FIXME: Mom, mom, they stole me the helper function to undo
|
||||
* do_ide_setup_pci_device() on the first device!
|
||||
*/
|
||||
if (ret < 0)
|
||||
goto out_free_bars;
|
||||
|
||||
/* fixup IRQ */
|
||||
if (ide_pci_is_in_compatibility_mode(pdev[i])) {
|
||||
hw[i*2].irq = pci_get_legacy_ide_irq(pdev[i], 0);
|
||||
hw[i*2 + 1].irq = pci_get_legacy_ide_irq(pdev[i], 1);
|
||||
} else
|
||||
hw[i*2 + 1].irq = hw[i*2].irq = ret;
|
||||
}
|
||||
|
||||
ret = ide_host_register(host, d, hws);
|
||||
if (ret)
|
||||
ide_host_free(host);
|
||||
else
|
||||
goto out;
|
||||
|
||||
out_free_bars:
|
||||
i = n_ports / 2;
|
||||
while (i--)
|
||||
pci_release_selected_regions(pdev[i], bars);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_init_two);
|
||||
|
||||
int ide_pci_init_one(struct pci_dev *dev, const struct ide_port_info *d,
|
||||
void *priv)
|
||||
{
|
||||
return ide_pci_init_two(dev, NULL, d, priv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_init_one);
|
||||
|
||||
void ide_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct pci_dev *dev2 = host->dev[1] ? to_pci_dev(host->dev[1]) : NULL;
|
||||
int bars;
|
||||
|
||||
if (host->host_flags & IDE_HFLAG_SINGLE)
|
||||
bars = (1 << 2) - 1;
|
||||
else
|
||||
bars = (1 << 4) - 1;
|
||||
|
||||
if ((host->host_flags & IDE_HFLAG_NO_DMA) == 0) {
|
||||
if (host->host_flags & IDE_HFLAG_CS5520)
|
||||
bars |= (1 << 2);
|
||||
else
|
||||
bars |= (1 << 4);
|
||||
}
|
||||
|
||||
ide_host_remove(host);
|
||||
|
||||
if (dev2)
|
||||
pci_release_selected_regions(dev2, bars);
|
||||
pci_release_selected_regions(dev, bars);
|
||||
|
||||
if (dev2)
|
||||
pci_disable_device(dev2);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int ide_pci_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
pci_save_state(dev);
|
||||
pci_disable_device(dev);
|
||||
pci_set_power_state(dev, pci_choose_state(dev, state));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_suspend);
|
||||
|
||||
int ide_pci_resume(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
int rc;
|
||||
|
||||
pci_set_power_state(dev, PCI_D0);
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pci_restore_state(dev);
|
||||
pci_set_master(dev);
|
||||
|
||||
if (host->init_chipset)
|
||||
host->init_chipset(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ide_pci_resume);
|
||||
#endif
|
@ -1,843 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2003 Red Hat
|
||||
* Copyright (C) 2007-2008 MontaVista Software, Inc.
|
||||
* Copyright (C) 2007-2008 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* Documentation for CMD680:
|
||||
* http://gkernel.sourceforge.net/specs/sii/sii-0680a-v1.31.pdf.bz2
|
||||
*
|
||||
* Documentation for SiI 3112:
|
||||
* http://gkernel.sourceforge.net/specs/sii/3112A_SiI-DS-0095-B2.pdf.bz2
|
||||
*
|
||||
* Errata and other documentation only available under NDA.
|
||||
*
|
||||
*
|
||||
* FAQ Items:
|
||||
* If you are using Marvell SATA-IDE adapters with Maxtor drives
|
||||
* ensure the system is set up for ATA100/UDMA5, not UDMA6.
|
||||
*
|
||||
* If you are using WD drives with SATA bridges you must set the
|
||||
* drive to "Single". "Master" will hang.
|
||||
*
|
||||
* If you have strange problems with nVidia chipset systems please
|
||||
* see the SI support documentation and update your system BIOS
|
||||
* if necessary
|
||||
*
|
||||
* The Dell DRAC4 has some interesting features including effectively hot
|
||||
* unplugging/replugging the virtual CD interface when the DRAC is reset.
|
||||
* This often causes drivers/ide/siimage to panic but is ok with the rather
|
||||
* smarter code in libata.
|
||||
*
|
||||
* TODO:
|
||||
* - VDMA support
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "siimage"
|
||||
|
||||
/**
|
||||
* pdev_is_sata - check if device is SATA
|
||||
* @pdev: PCI device to check
|
||||
*
|
||||
* Returns true if this is a SATA controller
|
||||
*/
|
||||
|
||||
static int pdev_is_sata(struct pci_dev *pdev)
|
||||
{
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
switch (pdev->device) {
|
||||
case PCI_DEVICE_ID_SII_3112:
|
||||
case PCI_DEVICE_ID_SII_1210SA:
|
||||
return 1;
|
||||
case PCI_DEVICE_ID_SII_680:
|
||||
return 0;
|
||||
}
|
||||
BUG();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_sata - check if hwif is SATA
|
||||
* @hwif: interface to check
|
||||
*
|
||||
* Returns true if this is a SATA controller
|
||||
*/
|
||||
|
||||
static inline int is_sata(ide_hwif_t *hwif)
|
||||
{
|
||||
return pdev_is_sata(to_pci_dev(hwif->dev));
|
||||
}
|
||||
|
||||
/**
|
||||
* siimage_selreg - return register base
|
||||
* @hwif: interface
|
||||
* @r: config offset
|
||||
*
|
||||
* Turn a config register offset into the right address in either
|
||||
* PCI space or MMIO space to access the control register in question
|
||||
* Thankfully this is a configuration operation, so isn't performance
|
||||
* critical.
|
||||
*/
|
||||
|
||||
static unsigned long siimage_selreg(ide_hwif_t *hwif, int r)
|
||||
{
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
|
||||
base += 0xA0 + r;
|
||||
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
||||
base += hwif->channel << 6;
|
||||
else
|
||||
base += hwif->channel << 4;
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* siimage_seldev - return register base
|
||||
* @hwif: interface
|
||||
* @r: config offset
|
||||
*
|
||||
* Turn a config register offset into the right address in either
|
||||
* PCI space or MMIO space to access the control register in question
|
||||
* including accounting for the unit shift.
|
||||
*/
|
||||
|
||||
static inline unsigned long siimage_seldev(ide_drive_t *drive, int r)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
u8 unit = drive->dn & 1;
|
||||
|
||||
base += 0xA0 + r;
|
||||
if (hwif->host_flags & IDE_HFLAG_MMIO)
|
||||
base += hwif->channel << 6;
|
||||
else
|
||||
base += hwif->channel << 4;
|
||||
base |= unit << unit;
|
||||
return base;
|
||||
}
|
||||
|
||||
static u8 sil_ioread8(struct pci_dev *dev, unsigned long addr)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
u8 tmp = 0;
|
||||
|
||||
if (host->host_priv)
|
||||
tmp = readb((void __iomem *)addr);
|
||||
else
|
||||
pci_read_config_byte(dev, addr, &tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static u16 sil_ioread16(struct pci_dev *dev, unsigned long addr)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
u16 tmp = 0;
|
||||
|
||||
if (host->host_priv)
|
||||
tmp = readw((void __iomem *)addr);
|
||||
else
|
||||
pci_read_config_word(dev, addr, &tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void sil_iowrite8(struct pci_dev *dev, u8 val, unsigned long addr)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
|
||||
if (host->host_priv)
|
||||
writeb(val, (void __iomem *)addr);
|
||||
else
|
||||
pci_write_config_byte(dev, addr, val);
|
||||
}
|
||||
|
||||
static void sil_iowrite16(struct pci_dev *dev, u16 val, unsigned long addr)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
|
||||
if (host->host_priv)
|
||||
writew(val, (void __iomem *)addr);
|
||||
else
|
||||
pci_write_config_word(dev, addr, val);
|
||||
}
|
||||
|
||||
static void sil_iowrite32(struct pci_dev *dev, u32 val, unsigned long addr)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
|
||||
if (host->host_priv)
|
||||
writel(val, (void __iomem *)addr);
|
||||
else
|
||||
pci_write_config_dword(dev, addr, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_udma_filter - compute UDMA mask
|
||||
* @drive: IDE device
|
||||
*
|
||||
* Compute the available UDMA speeds for the device on the interface.
|
||||
*
|
||||
* For the CMD680 this depends on the clocking mode (scsc), for the
|
||||
* SI3112 SATA controller life is a bit simpler.
|
||||
*/
|
||||
|
||||
static u8 sil_pata_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
u8 scsc, mask = 0;
|
||||
|
||||
base += (hwif->host_flags & IDE_HFLAG_MMIO) ? 0x4A : 0x8A;
|
||||
|
||||
scsc = sil_ioread8(dev, base);
|
||||
|
||||
switch (scsc & 0x30) {
|
||||
case 0x10: /* 133 */
|
||||
mask = ATA_UDMA6;
|
||||
break;
|
||||
case 0x20: /* 2xPCI */
|
||||
mask = ATA_UDMA6;
|
||||
break;
|
||||
case 0x00: /* 100 */
|
||||
mask = ATA_UDMA5;
|
||||
break;
|
||||
default: /* Disabled ? */
|
||||
BUG();
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static u8 sil_sata_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
char *m = (char *)&drive->id[ATA_ID_PROD];
|
||||
|
||||
return strstr(m, "Maxtor") ? ATA_UDMA5 : ATA_UDMA6;
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_set_pio_mode - set host controller for PIO mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Load the timing settings for this device mode into the
|
||||
* controller.
|
||||
*/
|
||||
|
||||
static void sil_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
static const u16 tf_speed[] = { 0x328a, 0x2283, 0x1281, 0x10c3, 0x10c1 };
|
||||
static const u16 data_speed[] = { 0x328a, 0x2283, 0x1104, 0x10c3, 0x10c1 };
|
||||
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
ide_drive_t *pair = ide_get_pair_dev(drive);
|
||||
u32 speedt = 0;
|
||||
u16 speedp = 0;
|
||||
unsigned long addr = siimage_seldev(drive, 0x04);
|
||||
unsigned long tfaddr = siimage_selreg(hwif, 0x02);
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
u8 tf_pio = pio;
|
||||
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
||||
u8 addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
|
||||
: (mmio ? 0xB4 : 0x80);
|
||||
u8 mode = 0;
|
||||
u8 unit = drive->dn & 1;
|
||||
|
||||
/* trim *taskfile* PIO to the slowest of the master/slave */
|
||||
if (pair) {
|
||||
u8 pair_pio = pair->pio_mode - XFER_PIO_0;
|
||||
|
||||
if (pair_pio < tf_pio)
|
||||
tf_pio = pair_pio;
|
||||
}
|
||||
|
||||
/* cheat for now and use the docs */
|
||||
speedp = data_speed[pio];
|
||||
speedt = tf_speed[tf_pio];
|
||||
|
||||
sil_iowrite16(dev, speedp, addr);
|
||||
sil_iowrite16(dev, speedt, tfaddr);
|
||||
|
||||
/* now set up IORDY */
|
||||
speedp = sil_ioread16(dev, tfaddr - 2);
|
||||
speedp &= ~0x200;
|
||||
|
||||
mode = sil_ioread8(dev, base + addr_mask);
|
||||
mode &= ~(unit ? 0x30 : 0x03);
|
||||
|
||||
if (ide_pio_need_iordy(drive, pio)) {
|
||||
speedp |= 0x200;
|
||||
mode |= unit ? 0x10 : 0x01;
|
||||
}
|
||||
|
||||
sil_iowrite16(dev, speedp, tfaddr - 2);
|
||||
sil_iowrite8(dev, mode, base + addr_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_set_dma_mode - set host controller for DMA mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* Tune the SiI chipset for the desired DMA mode.
|
||||
*/
|
||||
|
||||
static void sil_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
static const u8 ultra6[] = { 0x0F, 0x0B, 0x07, 0x05, 0x03, 0x02, 0x01 };
|
||||
static const u8 ultra5[] = { 0x0C, 0x07, 0x05, 0x04, 0x02, 0x01 };
|
||||
static const u16 dma[] = { 0x2208, 0x10C2, 0x10C1 };
|
||||
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
u16 ultra = 0, multi = 0;
|
||||
u8 mode = 0, unit = drive->dn & 1;
|
||||
u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
|
||||
u8 scsc = 0, addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
|
||||
: (mmio ? 0xB4 : 0x80);
|
||||
unsigned long ma = siimage_seldev(drive, 0x08);
|
||||
unsigned long ua = siimage_seldev(drive, 0x0C);
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
scsc = sil_ioread8 (dev, base + (mmio ? 0x4A : 0x8A));
|
||||
mode = sil_ioread8 (dev, base + addr_mask);
|
||||
multi = sil_ioread16(dev, ma);
|
||||
ultra = sil_ioread16(dev, ua);
|
||||
|
||||
mode &= ~(unit ? 0x30 : 0x03);
|
||||
ultra &= ~0x3F;
|
||||
scsc = ((scsc & 0x30) == 0x00) ? 0 : 1;
|
||||
|
||||
scsc = is_sata(hwif) ? 1 : scsc;
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
multi = dma[2];
|
||||
ultra |= scsc ? ultra6[speed - XFER_UDMA_0] :
|
||||
ultra5[speed - XFER_UDMA_0];
|
||||
mode |= unit ? 0x30 : 0x03;
|
||||
} else {
|
||||
multi = dma[speed - XFER_MW_DMA_0];
|
||||
mode |= unit ? 0x20 : 0x02;
|
||||
}
|
||||
|
||||
sil_iowrite8 (dev, mode, base + addr_mask);
|
||||
sil_iowrite16(dev, multi, ma);
|
||||
sil_iowrite16(dev, ultra, ua);
|
||||
}
|
||||
|
||||
static int sil_test_irq(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long addr = siimage_selreg(hwif, 1);
|
||||
u8 val = sil_ioread8(dev, addr);
|
||||
|
||||
/* Return 1 if INTRQ asserted */
|
||||
return (val & 8) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* siimage_mmio_dma_test_irq - check we caused an IRQ
|
||||
* @drive: drive we are testing
|
||||
*
|
||||
* Check if we caused an IDE DMA interrupt. We may also have caused
|
||||
* SATA status interrupts, if so we clean them up and continue.
|
||||
*/
|
||||
|
||||
static int siimage_mmio_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *sata_error_addr
|
||||
= (void __iomem *)hwif->sata_scr[SATA_ERROR_OFFSET];
|
||||
|
||||
if (sata_error_addr) {
|
||||
unsigned long base = (unsigned long)hwif->hwif_data;
|
||||
u32 ext_stat = readl((void __iomem *)(base + 0x10));
|
||||
u8 watchdog = 0;
|
||||
|
||||
if (ext_stat & ((hwif->channel) ? 0x40 : 0x10)) {
|
||||
u32 sata_error = readl(sata_error_addr);
|
||||
|
||||
writel(sata_error, sata_error_addr);
|
||||
watchdog = (sata_error & 0x00680000) ? 1 : 0;
|
||||
printk(KERN_WARNING "%s: sata_error = 0x%08x, "
|
||||
"watchdog = %d, %s\n",
|
||||
drive->name, sata_error, watchdog, __func__);
|
||||
} else
|
||||
watchdog = (ext_stat & 0x8000) ? 1 : 0;
|
||||
|
||||
ext_stat >>= 16;
|
||||
if (!(ext_stat & 0x0404) && !watchdog)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return 1 if INTR asserted */
|
||||
if (readb((void __iomem *)(hwif->dma_base + ATA_DMA_STATUS)) & 4)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int siimage_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->hwif->host_flags & IDE_HFLAG_MMIO)
|
||||
return siimage_mmio_dma_test_irq(drive);
|
||||
else
|
||||
return ide_dma_test_irq(drive);
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_sata_reset_poll - wait for SATA reset
|
||||
* @drive: drive we are resetting
|
||||
*
|
||||
* Poll the SATA phy and see whether it has come back from the dead
|
||||
* yet.
|
||||
*/
|
||||
|
||||
static blk_status_t sil_sata_reset_poll(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *sata_status_addr
|
||||
= (void __iomem *)hwif->sata_scr[SATA_STATUS_OFFSET];
|
||||
|
||||
if (sata_status_addr) {
|
||||
/* SATA Status is available only when in MMIO mode */
|
||||
u32 sata_stat = readl(sata_status_addr);
|
||||
|
||||
if ((sata_stat & 0x03) != 0x03) {
|
||||
printk(KERN_WARNING "%s: reset phy dead, status=0x%08x\n",
|
||||
hwif->name, sata_stat);
|
||||
return BLK_STS_IOERR;
|
||||
}
|
||||
}
|
||||
|
||||
return BLK_STS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_sata_pre_reset - reset hook
|
||||
* @drive: IDE device being reset
|
||||
*
|
||||
* For the SATA devices we need to handle recalibration/geometry
|
||||
* differently
|
||||
*/
|
||||
|
||||
static void sil_sata_pre_reset(ide_drive_t *drive)
|
||||
{
|
||||
if (drive->media == ide_disk) {
|
||||
drive->special_flags &=
|
||||
~(IDE_SFLAG_SET_GEOMETRY | IDE_SFLAG_RECALIBRATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_siimage - set up an SI device
|
||||
* @dev: PCI device
|
||||
*
|
||||
* Perform the initial PCI set up for this device. Attempt to switch
|
||||
* to 133 MHz clocking if the system isn't already set up to do it.
|
||||
*/
|
||||
|
||||
static int init_chipset_siimage(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
void __iomem *ioaddr = host->host_priv;
|
||||
unsigned long base, scsc_addr;
|
||||
u8 rev = dev->revision, tmp;
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, rev ? 1 : 255);
|
||||
|
||||
if (ioaddr)
|
||||
pci_set_master(dev);
|
||||
|
||||
base = (unsigned long)ioaddr;
|
||||
|
||||
if (ioaddr && pdev_is_sata(dev)) {
|
||||
u32 tmp32, irq_mask;
|
||||
|
||||
/* make sure IDE0/1 interrupts are not masked */
|
||||
irq_mask = (1 << 22) | (1 << 23);
|
||||
tmp32 = readl(ioaddr + 0x48);
|
||||
if (tmp32 & irq_mask) {
|
||||
tmp32 &= ~irq_mask;
|
||||
writel(tmp32, ioaddr + 0x48);
|
||||
readl(ioaddr + 0x48); /* flush */
|
||||
}
|
||||
writel(0, ioaddr + 0x148);
|
||||
writel(0, ioaddr + 0x1C8);
|
||||
}
|
||||
|
||||
sil_iowrite8(dev, 0, base ? (base + 0xB4) : 0x80);
|
||||
sil_iowrite8(dev, 0, base ? (base + 0xF4) : 0x84);
|
||||
|
||||
scsc_addr = base ? (base + 0x4A) : 0x8A;
|
||||
tmp = sil_ioread8(dev, scsc_addr);
|
||||
|
||||
switch (tmp & 0x30) {
|
||||
case 0x00:
|
||||
/* On 100 MHz clocking, try and switch to 133 MHz */
|
||||
sil_iowrite8(dev, tmp | 0x10, scsc_addr);
|
||||
break;
|
||||
case 0x30:
|
||||
/* Clocking is disabled, attempt to force 133MHz clocking. */
|
||||
sil_iowrite8(dev, tmp & ~0x20, scsc_addr);
|
||||
case 0x10:
|
||||
/* On 133Mhz clocking. */
|
||||
break;
|
||||
case 0x20:
|
||||
/* On PCIx2 clocking. */
|
||||
break;
|
||||
}
|
||||
|
||||
tmp = sil_ioread8(dev, scsc_addr);
|
||||
|
||||
sil_iowrite8 (dev, 0x72, base + 0xA1);
|
||||
sil_iowrite16(dev, 0x328A, base + 0xA2);
|
||||
sil_iowrite32(dev, 0x62DD62DD, base + 0xA4);
|
||||
sil_iowrite32(dev, 0x43924392, base + 0xA8);
|
||||
sil_iowrite32(dev, 0x40094009, base + 0xAC);
|
||||
sil_iowrite8 (dev, 0x72, base ? (base + 0xE1) : 0xB1);
|
||||
sil_iowrite16(dev, 0x328A, base ? (base + 0xE2) : 0xB2);
|
||||
sil_iowrite32(dev, 0x62DD62DD, base ? (base + 0xE4) : 0xB4);
|
||||
sil_iowrite32(dev, 0x43924392, base ? (base + 0xE8) : 0xB8);
|
||||
sil_iowrite32(dev, 0x40094009, base ? (base + 0xEC) : 0xBC);
|
||||
|
||||
if (base && pdev_is_sata(dev)) {
|
||||
writel(0xFFFF0000, ioaddr + 0x108);
|
||||
writel(0xFFFF0000, ioaddr + 0x188);
|
||||
writel(0x00680000, ioaddr + 0x148);
|
||||
writel(0x00680000, ioaddr + 0x1C8);
|
||||
}
|
||||
|
||||
/* report the clocking mode of the controller */
|
||||
if (!pdev_is_sata(dev)) {
|
||||
static const char *clk_str[] =
|
||||
{ "== 100", "== 133", "== 2X PCI", "DISABLED!" };
|
||||
|
||||
tmp >>= 4;
|
||||
printk(KERN_INFO DRV_NAME " %s: BASE CLOCK %s\n",
|
||||
pci_name(dev), clk_str[tmp & 3]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_mmio_iops_siimage - set up the iops for MMIO
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* The basic setup here is fairly simple, we can use standard MMIO
|
||||
* operations. However we do have to set the taskfile register offsets
|
||||
* by hand as there isn't a standard defined layout for them this time.
|
||||
*
|
||||
* The hardware supports buffered taskfiles and also some rather nice
|
||||
* extended PRD tables. For better SI3112 support use the libata driver
|
||||
*/
|
||||
|
||||
static void init_mmio_iops_siimage(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
void *addr = host->host_priv;
|
||||
u8 ch = hwif->channel;
|
||||
struct ide_io_ports *io_ports = &hwif->io_ports;
|
||||
unsigned long base;
|
||||
|
||||
/*
|
||||
* Fill in the basic hwif bits
|
||||
*/
|
||||
hwif->host_flags |= IDE_HFLAG_MMIO;
|
||||
|
||||
hwif->hwif_data = addr;
|
||||
|
||||
/*
|
||||
* Now set up the hw. We have to do this ourselves as the
|
||||
* MMIO layout isn't the same as the standard port based I/O.
|
||||
*/
|
||||
memset(io_ports, 0, sizeof(*io_ports));
|
||||
|
||||
base = (unsigned long)addr;
|
||||
if (ch)
|
||||
base += 0xC0;
|
||||
else
|
||||
base += 0x80;
|
||||
|
||||
/*
|
||||
* The buffered task file doesn't have status/control, so we
|
||||
* can't currently use it sanely since we want to use LBA48 mode.
|
||||
*/
|
||||
io_ports->data_addr = base;
|
||||
io_ports->error_addr = base + 1;
|
||||
io_ports->nsect_addr = base + 2;
|
||||
io_ports->lbal_addr = base + 3;
|
||||
io_ports->lbam_addr = base + 4;
|
||||
io_ports->lbah_addr = base + 5;
|
||||
io_ports->device_addr = base + 6;
|
||||
io_ports->status_addr = base + 7;
|
||||
io_ports->ctl_addr = base + 10;
|
||||
|
||||
if (pdev_is_sata(dev)) {
|
||||
base = (unsigned long)addr;
|
||||
if (ch)
|
||||
base += 0x80;
|
||||
hwif->sata_scr[SATA_STATUS_OFFSET] = base + 0x104;
|
||||
hwif->sata_scr[SATA_ERROR_OFFSET] = base + 0x108;
|
||||
hwif->sata_scr[SATA_CONTROL_OFFSET] = base + 0x100;
|
||||
}
|
||||
|
||||
hwif->irq = dev->irq;
|
||||
|
||||
hwif->dma_base = (unsigned long)addr + (ch ? 0x08 : 0x00);
|
||||
}
|
||||
|
||||
static int is_dev_seagate_sata(ide_drive_t *drive)
|
||||
{
|
||||
const char *s = (const char *)&drive->id[ATA_ID_PROD];
|
||||
unsigned len = strnlen(s, ATA_ID_PROD_LEN);
|
||||
|
||||
if ((len > 4) && (!memcmp(s, "ST", 2)))
|
||||
if ((!memcmp(s + len - 2, "AS", 2)) ||
|
||||
(!memcmp(s + len - 3, "ASL", 3))) {
|
||||
printk(KERN_INFO "%s: applying pessimistic Seagate "
|
||||
"errata fix\n", drive->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_quirkproc - post probe fixups
|
||||
* @drive: drive
|
||||
*
|
||||
* Called after drive probe we use this to decide whether the
|
||||
* Seagate fixup must be applied. This used to be in init_iops but
|
||||
* that can occur before we know what drives are present.
|
||||
*/
|
||||
|
||||
static void sil_quirkproc(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
|
||||
/* Try and rise the rqsize */
|
||||
if (!is_sata(hwif) || !is_dev_seagate_sata(drive))
|
||||
hwif->rqsize = 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_iops_siimage - set up iops
|
||||
* @hwif: interface to set up
|
||||
*
|
||||
* Do the basic setup for the SIIMAGE hardware interface
|
||||
* and then do the MMIO setup if we can. This is the first
|
||||
* look in we get for setting up the hwif so that we
|
||||
* can get the iops right before using them.
|
||||
*/
|
||||
|
||||
static void init_iops_siimage(ide_hwif_t *hwif)
|
||||
{
|
||||
struct ide_host *host = dev_get_drvdata(hwif->dev);
|
||||
|
||||
hwif->hwif_data = NULL;
|
||||
|
||||
/* Pessimal until we finish probing */
|
||||
hwif->rqsize = 15;
|
||||
|
||||
if (host->host_priv)
|
||||
init_mmio_iops_siimage(hwif);
|
||||
}
|
||||
|
||||
/**
|
||||
* sil_cable_detect - cable detection
|
||||
* @hwif: interface to check
|
||||
*
|
||||
* Check for the presence of an ATA66 capable cable on the interface.
|
||||
*/
|
||||
|
||||
static u8 sil_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long addr = siimage_selreg(hwif, 0);
|
||||
u8 ata66 = sil_ioread8(dev, addr);
|
||||
|
||||
return (ata66 & 0x01) ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops sil_pata_port_ops = {
|
||||
.set_pio_mode = sil_set_pio_mode,
|
||||
.set_dma_mode = sil_set_dma_mode,
|
||||
.quirkproc = sil_quirkproc,
|
||||
.test_irq = sil_test_irq,
|
||||
.udma_filter = sil_pata_udma_filter,
|
||||
.cable_detect = sil_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops sil_sata_port_ops = {
|
||||
.set_pio_mode = sil_set_pio_mode,
|
||||
.set_dma_mode = sil_set_dma_mode,
|
||||
.reset_poll = sil_sata_reset_poll,
|
||||
.pre_reset = sil_sata_pre_reset,
|
||||
.quirkproc = sil_quirkproc,
|
||||
.test_irq = sil_test_irq,
|
||||
.udma_filter = sil_sata_udma_filter,
|
||||
.cable_detect = sil_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops sil_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = ide_dma_start,
|
||||
.dma_end = ide_dma_end,
|
||||
.dma_test_irq = siimage_dma_test_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
#define DECLARE_SII_DEV(p_ops) \
|
||||
{ \
|
||||
.name = DRV_NAME, \
|
||||
.init_chipset = init_chipset_siimage, \
|
||||
.init_iops = init_iops_siimage, \
|
||||
.port_ops = p_ops, \
|
||||
.dma_ops = &sil_dma_ops, \
|
||||
.pio_mask = ATA_PIO4, \
|
||||
.mwdma_mask = ATA_MWDMA2, \
|
||||
.udma_mask = ATA_UDMA6, \
|
||||
}
|
||||
|
||||
static const struct ide_port_info siimage_chipsets[] = {
|
||||
/* 0: SiI680 */ DECLARE_SII_DEV(&sil_pata_port_ops),
|
||||
/* 1: SiI3112 */ DECLARE_SII_DEV(&sil_sata_port_ops)
|
||||
};
|
||||
|
||||
/**
|
||||
* siimage_init_one - PCI layer discovery entry
|
||||
* @dev: PCI device
|
||||
* @id: ident table entry
|
||||
*
|
||||
* Called by the PCI code when it finds an SiI680 or SiI3112 controller.
|
||||
* We then use the IDE PCI generic helper to do most of the work.
|
||||
*/
|
||||
|
||||
static int siimage_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *ioaddr = NULL;
|
||||
resource_size_t bar5 = pci_resource_start(dev, 5);
|
||||
unsigned long barsize = pci_resource_len(dev, 5);
|
||||
int rc;
|
||||
struct ide_port_info d;
|
||||
u8 idx = id->driver_data;
|
||||
u8 BA5_EN;
|
||||
|
||||
d = siimage_chipsets[idx];
|
||||
|
||||
if (idx) {
|
||||
static int first = 1;
|
||||
|
||||
if (first) {
|
||||
printk(KERN_INFO DRV_NAME ": For full SATA support you "
|
||||
"should use the libata sata_sil module.\n");
|
||||
first = 0;
|
||||
}
|
||||
|
||||
d.host_flags |= IDE_HFLAG_NO_ATAPI_DMA;
|
||||
}
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
pci_read_config_byte(dev, 0x8A, &BA5_EN);
|
||||
if ((BA5_EN & 0x01) || bar5) {
|
||||
/*
|
||||
* Drop back to PIO if we can't map the MMIO. Some systems
|
||||
* seem to get terminally confused in the PCI spaces.
|
||||
*/
|
||||
if (!request_mem_region(bar5, barsize, d.name)) {
|
||||
printk(KERN_WARNING DRV_NAME " %s: MMIO ports not "
|
||||
"available\n", pci_name(dev));
|
||||
} else {
|
||||
ioaddr = pci_ioremap_bar(dev, 5);
|
||||
if (ioaddr == NULL)
|
||||
release_mem_region(bar5, barsize);
|
||||
}
|
||||
}
|
||||
|
||||
rc = ide_pci_init_one(dev, &d, ioaddr);
|
||||
if (rc) {
|
||||
if (ioaddr) {
|
||||
iounmap(ioaddr);
|
||||
release_mem_region(bar5, barsize);
|
||||
}
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void siimage_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
void __iomem *ioaddr = host->host_priv;
|
||||
|
||||
ide_pci_remove(dev);
|
||||
|
||||
if (ioaddr) {
|
||||
resource_size_t bar5 = pci_resource_start(dev, 5);
|
||||
unsigned long barsize = pci_resource_len(dev, 5);
|
||||
|
||||
iounmap(ioaddr);
|
||||
release_mem_region(bar5, barsize);
|
||||
}
|
||||
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id siimage_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), 0 },
|
||||
#ifdef CONFIG_BLK_DEV_IDE_SATA
|
||||
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_3112), 1 },
|
||||
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_1210SA), 1 },
|
||||
#endif
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, siimage_pci_tbl);
|
||||
|
||||
static struct pci_driver siimage_pci_driver = {
|
||||
.name = "SiI_IDE",
|
||||
.id_table = siimage_pci_tbl,
|
||||
.probe = siimage_init_one,
|
||||
.remove = siimage_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init siimage_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&siimage_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit siimage_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&siimage_pci_driver);
|
||||
}
|
||||
|
||||
module_init(siimage_ide_init);
|
||||
module_exit(siimage_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick, Alan Cox");
|
||||
MODULE_DESCRIPTION("PCI driver module for SiI IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,637 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 1999-2000 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2002 Lionel Bouton <Lionel.Bouton@inet6.fr>, Maintainer
|
||||
* Copyright (C) 2003 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (C) 2007-2009 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
*
|
||||
* Thanks :
|
||||
*
|
||||
* SiS Taiwan : for direct support and hardware.
|
||||
* Daniela Engert : for initial ATA100 advices and numerous others.
|
||||
* John Fremlin, Manfred Spraul, Dave Morgan, Peter Kjellerstedt :
|
||||
* for checking code correctness, providing patches.
|
||||
*
|
||||
*
|
||||
* Original tests and design on the SiS620 chipset.
|
||||
* ATA100 tests and design on the SiS735 chipset.
|
||||
* ATA16/33 support from specs
|
||||
* ATA133 support for SiS961/962 by L.C. Chang <lcchang@sis.com.tw>
|
||||
* ATA133 961/962/963 fixes by Vojtech Pavlik <vojtech@suse.cz>
|
||||
*
|
||||
* Documentation:
|
||||
* SiS chipset documentation available under NDA to companies only
|
||||
* (not to individuals).
|
||||
*/
|
||||
|
||||
/*
|
||||
* The original SiS5513 comes from a SiS5511/55112/5513 chipset. The original
|
||||
* SiS5513 was also used in the SiS5596/5513 chipset. Thus if we see a SiS5511
|
||||
* or SiS5596, we can assume we see the first MWDMA-16 capable SiS5513 chip.
|
||||
*
|
||||
* Later SiS chipsets integrated the 5513 functionality into the NorthBridge,
|
||||
* starting with SiS5571 and up to SiS745. The PCI ID didn't change, though. We
|
||||
* can figure out that we have a more modern and more capable 5513 by looking
|
||||
* for the respective NorthBridge IDs.
|
||||
*
|
||||
* Even later (96x family) SiS chipsets use the MuTIOL link and place the 5513
|
||||
* into the SouthBrige. Here we cannot rely on looking up the NorthBridge PCI
|
||||
* ID, while the now ATA-133 capable 5513 still has the same PCI ID.
|
||||
* Fortunately the 5513 can be 'unmasked' by fiddling with some config space
|
||||
* bits, changing its device id to the true one - 5517 for 961 and 5518 for
|
||||
* 962/963.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#define DRV_NAME "sis5513"
|
||||
|
||||
/* registers layout and init values are chipset family dependent */
|
||||
#undef ATA_16
|
||||
#define ATA_16 0x01
|
||||
#define ATA_33 0x02
|
||||
#define ATA_66 0x03
|
||||
#define ATA_100a 0x04 /* SiS730/SiS550 is ATA100 with ATA66 layout */
|
||||
#define ATA_100 0x05
|
||||
#define ATA_133a 0x06 /* SiS961b with 133 support */
|
||||
#define ATA_133 0x07 /* SiS962/963 */
|
||||
|
||||
static u8 chipset_family;
|
||||
|
||||
/*
|
||||
* Devices supported
|
||||
*/
|
||||
static const struct {
|
||||
const char *name;
|
||||
u16 host_id;
|
||||
u8 chipset_family;
|
||||
u8 flags;
|
||||
} SiSHostChipInfo[] = {
|
||||
{ "SiS968", PCI_DEVICE_ID_SI_968, ATA_133 },
|
||||
{ "SiS966", PCI_DEVICE_ID_SI_966, ATA_133 },
|
||||
{ "SiS965", PCI_DEVICE_ID_SI_965, ATA_133 },
|
||||
{ "SiS745", PCI_DEVICE_ID_SI_745, ATA_100 },
|
||||
{ "SiS735", PCI_DEVICE_ID_SI_735, ATA_100 },
|
||||
{ "SiS733", PCI_DEVICE_ID_SI_733, ATA_100 },
|
||||
{ "SiS635", PCI_DEVICE_ID_SI_635, ATA_100 },
|
||||
{ "SiS633", PCI_DEVICE_ID_SI_633, ATA_100 },
|
||||
|
||||
{ "SiS730", PCI_DEVICE_ID_SI_730, ATA_100a },
|
||||
{ "SiS550", PCI_DEVICE_ID_SI_550, ATA_100a },
|
||||
|
||||
{ "SiS640", PCI_DEVICE_ID_SI_640, ATA_66 },
|
||||
{ "SiS630", PCI_DEVICE_ID_SI_630, ATA_66 },
|
||||
{ "SiS620", PCI_DEVICE_ID_SI_620, ATA_66 },
|
||||
{ "SiS540", PCI_DEVICE_ID_SI_540, ATA_66 },
|
||||
{ "SiS530", PCI_DEVICE_ID_SI_530, ATA_66 },
|
||||
|
||||
{ "SiS5600", PCI_DEVICE_ID_SI_5600, ATA_33 },
|
||||
{ "SiS5598", PCI_DEVICE_ID_SI_5598, ATA_33 },
|
||||
{ "SiS5597", PCI_DEVICE_ID_SI_5597, ATA_33 },
|
||||
{ "SiS5591/2", PCI_DEVICE_ID_SI_5591, ATA_33 },
|
||||
{ "SiS5582", PCI_DEVICE_ID_SI_5582, ATA_33 },
|
||||
{ "SiS5581", PCI_DEVICE_ID_SI_5581, ATA_33 },
|
||||
|
||||
{ "SiS5596", PCI_DEVICE_ID_SI_5596, ATA_16 },
|
||||
{ "SiS5571", PCI_DEVICE_ID_SI_5571, ATA_16 },
|
||||
{ "SiS5517", PCI_DEVICE_ID_SI_5517, ATA_16 },
|
||||
{ "SiS551x", PCI_DEVICE_ID_SI_5511, ATA_16 },
|
||||
};
|
||||
|
||||
/* Cycle time bits and values vary across chip dma capabilities
|
||||
These three arrays hold the register layout and the values to set.
|
||||
Indexed by chipset_family and (dma_mode - XFER_UDMA_0) */
|
||||
|
||||
/* {0, ATA_16, ATA_33, ATA_66, ATA_100a, ATA_100, ATA_133} */
|
||||
static u8 cycle_time_offset[] = { 0, 0, 5, 4, 4, 0, 0 };
|
||||
static u8 cycle_time_range[] = { 0, 0, 2, 3, 3, 4, 4 };
|
||||
static u8 cycle_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, /* no UDMA */
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, /* no UDMA */
|
||||
{ 3, 2, 1, 0, 0, 0, 0 }, /* ATA_33 */
|
||||
{ 7, 5, 3, 2, 1, 0, 0 }, /* ATA_66 */
|
||||
{ 7, 5, 3, 2, 1, 0, 0 }, /* ATA_100a (730 specific),
|
||||
different cycle_time range and offset */
|
||||
{ 11, 7, 5, 4, 2, 1, 0 }, /* ATA_100 */
|
||||
{ 15, 10, 7, 5, 3, 2, 1 }, /* ATA_133a (earliest 691 southbridges) */
|
||||
{ 15, 10, 7, 5, 3, 2, 1 }, /* ATA_133 */
|
||||
};
|
||||
/* CRC Valid Setup Time vary across IDE clock setting 33/66/100/133
|
||||
See SiS962 data sheet for more detail */
|
||||
static u8 cvs_time_value[][XFER_UDMA_6 - XFER_UDMA_0 + 1] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, /* no UDMA */
|
||||
{ 0, 0, 0, 0, 0, 0, 0 }, /* no UDMA */
|
||||
{ 2, 1, 1, 0, 0, 0, 0 },
|
||||
{ 4, 3, 2, 1, 0, 0, 0 },
|
||||
{ 4, 3, 2, 1, 0, 0, 0 },
|
||||
{ 6, 4, 3, 1, 1, 1, 0 },
|
||||
{ 9, 6, 4, 2, 2, 2, 2 },
|
||||
{ 9, 6, 4, 2, 2, 2, 2 },
|
||||
};
|
||||
/* Initialize time, Active time, Recovery time vary across
|
||||
IDE clock settings. These 3 arrays hold the register value
|
||||
for PIO0/1/2/3/4 and DMA0/1/2 mode in order */
|
||||
static u8 ini_time_value[][8] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 2, 1, 0, 0, 0, 1, 0, 0 },
|
||||
{ 4, 3, 1, 1, 1, 3, 1, 1 },
|
||||
{ 4, 3, 1, 1, 1, 3, 1, 1 },
|
||||
{ 6, 4, 2, 2, 2, 4, 2, 2 },
|
||||
{ 9, 6, 3, 3, 3, 6, 3, 3 },
|
||||
{ 9, 6, 3, 3, 3, 6, 3, 3 },
|
||||
};
|
||||
static u8 act_time_value[][8] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 9, 9, 9, 2, 2, 7, 2, 2 },
|
||||
{ 19, 19, 19, 5, 4, 14, 5, 4 },
|
||||
{ 19, 19, 19, 5, 4, 14, 5, 4 },
|
||||
{ 28, 28, 28, 7, 6, 21, 7, 6 },
|
||||
{ 38, 38, 38, 10, 9, 28, 10, 9 },
|
||||
{ 38, 38, 38, 10, 9, 28, 10, 9 },
|
||||
};
|
||||
static u8 rco_time_value[][8] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 9, 2, 0, 2, 0, 7, 1, 1 },
|
||||
{ 19, 5, 1, 5, 2, 16, 3, 2 },
|
||||
{ 19, 5, 1, 5, 2, 16, 3, 2 },
|
||||
{ 30, 9, 3, 9, 4, 25, 6, 4 },
|
||||
{ 40, 12, 4, 12, 5, 34, 12, 5 },
|
||||
{ 40, 12, 4, 12, 5, 34, 12, 5 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Printing configuration
|
||||
*/
|
||||
/* Used for chipset type printing at boot time */
|
||||
static char *chipset_capability[] = {
|
||||
"ATA", "ATA 16",
|
||||
"ATA 33", "ATA 66",
|
||||
"ATA 100 (1st gen)", "ATA 100 (2nd gen)",
|
||||
"ATA 133 (1st gen)", "ATA 133 (2nd gen)"
|
||||
};
|
||||
|
||||
/*
|
||||
* Configuration functions
|
||||
*/
|
||||
|
||||
static u8 sis_ata133_get_base(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u32 reg54 = 0;
|
||||
|
||||
pci_read_config_dword(dev, 0x54, ®54);
|
||||
|
||||
return ((reg54 & 0x40000000) ? 0x70 : 0x40) + drive->dn * 4;
|
||||
}
|
||||
|
||||
static void sis_ata16_program_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u16 t1 = 0;
|
||||
u8 drive_pci = 0x40 + drive->dn * 2;
|
||||
|
||||
const u16 pio_timings[] = { 0x000, 0x607, 0x404, 0x303, 0x301 };
|
||||
const u16 mwdma_timings[] = { 0x008, 0x302, 0x301 };
|
||||
|
||||
pci_read_config_word(dev, drive_pci, &t1);
|
||||
|
||||
/* clear active/recovery timings */
|
||||
t1 &= ~0x070f;
|
||||
if (mode >= XFER_MW_DMA_0) {
|
||||
if (chipset_family > ATA_16)
|
||||
t1 &= ~0x8000; /* disable UDMA */
|
||||
t1 |= mwdma_timings[mode - XFER_MW_DMA_0];
|
||||
} else
|
||||
t1 |= pio_timings[mode - XFER_PIO_0];
|
||||
|
||||
pci_write_config_word(dev, drive_pci, t1);
|
||||
}
|
||||
|
||||
static void sis_ata100_program_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u8 t1, drive_pci = 0x40 + drive->dn * 2;
|
||||
|
||||
/* timing bits: 7:4 active 3:0 recovery */
|
||||
const u8 pio_timings[] = { 0x00, 0x67, 0x44, 0x33, 0x31 };
|
||||
const u8 mwdma_timings[] = { 0x08, 0x32, 0x31 };
|
||||
|
||||
if (mode >= XFER_MW_DMA_0) {
|
||||
u8 t2 = 0;
|
||||
|
||||
pci_read_config_byte(dev, drive_pci, &t2);
|
||||
t2 &= ~0x80; /* disable UDMA */
|
||||
pci_write_config_byte(dev, drive_pci, t2);
|
||||
|
||||
t1 = mwdma_timings[mode - XFER_MW_DMA_0];
|
||||
} else
|
||||
t1 = pio_timings[mode - XFER_PIO_0];
|
||||
|
||||
pci_write_config_byte(dev, drive_pci + 1, t1);
|
||||
}
|
||||
|
||||
static void sis_ata133_program_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u32 t1 = 0;
|
||||
u8 drive_pci = sis_ata133_get_base(drive), clk, idx;
|
||||
|
||||
pci_read_config_dword(dev, drive_pci, &t1);
|
||||
|
||||
t1 &= 0xc0c00fff;
|
||||
clk = (t1 & 0x08) ? ATA_133 : ATA_100;
|
||||
if (mode >= XFER_MW_DMA_0) {
|
||||
t1 &= ~0x04; /* disable UDMA */
|
||||
idx = mode - XFER_MW_DMA_0 + 5;
|
||||
} else
|
||||
idx = mode - XFER_PIO_0;
|
||||
t1 |= ini_time_value[clk][idx] << 12;
|
||||
t1 |= act_time_value[clk][idx] << 16;
|
||||
t1 |= rco_time_value[clk][idx] << 24;
|
||||
|
||||
pci_write_config_dword(dev, drive_pci, t1);
|
||||
}
|
||||
|
||||
static void sis_program_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
if (chipset_family < ATA_100) /* ATA_16/33/66/100a */
|
||||
sis_ata16_program_timings(drive, mode);
|
||||
else if (chipset_family < ATA_133) /* ATA_100/133a */
|
||||
sis_ata100_program_timings(drive, mode);
|
||||
else /* ATA_133 */
|
||||
sis_ata133_program_timings(drive, mode);
|
||||
}
|
||||
|
||||
static void config_drive_art_rwp(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 reg4bh = 0;
|
||||
u8 rw_prefetch = 0;
|
||||
|
||||
pci_read_config_byte(dev, 0x4b, ®4bh);
|
||||
|
||||
rw_prefetch = reg4bh & ~(0x11 << drive->dn);
|
||||
|
||||
if (drive->media == ide_disk)
|
||||
rw_prefetch |= 0x11 << drive->dn;
|
||||
|
||||
if (reg4bh != rw_prefetch)
|
||||
pci_write_config_byte(dev, 0x4b, rw_prefetch);
|
||||
}
|
||||
|
||||
static void sis_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
config_drive_art_rwp(drive);
|
||||
sis_program_timings(drive, drive->pio_mode);
|
||||
}
|
||||
|
||||
static void sis_ata133_program_udma_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u32 regdw = 0;
|
||||
u8 drive_pci = sis_ata133_get_base(drive), clk, idx;
|
||||
|
||||
pci_read_config_dword(dev, drive_pci, ®dw);
|
||||
|
||||
regdw |= 0x04;
|
||||
regdw &= 0xfffff00f;
|
||||
/* check if ATA133 enable */
|
||||
clk = (regdw & 0x08) ? ATA_133 : ATA_100;
|
||||
idx = mode - XFER_UDMA_0;
|
||||
regdw |= cycle_time_value[clk][idx] << 4;
|
||||
regdw |= cvs_time_value[clk][idx] << 8;
|
||||
|
||||
pci_write_config_dword(dev, drive_pci, regdw);
|
||||
}
|
||||
|
||||
static void sis_ata33_program_udma_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u8 drive_pci = 0x40 + drive->dn * 2, reg = 0, i = chipset_family;
|
||||
|
||||
pci_read_config_byte(dev, drive_pci + 1, ®);
|
||||
|
||||
/* force the UDMA bit on if we want to use UDMA */
|
||||
reg |= 0x80;
|
||||
/* clean reg cycle time bits */
|
||||
reg &= ~((0xff >> (8 - cycle_time_range[i])) << cycle_time_offset[i]);
|
||||
/* set reg cycle time bits */
|
||||
reg |= cycle_time_value[i][mode - XFER_UDMA_0] << cycle_time_offset[i];
|
||||
|
||||
pci_write_config_byte(dev, drive_pci + 1, reg);
|
||||
}
|
||||
|
||||
static void sis_program_udma_timings(ide_drive_t *drive, const u8 mode)
|
||||
{
|
||||
if (chipset_family >= ATA_133) /* ATA_133 */
|
||||
sis_ata133_program_udma_timings(drive, mode);
|
||||
else /* ATA_33/66/100a/100/133a */
|
||||
sis_ata33_program_udma_timings(drive, mode);
|
||||
}
|
||||
|
||||
static void sis_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
if (speed >= XFER_UDMA_0)
|
||||
sis_program_udma_timings(drive, speed);
|
||||
else
|
||||
sis_program_timings(drive, speed);
|
||||
}
|
||||
|
||||
static u8 sis_ata133_udma_filter(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u32 regdw = 0;
|
||||
u8 drive_pci = sis_ata133_get_base(drive);
|
||||
|
||||
pci_read_config_dword(dev, drive_pci, ®dw);
|
||||
|
||||
/* if ATA133 disable, we should not set speed above UDMA5 */
|
||||
return (regdw & 0x08) ? ATA_UDMA6 : ATA_UDMA5;
|
||||
}
|
||||
|
||||
static int sis_find_family(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *host;
|
||||
int i = 0;
|
||||
|
||||
chipset_family = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !chipset_family; i++) {
|
||||
|
||||
host = pci_get_device(PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, NULL);
|
||||
|
||||
if (!host)
|
||||
continue;
|
||||
|
||||
chipset_family = SiSHostChipInfo[i].chipset_family;
|
||||
|
||||
/* Special case for SiS630 : 630S/ET is ATA_100a */
|
||||
if (SiSHostChipInfo[i].host_id == PCI_DEVICE_ID_SI_630) {
|
||||
if (host->revision >= 0x30)
|
||||
chipset_family = ATA_100a;
|
||||
}
|
||||
pci_dev_put(host);
|
||||
|
||||
printk(KERN_INFO DRV_NAME " %s: %s %s controller\n",
|
||||
pci_name(dev), SiSHostChipInfo[i].name,
|
||||
chipset_capability[chipset_family]);
|
||||
}
|
||||
|
||||
if (!chipset_family) { /* Belongs to pci-quirks */
|
||||
|
||||
u32 idemisc;
|
||||
u16 trueid;
|
||||
|
||||
/* Disable ID masking and register remapping */
|
||||
pci_read_config_dword(dev, 0x54, &idemisc);
|
||||
pci_write_config_dword(dev, 0x54, (idemisc & 0x7fffffff));
|
||||
pci_read_config_word(dev, PCI_DEVICE_ID, &trueid);
|
||||
pci_write_config_dword(dev, 0x54, idemisc);
|
||||
|
||||
if (trueid == 0x5518) {
|
||||
printk(KERN_INFO DRV_NAME " %s: SiS 962/963 MuTIOL IDE UDMA133 controller\n",
|
||||
pci_name(dev));
|
||||
chipset_family = ATA_133;
|
||||
|
||||
/* Check for 5513 compatibility mapping
|
||||
* We must use this, else the port enabled code will fail,
|
||||
* as it expects the enablebits at 0x4a.
|
||||
*/
|
||||
if ((idemisc & 0x40000000) == 0) {
|
||||
pci_write_config_dword(dev, 0x54, idemisc | 0x40000000);
|
||||
printk(KERN_INFO DRV_NAME " %s: Switching to 5513 register mapping\n",
|
||||
pci_name(dev));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!chipset_family) { /* Belongs to pci-quirks */
|
||||
|
||||
struct pci_dev *lpc_bridge;
|
||||
u16 trueid;
|
||||
u8 prefctl;
|
||||
u8 idecfg;
|
||||
|
||||
pci_read_config_byte(dev, 0x4a, &idecfg);
|
||||
pci_write_config_byte(dev, 0x4a, idecfg | 0x10);
|
||||
pci_read_config_word(dev, PCI_DEVICE_ID, &trueid);
|
||||
pci_write_config_byte(dev, 0x4a, idecfg);
|
||||
|
||||
if (trueid == 0x5517) { /* SiS 961/961B */
|
||||
|
||||
lpc_bridge = pci_get_slot(dev->bus, 0x10); /* Bus 0, Dev 2, Fn 0 */
|
||||
pci_read_config_byte(dev, 0x49, &prefctl);
|
||||
pci_dev_put(lpc_bridge);
|
||||
|
||||
if (lpc_bridge->revision == 0x10 && (prefctl & 0x80)) {
|
||||
printk(KERN_INFO DRV_NAME " %s: SiS 961B MuTIOL IDE UDMA133 controller\n",
|
||||
pci_name(dev));
|
||||
chipset_family = ATA_133a;
|
||||
} else {
|
||||
printk(KERN_INFO DRV_NAME " %s: SiS 961 MuTIOL IDE UDMA100 controller\n",
|
||||
pci_name(dev));
|
||||
chipset_family = ATA_100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chipset_family;
|
||||
}
|
||||
|
||||
static int init_chipset_sis5513(struct pci_dev *dev)
|
||||
{
|
||||
/* Make general config ops here
|
||||
1/ tell IDE channels to operate in Compatibility mode only
|
||||
2/ tell old chips to allow per drive IDE timings */
|
||||
|
||||
u8 reg;
|
||||
u16 regw;
|
||||
|
||||
switch (chipset_family) {
|
||||
case ATA_133:
|
||||
/* SiS962 operation mode */
|
||||
pci_read_config_word(dev, 0x50, ®w);
|
||||
if (regw & 0x08)
|
||||
pci_write_config_word(dev, 0x50, regw&0xfff7);
|
||||
pci_read_config_word(dev, 0x52, ®w);
|
||||
if (regw & 0x08)
|
||||
pci_write_config_word(dev, 0x52, regw&0xfff7);
|
||||
break;
|
||||
case ATA_133a:
|
||||
case ATA_100:
|
||||
/* Fixup latency */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x80);
|
||||
/* Set compatibility bit */
|
||||
pci_read_config_byte(dev, 0x49, ®);
|
||||
if (!(reg & 0x01))
|
||||
pci_write_config_byte(dev, 0x49, reg|0x01);
|
||||
break;
|
||||
case ATA_100a:
|
||||
case ATA_66:
|
||||
/* Fixup latency */
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x10);
|
||||
|
||||
/* On ATA_66 chips the bit was elsewhere */
|
||||
pci_read_config_byte(dev, 0x52, ®);
|
||||
if (!(reg & 0x04))
|
||||
pci_write_config_byte(dev, 0x52, reg|0x04);
|
||||
break;
|
||||
case ATA_33:
|
||||
/* On ATA_33 we didn't have a single bit to set */
|
||||
pci_read_config_byte(dev, 0x09, ®);
|
||||
if ((reg & 0x0f) != 0x00)
|
||||
pci_write_config_byte(dev, 0x09, reg&0xf0);
|
||||
fallthrough;
|
||||
case ATA_16:
|
||||
/* force per drive recovery and active timings
|
||||
needed on ATA_33 and below chips */
|
||||
pci_read_config_byte(dev, 0x52, ®);
|
||||
if (!(reg & 0x08))
|
||||
pci_write_config_byte(dev, 0x52, reg|0x08);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sis_laptop {
|
||||
u16 device;
|
||||
u16 subvendor;
|
||||
u16 subdevice;
|
||||
};
|
||||
|
||||
static const struct sis_laptop sis_laptop[] = {
|
||||
/* devid, subvendor, subdev */
|
||||
{ 0x5513, 0x1043, 0x1107 }, /* ASUS A6K */
|
||||
{ 0x5513, 0x1734, 0x105f }, /* FSC Amilo A1630 */
|
||||
{ 0x5513, 0x1071, 0x8640 }, /* EasyNote K5305 */
|
||||
/* end marker */
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
static u8 sis_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
const struct sis_laptop *lap = &sis_laptop[0];
|
||||
u8 ata66 = 0;
|
||||
|
||||
while (lap->device) {
|
||||
if (lap->device == pdev->device &&
|
||||
lap->subvendor == pdev->subsystem_vendor &&
|
||||
lap->subdevice == pdev->subsystem_device)
|
||||
return ATA_CBL_PATA40_SHORT;
|
||||
lap++;
|
||||
}
|
||||
|
||||
if (chipset_family >= ATA_133) {
|
||||
u16 regw = 0;
|
||||
u16 reg_addr = hwif->channel ? 0x52: 0x50;
|
||||
pci_read_config_word(pdev, reg_addr, ®w);
|
||||
ata66 = (regw & 0x8000) ? 0 : 1;
|
||||
} else if (chipset_family >= ATA_66) {
|
||||
u8 reg48h = 0;
|
||||
u8 mask = hwif->channel ? 0x20 : 0x10;
|
||||
pci_read_config_byte(pdev, 0x48, ®48h);
|
||||
ata66 = (reg48h & mask) ? 0 : 1;
|
||||
}
|
||||
|
||||
return ata66 ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops sis_port_ops = {
|
||||
.set_pio_mode = sis_set_pio_mode,
|
||||
.set_dma_mode = sis_set_dma_mode,
|
||||
.cable_detect = sis_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_ops sis_ata133_port_ops = {
|
||||
.set_pio_mode = sis_set_pio_mode,
|
||||
.set_dma_mode = sis_set_dma_mode,
|
||||
.udma_filter = sis_ata133_udma_filter,
|
||||
.cable_detect = sis_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info sis5513_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_sis5513,
|
||||
.enablebits = { {0x4a, 0x02, 0x02}, {0x4a, 0x04, 0x04} },
|
||||
.host_flags = IDE_HFLAG_NO_AUTODMA,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
};
|
||||
|
||||
static int sis5513_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ide_port_info d = sis5513_chipset;
|
||||
u8 udma_rates[] = { 0x00, 0x00, 0x07, 0x1f, 0x3f, 0x3f, 0x7f, 0x7f };
|
||||
int rc;
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (sis_find_family(dev) == 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (chipset_family >= ATA_133)
|
||||
d.port_ops = &sis_ata133_port_ops;
|
||||
else
|
||||
d.port_ops = &sis_port_ops;
|
||||
|
||||
d.udma_mask = udma_rates[chipset_family];
|
||||
|
||||
return ide_pci_init_one(dev, &d, NULL);
|
||||
}
|
||||
|
||||
static void sis5513_remove(struct pci_dev *dev)
|
||||
{
|
||||
ide_pci_remove(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id sis5513_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_5513), 0 },
|
||||
{ PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_5518), 0 },
|
||||
{ PCI_VDEVICE(SI, PCI_DEVICE_ID_SI_1180), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sis5513_pci_tbl);
|
||||
|
||||
static struct pci_driver sis5513_pci_driver = {
|
||||
.name = "SIS_IDE",
|
||||
.id_table = sis5513_pci_tbl,
|
||||
.probe = sis5513_init_one,
|
||||
.remove = sis5513_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init sis5513_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&sis5513_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit sis5513_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&sis5513_pci_driver);
|
||||
}
|
||||
|
||||
module_init(sis5513_ide_init);
|
||||
module_exit(sis5513_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Lionel Bouton, L C Chang, Andre Hedrick, Vojtech Pavlik");
|
||||
MODULE_DESCRIPTION("PCI driver module for SIS IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,367 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* SL82C105/Winbond 553 IDE driver
|
||||
*
|
||||
* Maintainer unknown.
|
||||
*
|
||||
* Drive tuning added from Rebel.com's kernel sources
|
||||
* -- Russell King (15/11/98) linux@arm.linux.org.uk
|
||||
*
|
||||
* Merge in Russell's HW workarounds, fix various problems
|
||||
* with the timing registers setup.
|
||||
* -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org
|
||||
*
|
||||
* Copyright (C) 2006-2007,2009 MontaVista Software, Inc. <source@mvista.com>
|
||||
* Copyright (C) 2007 Bartlomiej Zolnierkiewicz
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "sl82c105"
|
||||
|
||||
/*
|
||||
* SL82C105 PCI config register 0x40 bits.
|
||||
*/
|
||||
#define CTRL_IDE_IRQB (1 << 30)
|
||||
#define CTRL_IDE_IRQA (1 << 28)
|
||||
#define CTRL_LEGIRQ (1 << 11)
|
||||
#define CTRL_P1F16 (1 << 5)
|
||||
#define CTRL_P1EN (1 << 4)
|
||||
#define CTRL_P0F16 (1 << 1)
|
||||
#define CTRL_P0EN (1 << 0)
|
||||
|
||||
/*
|
||||
* Convert a PIO mode and cycle time to the required on/off times
|
||||
* for the interface. This has protection against runaway timings.
|
||||
*/
|
||||
static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio)
|
||||
{
|
||||
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
|
||||
unsigned int cmd_on, cmd_off;
|
||||
u8 iordy = 0;
|
||||
|
||||
cmd_on = (t->active + 29) / 30;
|
||||
cmd_off = (ide_pio_cycle_time(drive, pio) - 30 * cmd_on + 29) / 30;
|
||||
|
||||
if (cmd_on == 0)
|
||||
cmd_on = 1;
|
||||
|
||||
if (cmd_off == 0)
|
||||
cmd_off = 1;
|
||||
|
||||
if (ide_pio_need_iordy(drive, pio))
|
||||
iordy = 0x40;
|
||||
|
||||
return (cmd_on - 1) << 8 | (cmd_off - 1) | iordy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the chipset for PIO mode.
|
||||
*/
|
||||
static void sl82c105_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long timings = (unsigned long)ide_get_drivedata(drive);
|
||||
int reg = 0x44 + drive->dn * 4;
|
||||
u16 drv_ctrl;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
drv_ctrl = get_pio_timings(drive, pio);
|
||||
|
||||
/*
|
||||
* Store the PIO timings so that we can restore them
|
||||
* in case DMA will be turned off...
|
||||
*/
|
||||
timings &= 0xffff0000;
|
||||
timings |= drv_ctrl;
|
||||
ide_set_drivedata(drive, (void *)timings);
|
||||
|
||||
pci_write_config_word(dev, reg, drv_ctrl);
|
||||
pci_read_config_word (dev, reg, &drv_ctrl);
|
||||
|
||||
printk(KERN_DEBUG "%s: selected %s (%dns) (%04X)\n", drive->name,
|
||||
ide_xfer_verbose(pio + XFER_PIO_0),
|
||||
ide_pio_cycle_time(drive, pio), drv_ctrl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the chipset for DMA mode.
|
||||
*/
|
||||
static void sl82c105_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
|
||||
unsigned long timings = (unsigned long)ide_get_drivedata(drive);
|
||||
u16 drv_ctrl;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
drv_ctrl = mwdma_timings[speed - XFER_MW_DMA_0];
|
||||
|
||||
/*
|
||||
* Store the DMA timings so that we can actually program
|
||||
* them when DMA will be turned on...
|
||||
*/
|
||||
timings &= 0x0000ffff;
|
||||
timings |= (unsigned long)drv_ctrl << 16;
|
||||
ide_set_drivedata(drive, (void *)timings);
|
||||
}
|
||||
|
||||
static int sl82c105_test_irq(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
|
||||
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
|
||||
return (val & mask) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The SL82C105 holds off all IDE interrupts while in DMA mode until
|
||||
* all DMA activity is completed. Sometimes this causes problems (eg,
|
||||
* when the drive wants to report an error condition).
|
||||
*
|
||||
* 0x7e is a "chip testing" register. Bit 2 resets the DMA controller
|
||||
* state machine. We need to kick this to work around various bugs.
|
||||
*/
|
||||
static inline void sl82c105_reset_host(struct pci_dev *dev)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
pci_read_config_word(dev, 0x7e, &val);
|
||||
pci_write_config_word(dev, 0x7e, val | (1 << 2));
|
||||
pci_write_config_word(dev, 0x7e, val & ~(1 << 2));
|
||||
}
|
||||
|
||||
/*
|
||||
* If we get an IRQ timeout, it might be that the DMA state machine
|
||||
* got confused. Fix from Todd Inglett. Details from Winbond.
|
||||
*
|
||||
* This function is called when the IDE timer expires, the drive
|
||||
* indicates that it is READY, and we were waiting for DMA to complete.
|
||||
*/
|
||||
static void sl82c105_dma_lost_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u32 val, mask = hwif->channel ? CTRL_IDE_IRQB : CTRL_IDE_IRQA;
|
||||
u8 dma_cmd;
|
||||
|
||||
printk(KERN_WARNING "sl82c105: lost IRQ, resetting host\n");
|
||||
|
||||
/*
|
||||
* Check the raw interrupt from the drive.
|
||||
*/
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
if (val & mask)
|
||||
printk(KERN_INFO "sl82c105: drive was requesting IRQ, "
|
||||
"but host lost it\n");
|
||||
|
||||
/*
|
||||
* Was DMA enabled? If so, disable it - we're resetting the
|
||||
* host. The IDE layer will be handling the drive for us.
|
||||
*/
|
||||
dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
|
||||
if (dma_cmd & 1) {
|
||||
outb(dma_cmd & ~1, hwif->dma_base + ATA_DMA_CMD);
|
||||
printk(KERN_INFO "sl82c105: DMA was enabled\n");
|
||||
}
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* ATAPI devices can cause the SL82C105 DMA state machine to go gaga.
|
||||
* Winbond recommend that the DMA state machine is reset prior to
|
||||
* setting the bus master DMA enable bit.
|
||||
*
|
||||
* The generic IDE core will have disabled the BMEN bit before this
|
||||
* function is called.
|
||||
*/
|
||||
static void sl82c105_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
int reg = 0x44 + drive->dn * 4;
|
||||
|
||||
pci_write_config_word(dev, reg,
|
||||
(unsigned long)ide_get_drivedata(drive) >> 16);
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static void sl82c105_dma_clear(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
|
||||
sl82c105_reset_host(dev);
|
||||
}
|
||||
|
||||
static int sl82c105_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
int reg = 0x44 + drive->dn * 4;
|
||||
int ret = ide_dma_end(drive);
|
||||
|
||||
pci_write_config_word(dev, reg,
|
||||
(unsigned long)ide_get_drivedata(drive));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* ATA reset will clear the 16 bits mode in the control
|
||||
* register, we need to reprogram it
|
||||
*/
|
||||
static void sl82c105_resetproc(ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
|
||||
u32 val;
|
||||
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
val |= (CTRL_P1F16 | CTRL_P0F16);
|
||||
pci_write_config_dword(dev, 0x40, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the revision of the Winbond bridge
|
||||
* which this function is part of.
|
||||
*/
|
||||
static u8 sl82c105_bridge_revision(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_dev *bridge;
|
||||
|
||||
/*
|
||||
* The bridge should be part of the same device, but function 0.
|
||||
*/
|
||||
bridge = pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
|
||||
dev->bus->number,
|
||||
PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
|
||||
if (!bridge)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Make sure it is a Winbond 553 and is an ISA bridge.
|
||||
*/
|
||||
if (bridge->vendor != PCI_VENDOR_ID_WINBOND ||
|
||||
bridge->device != PCI_DEVICE_ID_WINBOND_83C553 ||
|
||||
bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA) {
|
||||
pci_dev_put(bridge);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* We need to find function 0's revision, not function 1
|
||||
*/
|
||||
pci_dev_put(bridge);
|
||||
|
||||
return bridge->revision;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the PCI device
|
||||
*
|
||||
* --BenH: It's arch fixup code that should enable channels that
|
||||
* have not been enabled by firmware. I decided we can still enable
|
||||
* channel 0 here at least, but channel 1 has to be enabled by
|
||||
* firmware or arch code. We still set both to 16 bits mode.
|
||||
*/
|
||||
static int init_chipset_sl82c105(struct pci_dev *dev)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
pci_read_config_dword(dev, 0x40, &val);
|
||||
val |= CTRL_P0EN | CTRL_P0F16 | CTRL_P1F16;
|
||||
pci_write_config_dword(dev, 0x40, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops sl82c105_port_ops = {
|
||||
.set_pio_mode = sl82c105_set_pio_mode,
|
||||
.set_dma_mode = sl82c105_set_dma_mode,
|
||||
.resetproc = sl82c105_resetproc,
|
||||
.test_irq = sl82c105_test_irq,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops sl82c105_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = sl82c105_dma_start,
|
||||
.dma_end = sl82c105_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = sl82c105_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_clear = sl82c105_dma_clear,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info sl82c105_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_sl82c105,
|
||||
.enablebits = {{0x40,0x01,0x01}, {0x40,0x10,0x10}},
|
||||
.port_ops = &sl82c105_port_ops,
|
||||
.dma_ops = &sl82c105_dma_ops,
|
||||
.host_flags = IDE_HFLAG_IO_32BIT |
|
||||
IDE_HFLAG_UNMASK_IRQS |
|
||||
IDE_HFLAG_SERIALIZE_DMA |
|
||||
IDE_HFLAG_NO_AUTODMA,
|
||||
.pio_mask = ATA_PIO5,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
};
|
||||
|
||||
static int sl82c105_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct ide_port_info d = sl82c105_chipset;
|
||||
u8 rev = sl82c105_bridge_revision(dev);
|
||||
|
||||
if (rev <= 5) {
|
||||
/*
|
||||
* Never ever EVER under any circumstances enable
|
||||
* DMA when the bridge is this old.
|
||||
*/
|
||||
printk(KERN_INFO DRV_NAME ": Winbond W83C553 bridge "
|
||||
"revision %d, BM-DMA disabled\n", rev);
|
||||
d.dma_ops = NULL;
|
||||
d.mwdma_mask = 0;
|
||||
d.host_flags &= ~IDE_HFLAG_SERIALIZE_DMA;
|
||||
}
|
||||
|
||||
return ide_pci_init_one(dev, &d, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id sl82c105_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(WINBOND, PCI_DEVICE_ID_WINBOND_82C105), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl);
|
||||
|
||||
static struct pci_driver sl82c105_pci_driver = {
|
||||
.name = "W82C105_IDE",
|
||||
.id_table = sl82c105_pci_tbl,
|
||||
.probe = sl82c105_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init sl82c105_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&sl82c105_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit sl82c105_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&sl82c105_pci_driver);
|
||||
}
|
||||
|
||||
module_init(sl82c105_ide_init);
|
||||
module_exit(sl82c105_ide_exit);
|
||||
|
||||
MODULE_DESCRIPTION("PCI driver module for W82C105 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,182 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org>
|
||||
* Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This is a look-alike variation of the ICH0 PIIX4 Ultra-66,
|
||||
* but this keeps the ISA-Bridge and slots alive.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "slc90e66"
|
||||
|
||||
static DEFINE_SPINLOCK(slc90e66_lock);
|
||||
|
||||
static void slc90e66_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
int is_slave = drive->dn & 1;
|
||||
int master_port = hwif->channel ? 0x42 : 0x40;
|
||||
int slave_port = 0x44;
|
||||
unsigned long flags;
|
||||
u16 master_data;
|
||||
u8 slave_data;
|
||||
int control = 0;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
/* ISP RTC */
|
||||
static const u8 timings[][2] = {
|
||||
{ 0, 0 },
|
||||
{ 0, 0 },
|
||||
{ 1, 0 },
|
||||
{ 2, 1 },
|
||||
{ 2, 3 }, };
|
||||
|
||||
spin_lock_irqsave(&slc90e66_lock, flags);
|
||||
pci_read_config_word(dev, master_port, &master_data);
|
||||
|
||||
if (pio > 1)
|
||||
control |= 1; /* Programmable timing on */
|
||||
if (drive->media == ide_disk)
|
||||
control |= 4; /* Prefetch, post write */
|
||||
if (ide_pio_need_iordy(drive, pio))
|
||||
control |= 2; /* IORDY */
|
||||
if (is_slave) {
|
||||
master_data |= 0x4000;
|
||||
master_data &= ~0x0070;
|
||||
if (pio > 1) {
|
||||
/* Set PPE, IE and TIME */
|
||||
master_data |= control << 4;
|
||||
}
|
||||
pci_read_config_byte(dev, slave_port, &slave_data);
|
||||
slave_data &= hwif->channel ? 0x0f : 0xf0;
|
||||
slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) <<
|
||||
(hwif->channel ? 4 : 0);
|
||||
} else {
|
||||
master_data &= ~0x3307;
|
||||
if (pio > 1) {
|
||||
/* enable PPE, IE and TIME */
|
||||
master_data |= control;
|
||||
}
|
||||
master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8);
|
||||
}
|
||||
pci_write_config_word(dev, master_port, master_data);
|
||||
if (is_slave)
|
||||
pci_write_config_byte(dev, slave_port, slave_data);
|
||||
spin_unlock_irqrestore(&slc90e66_lock, flags);
|
||||
}
|
||||
|
||||
static void slc90e66_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 maslave = hwif->channel ? 0x42 : 0x40;
|
||||
int sitre = 0, a_speed = 7 << (drive->dn * 4);
|
||||
int u_speed = 0, u_flag = 1 << drive->dn;
|
||||
u16 reg4042, reg44, reg48, reg4a;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
pci_read_config_word(dev, maslave, ®4042);
|
||||
sitre = (reg4042 & 0x4000) ? 1 : 0;
|
||||
pci_read_config_word(dev, 0x44, ®44);
|
||||
pci_read_config_word(dev, 0x48, ®48);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
|
||||
if (speed >= XFER_UDMA_0) {
|
||||
u_speed = (speed - XFER_UDMA_0) << (drive->dn * 4);
|
||||
|
||||
if (!(reg48 & u_flag))
|
||||
pci_write_config_word(dev, 0x48, reg48|u_flag);
|
||||
if ((reg4a & a_speed) != u_speed) {
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
pci_read_config_word(dev, 0x4a, ®4a);
|
||||
pci_write_config_word(dev, 0x4a, reg4a|u_speed);
|
||||
}
|
||||
} else {
|
||||
const u8 mwdma_to_pio[] = { 0, 3, 4 };
|
||||
|
||||
if (reg48 & u_flag)
|
||||
pci_write_config_word(dev, 0x48, reg48 & ~u_flag);
|
||||
if (reg4a & a_speed)
|
||||
pci_write_config_word(dev, 0x4a, reg4a & ~a_speed);
|
||||
|
||||
if (speed >= XFER_MW_DMA_0)
|
||||
drive->pio_mode =
|
||||
mwdma_to_pio[speed - XFER_MW_DMA_0] + XFER_PIO_0;
|
||||
else
|
||||
drive->pio_mode = XFER_PIO_2; /* for SWDMA2 */
|
||||
|
||||
slc90e66_set_pio_mode(hwif, drive);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 slc90e66_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u8 reg47 = 0, mask = hwif->channel ? 0x01 : 0x02;
|
||||
|
||||
pci_read_config_byte(dev, 0x47, ®47);
|
||||
|
||||
/* bit[0(1)]: 0:80, 1:40 */
|
||||
return (reg47 & mask) ? ATA_CBL_PATA40 : ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops slc90e66_port_ops = {
|
||||
.set_pio_mode = slc90e66_set_pio_mode,
|
||||
.set_dma_mode = slc90e66_set_dma_mode,
|
||||
.cable_detect = slc90e66_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info slc90e66_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.enablebits = { {0x41, 0x80, 0x80}, {0x43, 0x80, 0x80} },
|
||||
.port_ops = &slc90e66_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.swdma_mask = ATA_SWDMA2_ONLY,
|
||||
.mwdma_mask = ATA_MWDMA12_ONLY,
|
||||
.udma_mask = ATA_UDMA4,
|
||||
};
|
||||
|
||||
static int slc90e66_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &slc90e66_chipset, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id slc90e66_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl);
|
||||
|
||||
static struct pci_driver slc90e66_pci_driver = {
|
||||
.name = "SLC90e66_IDE",
|
||||
.id_table = slc90e66_pci_tbl,
|
||||
.probe = slc90e66_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init slc90e66_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&slc90e66_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit slc90e66_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&slc90e66_pci_driver);
|
||||
}
|
||||
|
||||
module_init(slc90e66_ide_init);
|
||||
module_exit(slc90e66_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for SLC90E66 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,270 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2002 Toshiba Corporation
|
||||
* Copyright (C) 2005-2006 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define DRV_NAME "tc86c001"
|
||||
|
||||
static void tc86c001_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
unsigned long scr_port = hwif->config_data + (drive->dn ? 0x02 : 0x00);
|
||||
u16 mode, scr = inw(scr_port);
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
switch (speed) {
|
||||
case XFER_UDMA_4: mode = 0x00c0; break;
|
||||
case XFER_UDMA_3: mode = 0x00b0; break;
|
||||
case XFER_UDMA_2: mode = 0x00a0; break;
|
||||
case XFER_UDMA_1: mode = 0x0090; break;
|
||||
case XFER_UDMA_0: mode = 0x0080; break;
|
||||
case XFER_MW_DMA_2: mode = 0x0070; break;
|
||||
case XFER_MW_DMA_1: mode = 0x0060; break;
|
||||
case XFER_MW_DMA_0: mode = 0x0050; break;
|
||||
case XFER_PIO_4: mode = 0x0400; break;
|
||||
case XFER_PIO_3: mode = 0x0300; break;
|
||||
case XFER_PIO_2: mode = 0x0200; break;
|
||||
case XFER_PIO_1: mode = 0x0100; break;
|
||||
case XFER_PIO_0:
|
||||
default: mode = 0x0000; break;
|
||||
}
|
||||
|
||||
scr &= (speed < XFER_MW_DMA_0) ? 0xf8ff : 0xff0f;
|
||||
scr |= mode;
|
||||
outw(scr, scr_port);
|
||||
}
|
||||
|
||||
static void tc86c001_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
drive->dma_mode = drive->pio_mode;
|
||||
tc86c001_set_mode(hwif, drive);
|
||||
}
|
||||
|
||||
/*
|
||||
* HACKITY HACK
|
||||
*
|
||||
* This is a workaround for the limitation 5 of the TC86C001 IDE controller:
|
||||
* if a DMA transfer terminates prematurely, the controller leaves the device's
|
||||
* interrupt request (INTRQ) pending and does not generate a PCI interrupt (or
|
||||
* set the interrupt bit in the DMA status register), thus no PCI interrupt
|
||||
* will occur until a DMA transfer has been successfully completed.
|
||||
*
|
||||
* We work around this by initiating dummy, zero-length DMA transfer on
|
||||
* a DMA timeout expiration. I found no better way to do this with the current
|
||||
* IDE core than to temporarily replace a higher level driver's timer expiry
|
||||
* handler with our own backing up to that handler in case our recovery fails.
|
||||
*/
|
||||
static int tc86c001_timer_expiry(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
ide_expiry_t *expiry = ide_get_hwifdata(hwif);
|
||||
u8 dma_stat = inb(hwif->dma_base + ATA_DMA_STATUS);
|
||||
|
||||
/* Restore a higher level driver's expiry handler first. */
|
||||
hwif->expiry = expiry;
|
||||
|
||||
if ((dma_stat & 5) == 1) { /* DMA active and no interrupt */
|
||||
unsigned long sc_base = hwif->config_data;
|
||||
unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04);
|
||||
u8 dma_cmd = inb(hwif->dma_base + ATA_DMA_CMD);
|
||||
|
||||
printk(KERN_WARNING "%s: DMA interrupt possibly stuck, "
|
||||
"attempting recovery...\n", drive->name);
|
||||
|
||||
/* Stop DMA */
|
||||
outb(dma_cmd & ~0x01, hwif->dma_base + ATA_DMA_CMD);
|
||||
|
||||
/* Setup the dummy DMA transfer */
|
||||
outw(0, sc_base + 0x0a); /* Sector Count */
|
||||
outw(0, twcr_port); /* Transfer Word Count 1 or 2 */
|
||||
|
||||
/* Start the dummy DMA transfer */
|
||||
|
||||
/* clear R_OR_WCTR for write */
|
||||
outb(0x00, hwif->dma_base + ATA_DMA_CMD);
|
||||
/* set START_STOPBM */
|
||||
outb(0x01, hwif->dma_base + ATA_DMA_CMD);
|
||||
|
||||
/*
|
||||
* If an interrupt was pending, it should come thru shortly.
|
||||
* If not, a higher level driver's expiry handler should
|
||||
* eventually cause some kind of recovery from the DMA stall.
|
||||
*/
|
||||
return WAIT_MIN_SLEEP;
|
||||
}
|
||||
|
||||
/* Chain to the restored expiry handler if DMA wasn't active. */
|
||||
if (likely(expiry != NULL))
|
||||
return expiry(drive);
|
||||
|
||||
/* If there was no handler, "emulate" that for ide_timer_expiry()... */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void tc86c001_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned long sc_base = hwif->config_data;
|
||||
unsigned long twcr_port = sc_base + (drive->dn ? 0x06 : 0x04);
|
||||
unsigned long nsectors = blk_rq_sectors(hwif->rq);
|
||||
|
||||
/*
|
||||
* We have to manually load the sector count and size into
|
||||
* the appropriate system control registers for DMA to work
|
||||
* with LBA48 and ATAPI devices...
|
||||
*/
|
||||
outw(nsectors, sc_base + 0x0a); /* Sector Count */
|
||||
outw(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */
|
||||
|
||||
/* Install our timeout expiry hook, saving the current handler... */
|
||||
ide_set_hwifdata(hwif, hwif->expiry);
|
||||
hwif->expiry = &tc86c001_timer_expiry;
|
||||
|
||||
ide_dma_start(drive);
|
||||
}
|
||||
|
||||
static u8 tc86c001_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long sc_base = pci_resource_start(dev, 5);
|
||||
u16 scr1 = inw(sc_base + 0x00);
|
||||
|
||||
/*
|
||||
* System Control 1 Register bit 13 (PDIAGN):
|
||||
* 0=80-pin cable, 1=40-pin cable
|
||||
*/
|
||||
return (scr1 & 0x2000) ? ATA_CBL_PATA40 : ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
static void init_hwif_tc86c001(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned long sc_base = pci_resource_start(dev, 5);
|
||||
u16 scr1 = inw(sc_base + 0x00);
|
||||
|
||||
/* System Control 1 Register bit 15 (Soft Reset) set */
|
||||
outw(scr1 | 0x8000, sc_base + 0x00);
|
||||
|
||||
/* System Control 1 Register bit 14 (FIFO Reset) set */
|
||||
outw(scr1 | 0x4000, sc_base + 0x00);
|
||||
|
||||
/* System Control 1 Register: reset clear */
|
||||
outw(scr1 & ~0xc000, sc_base + 0x00);
|
||||
|
||||
/* Store the system control register base for convenience... */
|
||||
hwif->config_data = sc_base;
|
||||
|
||||
if (!hwif->dma_base)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Sector Count Control Register bits 0 and 1 set:
|
||||
* software sets Sector Count Register for master and slave device
|
||||
*/
|
||||
outw(0x0003, sc_base + 0x0c);
|
||||
|
||||
/* Sector Count Register limit */
|
||||
hwif->rqsize = 0xffff;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops tc86c001_port_ops = {
|
||||
.set_pio_mode = tc86c001_set_pio_mode,
|
||||
.set_dma_mode = tc86c001_set_mode,
|
||||
.cable_detect = tc86c001_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops tc86c001_dma_ops = {
|
||||
.dma_host_set = ide_dma_host_set,
|
||||
.dma_setup = ide_dma_setup,
|
||||
.dma_start = tc86c001_dma_start,
|
||||
.dma_end = ide_dma_end,
|
||||
.dma_test_irq = ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info tc86c001_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_hwif = init_hwif_tc86c001,
|
||||
.port_ops = &tc86c001_port_ops,
|
||||
.dma_ops = &tc86c001_dma_ops,
|
||||
.host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_OFF_BOARD,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA4,
|
||||
};
|
||||
|
||||
static int tc86c001_init_one(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pci_enable_device(dev);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = pci_request_region(dev, 5, DRV_NAME);
|
||||
if (rc) {
|
||||
printk(KERN_ERR DRV_NAME ": system control regs already in use");
|
||||
goto out_disable;
|
||||
}
|
||||
|
||||
rc = ide_pci_init_one(dev, &tc86c001_chipset, NULL);
|
||||
if (rc)
|
||||
goto out_release;
|
||||
|
||||
goto out;
|
||||
|
||||
out_release:
|
||||
pci_release_region(dev, 5);
|
||||
out_disable:
|
||||
pci_disable_device(dev);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tc86c001_remove(struct pci_dev *dev)
|
||||
{
|
||||
ide_pci_remove(dev);
|
||||
pci_release_region(dev, 5);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id tc86c001_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_IDE), 0 },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, tc86c001_pci_tbl);
|
||||
|
||||
static struct pci_driver tc86c001_pci_driver = {
|
||||
.name = "TC86C001",
|
||||
.id_table = tc86c001_pci_tbl,
|
||||
.probe = tc86c001_init_one,
|
||||
.remove = tc86c001_remove,
|
||||
};
|
||||
|
||||
static int __init tc86c001_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&tc86c001_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit tc86c001_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&tc86c001_pci_driver);
|
||||
}
|
||||
|
||||
module_init(tc86c001_ide_init);
|
||||
module_exit(tc86c001_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
|
||||
MODULE_DESCRIPTION("PCI driver module for TC86C001 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,143 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* IDE Chipset driver for the Compaq TriFlex IDE controller.
|
||||
*
|
||||
* Known to work with the Compaq Workstation 5x00 series.
|
||||
*
|
||||
* Copyright (C) 2002 Hewlett-Packard Development Group, L.P.
|
||||
* Author: Torben Mathiasen <torben.mathiasen@hp.com>
|
||||
*
|
||||
* Loosely based on the piix & svwks drivers.
|
||||
*
|
||||
* Documentation:
|
||||
* Not publicly available.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DRV_NAME "triflex"
|
||||
|
||||
static void triflex_set_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
u32 triflex_timings = 0;
|
||||
u16 timing = 0;
|
||||
u8 channel_offset = hwif->channel ? 0x74 : 0x70, unit = drive->dn & 1;
|
||||
|
||||
pci_read_config_dword(dev, channel_offset, &triflex_timings);
|
||||
|
||||
switch (drive->dma_mode) {
|
||||
case XFER_MW_DMA_2:
|
||||
timing = 0x0103;
|
||||
break;
|
||||
case XFER_MW_DMA_1:
|
||||
timing = 0x0203;
|
||||
break;
|
||||
case XFER_MW_DMA_0:
|
||||
timing = 0x0808;
|
||||
break;
|
||||
case XFER_SW_DMA_2:
|
||||
case XFER_SW_DMA_1:
|
||||
case XFER_SW_DMA_0:
|
||||
timing = 0x0f0f;
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
timing = 0x0202;
|
||||
break;
|
||||
case XFER_PIO_3:
|
||||
timing = 0x0204;
|
||||
break;
|
||||
case XFER_PIO_2:
|
||||
timing = 0x0404;
|
||||
break;
|
||||
case XFER_PIO_1:
|
||||
timing = 0x0508;
|
||||
break;
|
||||
case XFER_PIO_0:
|
||||
timing = 0x0808;
|
||||
break;
|
||||
}
|
||||
|
||||
triflex_timings &= ~(0xFFFF << (16 * unit));
|
||||
triflex_timings |= (timing << (16 * unit));
|
||||
|
||||
pci_write_config_dword(dev, channel_offset, triflex_timings);
|
||||
}
|
||||
|
||||
static void triflex_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
drive->dma_mode = drive->pio_mode;
|
||||
triflex_set_mode(hwif, drive);
|
||||
}
|
||||
|
||||
static const struct ide_port_ops triflex_port_ops = {
|
||||
.set_pio_mode = triflex_set_pio_mode,
|
||||
.set_dma_mode = triflex_set_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info triflex_device = {
|
||||
.name = DRV_NAME,
|
||||
.enablebits = {{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}},
|
||||
.port_ops = &triflex_port_ops,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.swdma_mask = ATA_SWDMA2,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
};
|
||||
|
||||
static int triflex_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &triflex_device, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id triflex_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, triflex_pci_tbl);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int triflex_ide_pci_suspend(struct pci_dev *dev, pm_message_t state)
|
||||
{
|
||||
/*
|
||||
* We must not disable or powerdown the device.
|
||||
* APM bios refuses to suspend if IDE is not accessible.
|
||||
*/
|
||||
pci_save_state(dev);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define triflex_ide_pci_suspend NULL
|
||||
#endif
|
||||
|
||||
static struct pci_driver triflex_pci_driver = {
|
||||
.name = "TRIFLEX_IDE",
|
||||
.id_table = triflex_pci_tbl,
|
||||
.probe = triflex_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
.suspend = triflex_ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init triflex_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&triflex_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit triflex_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&triflex_pci_driver);
|
||||
}
|
||||
|
||||
module_init(triflex_ide_init);
|
||||
module_exit(triflex_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Torben Mathiasen");
|
||||
MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
|
@ -1,374 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 1997-1998 Mark Lord
|
||||
* Copyright (c) 2007 MontaVista Software, Inc. <source@mvista.com>
|
||||
*
|
||||
* May be copied or modified under the terms of the GNU General Public License
|
||||
*
|
||||
* June 22, 2004 - get rid of check_region
|
||||
* - Jesper Juhl
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This module provides support for the bus-master IDE DMA function
|
||||
* of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards,
|
||||
* including a "Precision Instruments" board. The TRM290 pre-dates
|
||||
* the sff-8038 standard (ide-dma.c) by a few months, and differs
|
||||
* significantly enough to warrant separate routines for some functions,
|
||||
* while re-using others from ide-dma.c.
|
||||
*
|
||||
* EXPERIMENTAL! It works for me (a sample of one).
|
||||
*
|
||||
* Works reliably for me in DMA mode (READs only),
|
||||
* DMA WRITEs are disabled by default (see #define below);
|
||||
*
|
||||
* DMA is not enabled automatically for this chipset,
|
||||
* but can be turned on manually (with "hdparm -d1") at run time.
|
||||
*
|
||||
* I need volunteers with "spare" drives for further testing
|
||||
* and development, and maybe to help figure out the peculiarities.
|
||||
* Even knowing the registers (below), some things behave strangely.
|
||||
*/
|
||||
|
||||
#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */
|
||||
|
||||
/*
|
||||
* TRM-290 PCI-IDE2 Bus Master Chip
|
||||
* ================================
|
||||
* The configuration registers are addressed in normal I/O port space
|
||||
* and are used as follows:
|
||||
*
|
||||
* trm290_base depends on jumper settings, and is probed for by ide-dma.c
|
||||
*
|
||||
* trm290_base+2 when WRITTEN: chiptest register (byte, write-only)
|
||||
* bit7 must always be written as "1"
|
||||
* bits6-2 undefined
|
||||
* bit1 1=legacy_compatible_mode, 0=native_pci_mode
|
||||
* bit0 1=test_mode, 0=normal(default)
|
||||
*
|
||||
* trm290_base+2 when READ: status register (byte, read-only)
|
||||
* bits7-2 undefined
|
||||
* bit1 channel0 busmaster interrupt status 0=none, 1=asserted
|
||||
* bit0 channel0 interrupt status 0=none, 1=asserted
|
||||
*
|
||||
* trm290_base+3 Interrupt mask register
|
||||
* bits7-5 undefined
|
||||
* bit4 legacy_header: 1=present, 0=absent
|
||||
* bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only)
|
||||
* bit2 channel1 interrupt status 0=none, 1=asserted (read only)
|
||||
* bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default)
|
||||
* bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default)
|
||||
*
|
||||
* trm290_base+1 "CPR" Config Pointer Register (byte)
|
||||
* bit7 1=autoincrement CPR bits 2-0 after each access of CDR
|
||||
* bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state
|
||||
* bit5 0=enabled master burst access (default), 1=disable (write only)
|
||||
* bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast
|
||||
* bit3 0=primary IDE channel, 1=secondary IDE channel
|
||||
* bits2-0 register index for accesses through CDR port
|
||||
*
|
||||
* trm290_base+0 "CDR" Config Data Register (word)
|
||||
* two sets of seven config registers,
|
||||
* selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6),
|
||||
* each index defined below:
|
||||
*
|
||||
* Index-0 Base address register for command block (word)
|
||||
* defaults: 0x1f0 for primary, 0x170 for secondary
|
||||
*
|
||||
* Index-1 general config register (byte)
|
||||
* bit7 1=DMA enable, 0=DMA disable
|
||||
* bit6 1=activate IDE_RESET, 0=no action (default)
|
||||
* bit5 1=enable IORDY, 0=disable IORDY (default)
|
||||
* bit4 0=16-bit data port(default), 1=8-bit (XT) data port
|
||||
* bit3 interrupt polarity: 1=active_low, 0=active_high(default)
|
||||
* bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only)
|
||||
* bit1 bus_master_mode(?): 1=enable, 0=disable(default)
|
||||
* bit0 enable_io_ports: 1=enable(default), 0=disable
|
||||
*
|
||||
* Index-2 read-ahead counter preload bits 0-7 (byte, write only)
|
||||
* bits7-0 bits7-0 of readahead count
|
||||
*
|
||||
* Index-3 read-ahead config register (byte, write only)
|
||||
* bit7 1=enable_readahead, 0=disable_readahead(default)
|
||||
* bit6 1=clear_FIFO, 0=no_action
|
||||
* bit5 undefined
|
||||
* bit4 mode4 timing control: 1=enable, 0=disable(default)
|
||||
* bit3 undefined
|
||||
* bit2 undefined
|
||||
* bits1-0 bits9-8 of read-ahead count
|
||||
*
|
||||
* Index-4 base address register for control block (word)
|
||||
* defaults: 0x3f6 for primary, 0x376 for secondary
|
||||
*
|
||||
* Index-5 data port timings (shared by both drives) (byte)
|
||||
* standard PCI "clk" (clock) counts, default value = 0xf5
|
||||
*
|
||||
* bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk
|
||||
* bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk,
|
||||
* 011=4clk, 100=5clk, 101=6clk,
|
||||
* 110=8clk, 111=12clk
|
||||
* bits2-0 active time: 000=2clk, 001=3clk, 010=4clk,
|
||||
* 011=5clk, 100=6clk, 101=8clk,
|
||||
* 110=12clk, 111=16clk
|
||||
*
|
||||
* Index-6 command/control port timings (shared by both drives) (byte)
|
||||
* same layout as Index-5, default value = 0xde
|
||||
*
|
||||
* Suggested CDR programming for PIO mode0 (600ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary
|
||||
*
|
||||
* Suggested CDR programming for PIO mode3 (180ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary
|
||||
*
|
||||
* Suggested CDR programming for PIO mode4 (120ns):
|
||||
* 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary
|
||||
* 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ide.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "trm290"
|
||||
|
||||
static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u16 reg = 0;
|
||||
unsigned long flags;
|
||||
|
||||
/* select PIO or DMA */
|
||||
reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82);
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
if (reg != hwif->select_data) {
|
||||
hwif->select_data = reg;
|
||||
/* set PIO/DMA */
|
||||
outb(0x51 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
outw(reg & 0xff, hwif->config_data);
|
||||
}
|
||||
|
||||
/* enable IRQ if not probing */
|
||||
if (drive->dev_flags & IDE_DFLAG_PRESENT) {
|
||||
reg = inw(hwif->config_data + 3);
|
||||
reg &= 0x13;
|
||||
reg &= ~(1 << hwif->channel);
|
||||
outw(reg, hwif->config_data + 3);
|
||||
}
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static void trm290_dev_select(ide_drive_t *drive)
|
||||
{
|
||||
trm290_prepare_drive(drive, !!(drive->dev_flags & IDE_DFLAG_USING_DMA));
|
||||
|
||||
outb(drive->select | ATA_DEVICE_OBS, drive->hwif->io_ports.device_addr);
|
||||
}
|
||||
|
||||
static int trm290_dma_check(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||
{
|
||||
if (cmd->tf_flags & IDE_TFLAG_WRITE) {
|
||||
#ifdef TRM290_NO_DMA_WRITES
|
||||
/* always use PIO for writes */
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trm290_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
unsigned int count, rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 1 : 2;
|
||||
|
||||
count = ide_build_dmatable(drive, cmd);
|
||||
if (count == 0)
|
||||
/* try PIO instead of DMA */
|
||||
return 1;
|
||||
|
||||
outl(hwif->dmatable_dma | rw, hwif->dma_base);
|
||||
/* start DMA */
|
||||
outw(count * 2 - 1, hwif->dma_base + 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trm290_dma_start(ide_drive_t *drive)
|
||||
{
|
||||
trm290_prepare_drive(drive, 1);
|
||||
}
|
||||
|
||||
static int trm290_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
u16 status = inw(drive->hwif->dma_base + 2);
|
||||
|
||||
trm290_prepare_drive(drive, 0);
|
||||
|
||||
return status != 0x00ff;
|
||||
}
|
||||
|
||||
static int trm290_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
u16 status = inw(drive->hwif->dma_base + 2);
|
||||
|
||||
return status == 0x00ff;
|
||||
}
|
||||
|
||||
static void trm290_dma_host_set(ide_drive_t *drive, int on)
|
||||
{
|
||||
}
|
||||
|
||||
static void init_hwif_trm290(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
unsigned int cfg_base = pci_resource_start(dev, 4);
|
||||
unsigned long flags;
|
||||
u8 reg = 0;
|
||||
|
||||
if ((dev->class & 5) && cfg_base)
|
||||
printk(KERN_INFO DRV_NAME " %s: chip", pci_name(dev));
|
||||
else {
|
||||
cfg_base = 0x3df0;
|
||||
printk(KERN_INFO DRV_NAME " %s: using default", pci_name(dev));
|
||||
}
|
||||
printk(KERN_CONT " config base at 0x%04x\n", cfg_base);
|
||||
hwif->config_data = cfg_base;
|
||||
hwif->dma_base = (cfg_base + 4) ^ (hwif->channel ? 0x80 : 0);
|
||||
|
||||
printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n",
|
||||
hwif->name, hwif->dma_base, hwif->dma_base + 3);
|
||||
|
||||
if (ide_allocate_dma_engine(hwif))
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
/* put config reg into first byte of hwif->select_data */
|
||||
outb(0x51 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
/* select PIO as default */
|
||||
hwif->select_data = 0x21;
|
||||
outb(hwif->select_data, hwif->config_data);
|
||||
/* get IRQ info */
|
||||
reg = inb(hwif->config_data + 3);
|
||||
/* mask IRQs for both ports */
|
||||
reg = (reg & 0x10) | 0x03;
|
||||
outb(reg, hwif->config_data + 3);
|
||||
local_irq_restore(flags);
|
||||
|
||||
if (reg & 0x10)
|
||||
/* legacy mode */
|
||||
hwif->irq = hwif->channel ? 15 : 14;
|
||||
|
||||
#if 1
|
||||
{
|
||||
/*
|
||||
* My trm290-based card doesn't seem to work with all possible values
|
||||
* for the control basereg, so this kludge ensures that we use only
|
||||
* values that are known to work. Ugh. -ml
|
||||
*/
|
||||
u16 new, old, compat = hwif->channel ? 0x374 : 0x3f4;
|
||||
static u16 next_offset = 0;
|
||||
u8 old_mask;
|
||||
|
||||
outb(0x54 | (hwif->channel << 3), hwif->config_data + 1);
|
||||
old = inw(hwif->config_data);
|
||||
old &= ~1;
|
||||
old_mask = inb(old + 2);
|
||||
if (old != compat && old_mask == 0xff) {
|
||||
/* leave lower 10 bits untouched */
|
||||
compat += (next_offset += 0x400);
|
||||
hwif->io_ports.ctl_addr = compat + 2;
|
||||
outw(compat | 1, hwif->config_data);
|
||||
new = inw(hwif->config_data);
|
||||
printk(KERN_INFO "%s: control basereg workaround: "
|
||||
"old=0x%04x, new=0x%04x\n",
|
||||
hwif->name, old, new & ~1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops trm290_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = trm290_dev_select,
|
||||
.tf_load = ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = ide_input_data,
|
||||
.output_data = ide_output_data,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops trm290_dma_ops = {
|
||||
.dma_host_set = trm290_dma_host_set,
|
||||
.dma_setup = trm290_dma_setup,
|
||||
.dma_start = trm290_dma_start,
|
||||
.dma_end = trm290_dma_end,
|
||||
.dma_test_irq = trm290_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_check = trm290_dma_check,
|
||||
};
|
||||
|
||||
static const struct ide_port_info trm290_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_hwif = init_hwif_trm290,
|
||||
.tp_ops = &trm290_tp_ops,
|
||||
.dma_ops = &trm290_dma_ops,
|
||||
.host_flags = IDE_HFLAG_TRM290 |
|
||||
IDE_HFLAG_NO_ATAPI_DMA |
|
||||
#if 0 /* play it safe for now */
|
||||
IDE_HFLAG_TRUST_BIOS_FOR_DMA |
|
||||
#endif
|
||||
IDE_HFLAG_NO_AUTODMA |
|
||||
IDE_HFLAG_NO_LBA48,
|
||||
};
|
||||
|
||||
static int trm290_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
return ide_pci_init_one(dev, &trm290_chipset, NULL);
|
||||
}
|
||||
|
||||
static const struct pci_device_id trm290_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290), 0 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, trm290_pci_tbl);
|
||||
|
||||
static struct pci_driver trm290_pci_driver = {
|
||||
.name = "TRM290_IDE",
|
||||
.id_table = trm290_pci_tbl,
|
||||
.probe = trm290_init_one,
|
||||
.remove = ide_pci_remove,
|
||||
};
|
||||
|
||||
static int __init trm290_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&trm290_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit trm290_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&trm290_pci_driver);
|
||||
}
|
||||
|
||||
module_init(trm290_ide_init);
|
||||
module_exit(trm290_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Lord");
|
||||
MODULE_DESCRIPTION("PCI driver module for Tekram TRM290 IDE");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,209 +0,0 @@
|
||||
/*
|
||||
* TX4938 internal IDE driver
|
||||
* Based on tx4939ide.c.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* (C) Copyright TOSHIBA CORPORATION 2005-2007
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/ide.h>
|
||||
#include <asm/txx9/tx4938.h>
|
||||
|
||||
static void tx4938ide_tune_ebusc(unsigned int ebus_ch,
|
||||
unsigned int gbus_clock,
|
||||
u8 pio)
|
||||
{
|
||||
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
|
||||
u64 cr = __raw_readq(&tx4938_ebuscptr->cr[ebus_ch]);
|
||||
unsigned int sp = (cr >> 4) & 3;
|
||||
unsigned int clock = gbus_clock / (4 - sp);
|
||||
unsigned int cycle = 1000000000 / clock;
|
||||
unsigned int shwt;
|
||||
int wt;
|
||||
|
||||
/* Minimum DIOx- active time */
|
||||
wt = DIV_ROUND_UP(t->act8b, cycle) - 2;
|
||||
/* IORDY setup time: 35ns */
|
||||
wt = max_t(int, wt, DIV_ROUND_UP(35, cycle));
|
||||
/* actual wait-cycle is max(wt & ~1, 1) */
|
||||
if (wt > 2 && (wt & 1))
|
||||
wt++;
|
||||
wt &= ~1;
|
||||
/* Address-valid to DIOR/DIOW setup */
|
||||
shwt = DIV_ROUND_UP(t->setup, cycle);
|
||||
|
||||
/* -DIOx recovery time (SHWT * 4) and cycle time requirement */
|
||||
while ((shwt * 4 + wt + (wt ? 2 : 3)) * cycle < t->cycle)
|
||||
shwt++;
|
||||
if (shwt > 7) {
|
||||
pr_warn("tx4938ide: SHWT violation (%d)\n", shwt);
|
||||
shwt = 7;
|
||||
}
|
||||
pr_debug("tx4938ide: ebus %d, bus cycle %dns, WT %d, SHWT %d\n",
|
||||
ebus_ch, cycle, wt, shwt);
|
||||
|
||||
__raw_writeq((cr & ~0x3f007ull) | (wt << 12) | shwt,
|
||||
&tx4938_ebuscptr->cr[ebus_ch]);
|
||||
}
|
||||
|
||||
static void tx4938ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
struct tx4938ide_platform_info *pdata = dev_get_platdata(hwif->dev);
|
||||
u8 safe = drive->pio_mode - XFER_PIO_0;
|
||||
ide_drive_t *pair;
|
||||
|
||||
pair = ide_get_pair_dev(drive);
|
||||
if (pair)
|
||||
safe = min_t(u8, safe, pair->pio_mode - XFER_PIO_0);
|
||||
tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, safe);
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
|
||||
/* custom iops (independent from SWAP_IO_SPACE) */
|
||||
static void tx4938ide_input_data_swap(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long port = drive->hwif->io_ports.data_addr;
|
||||
unsigned short *ptr = buf;
|
||||
unsigned int count = (len + 1) / 2;
|
||||
|
||||
while (count--)
|
||||
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
|
||||
__ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
|
||||
}
|
||||
|
||||
static void tx4938ide_output_data_swap(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long port = drive->hwif->io_ports.data_addr;
|
||||
unsigned short *ptr = buf;
|
||||
unsigned int count = (len + 1) / 2;
|
||||
|
||||
while (count--) {
|
||||
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
|
||||
ptr++;
|
||||
}
|
||||
__ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops tx4938ide_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = ide_dev_select,
|
||||
.tf_load = ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = tx4938ide_input_data_swap,
|
||||
.output_data = tx4938ide_output_data_swap,
|
||||
};
|
||||
|
||||
#endif /* __BIG_ENDIAN */
|
||||
|
||||
static const struct ide_port_ops tx4938ide_port_ops = {
|
||||
.set_pio_mode = tx4938ide_set_pio_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info tx4938ide_port_info __initconst = {
|
||||
.port_ops = &tx4938ide_port_ops,
|
||||
#ifdef __BIG_ENDIAN
|
||||
.tp_ops = &tx4938ide_tp_ops,
|
||||
#endif
|
||||
.host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
|
||||
.pio_mask = ATA_PIO5,
|
||||
.chipset = ide_generic,
|
||||
};
|
||||
|
||||
static int __init tx4938ide_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
struct ide_host *host;
|
||||
struct resource *res;
|
||||
struct tx4938ide_platform_info *pdata = dev_get_platdata(&pdev->dev);
|
||||
int irq, ret, i;
|
||||
unsigned long mapbase, mapctl;
|
||||
struct ide_port_info d = tx4938ide_port_info;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start,
|
||||
resource_size(res), "tx4938ide"))
|
||||
return -EBUSY;
|
||||
mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start,
|
||||
8 << pdata->ioport_shift);
|
||||
mapctl = (unsigned long)devm_ioremap(&pdev->dev,
|
||||
res->start + 0x10000 +
|
||||
(6 << pdata->ioport_shift),
|
||||
1 << pdata->ioport_shift);
|
||||
if (!mapbase || !mapctl)
|
||||
return -EBUSY;
|
||||
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
if (pdata->ioport_shift) {
|
||||
unsigned long port = mapbase;
|
||||
unsigned long ctl = mapctl;
|
||||
|
||||
hw.io_ports_array[0] = port;
|
||||
#ifdef __BIG_ENDIAN
|
||||
port++;
|
||||
ctl++;
|
||||
#endif
|
||||
for (i = 1; i <= 7; i++)
|
||||
hw.io_ports_array[i] =
|
||||
port + (i << pdata->ioport_shift);
|
||||
hw.io_ports.ctl_addr = ctl;
|
||||
} else
|
||||
ide_std_init_ports(&hw, mapbase, mapctl);
|
||||
hw.irq = irq;
|
||||
hw.dev = &pdev->dev;
|
||||
|
||||
pr_info("TX4938 IDE interface (base %#lx, ctl %#lx, irq %d)\n",
|
||||
mapbase, mapctl, hw.irq);
|
||||
if (pdata->gbus_clock)
|
||||
tx4938ide_tune_ebusc(pdata->ebus_ch, pdata->gbus_clock, 0);
|
||||
else
|
||||
d.port_ops = NULL;
|
||||
ret = ide_host_add(&d, hws, 1, &host);
|
||||
if (!ret)
|
||||
platform_set_drvdata(pdev, host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit tx4938ide_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ide_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
ide_host_remove(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver tx4938ide_driver = {
|
||||
.driver = {
|
||||
.name = "tx4938ide",
|
||||
},
|
||||
.remove = __exit_p(tx4938ide_remove),
|
||||
};
|
||||
|
||||
module_platform_driver_probe(tx4938ide_driver, tx4938ide_probe);
|
||||
|
||||
MODULE_DESCRIPTION("TX4938 internal IDE driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:tx4938ide");
|
@ -1,628 +0,0 @@
|
||||
/*
|
||||
* TX4939 internal IDE driver
|
||||
* Based on RBTX49xx patch from CELF patch archive.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* (C) Copyright TOSHIBA CORPORATION 2005-2007
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <asm/ide.h>
|
||||
|
||||
#define MODNAME "tx4939ide"
|
||||
|
||||
/* ATA Shadow Registers (8-bit except for Data which is 16-bit) */
|
||||
#define TX4939IDE_Data 0x000
|
||||
#define TX4939IDE_Error_Feature 0x001
|
||||
#define TX4939IDE_Sec 0x002
|
||||
#define TX4939IDE_LBA0 0x003
|
||||
#define TX4939IDE_LBA1 0x004
|
||||
#define TX4939IDE_LBA2 0x005
|
||||
#define TX4939IDE_DevHead 0x006
|
||||
#define TX4939IDE_Stat_Cmd 0x007
|
||||
#define TX4939IDE_AltStat_DevCtl 0x402
|
||||
/* H/W DMA Registers */
|
||||
#define TX4939IDE_DMA_Cmd 0x800 /* 8-bit */
|
||||
#define TX4939IDE_DMA_Stat 0x802 /* 8-bit */
|
||||
#define TX4939IDE_PRD_Ptr 0x804 /* 32-bit */
|
||||
/* ATA100 CORE Registers (16-bit) */
|
||||
#define TX4939IDE_Sys_Ctl 0xc00
|
||||
#define TX4939IDE_Xfer_Cnt_1 0xc08
|
||||
#define TX4939IDE_Xfer_Cnt_2 0xc0a
|
||||
#define TX4939IDE_Sec_Cnt 0xc10
|
||||
#define TX4939IDE_Start_Lo_Addr 0xc18
|
||||
#define TX4939IDE_Start_Up_Addr 0xc20
|
||||
#define TX4939IDE_Add_Ctl 0xc28
|
||||
#define TX4939IDE_Lo_Burst_Cnt 0xc30
|
||||
#define TX4939IDE_Up_Burst_Cnt 0xc38
|
||||
#define TX4939IDE_PIO_Addr 0xc88
|
||||
#define TX4939IDE_H_Rst_Tim 0xc90
|
||||
#define TX4939IDE_Int_Ctl 0xc98
|
||||
#define TX4939IDE_Pkt_Cmd 0xcb8
|
||||
#define TX4939IDE_Bxfer_Cnt_Hi 0xcc0
|
||||
#define TX4939IDE_Bxfer_Cnt_Lo 0xcc8
|
||||
#define TX4939IDE_Dev_TErr 0xcd0
|
||||
#define TX4939IDE_Pkt_Xfer_Ctl 0xcd8
|
||||
#define TX4939IDE_Start_TAddr 0xce0
|
||||
|
||||
/* bits for Int_Ctl */
|
||||
#define TX4939IDE_INT_ADDRERR 0x80
|
||||
#define TX4939IDE_INT_REACHMUL 0x40
|
||||
#define TX4939IDE_INT_DEVTIMING 0x20
|
||||
#define TX4939IDE_INT_UDMATERM 0x10
|
||||
#define TX4939IDE_INT_TIMER 0x08
|
||||
#define TX4939IDE_INT_BUSERR 0x04
|
||||
#define TX4939IDE_INT_XFEREND 0x02
|
||||
#define TX4939IDE_INT_HOST 0x01
|
||||
|
||||
#define TX4939IDE_IGNORE_INTS \
|
||||
(TX4939IDE_INT_ADDRERR | TX4939IDE_INT_REACHMUL | \
|
||||
TX4939IDE_INT_DEVTIMING | TX4939IDE_INT_UDMATERM | \
|
||||
TX4939IDE_INT_TIMER | TX4939IDE_INT_XFEREND)
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
#define tx4939ide_swizzlel(a) ((a) ^ 4)
|
||||
#define tx4939ide_swizzlew(a) ((a) ^ 6)
|
||||
#define tx4939ide_swizzleb(a) ((a) ^ 7)
|
||||
#else
|
||||
#define tx4939ide_swizzlel(a) (a)
|
||||
#define tx4939ide_swizzlew(a) (a)
|
||||
#define tx4939ide_swizzleb(a) (a)
|
||||
#endif
|
||||
|
||||
static u16 tx4939ide_readw(void __iomem *base, u32 reg)
|
||||
{
|
||||
return __raw_readw(base + tx4939ide_swizzlew(reg));
|
||||
}
|
||||
static u8 tx4939ide_readb(void __iomem *base, u32 reg)
|
||||
{
|
||||
return __raw_readb(base + tx4939ide_swizzleb(reg));
|
||||
}
|
||||
static void tx4939ide_writel(u32 val, void __iomem *base, u32 reg)
|
||||
{
|
||||
__raw_writel(val, base + tx4939ide_swizzlel(reg));
|
||||
}
|
||||
static void tx4939ide_writew(u16 val, void __iomem *base, u32 reg)
|
||||
{
|
||||
__raw_writew(val, base + tx4939ide_swizzlew(reg));
|
||||
}
|
||||
static void tx4939ide_writeb(u8 val, void __iomem *base, u32 reg)
|
||||
{
|
||||
__raw_writeb(val, base + tx4939ide_swizzleb(reg));
|
||||
}
|
||||
|
||||
#define TX4939IDE_BASE(hwif) ((void __iomem *)(hwif)->extra_base)
|
||||
|
||||
static void tx4939ide_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
int is_slave = drive->dn;
|
||||
u32 mask, val;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
u8 safe = pio;
|
||||
ide_drive_t *pair;
|
||||
|
||||
pair = ide_get_pair_dev(drive);
|
||||
if (pair)
|
||||
safe = min_t(u8, safe, pair->pio_mode - XFER_PIO_0);
|
||||
/*
|
||||
* Update Command Transfer Mode for master/slave and Data
|
||||
* Transfer Mode for this drive.
|
||||
*/
|
||||
mask = is_slave ? 0x07f00000 : 0x000007f0;
|
||||
val = ((safe << 8) | (pio << 4)) << (is_slave ? 16 : 0);
|
||||
hwif->select_data = (hwif->select_data & ~mask) | val;
|
||||
/* tx4939ide_tf_load_fixup() will set the Sys_Ctl register */
|
||||
}
|
||||
|
||||
static void tx4939ide_set_dma_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
u32 mask, val;
|
||||
const u8 mode = drive->dma_mode;
|
||||
|
||||
/* Update Data Transfer Mode for this drive. */
|
||||
if (mode >= XFER_UDMA_0)
|
||||
val = mode - XFER_UDMA_0 + 8;
|
||||
else
|
||||
val = mode - XFER_MW_DMA_0 + 5;
|
||||
if (drive->dn) {
|
||||
mask = 0x00f00000;
|
||||
val <<= 20;
|
||||
} else {
|
||||
mask = 0x000000f0;
|
||||
val <<= 4;
|
||||
}
|
||||
hwif->select_data = (hwif->select_data & ~mask) | val;
|
||||
/* tx4939ide_tf_load_fixup() will set the Sys_Ctl register */
|
||||
}
|
||||
|
||||
static u16 tx4939ide_check_error_ints(ide_hwif_t *hwif)
|
||||
{
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u16 ctl = tx4939ide_readw(base, TX4939IDE_Int_Ctl);
|
||||
|
||||
if (ctl & TX4939IDE_INT_BUSERR) {
|
||||
/* reset FIFO */
|
||||
u16 sysctl = tx4939ide_readw(base, TX4939IDE_Sys_Ctl);
|
||||
|
||||
tx4939ide_writew(sysctl | 0x4000, base, TX4939IDE_Sys_Ctl);
|
||||
/* wait 12GBUSCLK (typ. 60ns @ GBUS200MHz, max 270ns) */
|
||||
ndelay(270);
|
||||
tx4939ide_writew(sysctl, base, TX4939IDE_Sys_Ctl);
|
||||
}
|
||||
if (ctl & (TX4939IDE_INT_ADDRERR |
|
||||
TX4939IDE_INT_DEVTIMING | TX4939IDE_INT_BUSERR))
|
||||
pr_err("%s: Error interrupt %#x (%s%s%s )\n",
|
||||
hwif->name, ctl,
|
||||
ctl & TX4939IDE_INT_ADDRERR ? " Address-Error" : "",
|
||||
ctl & TX4939IDE_INT_DEVTIMING ? " DEV-Timing" : "",
|
||||
ctl & TX4939IDE_INT_BUSERR ? " Bus-Error" : "");
|
||||
return ctl;
|
||||
}
|
||||
|
||||
static void tx4939ide_clear_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif;
|
||||
void __iomem *base;
|
||||
u16 ctl;
|
||||
|
||||
/*
|
||||
* tx4939ide_dma_test_irq() and tx4939ide_dma_end() do all job
|
||||
* for DMA case.
|
||||
*/
|
||||
if (drive->waiting_for_dma)
|
||||
return;
|
||||
hwif = drive->hwif;
|
||||
base = TX4939IDE_BASE(hwif);
|
||||
ctl = tx4939ide_check_error_ints(hwif);
|
||||
tx4939ide_writew(ctl, base, TX4939IDE_Int_Ctl);
|
||||
}
|
||||
|
||||
static u8 tx4939ide_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
|
||||
return tx4939ide_readw(base, TX4939IDE_Sys_Ctl) & 0x2000 ?
|
||||
ATA_CBL_PATA40 : ATA_CBL_PATA80;
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
static void tx4939ide_dma_host_set(ide_drive_t *drive, int on)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 unit = drive->dn;
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u8 dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat);
|
||||
|
||||
if (on)
|
||||
dma_stat |= (1 << (5 + unit));
|
||||
else
|
||||
dma_stat &= ~(1 << (5 + unit));
|
||||
|
||||
tx4939ide_writeb(dma_stat, base, TX4939IDE_DMA_Stat);
|
||||
}
|
||||
#else
|
||||
#define tx4939ide_dma_host_set ide_dma_host_set
|
||||
#endif
|
||||
|
||||
static u8 tx4939ide_clear_dma_status(void __iomem *base)
|
||||
{
|
||||
u8 dma_stat;
|
||||
|
||||
/* read DMA status for INTR & ERROR flags */
|
||||
dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat);
|
||||
/* clear INTR & ERROR flags */
|
||||
tx4939ide_writeb(dma_stat | ATA_DMA_INTR | ATA_DMA_ERR, base,
|
||||
TX4939IDE_DMA_Stat);
|
||||
/* recover intmask cleared by writing to bit2 of DMA_Stat */
|
||||
tx4939ide_writew(TX4939IDE_IGNORE_INTS << 8, base, TX4939IDE_Int_Ctl);
|
||||
return dma_stat;
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
/* custom ide_build_dmatable to handle swapped layout */
|
||||
static int tx4939ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u32 *table = (u32 *)hwif->dmatable_cpu;
|
||||
unsigned int count = 0;
|
||||
int i;
|
||||
struct scatterlist *sg;
|
||||
|
||||
for_each_sg(hwif->sg_table, sg, cmd->sg_nents, i) {
|
||||
u32 cur_addr, cur_len, bcount;
|
||||
|
||||
cur_addr = sg_dma_address(sg);
|
||||
cur_len = sg_dma_len(sg);
|
||||
|
||||
/*
|
||||
* Fill in the DMA table, without crossing any 64kB boundaries.
|
||||
*/
|
||||
|
||||
while (cur_len) {
|
||||
if (count++ >= PRD_ENTRIES)
|
||||
goto use_pio_instead;
|
||||
|
||||
bcount = 0x10000 - (cur_addr & 0xffff);
|
||||
if (bcount > cur_len)
|
||||
bcount = cur_len;
|
||||
/*
|
||||
* This workaround for zero count seems required.
|
||||
* (standard ide_build_dmatable does it too)
|
||||
*/
|
||||
if (bcount == 0x10000)
|
||||
bcount = 0x8000;
|
||||
*table++ = bcount & 0xffff;
|
||||
*table++ = cur_addr;
|
||||
cur_addr += bcount;
|
||||
cur_len -= bcount;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
*(table - 2) |= 0x80000000;
|
||||
return count;
|
||||
}
|
||||
|
||||
use_pio_instead:
|
||||
printk(KERN_ERR "%s: %s\n", drive->name,
|
||||
count ? "DMA table too small" : "empty DMA table?");
|
||||
|
||||
return 0; /* revert to PIO for this request */
|
||||
}
|
||||
#else
|
||||
#define tx4939ide_build_dmatable ide_build_dmatable
|
||||
#endif
|
||||
|
||||
static int tx4939ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u8 rw = (cmd->tf_flags & IDE_TFLAG_WRITE) ? 0 : ATA_DMA_WR;
|
||||
|
||||
/* fall back to PIO! */
|
||||
if (tx4939ide_build_dmatable(drive, cmd) == 0)
|
||||
return 1;
|
||||
|
||||
/* PRD table */
|
||||
tx4939ide_writel(hwif->dmatable_dma, base, TX4939IDE_PRD_Ptr);
|
||||
|
||||
/* specify r/w */
|
||||
tx4939ide_writeb(rw, base, TX4939IDE_DMA_Cmd);
|
||||
|
||||
/* clear INTR & ERROR flags */
|
||||
tx4939ide_clear_dma_status(base);
|
||||
|
||||
tx4939ide_writew(SECTOR_SIZE / 2, base, drive->dn ?
|
||||
TX4939IDE_Xfer_Cnt_2 : TX4939IDE_Xfer_Cnt_1);
|
||||
|
||||
tx4939ide_writew(blk_rq_sectors(cmd->rq), base, TX4939IDE_Sec_Cnt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tx4939ide_dma_end(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
u8 dma_stat, dma_cmd;
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u16 ctl = tx4939ide_readw(base, TX4939IDE_Int_Ctl);
|
||||
|
||||
/* get DMA command mode */
|
||||
dma_cmd = tx4939ide_readb(base, TX4939IDE_DMA_Cmd);
|
||||
/* stop DMA */
|
||||
tx4939ide_writeb(dma_cmd & ~ATA_DMA_START, base, TX4939IDE_DMA_Cmd);
|
||||
|
||||
/* read and clear the INTR & ERROR bits */
|
||||
dma_stat = tx4939ide_clear_dma_status(base);
|
||||
|
||||
#define CHECK_DMA_MASK (ATA_DMA_ACTIVE | ATA_DMA_ERR | ATA_DMA_INTR)
|
||||
|
||||
/* verify good DMA status */
|
||||
if ((dma_stat & CHECK_DMA_MASK) == 0 &&
|
||||
(ctl & (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST)) ==
|
||||
(TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST))
|
||||
/* INT_IDE lost... bug? */
|
||||
return 0;
|
||||
return ((dma_stat & CHECK_DMA_MASK) !=
|
||||
ATA_DMA_INTR) ? 0x10 | dma_stat : 0;
|
||||
}
|
||||
|
||||
/* returns 1 if DMA IRQ issued, 0 otherwise */
|
||||
static int tx4939ide_dma_test_irq(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u16 ctl, ide_int;
|
||||
u8 dma_stat, stat;
|
||||
int found = 0;
|
||||
|
||||
ctl = tx4939ide_check_error_ints(hwif);
|
||||
ide_int = ctl & (TX4939IDE_INT_XFEREND | TX4939IDE_INT_HOST);
|
||||
switch (ide_int) {
|
||||
case TX4939IDE_INT_HOST:
|
||||
/* On error, XFEREND might not be asserted. */
|
||||
stat = tx4939ide_readb(base, TX4939IDE_AltStat_DevCtl);
|
||||
if ((stat & (ATA_BUSY | ATA_DRQ | ATA_ERR)) == ATA_ERR)
|
||||
found = 1;
|
||||
else
|
||||
/* Wait for XFEREND (Mask HOST and unmask XFEREND) */
|
||||
ctl &= ~TX4939IDE_INT_XFEREND << 8;
|
||||
ctl |= ide_int << 8;
|
||||
break;
|
||||
case TX4939IDE_INT_HOST | TX4939IDE_INT_XFEREND:
|
||||
dma_stat = tx4939ide_readb(base, TX4939IDE_DMA_Stat);
|
||||
if (!(dma_stat & ATA_DMA_INTR))
|
||||
pr_warn("%s: weird interrupt status. "
|
||||
"DMA_Stat %#02x int_ctl %#04x\n",
|
||||
hwif->name, dma_stat, ctl);
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Do not clear XFEREND, HOST now. They will be cleared by
|
||||
* clearing bit2 of DMA_Stat.
|
||||
*/
|
||||
ctl &= ~ide_int;
|
||||
tx4939ide_writew(ctl, base, TX4939IDE_Int_Ctl);
|
||||
return found;
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
static u8 tx4939ide_dma_sff_read_status(ide_hwif_t *hwif)
|
||||
{
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
|
||||
return tx4939ide_readb(base, TX4939IDE_DMA_Stat);
|
||||
}
|
||||
#else
|
||||
#define tx4939ide_dma_sff_read_status ide_dma_sff_read_status
|
||||
#endif
|
||||
|
||||
static void tx4939ide_init_hwif(ide_hwif_t *hwif)
|
||||
{
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
|
||||
/* Soft Reset */
|
||||
tx4939ide_writew(0x8000, base, TX4939IDE_Sys_Ctl);
|
||||
/* at least 20 GBUSCLK (typ. 100ns @ GBUS200MHz, max 450ns) */
|
||||
ndelay(450);
|
||||
tx4939ide_writew(0x0000, base, TX4939IDE_Sys_Ctl);
|
||||
/* mask some interrupts and clear all interrupts */
|
||||
tx4939ide_writew((TX4939IDE_IGNORE_INTS << 8) | 0xff, base,
|
||||
TX4939IDE_Int_Ctl);
|
||||
|
||||
tx4939ide_writew(0x0008, base, TX4939IDE_Lo_Burst_Cnt);
|
||||
tx4939ide_writew(0, base, TX4939IDE_Up_Burst_Cnt);
|
||||
}
|
||||
|
||||
static int tx4939ide_init_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
|
||||
{
|
||||
hwif->dma_base =
|
||||
hwif->extra_base + tx4939ide_swizzleb(TX4939IDE_DMA_Cmd);
|
||||
/*
|
||||
* Note that we cannot use ATA_DMA_TABLE_OFS, ATA_DMA_STATUS
|
||||
* for big endian.
|
||||
*/
|
||||
return ide_allocate_dma_engine(hwif);
|
||||
}
|
||||
|
||||
static void tx4939ide_tf_load_fixup(ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *hwif = drive->hwif;
|
||||
void __iomem *base = TX4939IDE_BASE(hwif);
|
||||
u16 sysctl = hwif->select_data >> (drive->dn ? 16 : 0);
|
||||
|
||||
/*
|
||||
* Fix ATA100 CORE System Control Register. (The write to the
|
||||
* Device/Head register may write wrong data to the System
|
||||
* Control Register)
|
||||
* While Sys_Ctl is written here, dev_select() is not needed.
|
||||
*/
|
||||
tx4939ide_writew(sysctl, base, TX4939IDE_Sys_Ctl);
|
||||
}
|
||||
|
||||
static void tx4939ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf,
|
||||
u8 valid)
|
||||
{
|
||||
ide_tf_load(drive, tf, valid);
|
||||
|
||||
if (valid & IDE_VALID_DEVICE)
|
||||
tx4939ide_tf_load_fixup(drive);
|
||||
}
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
|
||||
/* custom iops (independent from SWAP_IO_SPACE) */
|
||||
static void tx4939ide_input_data_swap(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long port = drive->hwif->io_ports.data_addr;
|
||||
unsigned short *ptr = buf;
|
||||
unsigned int count = (len + 1) / 2;
|
||||
|
||||
while (count--)
|
||||
*ptr++ = cpu_to_le16(__raw_readw((void __iomem *)port));
|
||||
__ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
|
||||
}
|
||||
|
||||
static void tx4939ide_output_data_swap(ide_drive_t *drive, struct ide_cmd *cmd,
|
||||
void *buf, unsigned int len)
|
||||
{
|
||||
unsigned long port = drive->hwif->io_ports.data_addr;
|
||||
unsigned short *ptr = buf;
|
||||
unsigned int count = (len + 1) / 2;
|
||||
|
||||
while (count--) {
|
||||
__raw_writew(le16_to_cpu(*ptr), (void __iomem *)port);
|
||||
ptr++;
|
||||
}
|
||||
__ide_flush_dcache_range((unsigned long)buf, roundup(len, 2));
|
||||
}
|
||||
|
||||
static const struct ide_tp_ops tx4939ide_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = ide_dev_select,
|
||||
.tf_load = tx4939ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = tx4939ide_input_data_swap,
|
||||
.output_data = tx4939ide_output_data_swap,
|
||||
};
|
||||
|
||||
#else /* __LITTLE_ENDIAN */
|
||||
|
||||
static const struct ide_tp_ops tx4939ide_tp_ops = {
|
||||
.exec_command = ide_exec_command,
|
||||
.read_status = ide_read_status,
|
||||
.read_altstatus = ide_read_altstatus,
|
||||
.write_devctl = ide_write_devctl,
|
||||
|
||||
.dev_select = ide_dev_select,
|
||||
.tf_load = tx4939ide_tf_load,
|
||||
.tf_read = ide_tf_read,
|
||||
|
||||
.input_data = ide_input_data,
|
||||
.output_data = ide_output_data,
|
||||
};
|
||||
|
||||
#endif /* __LITTLE_ENDIAN */
|
||||
|
||||
static const struct ide_port_ops tx4939ide_port_ops = {
|
||||
.set_pio_mode = tx4939ide_set_pio_mode,
|
||||
.set_dma_mode = tx4939ide_set_dma_mode,
|
||||
.clear_irq = tx4939ide_clear_irq,
|
||||
.cable_detect = tx4939ide_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_dma_ops tx4939ide_dma_ops = {
|
||||
.dma_host_set = tx4939ide_dma_host_set,
|
||||
.dma_setup = tx4939ide_dma_setup,
|
||||
.dma_start = ide_dma_start,
|
||||
.dma_end = tx4939ide_dma_end,
|
||||
.dma_test_irq = tx4939ide_dma_test_irq,
|
||||
.dma_lost_irq = ide_dma_lost_irq,
|
||||
.dma_timer_expiry = ide_dma_sff_timer_expiry,
|
||||
.dma_sff_read_status = tx4939ide_dma_sff_read_status,
|
||||
};
|
||||
|
||||
static const struct ide_port_info tx4939ide_port_info __initconst = {
|
||||
.init_hwif = tx4939ide_init_hwif,
|
||||
.init_dma = tx4939ide_init_dma,
|
||||
.port_ops = &tx4939ide_port_ops,
|
||||
.dma_ops = &tx4939ide_dma_ops,
|
||||
.tp_ops = &tx4939ide_tp_ops,
|
||||
.host_flags = IDE_HFLAG_MMIO,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
.udma_mask = ATA_UDMA5,
|
||||
.chipset = ide_generic,
|
||||
};
|
||||
|
||||
static int __init tx4939ide_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ide_hw hw, *hws[] = { &hw };
|
||||
struct ide_host *host;
|
||||
struct resource *res;
|
||||
int irq, ret;
|
||||
unsigned long mapbase;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
if (!devm_request_mem_region(&pdev->dev, res->start,
|
||||
resource_size(res), MODNAME))
|
||||
return -EBUSY;
|
||||
mapbase = (unsigned long)devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!mapbase)
|
||||
return -EBUSY;
|
||||
memset(&hw, 0, sizeof(hw));
|
||||
hw.io_ports.data_addr =
|
||||
mapbase + tx4939ide_swizzlew(TX4939IDE_Data);
|
||||
hw.io_ports.error_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_Error_Feature);
|
||||
hw.io_ports.nsect_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_Sec);
|
||||
hw.io_ports.lbal_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_LBA0);
|
||||
hw.io_ports.lbam_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_LBA1);
|
||||
hw.io_ports.lbah_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_LBA2);
|
||||
hw.io_ports.device_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_DevHead);
|
||||
hw.io_ports.command_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_Stat_Cmd);
|
||||
hw.io_ports.ctl_addr =
|
||||
mapbase + tx4939ide_swizzleb(TX4939IDE_AltStat_DevCtl);
|
||||
hw.irq = irq;
|
||||
hw.dev = &pdev->dev;
|
||||
|
||||
pr_info("TX4939 IDE interface (base %#lx, irq %d)\n", mapbase, irq);
|
||||
host = ide_host_alloc(&tx4939ide_port_info, hws, 1);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
/* use extra_base for base address of the all registers */
|
||||
host->ports[0]->extra_base = mapbase;
|
||||
ret = ide_host_register(host, &tx4939ide_port_info, hws);
|
||||
if (ret) {
|
||||
ide_host_free(host);
|
||||
return ret;
|
||||
}
|
||||
platform_set_drvdata(pdev, host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit tx4939ide_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ide_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
ide_host_remove(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tx4939ide_resume(struct platform_device *dev)
|
||||
{
|
||||
struct ide_host *host = platform_get_drvdata(dev);
|
||||
ide_hwif_t *hwif = host->ports[0];
|
||||
|
||||
tx4939ide_init_hwif(hwif);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define tx4939ide_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver tx4939ide_driver = {
|
||||
.driver = {
|
||||
.name = MODNAME,
|
||||
},
|
||||
.remove = __exit_p(tx4939ide_remove),
|
||||
.resume = tx4939ide_resume,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(tx4939ide_driver, tx4939ide_probe);
|
||||
|
||||
MODULE_DESCRIPTION("TX4939 internal IDE driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:tx4939ide");
|
@ -1,184 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (C) 1995-1996 Linus Torvalds & author (see below)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien)
|
||||
*
|
||||
* This file provides support for the advanced features
|
||||
* of the UMC 8672 IDE interface.
|
||||
*
|
||||
* Version 0.01 Initial version, hacked out of ide.c,
|
||||
* and #include'd rather than compiled separately.
|
||||
* This will get cleaned up in a subsequent release.
|
||||
*
|
||||
* Version 0.02 now configs/compiles separate from ide.c -ml
|
||||
* Version 0.03 enhanced auto-tune, fix display bug
|
||||
* Version 0.05 replace sti() with restore_flags() -ml
|
||||
* add detection of possible race condition -ml
|
||||
*/
|
||||
|
||||
/*
|
||||
* VLB Controller Support from
|
||||
* Wolfram Podien
|
||||
* Rohoefe 3
|
||||
* D28832 Achim
|
||||
* Germany
|
||||
*
|
||||
* To enable UMC8672 support there must a lilo line like
|
||||
* append="ide0=umc8672"...
|
||||
* To set the speed according to the abilities of the hardware there must be a
|
||||
* line like
|
||||
* #define UMC_DRIVE0 11
|
||||
* in the beginning of the driver, which sets the speed of drive 0 to 11 (there
|
||||
* are some lines present). 0 - 11 are allowed speed values. These values are
|
||||
* the results from the DOS speed test program supplied from UMC. 11 is the
|
||||
* highest speed (about PIO mode 3)
|
||||
*/
|
||||
#define REALLY_SLOW_IO /* some systems can safely undef this */
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define DRV_NAME "umc8672"
|
||||
|
||||
/*
|
||||
* Default speeds. These can be changed with "auto-tune" and/or hdparm.
|
||||
*/
|
||||
#define UMC_DRIVE0 1 /* DOS measured drive speeds */
|
||||
#define UMC_DRIVE1 1 /* 0 to 11 allowed */
|
||||
#define UMC_DRIVE2 1 /* 11 = Fastest Speed */
|
||||
#define UMC_DRIVE3 1 /* In case of crash reduce speed */
|
||||
|
||||
static u8 current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3};
|
||||
static const u8 pio_to_umc [5] = {0, 3, 7, 10, 11}; /* rough guesses */
|
||||
|
||||
/* 0 1 2 3 4 5 6 7 8 9 10 11 */
|
||||
static const u8 speedtab [3][12] = {
|
||||
{0x0f, 0x0b, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1},
|
||||
{0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1},
|
||||
{0xff, 0xcb, 0xc0, 0x58, 0x36, 0x33, 0x23, 0x22, 0x21, 0x11, 0x10, 0x0}
|
||||
};
|
||||
|
||||
static void out_umc(char port, char wert)
|
||||
{
|
||||
outb_p(port, 0x108);
|
||||
outb_p(wert, 0x109);
|
||||
}
|
||||
|
||||
static inline u8 in_umc(char port)
|
||||
{
|
||||
outb_p(port, 0x108);
|
||||
return inb_p(0x109);
|
||||
}
|
||||
|
||||
static void umc_set_speeds(u8 speeds[])
|
||||
{
|
||||
int i, tmp;
|
||||
|
||||
outb_p(0x5A, 0x108); /* enable umc */
|
||||
|
||||
out_umc(0xd7, (speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4)));
|
||||
out_umc(0xd6, (speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4)));
|
||||
tmp = 0;
|
||||
for (i = 3; i >= 0; i--)
|
||||
tmp = (tmp << 2) | speedtab[1][speeds[i]];
|
||||
out_umc(0xdc, tmp);
|
||||
for (i = 0; i < 4; i++) {
|
||||
out_umc(0xd0 + i, speedtab[2][speeds[i]]);
|
||||
out_umc(0xd8 + i, speedtab[2][speeds[i]]);
|
||||
}
|
||||
outb_p(0xa5, 0x108); /* disable umc */
|
||||
|
||||
printk("umc8672: drive speeds [0 to 11]: %d %d %d %d\n",
|
||||
speeds[0], speeds[1], speeds[2], speeds[3]);
|
||||
}
|
||||
|
||||
static void umc_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
ide_hwif_t *mate = hwif->mate;
|
||||
unsigned long flags;
|
||||
const u8 pio = drive->pio_mode - XFER_PIO_0;
|
||||
|
||||
printk("%s: setting umc8672 to PIO mode%d (speed %d)\n",
|
||||
drive->name, pio, pio_to_umc[pio]);
|
||||
if (mate)
|
||||
spin_lock_irqsave(&mate->lock, flags);
|
||||
if (mate && mate->handler) {
|
||||
printk(KERN_ERR "umc8672: other interface is busy: exiting tune_umc()\n");
|
||||
} else {
|
||||
current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio];
|
||||
umc_set_speeds(current_speeds);
|
||||
}
|
||||
if (mate)
|
||||
spin_unlock_irqrestore(&mate->lock, flags);
|
||||
}
|
||||
|
||||
static const struct ide_port_ops umc8672_port_ops = {
|
||||
.set_pio_mode = umc_set_pio_mode,
|
||||
};
|
||||
|
||||
static const struct ide_port_info umc8672_port_info __initconst = {
|
||||
.name = DRV_NAME,
|
||||
.chipset = ide_umc8672,
|
||||
.port_ops = &umc8672_port_ops,
|
||||
.host_flags = IDE_HFLAG_NO_DMA,
|
||||
.pio_mask = ATA_PIO4,
|
||||
};
|
||||
|
||||
static int __init umc8672_probe(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!request_region(0x108, 2, "umc8672")) {
|
||||
printk(KERN_ERR "umc8672: ports 0x108-0x109 already in use.\n");
|
||||
return 1;
|
||||
}
|
||||
local_irq_save(flags);
|
||||
outb_p(0x5A, 0x108); /* enable umc */
|
||||
if (in_umc (0xd5) != 0xa0) {
|
||||
local_irq_restore(flags);
|
||||
printk(KERN_ERR "umc8672: not found\n");
|
||||
release_region(0x108, 2);
|
||||
return 1;
|
||||
}
|
||||
outb_p(0xa5, 0x108); /* disable umc */
|
||||
|
||||
umc_set_speeds(current_speeds);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return ide_legacy_device_add(&umc8672_port_info, 0);
|
||||
}
|
||||
|
||||
static bool probe_umc8672;
|
||||
|
||||
module_param_named(probe, probe_umc8672, bool, 0);
|
||||
MODULE_PARM_DESC(probe, "probe for UMC8672 chipset");
|
||||
|
||||
static int __init umc8672_init(void)
|
||||
{
|
||||
if (probe_umc8672 == 0)
|
||||
goto out;
|
||||
|
||||
if (umc8672_probe() == 0)
|
||||
return 0;
|
||||
out:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
module_init(umc8672_init);
|
||||
|
||||
MODULE_AUTHOR("Wolfram Podien");
|
||||
MODULE_DESCRIPTION("Support for UMC 8672 IDE chipset");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,532 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* VIA IDE driver for Linux. Supported southbridges:
|
||||
*
|
||||
* vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b,
|
||||
* vt82c686, vt82c686a, vt82c686b, vt8231, vt8233, vt8233c, vt8233a,
|
||||
* vt8235, vt8237, vt8237a
|
||||
*
|
||||
* Copyright (c) 2000-2002 Vojtech Pavlik
|
||||
* Copyright (c) 2007-2010 Bartlomiej Zolnierkiewicz
|
||||
*
|
||||
* Based on the work of:
|
||||
* Michel Aubry
|
||||
* Jeff Garzik
|
||||
* Andre Hedrick
|
||||
*
|
||||
* Documentation:
|
||||
* Obsolete device documentation publicly available from via.com.tw
|
||||
* Current device documentation available under NDA only
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ide.h>
|
||||
#include <linux/dmi.h>
|
||||
|
||||
#ifdef CONFIG_PPC_CHRP
|
||||
#include <asm/processor.h>
|
||||
#endif
|
||||
|
||||
#define DRV_NAME "via82cxxx"
|
||||
|
||||
#define VIA_IDE_ENABLE 0x40
|
||||
#define VIA_IDE_CONFIG 0x41
|
||||
#define VIA_FIFO_CONFIG 0x43
|
||||
#define VIA_MISC_1 0x44
|
||||
#define VIA_MISC_2 0x45
|
||||
#define VIA_MISC_3 0x46
|
||||
#define VIA_DRIVE_TIMING 0x48
|
||||
#define VIA_8BIT_TIMING 0x4e
|
||||
#define VIA_ADDRESS_SETUP 0x4c
|
||||
#define VIA_UDMA_TIMING 0x50
|
||||
|
||||
#define VIA_BAD_PREQ 0x01 /* Crashes if PREQ# till DDACK# set */
|
||||
#define VIA_BAD_CLK66 0x02 /* 66 MHz clock doesn't work correctly */
|
||||
#define VIA_SET_FIFO 0x04 /* Needs to have FIFO split set */
|
||||
#define VIA_NO_UNMASK 0x08 /* Doesn't work with IRQ unmasking on */
|
||||
#define VIA_BAD_ID 0x10 /* Has wrong vendor ID (0x1107) */
|
||||
#define VIA_BAD_AST 0x20 /* Don't touch Address Setup Timing */
|
||||
#define VIA_SATA_PATA 0x80 /* SATA/PATA combined configuration */
|
||||
|
||||
enum {
|
||||
VIA_IDFLAG_SINGLE = (1 << 1), /* single channel controller */
|
||||
};
|
||||
|
||||
/*
|
||||
* VIA SouthBridge chips.
|
||||
*/
|
||||
|
||||
static struct via_isa_bridge {
|
||||
char *name;
|
||||
u16 id;
|
||||
u8 rev_min;
|
||||
u8 rev_max;
|
||||
u8 udma_mask;
|
||||
u8 flags;
|
||||
} via_isa_bridges[] = {
|
||||
{ "vx855", PCI_DEVICE_ID_VIA_VX855, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
|
||||
{ "vx800", PCI_DEVICE_ID_VIA_VX800, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
|
||||
{ "cx700", PCI_DEVICE_ID_VIA_CX700, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST | VIA_SATA_PATA },
|
||||
{ "vt8261", PCI_DEVICE_ID_VIA_8261, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8237s", PCI_DEVICE_ID_VIA_8237S, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt6410", PCI_DEVICE_ID_VIA_6410, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt6415", PCI_DEVICE_ID_VIA_6415, 0x00, 0xff, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8251", PCI_DEVICE_ID_VIA_8251, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8237a", PCI_DEVICE_ID_VIA_8237A, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8233a", PCI_DEVICE_ID_VIA_8233A, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ "vt8233c", PCI_DEVICE_ID_VIA_8233C_0, 0x00, 0x2f, ATA_UDMA5, },
|
||||
{ "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, ATA_UDMA5, },
|
||||
{ "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, ATA_UDMA5, },
|
||||
{ "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, ATA_UDMA5, },
|
||||
{ "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, ATA_UDMA4, },
|
||||
{ "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, ATA_UDMA2, VIA_BAD_CLK66 },
|
||||
{ "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, ATA_UDMA4, },
|
||||
{ "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, ATA_UDMA2, VIA_BAD_CLK66 },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, ATA_UDMA2, VIA_SET_FIFO },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, ATA_UDMA2, VIA_SET_FIFO | VIA_BAD_PREQ },
|
||||
{ "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, ATA_UDMA2, VIA_SET_FIFO },
|
||||
{ "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, ATA_UDMA2, VIA_SET_FIFO },
|
||||
{ "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, 0x00, VIA_SET_FIFO },
|
||||
{ "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, 0x00, VIA_SET_FIFO | VIA_NO_UNMASK },
|
||||
{ "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, 0x00, VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID },
|
||||
{ "vtxxxx", PCI_DEVICE_ID_VIA_ANON, 0x00, 0x2f, ATA_UDMA6, VIA_BAD_AST },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static unsigned int via_clock;
|
||||
static char *via_dma[] = { "16", "25", "33", "44", "66", "100", "133" };
|
||||
|
||||
struct via82cxxx_dev
|
||||
{
|
||||
struct via_isa_bridge *via_config;
|
||||
unsigned int via_80w;
|
||||
};
|
||||
|
||||
/**
|
||||
* via_set_speed - write timing registers
|
||||
* @dev: PCI device
|
||||
* @dn: device
|
||||
* @timing: IDE timing data to use
|
||||
*
|
||||
* via_set_speed writes timing values to the chipset registers
|
||||
*/
|
||||
|
||||
static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing)
|
||||
{
|
||||
struct pci_dev *dev = to_pci_dev(hwif->dev);
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct via82cxxx_dev *vdev = host->host_priv;
|
||||
u8 t;
|
||||
|
||||
if (~vdev->via_config->flags & VIA_BAD_AST) {
|
||||
pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t);
|
||||
t = (t & ~(3 << ((3 - dn) << 1))) | ((clamp_val(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
|
||||
pci_write_config_byte(dev, VIA_ADDRESS_SETUP, t);
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_8BIT_TIMING + (1 - (dn >> 1)),
|
||||
((clamp_val(timing->act8b, 1, 16) - 1) << 4) | (clamp_val(timing->rec8b, 1, 16) - 1));
|
||||
|
||||
pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn),
|
||||
((clamp_val(timing->active, 1, 16) - 1) << 4) | (clamp_val(timing->recover, 1, 16) - 1));
|
||||
|
||||
switch (vdev->via_config->udma_mask) {
|
||||
case ATA_UDMA2: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 5) - 2)) : 0x03; break;
|
||||
case ATA_UDMA4: t = timing->udma ? (0xe8 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x0f; break;
|
||||
case ATA_UDMA5: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
|
||||
case ATA_UDMA6: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
|
||||
}
|
||||
|
||||
/* Set UDMA unless device is not UDMA capable */
|
||||
if (vdev->via_config->udma_mask) {
|
||||
u8 udma_etc;
|
||||
|
||||
pci_read_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, &udma_etc);
|
||||
|
||||
/* clear transfer mode bit */
|
||||
udma_etc &= ~0x20;
|
||||
|
||||
if (timing->udma) {
|
||||
/* preserve 80-wire cable detection bit */
|
||||
udma_etc &= 0x10;
|
||||
udma_etc |= t;
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_UDMA_TIMING + 3 - dn, udma_etc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* via_set_drive - configure transfer mode
|
||||
* @hwif: port
|
||||
* @drive: Drive to set up
|
||||
*
|
||||
* via_set_drive() computes timing values configures the chipset to
|
||||
* a desired transfer mode. It also can be called by upper layers.
|
||||
*/
|
||||
|
||||
static void via_set_drive(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
ide_drive_t *peer = ide_get_pair_dev(drive);
|
||||
struct ide_host *host = dev_get_drvdata(hwif->dev);
|
||||
struct via82cxxx_dev *vdev = host->host_priv;
|
||||
struct ide_timing t, p;
|
||||
unsigned int T, UT;
|
||||
const u8 speed = drive->dma_mode;
|
||||
|
||||
T = 1000000000 / via_clock;
|
||||
|
||||
switch (vdev->via_config->udma_mask) {
|
||||
case ATA_UDMA2: UT = T; break;
|
||||
case ATA_UDMA4: UT = T/2; break;
|
||||
case ATA_UDMA5: UT = T/3; break;
|
||||
case ATA_UDMA6: UT = T/4; break;
|
||||
default: UT = T;
|
||||
}
|
||||
|
||||
ide_timing_compute(drive, speed, &t, T, UT);
|
||||
|
||||
if (peer) {
|
||||
ide_timing_compute(peer, peer->pio_mode, &p, T, UT);
|
||||
ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT);
|
||||
}
|
||||
|
||||
via_set_speed(hwif, drive->dn, &t);
|
||||
}
|
||||
|
||||
/**
|
||||
* via_set_pio_mode - set host controller for PIO mode
|
||||
* @hwif: port
|
||||
* @drive: drive
|
||||
*
|
||||
* A callback from the upper layers for PIO-only tuning.
|
||||
*/
|
||||
|
||||
static void via_set_pio_mode(ide_hwif_t *hwif, ide_drive_t *drive)
|
||||
{
|
||||
drive->dma_mode = drive->pio_mode;
|
||||
via_set_drive(hwif, drive);
|
||||
}
|
||||
|
||||
static struct via_isa_bridge *via_config_find(struct pci_dev **isa)
|
||||
{
|
||||
struct via_isa_bridge *via_config;
|
||||
|
||||
for (via_config = via_isa_bridges;
|
||||
via_config->id != PCI_DEVICE_ID_VIA_ANON; via_config++)
|
||||
if ((*isa = pci_get_device(PCI_VENDOR_ID_VIA +
|
||||
!!(via_config->flags & VIA_BAD_ID),
|
||||
via_config->id, NULL))) {
|
||||
|
||||
if ((*isa)->revision >= via_config->rev_min &&
|
||||
(*isa)->revision <= via_config->rev_max)
|
||||
break;
|
||||
pci_dev_put(*isa);
|
||||
}
|
||||
|
||||
return via_config;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check and handle 80-wire cable presence
|
||||
*/
|
||||
static void via_cable_detect(struct via82cxxx_dev *vdev, u32 u)
|
||||
{
|
||||
int i;
|
||||
|
||||
switch (vdev->via_config->udma_mask) {
|
||||
case ATA_UDMA4:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> (i & 16)) & 8) &&
|
||||
((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 2)) {
|
||||
/*
|
||||
* 2x PCI clock and
|
||||
* UDMA w/ < 3T/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
|
||||
case ATA_UDMA5:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> i) & 0x10) ||
|
||||
(((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 4))) {
|
||||
/* BIOS 80-wire bit or
|
||||
* UDMA w/ < 60ns/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
|
||||
case ATA_UDMA6:
|
||||
for (i = 24; i >= 0; i -= 8)
|
||||
if (((u >> i) & 0x10) ||
|
||||
(((u >> i) & 0x20) &&
|
||||
(((u >> i) & 7) < 6))) {
|
||||
/* BIOS 80-wire bit or
|
||||
* UDMA w/ < 60ns/cycle
|
||||
*/
|
||||
vdev->via_80w |= (1 << (1 - (i >> 4)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* init_chipset_via82cxxx - initialization handler
|
||||
* @dev: PCI device
|
||||
*
|
||||
* The initialization callback. Here we determine the IDE chip type
|
||||
* and initialize its drive independent registers.
|
||||
*/
|
||||
|
||||
static int init_chipset_via82cxxx(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct via82cxxx_dev *vdev = host->host_priv;
|
||||
struct via_isa_bridge *via_config = vdev->via_config;
|
||||
u8 t, v;
|
||||
u32 u;
|
||||
|
||||
/*
|
||||
* Detect cable and configure Clk66
|
||||
*/
|
||||
pci_read_config_dword(dev, VIA_UDMA_TIMING, &u);
|
||||
|
||||
via_cable_detect(vdev, u);
|
||||
|
||||
if (via_config->udma_mask == ATA_UDMA4) {
|
||||
/* Enable Clk66 */
|
||||
pci_write_config_dword(dev, VIA_UDMA_TIMING, u|0x80008);
|
||||
} else if (via_config->flags & VIA_BAD_CLK66) {
|
||||
/* Would cause trouble on 596a and 686 */
|
||||
pci_write_config_dword(dev, VIA_UDMA_TIMING, u & ~0x80008);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether interfaces are enabled.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, VIA_IDE_ENABLE, &v);
|
||||
|
||||
/*
|
||||
* Set up FIFO sizes and thresholds.
|
||||
*/
|
||||
|
||||
pci_read_config_byte(dev, VIA_FIFO_CONFIG, &t);
|
||||
|
||||
/* Disable PREQ# till DDACK# */
|
||||
if (via_config->flags & VIA_BAD_PREQ) {
|
||||
/* Would crash on 586b rev 41 */
|
||||
t &= 0x7f;
|
||||
}
|
||||
|
||||
/* Fix FIFO split between channels */
|
||||
if (via_config->flags & VIA_SET_FIFO) {
|
||||
t &= (t & 0x9f);
|
||||
switch (v & 3) {
|
||||
case 2: t |= 0x00; break; /* 16 on primary */
|
||||
case 1: t |= 0x60; break; /* 16 on secondary */
|
||||
case 3: t |= 0x20; break; /* 8 pri 8 sec */
|
||||
}
|
||||
}
|
||||
|
||||
pci_write_config_byte(dev, VIA_FIFO_CONFIG, t);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cable special cases
|
||||
*/
|
||||
|
||||
static const struct dmi_system_id cable_dmi_table[] = {
|
||||
{
|
||||
.ident = "Acer Ferrari 3400",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "Acer,Inc."),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "Ferrari 3400"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int via_cable_override(struct pci_dev *pdev)
|
||||
{
|
||||
/* Systems by DMI */
|
||||
if (dmi_check_system(cable_dmi_table))
|
||||
return 1;
|
||||
|
||||
/* Arima W730-K8/Targa Visionary 811/... */
|
||||
if (pdev->subsystem_vendor == 0x161F &&
|
||||
pdev->subsystem_device == 0x2032)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 via82cxxx_cable_detect(ide_hwif_t *hwif)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(hwif->dev);
|
||||
struct ide_host *host = pci_get_drvdata(pdev);
|
||||
struct via82cxxx_dev *vdev = host->host_priv;
|
||||
|
||||
if (via_cable_override(pdev))
|
||||
return ATA_CBL_PATA40_SHORT;
|
||||
|
||||
if ((vdev->via_config->flags & VIA_SATA_PATA) && hwif->channel == 0)
|
||||
return ATA_CBL_SATA;
|
||||
|
||||
if ((vdev->via_80w >> hwif->channel) & 1)
|
||||
return ATA_CBL_PATA80;
|
||||
else
|
||||
return ATA_CBL_PATA40;
|
||||
}
|
||||
|
||||
static const struct ide_port_ops via_port_ops = {
|
||||
.set_pio_mode = via_set_pio_mode,
|
||||
.set_dma_mode = via_set_drive,
|
||||
.cable_detect = via82cxxx_cable_detect,
|
||||
};
|
||||
|
||||
static const struct ide_port_info via82cxxx_chipset = {
|
||||
.name = DRV_NAME,
|
||||
.init_chipset = init_chipset_via82cxxx,
|
||||
.enablebits = { { 0x40, 0x02, 0x02 }, { 0x40, 0x01, 0x01 } },
|
||||
.port_ops = &via_port_ops,
|
||||
.host_flags = IDE_HFLAG_PIO_NO_BLACKLIST |
|
||||
IDE_HFLAG_POST_SET_MODE |
|
||||
IDE_HFLAG_IO_32BIT,
|
||||
.pio_mask = ATA_PIO5,
|
||||
.swdma_mask = ATA_SWDMA2,
|
||||
.mwdma_mask = ATA_MWDMA2,
|
||||
};
|
||||
|
||||
static int via_init_one(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct pci_dev *isa = NULL;
|
||||
struct via_isa_bridge *via_config;
|
||||
struct via82cxxx_dev *vdev;
|
||||
int rc;
|
||||
u8 idx = id->driver_data;
|
||||
struct ide_port_info d;
|
||||
|
||||
d = via82cxxx_chipset;
|
||||
|
||||
/*
|
||||
* Find the ISA bridge and check we know what it is.
|
||||
*/
|
||||
via_config = via_config_find(&isa);
|
||||
|
||||
/*
|
||||
* Print the boot message.
|
||||
*/
|
||||
printk(KERN_INFO DRV_NAME " %s: VIA %s (rev %02x) IDE %sDMA%s\n",
|
||||
pci_name(dev), via_config->name, isa->revision,
|
||||
via_config->udma_mask ? "U" : "MW",
|
||||
via_dma[via_config->udma_mask ?
|
||||
(fls(via_config->udma_mask) - 1) : 0]);
|
||||
|
||||
pci_dev_put(isa);
|
||||
|
||||
/*
|
||||
* Determine system bus clock.
|
||||
*/
|
||||
via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
|
||||
|
||||
switch (via_clock) {
|
||||
case 33000: via_clock = 33333; break;
|
||||
case 37000: via_clock = 37500; break;
|
||||
case 41000: via_clock = 41666; break;
|
||||
}
|
||||
|
||||
if (via_clock < 20000 || via_clock > 50000) {
|
||||
printk(KERN_WARNING DRV_NAME ": User given PCI clock speed "
|
||||
"impossible (%d), using 33 MHz instead.\n", via_clock);
|
||||
via_clock = 33333;
|
||||
}
|
||||
|
||||
if (idx == 1)
|
||||
d.enablebits[1].reg = d.enablebits[0].reg = 0;
|
||||
else
|
||||
d.host_flags |= IDE_HFLAG_NO_AUTODMA;
|
||||
|
||||
if (idx == VIA_IDFLAG_SINGLE)
|
||||
d.host_flags |= IDE_HFLAG_SINGLE;
|
||||
|
||||
if ((via_config->flags & VIA_NO_UNMASK) == 0)
|
||||
d.host_flags |= IDE_HFLAG_UNMASK_IRQS;
|
||||
|
||||
d.udma_mask = via_config->udma_mask;
|
||||
|
||||
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
|
||||
if (!vdev) {
|
||||
printk(KERN_ERR DRV_NAME " %s: out of memory :(\n",
|
||||
pci_name(dev));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vdev->via_config = via_config;
|
||||
|
||||
rc = ide_pci_init_one(dev, &d, vdev);
|
||||
if (rc)
|
||||
kfree(vdev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void via_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct ide_host *host = pci_get_drvdata(dev);
|
||||
struct via82cxxx_dev *vdev = host->host_priv;
|
||||
|
||||
ide_pci_remove(dev);
|
||||
kfree(vdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id via_pci_tbl[] = {
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), 0 },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C586_1), 0 },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_CX700_IDE), 0 },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_VX855_IDE), VIA_IDFLAG_SINGLE },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6410), 1 },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_6415), 1 },
|
||||
{ PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_SATA_EIDE), 1 },
|
||||
{ 0, },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, via_pci_tbl);
|
||||
|
||||
static struct pci_driver via_pci_driver = {
|
||||
.name = "VIA_IDE",
|
||||
.id_table = via_pci_tbl,
|
||||
.probe = via_init_one,
|
||||
.remove = via_remove,
|
||||
.suspend = ide_pci_suspend,
|
||||
.resume = ide_pci_resume,
|
||||
};
|
||||
|
||||
static int __init via_ide_init(void)
|
||||
{
|
||||
return ide_pci_register_driver(&via_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit via_ide_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&via_pci_driver);
|
||||
}
|
||||
|
||||
module_init(via_ide_init);
|
||||
module_exit(via_ide_exit);
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik, Bartlomiej Zolnierkiewicz, Michel Aubry, Jeff Garzik, Andre Hedrick");
|
||||
MODULE_DESCRIPTION("PCI driver module for VIA IDE");
|
||||
MODULE_LICENSE("GPL");
|
1623
include/linux/ide.h
1623
include/linux/ide.h
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user