ATA changes for 6.3-rc1
* Small cleanup of the pata_octeon driver to drop a useless platform callback, from Uwe. * Simplify ata_scsi_cmd_error_handler() code using the fact that ap->ops->error_handler is NULL most of the time, from Wenchao. * Several patches improving libata error handling. This is in preparation for supporting the command duration limits (CDL) feature. The changes allow handling corner cases of ATA NCQ errors which do not happen with regular drives but will be triggered with CDL drives. From Niklas. * Simplify the qc_fill_rtf operation, from me. * Improve SCSI command translation for the REPORT_SUPPORTED_OPERATION_CODES command, from me. * Cleanup of libata FUA handling. This falls short of enabling FUA for ATA drives that support it by default as there were concerns that old drives would break. The series howeverfixes several issues with the FUA support to ensure that FUA is reported as being supported only for drives that can handle all possible write cases (NCQ and non-NCQ). A check in the block layer is also added to ensure that we never see read FUA commands (current behavior). From me. * Several patches to move the old PARIDE (parallel port IDE) driver to libata as pata_parport. Given that this driver also needs protocol modules, the driver code resides in its own pata_parport directoy under drivers/ata. From Ondrej. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQSRPv8tYSvhwAzJdzjdoc3SxdoYdgUCY/VTnQAKCRDdoc3SxdoY dk77AQCA1frczKhcOFe2PK/FsFAiO9Nlx/snk7V95JdjVG8GlwEAkey7mvbXMfX0 fDbqpaCkWFb6SvwxdMSATlqUvwEpSQ8= =tqQP -----END PGP SIGNATURE----- Merge tag 'ata-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata Pull ATA updates from Damien Le Moal: - Small cleanup of the pata_octeon driver to drop a useless platform callback (Uwe) - Simplify ata_scsi_cmd_error_handler() code using the fact that ap->ops->error_handler is NULL most of the time (Wenchao) - Several patches improving libata error handling. This is in preparation for supporting the command duration limits (CDL) feature. The changes allow handling corner cases of ATA NCQ errors which do not happen with regular drives but will be triggered with CDL drives (Niklas) - Simplify the qc_fill_rtf operation (me) - Improve SCSI command translation for REPORT_SUPPORTED_OPERATION_CODES command (me) - Cleanup of libata FUA handling. This falls short of enabling FUA for ATA drives that support it by default as there were concerns that old drives would break. The series however fixes several issues with the FUA support to ensure that FUA is reported as being supported only for drives that can handle all possible write cases (NCQ and non-NCQ). A check in the block layer is also added to ensure that we never see read FUA commands (current behavior) (me) - Several patches to move the old PARIDE (parallel port IDE) driver to libata as pata_parport. Given that this driver also needs protocol modules, the driver code resides in its own pata_parport directoy under drivers/ata (Ondrej) * tag 'ata-6.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal/libata: ata: pata_parport: Fix ida_alloc return value error check drivers/block: Move PARIDE protocol modules to drivers/ata/pata_parport drivers/block: Remove PARIDE core and high-level protocols ata: pata_parport: add driver (PARIDE replacement) ata: libata: exclude FUA support for known buggy drives ata: libata: Fix FUA handling in ata_build_rw_tf() ata: libata: cleanup fua support detection ata: libata: Rename and cleanup ata_rwcmd_protocol() ata: libata: Introduce ata_ncq_supported() block: add a sanity check for non-write flush/fua bios ata: libata-scsi: improve ata_scsiop_maint_in() ata: libata-scsi: do not overwrite SCSI ML and status bytes ata: libata: move NCQ related ATA_DFLAGs ata: libata: respect successfully completed commands during errors ata: libata: read the shared status for successful NCQ commands once ata: libata: simplify qc_fill_rtf port operation interface ata: scsi: rename flag ATA_QCFLAG_FAILED to ATA_QCFLAG_EH ata: libata-eh: Cleanup ata_scsi_cmd_error_handler() ata: octeon: Drop empty platform remove function
This commit is contained in:
commit
6861eaf791
@ -3,6 +3,7 @@ Linux and parallel port IDE devices
|
|||||||
===================================
|
===================================
|
||||||
|
|
||||||
PARIDE v1.03 (c) 1997-8 Grant Guenther <grant@torque.net>
|
PARIDE v1.03 (c) 1997-8 Grant Guenther <grant@torque.net>
|
||||||
|
PATA_PARPORT (c) 2023 Ondrej Zary
|
||||||
|
|
||||||
1. Introduction
|
1. Introduction
|
||||||
===============
|
===============
|
||||||
@ -51,27 +52,15 @@ parallel port IDE subsystem, including:
|
|||||||
|
|
||||||
as well as most of the clone and no-name products on the market.
|
as well as most of the clone and no-name products on the market.
|
||||||
|
|
||||||
To support such a wide range of devices, PARIDE, the parallel port IDE
|
To support such a wide range of devices, pata_parport is actually structured
|
||||||
subsystem, is actually structured in three parts. There is a base
|
in two parts. There is a base pata_parport module which provides an interface
|
||||||
paride module which provides a registry and some common methods for
|
to kernel libata subsystem, registry and some common methods for accessing
|
||||||
accessing the parallel ports. The second component is a set of
|
the parallel ports.
|
||||||
high-level drivers for each of the different types of supported devices:
|
|
||||||
|
|
||||||
=== =============
|
The second component is a set of low-level protocol drivers for each of the
|
||||||
pd IDE disk
|
parallel port IDE adapter chips. Thanks to the interest and encouragement of
|
||||||
pcd ATAPI CD-ROM
|
Linux users from many parts of the world, support is available for almost all
|
||||||
pf ATAPI disk
|
known adapter protocols:
|
||||||
pt ATAPI tape
|
|
||||||
pg ATAPI generic
|
|
||||||
=== =============
|
|
||||||
|
|
||||||
(Currently, the pg driver is only used with CD-R drives).
|
|
||||||
|
|
||||||
The high-level drivers function according to the relevant standards.
|
|
||||||
The third component of PARIDE is a set of low-level protocol drivers
|
|
||||||
for each of the parallel port IDE adapter chips. Thanks to the interest
|
|
||||||
and encouragement of Linux users from many parts of the world,
|
|
||||||
support is available for almost all known adapter protocols:
|
|
||||||
|
|
||||||
==== ====================================== ====
|
==== ====================================== ====
|
||||||
aten ATEN EH-100 (HK)
|
aten ATEN EH-100 (HK)
|
||||||
@ -91,251 +80,87 @@ support is available for almost all known adapter protocols:
|
|||||||
==== ====================================== ====
|
==== ====================================== ====
|
||||||
|
|
||||||
|
|
||||||
2. Using the PARIDE subsystem
|
2. Using pata_parport subsystem
|
||||||
=============================
|
===============================
|
||||||
|
|
||||||
While configuring the Linux kernel, you may choose either to build
|
While configuring the Linux kernel, you may choose either to build
|
||||||
the PARIDE drivers into your kernel, or to build them as modules.
|
the pata_parport drivers into your kernel, or to build them as modules.
|
||||||
|
|
||||||
In either case, you will need to select "Parallel port IDE device support"
|
In either case, you will need to select "Parallel port IDE device support"
|
||||||
as well as at least one of the high-level drivers and at least one
|
and at least one of the parallel port communication protocols.
|
||||||
of the parallel port communication protocols. If you do not know
|
If you do not know what kind of parallel port adapter is used in your drive,
|
||||||
what kind of parallel port adapter is used in your drive, you could
|
you could begin by checking the file names and any text files on your DOS
|
||||||
begin by checking the file names and any text files on your DOS
|
|
||||||
installation floppy. Alternatively, you can look at the markings on
|
installation floppy. Alternatively, you can look at the markings on
|
||||||
the adapter chip itself. That's usually sufficient to identify the
|
the adapter chip itself. That's usually sufficient to identify the
|
||||||
correct device.
|
correct device.
|
||||||
|
|
||||||
You can actually select all the protocol modules, and allow the PARIDE
|
You can actually select all the protocol modules, and allow the pata_parport
|
||||||
subsystem to try them all for you.
|
subsystem to try them all for you.
|
||||||
|
|
||||||
For the "brand-name" products listed above, here are the protocol
|
For the "brand-name" products listed above, here are the protocol
|
||||||
and high-level drivers that you would use:
|
and high-level drivers that you would use:
|
||||||
|
|
||||||
================ ============ ====== ========
|
================ ============ ========
|
||||||
Manufacturer Model Driver Protocol
|
Manufacturer Model Protocol
|
||||||
================ ============ ====== ========
|
================ ============ ========
|
||||||
MicroSolutions CD-ROM pcd bpck
|
MicroSolutions CD-ROM bpck
|
||||||
MicroSolutions PD drive pf bpck
|
MicroSolutions PD drive bpck
|
||||||
MicroSolutions hard-drive pd bpck
|
MicroSolutions hard-drive bpck
|
||||||
MicroSolutions 8000t tape pt bpck
|
MicroSolutions 8000t tape bpck
|
||||||
SyQuest EZ, SparQ pd epat
|
SyQuest EZ, SparQ epat
|
||||||
Imation Superdisk pf epat
|
Imation Superdisk epat
|
||||||
Maxell Superdisk pf friq
|
Maxell Superdisk friq
|
||||||
Avatar Shark pd epat
|
Avatar Shark epat
|
||||||
FreeCom CD-ROM pcd frpw
|
FreeCom CD-ROM frpw
|
||||||
Hewlett-Packard 5GB Tape pt epat
|
Hewlett-Packard 5GB Tape epat
|
||||||
Hewlett-Packard 7200e (CD) pcd epat
|
Hewlett-Packard 7200e (CD) epat
|
||||||
Hewlett-Packard 7200e (CD-R) pg epat
|
Hewlett-Packard 7200e (CD-R) epat
|
||||||
================ ============ ====== ========
|
================ ============ ========
|
||||||
|
|
||||||
2.1 Configuring built-in drivers
|
All parports and all protocol drivers are probed automatically unless probe=0
|
||||||
---------------------------------
|
parameter is used. So just "modprobe epat" is enough for a Imation SuperDisk
|
||||||
|
drive to work.
|
||||||
|
|
||||||
We recommend that you get to know how the drivers work and how to
|
Manual device creation::
|
||||||
configure them as loadable modules, before attempting to compile a
|
|
||||||
kernel with the drivers built-in.
|
|
||||||
|
|
||||||
If you built all of your PARIDE support directly into your kernel,
|
# echo "port protocol mode unit delay" >/sys/bus/pata_parport/new_device
|
||||||
and you have just a single parallel port IDE device, your kernel should
|
|
||||||
locate it automatically for you. If you have more than one device,
|
|
||||||
you may need to give some command line options to your bootloader
|
|
||||||
(eg: LILO), how to do that is beyond the scope of this document.
|
|
||||||
|
|
||||||
The high-level drivers accept a number of command line parameters, all
|
where:
|
||||||
of which are documented in the source files in linux/drivers/block/paride.
|
|
||||||
By default, each driver will automatically try all parallel ports it
|
|
||||||
can find, and all protocol types that have been installed, until it finds
|
|
||||||
a parallel port IDE adapter. Once it finds one, the probe stops. So,
|
|
||||||
if you have more than one device, you will need to tell the drivers
|
|
||||||
how to identify them. This requires specifying the port address, the
|
|
||||||
protocol identification number and, for some devices, the drive's
|
|
||||||
chain ID. While your system is booting, a number of messages are
|
|
||||||
displayed on the console. Like all such messages, they can be
|
|
||||||
reviewed with the 'dmesg' command. Among those messages will be
|
|
||||||
some lines like::
|
|
||||||
|
|
||||||
paride: bpck registered as protocol 0
|
======== ================================================
|
||||||
paride: epat registered as protocol 1
|
port parport name (or "auto" for all parports)
|
||||||
|
protocol protocol name (or "auto" for all protocols)
|
||||||
The numbers will always be the same until you build a new kernel with
|
mode mode number (protocol-specific) or -1 for probe
|
||||||
different protocol selections. You should note these numbers as you
|
unit unit number (for backpack only, see below)
|
||||||
will need them to identify the devices.
|
delay I/O delay (see troubleshooting section below)
|
||||||
|
======== ================================================
|
||||||
|
|
||||||
If you happen to be using a MicroSolutions backpack device, you will
|
If you happen to be using a MicroSolutions backpack device, you will
|
||||||
also need to know the unit ID number for each drive. This is usually
|
also need to know the unit ID number for each drive. This is usually
|
||||||
the last two digits of the drive's serial number (but read MicroSolutions'
|
the last two digits of the drive's serial number (but read MicroSolutions'
|
||||||
documentation about this).
|
documentation about this).
|
||||||
|
|
||||||
As an example, let's assume that you have a MicroSolutions PD/CD drive
|
If you omit the parameters from the end, defaults will be used, e.g.:
|
||||||
with unit ID number 36 connected to the parallel port at 0x378, a SyQuest
|
|
||||||
EZ-135 connected to the chained port on the PD/CD drive and also an
|
|
||||||
Imation Superdisk connected to port 0x278. You could give the following
|
|
||||||
options on your boot command::
|
|
||||||
|
|
||||||
pd.drive0=0x378,1 pf.drive0=0x278,1 pf.drive1=0x378,0,36
|
Probe all parports with all protocols::
|
||||||
|
|
||||||
In the last option, pf.drive1 configures device /dev/pf1, the 0x378
|
# echo auto >/sys/bus/pata_parport/new_device
|
||||||
is the parallel port base address, the 0 is the protocol registration
|
|
||||||
number and 36 is the chain ID.
|
|
||||||
|
|
||||||
Please note: while PARIDE will work both with and without the
|
Probe parport0 using protocol epat and mode 4 (EPP-16)::
|
||||||
PARPORT parallel port sharing system that is included by the
|
|
||||||
"Parallel port support" option, PARPORT must be included and enabled
|
|
||||||
if you want to use chains of devices on the same parallel port.
|
|
||||||
|
|
||||||
2.2 Loading and configuring PARIDE as modules
|
# echo "parport0 epat 4" >/sys/bus/pata_parport/new_device
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
It is much faster and simpler to get to understand the PARIDE drivers
|
Probe parport0 using all protocols::
|
||||||
if you use them as loadable kernel modules.
|
|
||||||
|
|
||||||
Note 1:
|
# echo "parport0 auto" >/sys/bus/pata_parport/new_device
|
||||||
using these drivers with the "kerneld" automatic module loading
|
|
||||||
system is not recommended for beginners, and is not documented here.
|
|
||||||
|
|
||||||
Note 2:
|
Probe all parports using protoocol epat::
|
||||||
if you build PARPORT support as a loadable module, PARIDE must
|
|
||||||
also be built as loadable modules, and PARPORT must be loaded before
|
|
||||||
the PARIDE modules.
|
|
||||||
|
|
||||||
To use PARIDE, you must begin by::
|
# echo "auto epat" >/sys/bus/pata_parport/new_device
|
||||||
|
|
||||||
insmod paride
|
Deleting devices::
|
||||||
|
|
||||||
this loads a base module which provides a registry for the protocols,
|
# echo pata_parport.0 >/sys/bus/pata_parport/delete_device
|
||||||
among other tasks.
|
|
||||||
|
|
||||||
Then, load as many of the protocol modules as you think you might need.
|
|
||||||
As you load each module, it will register the protocols that it supports,
|
|
||||||
and print a log message to your kernel log file and your console. For
|
|
||||||
example::
|
|
||||||
|
|
||||||
# insmod epat
|
|
||||||
paride: epat registered as protocol 0
|
|
||||||
# insmod kbic
|
|
||||||
paride: k951 registered as protocol 1
|
|
||||||
paride: k971 registered as protocol 2
|
|
||||||
|
|
||||||
Finally, you can load high-level drivers for each kind of device that
|
|
||||||
you have connected. By default, each driver will autoprobe for a single
|
|
||||||
device, but you can support up to four similar devices by giving their
|
|
||||||
individual coordinates when you load the driver.
|
|
||||||
|
|
||||||
For example, if you had two no-name CD-ROM drives both using the
|
|
||||||
KingByte KBIC-951A adapter, one on port 0x378 and the other on 0x3bc
|
|
||||||
you could give the following command::
|
|
||||||
|
|
||||||
# insmod pcd drive0=0x378,1 drive1=0x3bc,1
|
|
||||||
|
|
||||||
For most adapters, giving a port address and protocol number is sufficient,
|
|
||||||
but check the source files in linux/drivers/block/paride for more
|
|
||||||
information. (Hopefully someone will write some man pages one day !).
|
|
||||||
|
|
||||||
As another example, here's what happens when PARPORT is installed, and
|
|
||||||
a SyQuest EZ-135 is attached to port 0x378::
|
|
||||||
|
|
||||||
# insmod paride
|
|
||||||
paride: version 1.0 installed
|
|
||||||
# insmod epat
|
|
||||||
paride: epat registered as protocol 0
|
|
||||||
# insmod pd
|
|
||||||
pd: pd version 1.0, major 45, cluster 64, nice 0
|
|
||||||
pda: Sharing parport1 at 0x378
|
|
||||||
pda: epat 1.0, Shuttle EPAT chip c3 at 0x378, mode 5 (EPP-32), delay 1
|
|
||||||
pda: SyQuest EZ135A, 262144 blocks [128M], (512/16/32), removable media
|
|
||||||
pda: pda1
|
|
||||||
|
|
||||||
Note that the last line is the output from the generic partition table
|
|
||||||
scanner - in this case it reports that it has found a disk with one partition.
|
|
||||||
|
|
||||||
2.3 Using a PARIDE device
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Once the drivers have been loaded, you can access PARIDE devices in the
|
|
||||||
same way as their traditional counterparts. You will probably need to
|
|
||||||
create the device "special files". Here is a simple script that you can
|
|
||||||
cut to a file and execute::
|
|
||||||
|
|
||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# mkd -- a script to create the device special files for the PARIDE subsystem
|
|
||||||
#
|
|
||||||
function mkdev {
|
|
||||||
mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
|
|
||||||
}
|
|
||||||
#
|
|
||||||
function pd {
|
|
||||||
D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
|
|
||||||
mkdev pd$D b 45 $[ $1 * 16 ]
|
|
||||||
for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
||||||
do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
|
|
||||||
done
|
|
||||||
}
|
|
||||||
#
|
|
||||||
cd /dev
|
|
||||||
#
|
|
||||||
for u in 0 1 2 3 ; do pd $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done
|
|
||||||
#
|
|
||||||
# end of mkd
|
|
||||||
|
|
||||||
With the device files and drivers in place, you can access PARIDE devices
|
|
||||||
like any other Linux device. For example, to mount a CD-ROM in pcd0, use::
|
|
||||||
|
|
||||||
mount /dev/pcd0 /cdrom
|
|
||||||
|
|
||||||
If you have a fresh Avatar Shark cartridge, and the drive is pda, you
|
|
||||||
might do something like::
|
|
||||||
|
|
||||||
fdisk /dev/pda -- make a new partition table with
|
|
||||||
partition 1 of type 83
|
|
||||||
|
|
||||||
mke2fs /dev/pda1 -- to build the file system
|
|
||||||
|
|
||||||
mkdir /shark -- make a place to mount the disk
|
|
||||||
|
|
||||||
mount /dev/pda1 /shark
|
|
||||||
|
|
||||||
Devices like the Imation superdisk work in the same way, except that
|
|
||||||
they do not have a partition table. For example to make a 120MB
|
|
||||||
floppy that you could share with a DOS system::
|
|
||||||
|
|
||||||
mkdosfs /dev/pf0
|
|
||||||
mount /dev/pf0 /mnt
|
|
||||||
|
|
||||||
|
|
||||||
2.4 The pf driver
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The pf driver is intended for use with parallel port ATAPI disk
|
|
||||||
devices. The most common devices in this category are PD drives
|
|
||||||
and LS-120 drives. Traditionally, media for these devices are not
|
|
||||||
partitioned. Consequently, the pf driver does not support partitioned
|
|
||||||
media. This may be changed in a future version of the driver.
|
|
||||||
|
|
||||||
2.5 Using the pt driver
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
The pt driver for parallel port ATAPI tape drives is a minimal driver.
|
|
||||||
It does not yet support many of the standard tape ioctl operations.
|
|
||||||
For best performance, a block size of 32KB should be used. You will
|
|
||||||
probably want to set the parallel port delay to 0, if you can.
|
|
||||||
|
|
||||||
2.6 Using the pg driver
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
The pg driver can be used in conjunction with the cdrecord program
|
|
||||||
to create CD-ROMs. Please get cdrecord version 1.6.1 or later
|
|
||||||
from ftp://ftp.fokus.gmd.de/pub/unix/cdrecord/ . To record CD-R media
|
|
||||||
your parallel port should ideally be set to EPP mode, and the "port delay"
|
|
||||||
should be set to 0. With those settings it is possible to record at 2x
|
|
||||||
speed without any buffer underruns. If you cannot get the driver to work
|
|
||||||
in EPP mode, try to use "bidirectional" or "PS/2" mode and 1x speeds only.
|
|
||||||
|
|
||||||
|
|
||||||
3. Troubleshooting
|
3. Troubleshooting
|
||||||
@ -344,9 +169,9 @@ in EPP mode, try to use "bidirectional" or "PS/2" mode and 1x speeds only.
|
|||||||
3.1 Use EPP mode if you can
|
3.1 Use EPP mode if you can
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
The most common problems that people report with the PARIDE drivers
|
The most common problems that people report with the pata_parport drivers
|
||||||
concern the parallel port CMOS settings. At this time, none of the
|
concern the parallel port CMOS settings. At this time, none of the
|
||||||
PARIDE protocol modules support ECP mode, or any ECP combination modes.
|
protocol modules support ECP mode, or any ECP combination modes.
|
||||||
If you are able to do so, please set your parallel port into EPP mode
|
If you are able to do so, please set your parallel port into EPP mode
|
||||||
using your CMOS setup procedure.
|
using your CMOS setup procedure.
|
||||||
|
|
||||||
@ -354,17 +179,14 @@ using your CMOS setup procedure.
|
|||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
Some parallel ports cannot reliably transfer data at full speed. To
|
Some parallel ports cannot reliably transfer data at full speed. To
|
||||||
offset the errors, the PARIDE protocol modules introduce a "port
|
offset the errors, the protocol modules introduce a "port
|
||||||
delay" between each access to the i/o ports. Each protocol sets
|
delay" between each access to the i/o ports. Each protocol sets
|
||||||
a default value for this delay. In most cases, the user can override
|
a default value for this delay. In most cases, the user can override
|
||||||
the default and set it to 0 - resulting in somewhat higher transfer
|
the default and set it to 0 - resulting in somewhat higher transfer
|
||||||
rates. In some rare cases (especially with older 486 systems) the
|
rates. In some rare cases (especially with older 486 systems) the
|
||||||
default delays are not long enough. if you experience corrupt data
|
default delays are not long enough. if you experience corrupt data
|
||||||
transfers, or unexpected failures, you may wish to increase the
|
transfers, or unexpected failures, you may wish to increase the
|
||||||
port delay. The delay can be programmed using the "driveN" parameters
|
port delay.
|
||||||
to each of the high-level drivers. Please see the notes above, or
|
|
||||||
read the comments at the beginning of the driver source files in
|
|
||||||
linux/drivers/block/paride.
|
|
||||||
|
|
||||||
3.3 Some drives need a printer reset
|
3.3 Some drives need a printer reset
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
@ -374,66 +196,12 @@ that do not always power up correctly. We have noticed this with some
|
|||||||
drives based on OnSpec and older Freecom adapters. In these rare cases,
|
drives based on OnSpec and older Freecom adapters. In these rare cases,
|
||||||
the adapter can often be reinitialised by issuing a "printer reset" on
|
the adapter can often be reinitialised by issuing a "printer reset" on
|
||||||
the parallel port. As the reset operation is potentially disruptive in
|
the parallel port. As the reset operation is potentially disruptive in
|
||||||
multiple device environments, the PARIDE drivers will not do it
|
multiple device environments, the pata_parport drivers will not do it
|
||||||
automatically. You can however, force a printer reset by doing::
|
automatically. You can however, force a printer reset by doing::
|
||||||
|
|
||||||
insmod lp reset=1
|
insmod lp reset=1
|
||||||
rmmod lp
|
rmmod lp
|
||||||
|
|
||||||
If you have one of these marginal cases, you should probably build
|
If you have one of these marginal cases, you should probably build
|
||||||
your paride drivers as modules, and arrange to do the printer reset
|
your pata_parport drivers as modules, and arrange to do the printer reset
|
||||||
before loading the PARIDE drivers.
|
before loading the pata_parport drivers.
|
||||||
|
|
||||||
3.4 Use the verbose option and dmesg if you need help
|
|
||||||
------------------------------------------------------
|
|
||||||
|
|
||||||
While a lot of testing has gone into these drivers to make them work
|
|
||||||
as smoothly as possible, problems will arise. If you do have problems,
|
|
||||||
please check all the obvious things first: does the drive work in
|
|
||||||
DOS with the manufacturer's drivers ? If that doesn't yield any useful
|
|
||||||
clues, then please make sure that only one drive is hooked to your system,
|
|
||||||
and that either (a) PARPORT is enabled or (b) no other device driver
|
|
||||||
is using your parallel port (check in /proc/ioports). Then, load the
|
|
||||||
appropriate drivers (you can load several protocol modules if you want)
|
|
||||||
as in::
|
|
||||||
|
|
||||||
# insmod paride
|
|
||||||
# insmod epat
|
|
||||||
# insmod bpck
|
|
||||||
# insmod kbic
|
|
||||||
...
|
|
||||||
# insmod pd verbose=1
|
|
||||||
|
|
||||||
(using the correct driver for the type of device you have, of course).
|
|
||||||
The verbose=1 parameter will cause the drivers to log a trace of their
|
|
||||||
activity as they attempt to locate your drive.
|
|
||||||
|
|
||||||
Use 'dmesg' to capture a log of all the PARIDE messages (any messages
|
|
||||||
beginning with paride:, a protocol module's name or a driver's name) and
|
|
||||||
include that with your bug report. You can submit a bug report in one
|
|
||||||
of two ways. Either send it directly to the author of the PARIDE suite,
|
|
||||||
by e-mail to grant@torque.net, or join the linux-parport mailing list
|
|
||||||
and post your report there.
|
|
||||||
|
|
||||||
3.5 For more information or help
|
|
||||||
---------------------------------
|
|
||||||
|
|
||||||
You can join the linux-parport mailing list by sending a mail message
|
|
||||||
to:
|
|
||||||
|
|
||||||
linux-parport-request@torque.net
|
|
||||||
|
|
||||||
with the single word::
|
|
||||||
|
|
||||||
subscribe
|
|
||||||
|
|
||||||
in the body of the mail message (not in the subject line). Please be
|
|
||||||
sure that your mail program is correctly set up when you do this, as
|
|
||||||
the list manager is a robot that will subscribe you using the reply
|
|
||||||
address in your mail headers. REMOVE any anti-spam gimmicks you may
|
|
||||||
have in your mail headers, when sending mail to the list server.
|
|
||||||
|
|
||||||
You might also find some useful information on the linux-parport
|
|
||||||
web pages (although they are not always up to date) at
|
|
||||||
|
|
||||||
http://web.archive.org/web/%2E/http://www.torque.net/parport/
|
|
||||||
|
@ -142,7 +142,6 @@ parameter is applicable::
|
|||||||
NFS Appropriate NFS support is enabled.
|
NFS Appropriate NFS support is enabled.
|
||||||
OF Devicetree is enabled.
|
OF Devicetree is enabled.
|
||||||
PV_OPS A paravirtualized kernel is enabled.
|
PV_OPS A paravirtualized kernel is enabled.
|
||||||
PARIDE The ParIDE (parallel port IDE) subsystem is enabled.
|
|
||||||
PARISC The PA-RISC architecture is enabled.
|
PARISC The PA-RISC architecture is enabled.
|
||||||
PCI PCI bus support is enabled.
|
PCI PCI bus support is enabled.
|
||||||
PCIE PCI Express support is enabled.
|
PCIE PCI Express support is enabled.
|
||||||
|
@ -2788,6 +2788,9 @@
|
|||||||
* [no]setxfer: Indicate if transfer speed mode setting
|
* [no]setxfer: Indicate if transfer speed mode setting
|
||||||
should be skipped.
|
should be skipped.
|
||||||
|
|
||||||
|
* [no]fua: Disable or enable FUA (Force Unit Access)
|
||||||
|
support for devices supporting this feature.
|
||||||
|
|
||||||
* dump_id: Dump IDENTIFY data.
|
* dump_id: Dump IDENTIFY data.
|
||||||
|
|
||||||
* disable: Disable this device.
|
* disable: Disable this device.
|
||||||
@ -4114,10 +4117,6 @@
|
|||||||
|
|
||||||
pcbit= [HW,ISDN]
|
pcbit= [HW,ISDN]
|
||||||
|
|
||||||
pcd. [PARIDE]
|
|
||||||
See header of drivers/block/paride/pcd.c.
|
|
||||||
See also Documentation/admin-guide/blockdev/paride.rst.
|
|
||||||
|
|
||||||
pci=option[,option...] [PCI] various PCI subsystem options.
|
pci=option[,option...] [PCI] various PCI subsystem options.
|
||||||
|
|
||||||
Some options herein operate on a specific device
|
Some options herein operate on a specific device
|
||||||
@ -4380,9 +4379,6 @@
|
|||||||
for debug and development, but should not be
|
for debug and development, but should not be
|
||||||
needed on a platform with proper driver support.
|
needed on a platform with proper driver support.
|
||||||
|
|
||||||
pd. [PARIDE]
|
|
||||||
See Documentation/admin-guide/blockdev/paride.rst.
|
|
||||||
|
|
||||||
pdcchassis= [PARISC,HW] Disable/Enable PDC Chassis Status codes at
|
pdcchassis= [PARISC,HW] Disable/Enable PDC Chassis Status codes at
|
||||||
boot time.
|
boot time.
|
||||||
Format: { 0 | 1 }
|
Format: { 0 | 1 }
|
||||||
@ -4395,12 +4391,6 @@
|
|||||||
allocator. This parameter is primarily for debugging
|
allocator. This parameter is primarily for debugging
|
||||||
and performance comparison.
|
and performance comparison.
|
||||||
|
|
||||||
pf. [PARIDE]
|
|
||||||
See Documentation/admin-guide/blockdev/paride.rst.
|
|
||||||
|
|
||||||
pg. [PARIDE]
|
|
||||||
See Documentation/admin-guide/blockdev/paride.rst.
|
|
||||||
|
|
||||||
pirq= [SMP,APIC] Manual mp-table setup
|
pirq= [SMP,APIC] Manual mp-table setup
|
||||||
See Documentation/x86/i386/IO-APIC.rst.
|
See Documentation/x86/i386/IO-APIC.rst.
|
||||||
|
|
||||||
@ -4562,9 +4552,6 @@
|
|||||||
|
|
||||||
pstore.backend= Specify the name of the pstore backend to use
|
pstore.backend= Specify the name of the pstore backend to use
|
||||||
|
|
||||||
pt. [PARIDE]
|
|
||||||
See Documentation/admin-guide/blockdev/paride.rst.
|
|
||||||
|
|
||||||
pti= [X86-64] Control Page Table Isolation of user and
|
pti= [X86-64] Control Page Table Isolation of user and
|
||||||
kernel address spaces. Disabling this feature
|
kernel address spaces. Disabling this feature
|
||||||
removes hardening, but improves performance of
|
removes hardening, but improves performance of
|
||||||
|
@ -15770,13 +15770,6 @@ F: arch/*/include/asm/paravirt*.h
|
|||||||
F: arch/*/kernel/paravirt*
|
F: arch/*/kernel/paravirt*
|
||||||
F: include/linux/hypervisor.h
|
F: include/linux/hypervisor.h
|
||||||
|
|
||||||
PARIDE DRIVERS FOR PARALLEL PORT IDE DEVICES
|
|
||||||
M: Tim Waugh <tim@cyberelk.net>
|
|
||||||
L: linux-parport@lists.infradead.org (subscribers-only)
|
|
||||||
S: Maintained
|
|
||||||
F: Documentation/admin-guide/blockdev/paride.rst
|
|
||||||
F: drivers/block/paride/
|
|
||||||
|
|
||||||
PARISC ARCHITECTURE
|
PARISC ARCHITECTURE
|
||||||
M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
|
M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
|
||||||
M: Helge Deller <deller@gmx.de>
|
M: Helge Deller <deller@gmx.de>
|
||||||
|
@ -753,14 +753,18 @@ void submit_bio_noacct(struct bio *bio)
|
|||||||
* Filter flush bio's early so that bio based drivers without flush
|
* Filter flush bio's early so that bio based drivers without flush
|
||||||
* support don't have to worry about them.
|
* support don't have to worry about them.
|
||||||
*/
|
*/
|
||||||
if (op_is_flush(bio->bi_opf) &&
|
if (op_is_flush(bio->bi_opf)) {
|
||||||
!test_bit(QUEUE_FLAG_WC, &q->queue_flags)) {
|
if (WARN_ON_ONCE(bio_op(bio) != REQ_OP_WRITE &&
|
||||||
|
bio_op(bio) != REQ_OP_ZONE_APPEND))
|
||||||
|
goto end_io;
|
||||||
|
if (!test_bit(QUEUE_FLAG_WC, &q->queue_flags)) {
|
||||||
bio->bi_opf &= ~(REQ_PREFLUSH | REQ_FUA);
|
bio->bi_opf &= ~(REQ_PREFLUSH | REQ_FUA);
|
||||||
if (!bio_sectors(bio)) {
|
if (!bio_sectors(bio)) {
|
||||||
status = BLK_STS_OK;
|
status = BLK_STS_OK;
|
||||||
goto end_io;
|
goto end_io;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
|
if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags))
|
||||||
bio_clear_polled(bio);
|
bio_clear_polled(bio);
|
||||||
|
@ -98,7 +98,6 @@ obj-$(CONFIG_DIO) += dio/
|
|||||||
obj-$(CONFIG_SBUS) += sbus/
|
obj-$(CONFIG_SBUS) += sbus/
|
||||||
obj-$(CONFIG_ZORRO) += zorro/
|
obj-$(CONFIG_ZORRO) += zorro/
|
||||||
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
|
||||||
obj-$(CONFIG_PARIDE) += block/paride/
|
|
||||||
obj-$(CONFIG_TC) += tc/
|
obj-$(CONFIG_TC) += tc/
|
||||||
obj-$(CONFIG_USB_PHY) += usb/
|
obj-$(CONFIG_USB_PHY) += usb/
|
||||||
obj-$(CONFIG_USB) += usb/
|
obj-$(CONFIG_USB) += usb/
|
||||||
|
@ -1144,6 +1144,20 @@ config PATA_WINBOND_VLB
|
|||||||
Support for the Winbond W83759A controller on Vesa Local Bus
|
Support for the Winbond W83759A controller on Vesa Local Bus
|
||||||
systems.
|
systems.
|
||||||
|
|
||||||
|
config PATA_PARPORT
|
||||||
|
tristate "Parallel port IDE device support"
|
||||||
|
depends on PARPORT_PC
|
||||||
|
help
|
||||||
|
There are many external CD-ROM and disk devices that connect through
|
||||||
|
your computer's parallel port. Most of them are actually IDE devices
|
||||||
|
using a parallel port IDE adapter. This option enables the
|
||||||
|
PATA_PARPORT subsystem which contains drivers for many of these
|
||||||
|
external drives.
|
||||||
|
Read <file:Documentation/admin-guide/blockdev/paride.rst> for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
source "drivers/ata/pata_parport/Kconfig"
|
||||||
|
|
||||||
comment "Generic fallback / legacy drivers"
|
comment "Generic fallback / legacy drivers"
|
||||||
|
|
||||||
config PATA_ACPI
|
config PATA_ACPI
|
||||||
|
@ -112,6 +112,8 @@ obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
|
|||||||
|
|
||||||
obj-$(CONFIG_PATA_PXA) += pata_pxa.o
|
obj-$(CONFIG_PATA_PXA) += pata_pxa.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PATA_PARPORT) += pata_parport/
|
||||||
|
|
||||||
# Should be last but two libata driver
|
# Should be last but two libata driver
|
||||||
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
|
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
|
||||||
# Should be last but one libata driver
|
# Should be last but one libata driver
|
||||||
|
@ -57,7 +57,7 @@ struct acard_sg {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc);
|
static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc);
|
||||||
static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
|
static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
|
||||||
static int acard_ahci_port_start(struct ata_port *ap);
|
static int acard_ahci_port_start(struct ata_port *ap);
|
||||||
static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
|
static int acard_ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ static enum ata_completion_errors acard_ahci_qc_prep(struct ata_queued_cmd *qc)
|
|||||||
return AC_ERR_OK;
|
return AC_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct ahci_port_priv *pp = qc->ap->private_data;
|
struct ahci_port_priv *pp = qc->ap->private_data;
|
||||||
u8 *rx_fis = pp->rx_fis;
|
u8 *rx_fis = pp->rx_fis;
|
||||||
@ -263,13 +263,11 @@ static bool acard_ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
* Setup FIS.
|
* Setup FIS.
|
||||||
*/
|
*/
|
||||||
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
|
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
|
||||||
!(qc->flags & ATA_QCFLAG_FAILED)) {
|
!(qc->flags & ATA_QCFLAG_EH)) {
|
||||||
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
|
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
|
||||||
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
|
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
|
||||||
} else
|
} else
|
||||||
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
|
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int acard_ahci_port_start(struct ata_port *ap)
|
static int acard_ahci_port_start(struct ata_port *ap)
|
||||||
|
@ -73,11 +73,6 @@ static int ahci_octeon_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ahci_octeon_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id octeon_ahci_match[] = {
|
static const struct of_device_id octeon_ahci_match[] = {
|
||||||
{ .compatible = "cavium,octeon-7130-sata-uctl", },
|
{ .compatible = "cavium,octeon-7130-sata-uctl", },
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
@ -86,7 +81,6 @@ MODULE_DEVICE_TABLE(of, octeon_ahci_match);
|
|||||||
|
|
||||||
static struct platform_driver ahci_octeon_driver = {
|
static struct platform_driver ahci_octeon_driver = {
|
||||||
.probe = ahci_octeon_probe,
|
.probe = ahci_octeon_probe,
|
||||||
.remove = ahci_octeon_remove,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "octeon-ahci",
|
.name = "octeon-ahci",
|
||||||
.of_match_table = octeon_ahci_match,
|
.of_match_table = octeon_ahci_match,
|
||||||
|
@ -55,7 +55,8 @@ static ssize_t ahci_transmit_led_message(struct ata_port *ap, u32 state,
|
|||||||
|
|
||||||
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
|
static int ahci_scr_read(struct ata_link *link, unsigned int sc_reg, u32 *val);
|
||||||
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
|
static int ahci_scr_write(struct ata_link *link, unsigned int sc_reg, u32 val);
|
||||||
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
|
static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc);
|
||||||
|
static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask);
|
||||||
static int ahci_port_start(struct ata_port *ap);
|
static int ahci_port_start(struct ata_port *ap);
|
||||||
static void ahci_port_stop(struct ata_port *ap);
|
static void ahci_port_stop(struct ata_port *ap);
|
||||||
static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
|
static enum ata_completion_errors ahci_qc_prep(struct ata_queued_cmd *qc);
|
||||||
@ -157,6 +158,7 @@ struct ata_port_operations ahci_ops = {
|
|||||||
.qc_prep = ahci_qc_prep,
|
.qc_prep = ahci_qc_prep,
|
||||||
.qc_issue = ahci_qc_issue,
|
.qc_issue = ahci_qc_issue,
|
||||||
.qc_fill_rtf = ahci_qc_fill_rtf,
|
.qc_fill_rtf = ahci_qc_fill_rtf,
|
||||||
|
.qc_ncq_fill_rtf = ahci_qc_ncq_fill_rtf,
|
||||||
|
|
||||||
.freeze = ahci_freeze,
|
.freeze = ahci_freeze,
|
||||||
.thaw = ahci_thaw,
|
.thaw = ahci_thaw,
|
||||||
@ -1847,18 +1849,47 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
|
|||||||
ata_port_abort(ap);
|
ata_port_abort(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ahci_handle_port_interrupt(struct ata_port *ap,
|
static void ahci_qc_complete(struct ata_port *ap, void __iomem *port_mmio)
|
||||||
void __iomem *port_mmio, u32 status)
|
|
||||||
{
|
{
|
||||||
struct ata_eh_info *ehi = &ap->link.eh_info;
|
struct ata_eh_info *ehi = &ap->link.eh_info;
|
||||||
struct ahci_port_priv *pp = ap->private_data;
|
struct ahci_port_priv *pp = ap->private_data;
|
||||||
struct ahci_host_priv *hpriv = ap->host->private_data;
|
|
||||||
int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
|
|
||||||
u32 qc_active = 0;
|
u32 qc_active = 0;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pp->active_link is not reliable once FBS is enabled, both
|
||||||
|
* PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
|
||||||
|
* NCQ and non-NCQ commands may be in flight at the same time.
|
||||||
|
*/
|
||||||
|
if (pp->fbs_enabled) {
|
||||||
|
if (ap->qc_active) {
|
||||||
|
qc_active = readl(port_mmio + PORT_SCR_ACT);
|
||||||
|
qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* pp->active_link is valid iff any command is in flight */
|
||||||
|
if (ap->qc_active && pp->active_link->sactive)
|
||||||
|
qc_active = readl(port_mmio + PORT_SCR_ACT);
|
||||||
|
else
|
||||||
|
qc_active = readl(port_mmio + PORT_CMD_ISSUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = ata_qc_complete_multiple(ap, qc_active);
|
||||||
|
if (unlikely(rc < 0 && !(ap->pflags & ATA_PFLAG_RESETTING))) {
|
||||||
|
ehi->err_mask |= AC_ERR_HSM;
|
||||||
|
ehi->action |= ATA_EH_RESET;
|
||||||
|
ata_port_freeze(ap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ahci_handle_port_interrupt(struct ata_port *ap,
|
||||||
|
void __iomem *port_mmio, u32 status)
|
||||||
|
{
|
||||||
|
struct ahci_port_priv *pp = ap->private_data;
|
||||||
|
struct ahci_host_priv *hpriv = ap->host->private_data;
|
||||||
|
|
||||||
/* ignore BAD_PMP while resetting */
|
/* ignore BAD_PMP while resetting */
|
||||||
if (unlikely(resetting))
|
if (unlikely(ap->pflags & ATA_PFLAG_RESETTING))
|
||||||
status &= ~PORT_IRQ_BAD_PMP;
|
status &= ~PORT_IRQ_BAD_PMP;
|
||||||
|
|
||||||
if (sata_lpm_ignore_phy_events(&ap->link)) {
|
if (sata_lpm_ignore_phy_events(&ap->link)) {
|
||||||
@ -1867,6 +1898,12 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(status & PORT_IRQ_ERROR)) {
|
if (unlikely(status & PORT_IRQ_ERROR)) {
|
||||||
|
/*
|
||||||
|
* Before getting the error notification, we may have
|
||||||
|
* received SDB FISes notifying successful completions.
|
||||||
|
* Handle these first and then handle the error.
|
||||||
|
*/
|
||||||
|
ahci_qc_complete(ap, port_mmio);
|
||||||
ahci_error_intr(ap, status);
|
ahci_error_intr(ap, status);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1903,32 +1940,8 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pp->active_link is not reliable once FBS is enabled, both
|
/* Handle completed commands */
|
||||||
* PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
|
ahci_qc_complete(ap, port_mmio);
|
||||||
* NCQ and non-NCQ commands may be in flight at the same time.
|
|
||||||
*/
|
|
||||||
if (pp->fbs_enabled) {
|
|
||||||
if (ap->qc_active) {
|
|
||||||
qc_active = readl(port_mmio + PORT_SCR_ACT);
|
|
||||||
qc_active |= readl(port_mmio + PORT_CMD_ISSUE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* pp->active_link is valid iff any command is in flight */
|
|
||||||
if (ap->qc_active && pp->active_link->sactive)
|
|
||||||
qc_active = readl(port_mmio + PORT_SCR_ACT);
|
|
||||||
else
|
|
||||||
qc_active = readl(port_mmio + PORT_CMD_ISSUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
rc = ata_qc_complete_multiple(ap, qc_active);
|
|
||||||
|
|
||||||
/* while resetting, invalid completions are expected */
|
|
||||||
if (unlikely(rc < 0 && !resetting)) {
|
|
||||||
ehi->err_mask |= AC_ERR_HSM;
|
|
||||||
ehi->action |= ATA_EH_RESET;
|
|
||||||
ata_port_freeze(ap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ahci_port_intr(struct ata_port *ap)
|
static void ahci_port_intr(struct ata_port *ap)
|
||||||
@ -2053,11 +2066,18 @@ unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ahci_qc_issue);
|
EXPORT_SYMBOL_GPL(ahci_qc_issue);
|
||||||
|
|
||||||
static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct ahci_port_priv *pp = qc->ap->private_data;
|
struct ahci_port_priv *pp = qc->ap->private_data;
|
||||||
u8 *rx_fis = pp->rx_fis;
|
u8 *rx_fis = pp->rx_fis;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* rtf may already be filled (e.g. for successful NCQ commands).
|
||||||
|
* If that is the case, we have nothing to do.
|
||||||
|
*/
|
||||||
|
if (qc->flags & ATA_QCFLAG_RTF_FILLED)
|
||||||
|
return;
|
||||||
|
|
||||||
if (pp->fbs_enabled)
|
if (pp->fbs_enabled)
|
||||||
rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
|
rx_fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
|
||||||
|
|
||||||
@ -2068,9 +2088,12 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
* Setup FIS.
|
* Setup FIS.
|
||||||
*/
|
*/
|
||||||
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
|
if (qc->tf.protocol == ATA_PROT_PIO && qc->dma_dir == DMA_FROM_DEVICE &&
|
||||||
!(qc->flags & ATA_QCFLAG_FAILED)) {
|
!(qc->flags & ATA_QCFLAG_EH)) {
|
||||||
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
|
ata_tf_from_fis(rx_fis + RX_FIS_PIO_SETUP, &qc->result_tf);
|
||||||
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
|
qc->result_tf.status = (rx_fis + RX_FIS_PIO_SETUP)[15];
|
||||||
|
qc->flags |= ATA_QCFLAG_RTF_FILLED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For NCQ commands, we never get a D2H FIS, so reading the D2H Register
|
* For NCQ commands, we never get a D2H FIS, so reading the D2H Register
|
||||||
@ -2080,15 +2103,85 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
* instead. However, the SDB FIS does not contain the LBA, so we can't
|
* instead. However, the SDB FIS does not contain the LBA, so we can't
|
||||||
* use the ata_tf_from_fis() helper.
|
* use the ata_tf_from_fis() helper.
|
||||||
*/
|
*/
|
||||||
} else if (ata_is_ncq(qc->tf.protocol)) {
|
if (ata_is_ncq(qc->tf.protocol)) {
|
||||||
const u8 *fis = rx_fis + RX_FIS_SDB;
|
const u8 *fis = rx_fis + RX_FIS_SDB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Successful NCQ commands have been filled already.
|
||||||
|
* A failed NCQ command will read the status here.
|
||||||
|
* (Note that a failed NCQ command will get a more specific
|
||||||
|
* error when reading the NCQ Command Error log.)
|
||||||
|
*/
|
||||||
qc->result_tf.status = fis[2];
|
qc->result_tf.status = fis[2];
|
||||||
qc->result_tf.error = fis[3];
|
qc->result_tf.error = fis[3];
|
||||||
} else
|
qc->flags |= ATA_QCFLAG_RTF_FILLED;
|
||||||
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
ata_tf_from_fis(rx_fis + RX_FIS_D2H_REG, &qc->result_tf);
|
||||||
|
qc->flags |= ATA_QCFLAG_RTF_FILLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ahci_qc_ncq_fill_rtf(struct ata_port *ap, u64 done_mask)
|
||||||
|
{
|
||||||
|
struct ahci_port_priv *pp = ap->private_data;
|
||||||
|
const u8 *fis;
|
||||||
|
|
||||||
|
/* No outstanding commands. */
|
||||||
|
if (!ap->qc_active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FBS not enabled, so read status and error once, since they are shared
|
||||||
|
* for all QCs.
|
||||||
|
*/
|
||||||
|
if (!pp->fbs_enabled) {
|
||||||
|
u8 status, error;
|
||||||
|
|
||||||
|
/* No outstanding NCQ commands. */
|
||||||
|
if (!pp->active_link->sactive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fis = pp->rx_fis + RX_FIS_SDB;
|
||||||
|
status = fis[2];
|
||||||
|
error = fis[3];
|
||||||
|
|
||||||
|
while (done_mask) {
|
||||||
|
struct ata_queued_cmd *qc;
|
||||||
|
unsigned int tag = __ffs64(done_mask);
|
||||||
|
|
||||||
|
qc = ata_qc_from_tag(ap, tag);
|
||||||
|
if (qc && ata_is_ncq(qc->tf.protocol)) {
|
||||||
|
qc->result_tf.status = status;
|
||||||
|
qc->result_tf.error = error;
|
||||||
|
qc->flags |= ATA_QCFLAG_RTF_FILLED;
|
||||||
|
}
|
||||||
|
done_mask &= ~(1ULL << tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FBS enabled, so read the status and error for each QC, since the QCs
|
||||||
|
* can belong to different PMP links. (Each PMP link has its own FIS
|
||||||
|
* Receive Area.)
|
||||||
|
*/
|
||||||
|
while (done_mask) {
|
||||||
|
struct ata_queued_cmd *qc;
|
||||||
|
unsigned int tag = __ffs64(done_mask);
|
||||||
|
|
||||||
|
qc = ata_qc_from_tag(ap, tag);
|
||||||
|
if (qc && ata_is_ncq(qc->tf.protocol)) {
|
||||||
|
fis = pp->rx_fis;
|
||||||
|
fis += qc->dev->link->pmp * AHCI_RX_FIS_SZ;
|
||||||
|
fis += RX_FIS_SDB;
|
||||||
|
qc->result_tf.status = fis[2];
|
||||||
|
qc->result_tf.error = fis[3];
|
||||||
|
qc->flags |= ATA_QCFLAG_RTF_FILLED;
|
||||||
|
}
|
||||||
|
done_mask &= ~(1ULL << tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ahci_freeze(struct ata_port *ap)
|
static void ahci_freeze(struct ata_port *ap)
|
||||||
@ -2138,7 +2231,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
|
|||||||
struct ata_port *ap = qc->ap;
|
struct ata_port *ap = qc->ap;
|
||||||
|
|
||||||
/* make DMA engine forget about the failed command */
|
/* make DMA engine forget about the failed command */
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
ahci_kick_engine(ap);
|
ahci_kick_engine(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +552,7 @@ static const u8 ata_rw_cmds[] = {
|
|||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
ATA_CMD_WRITE_MULTI_FUA_EXT,
|
0,
|
||||||
/* pio */
|
/* pio */
|
||||||
ATA_CMD_PIO_READ,
|
ATA_CMD_PIO_READ,
|
||||||
ATA_CMD_PIO_WRITE,
|
ATA_CMD_PIO_WRITE,
|
||||||
@ -574,17 +574,18 @@ static const u8 ata_rw_cmds[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ata_rwcmd_protocol - set taskfile r/w commands and protocol
|
* ata_set_rwcmd_protocol - set taskfile r/w command and protocol
|
||||||
* @tf: command to examine and configure
|
* @dev: target device for the taskfile
|
||||||
* @dev: device tf belongs to
|
* @tf: taskfile to examine and configure
|
||||||
*
|
*
|
||||||
* Examine the device configuration and tf->flags to calculate
|
* Examine the device configuration and tf->flags to determine
|
||||||
* the proper read/write commands and protocol to use.
|
* the proper read/write command and protocol to use for @tf.
|
||||||
*
|
*
|
||||||
* LOCKING:
|
* LOCKING:
|
||||||
* caller.
|
* caller.
|
||||||
*/
|
*/
|
||||||
static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
|
static bool ata_set_rwcmd_protocol(struct ata_device *dev,
|
||||||
|
struct ata_taskfile *tf)
|
||||||
{
|
{
|
||||||
u8 cmd;
|
u8 cmd;
|
||||||
|
|
||||||
@ -607,11 +608,12 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd = ata_rw_cmds[index + fua + lba48 + write];
|
cmd = ata_rw_cmds[index + fua + lba48 + write];
|
||||||
if (cmd) {
|
if (!cmd)
|
||||||
|
return false;
|
||||||
|
|
||||||
tf->command = cmd;
|
tf->command = cmd;
|
||||||
return 0;
|
|
||||||
}
|
return true;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -725,7 +727,8 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
|
|||||||
} else if (dev->flags & ATA_DFLAG_LBA) {
|
} else if (dev->flags & ATA_DFLAG_LBA) {
|
||||||
tf->flags |= ATA_TFLAG_LBA;
|
tf->flags |= ATA_TFLAG_LBA;
|
||||||
|
|
||||||
if (lba_28_ok(block, n_block)) {
|
/* We need LBA48 for FUA writes */
|
||||||
|
if (!(tf->flags & ATA_TFLAG_FUA) && lba_28_ok(block, n_block)) {
|
||||||
/* use LBA28 */
|
/* use LBA28 */
|
||||||
tf->device |= (block >> 24) & 0xf;
|
tf->device |= (block >> 24) & 0xf;
|
||||||
} else if (lba_48_ok(block, n_block)) {
|
} else if (lba_48_ok(block, n_block)) {
|
||||||
@ -740,11 +743,12 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
|
|||||||
tf->hob_lbah = (block >> 40) & 0xff;
|
tf->hob_lbah = (block >> 40) & 0xff;
|
||||||
tf->hob_lbam = (block >> 32) & 0xff;
|
tf->hob_lbam = (block >> 32) & 0xff;
|
||||||
tf->hob_lbal = (block >> 24) & 0xff;
|
tf->hob_lbal = (block >> 24) & 0xff;
|
||||||
} else
|
} else {
|
||||||
/* request too large even for LBA48 */
|
/* request too large even for LBA48 */
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
|
if (unlikely(!ata_set_rwcmd_protocol(dev, tf)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
tf->nsect = n_block & 0xff;
|
tf->nsect = n_block & 0xff;
|
||||||
@ -762,7 +766,7 @@ int ata_build_rw_tf(struct ata_queued_cmd *qc, u64 block, u32 n_block,
|
|||||||
if (!lba_28_ok(block, n_block))
|
if (!lba_28_ok(block, n_block))
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
if (unlikely(ata_rwcmd_protocol(tf, dev) < 0))
|
if (unlikely(!ata_set_rwcmd_protocol(dev, tf)))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
/* Convert LBA to CHS */
|
/* Convert LBA to CHS */
|
||||||
@ -1590,7 +1594,7 @@ static unsigned ata_exec_internal_sg(struct ata_device *dev,
|
|||||||
ap->ops->post_internal_cmd(qc);
|
ap->ops->post_internal_cmd(qc);
|
||||||
|
|
||||||
/* perform minimal error analysis */
|
/* perform minimal error analysis */
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED) {
|
if (qc->flags & ATA_QCFLAG_EH) {
|
||||||
if (qc->result_tf.status & (ATA_ERR | ATA_DF))
|
if (qc->result_tf.status & (ATA_ERR | ATA_DF))
|
||||||
qc->err_mask |= AC_ERR_DEV;
|
qc->err_mask |= AC_ERR_DEV;
|
||||||
|
|
||||||
@ -2420,6 +2424,28 @@ static void ata_dev_config_chs(struct ata_device *dev)
|
|||||||
dev->heads, dev->sectors);
|
dev->heads, dev->sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ata_dev_config_fua(struct ata_device *dev)
|
||||||
|
{
|
||||||
|
/* Ignore FUA support if its use is disabled globally */
|
||||||
|
if (!libata_fua)
|
||||||
|
goto nofua;
|
||||||
|
|
||||||
|
/* Ignore devices without support for WRITE DMA FUA EXT */
|
||||||
|
if (!(dev->flags & ATA_DFLAG_LBA48) || !ata_id_has_fua(dev->id))
|
||||||
|
goto nofua;
|
||||||
|
|
||||||
|
/* Ignore known bad devices and devices that lack NCQ support */
|
||||||
|
if (!ata_ncq_supported(dev) || (dev->horkage & ATA_HORKAGE_NO_FUA))
|
||||||
|
goto nofua;
|
||||||
|
|
||||||
|
dev->flags |= ATA_DFLAG_FUA;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
nofua:
|
||||||
|
dev->flags &= ~ATA_DFLAG_FUA;
|
||||||
|
}
|
||||||
|
|
||||||
static void ata_dev_config_devslp(struct ata_device *dev)
|
static void ata_dev_config_devslp(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
u8 *sata_setting = dev->link->ap->sector_buf;
|
u8 *sata_setting = dev->link->ap->sector_buf;
|
||||||
@ -2508,7 +2534,8 @@ static void ata_dev_print_features(struct ata_device *dev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
ata_dev_info(dev,
|
ata_dev_info(dev,
|
||||||
"Features:%s%s%s%s%s%s\n",
|
"Features:%s%s%s%s%s%s%s\n",
|
||||||
|
dev->flags & ATA_DFLAG_FUA ? " FUA" : "",
|
||||||
dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "",
|
dev->flags & ATA_DFLAG_TRUSTED ? " Trust" : "",
|
||||||
dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "",
|
dev->flags & ATA_DFLAG_DA ? " Dev-Attention" : "",
|
||||||
dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "",
|
dev->flags & ATA_DFLAG_DEVSLP ? " Dev-Sleep" : "",
|
||||||
@ -2669,6 +2696,7 @@ int ata_dev_configure(struct ata_device *dev)
|
|||||||
ata_dev_config_chs(dev);
|
ata_dev_config_chs(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ata_dev_config_fua(dev);
|
||||||
ata_dev_config_devslp(dev);
|
ata_dev_config_devslp(dev);
|
||||||
ata_dev_config_sense_reporting(dev);
|
ata_dev_config_sense_reporting(dev);
|
||||||
ata_dev_config_zac(dev);
|
ata_dev_config_zac(dev);
|
||||||
@ -4106,6 +4134,12 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
|
|||||||
*/
|
*/
|
||||||
{ "SATADOM-ML 3ME", NULL, ATA_HORKAGE_NO_LOG_DIR },
|
{ "SATADOM-ML 3ME", NULL, ATA_HORKAGE_NO_LOG_DIR },
|
||||||
|
|
||||||
|
/* Buggy FUA */
|
||||||
|
{ "Maxtor", "BANC1G10", ATA_HORKAGE_NO_FUA },
|
||||||
|
{ "WDC*WD2500J*", NULL, ATA_HORKAGE_NO_FUA },
|
||||||
|
{ "OCZ-VERTEX*", NULL, ATA_HORKAGE_NO_FUA },
|
||||||
|
{ "INTEL*SSDSC2CT*", NULL, ATA_HORKAGE_NO_FUA },
|
||||||
|
|
||||||
/* End Marker */
|
/* End Marker */
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
@ -4686,10 +4720,10 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
|
|||||||
/* XXX: New EH and old EH use different mechanisms to
|
/* XXX: New EH and old EH use different mechanisms to
|
||||||
* synchronize EH with regular execution path.
|
* synchronize EH with regular execution path.
|
||||||
*
|
*
|
||||||
* In new EH, a failed qc is marked with ATA_QCFLAG_FAILED.
|
* In new EH, a qc owned by EH is marked with ATA_QCFLAG_EH.
|
||||||
* Normal execution path is responsible for not accessing a
|
* Normal execution path is responsible for not accessing a
|
||||||
* failed qc. libata core enforces the rule by returning NULL
|
* qc owned by EH. libata core enforces the rule by returning NULL
|
||||||
* from ata_qc_from_tag() for failed qcs.
|
* from ata_qc_from_tag() for qcs owned by EH.
|
||||||
*
|
*
|
||||||
* Old EH depends on ata_qc_complete() nullifying completion
|
* Old EH depends on ata_qc_complete() nullifying completion
|
||||||
* requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does
|
* requests if ATA_QCFLAG_EH_SCHEDULED is set. Old EH does
|
||||||
@ -4701,7 +4735,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
|
|||||||
struct ata_eh_info *ehi = &dev->link->eh_info;
|
struct ata_eh_info *ehi = &dev->link->eh_info;
|
||||||
|
|
||||||
if (unlikely(qc->err_mask))
|
if (unlikely(qc->err_mask))
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finish internal commands without any further processing
|
* Finish internal commands without any further processing
|
||||||
@ -4718,7 +4752,7 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
|
|||||||
* Non-internal qc has failed. Fill the result TF and
|
* Non-internal qc has failed. Fill the result TF and
|
||||||
* summon EH.
|
* summon EH.
|
||||||
*/
|
*/
|
||||||
if (unlikely(qc->flags & ATA_QCFLAG_FAILED)) {
|
if (unlikely(qc->flags & ATA_QCFLAG_EH)) {
|
||||||
fill_result_tf(qc);
|
fill_result_tf(qc);
|
||||||
trace_ata_qc_complete_failed(qc);
|
trace_ata_qc_complete_failed(qc);
|
||||||
ata_qc_schedule_eh(qc);
|
ata_qc_schedule_eh(qc);
|
||||||
@ -6217,6 +6251,7 @@ static const struct ata_force_param force_tbl[] __initconst = {
|
|||||||
force_horkage_onoff(lpm, ATA_HORKAGE_NOLPM),
|
force_horkage_onoff(lpm, ATA_HORKAGE_NOLPM),
|
||||||
force_horkage_onoff(setxfer, ATA_HORKAGE_NOSETXFER),
|
force_horkage_onoff(setxfer, ATA_HORKAGE_NOSETXFER),
|
||||||
force_horkage_on(dump_id, ATA_HORKAGE_DUMP_ID),
|
force_horkage_on(dump_id, ATA_HORKAGE_DUMP_ID),
|
||||||
|
force_horkage_onoff(fua, ATA_HORKAGE_NO_FUA),
|
||||||
|
|
||||||
force_horkage_on(disable, ATA_HORKAGE_DISABLE),
|
force_horkage_on(disable, ATA_HORKAGE_DISABLE),
|
||||||
};
|
};
|
||||||
|
@ -565,17 +565,23 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
struct scsi_cmnd *scmd, *tmp;
|
||||||
|
int nr_timedout = 0;
|
||||||
|
|
||||||
/* make sure sff pio task is not running */
|
/* make sure sff pio task is not running */
|
||||||
ata_sff_flush_pio_task(ap);
|
ata_sff_flush_pio_task(ap);
|
||||||
|
|
||||||
|
if (!ap->ops->error_handler)
|
||||||
|
return;
|
||||||
|
|
||||||
/* synchronize with host lock and sort out timeouts */
|
/* synchronize with host lock and sort out timeouts */
|
||||||
|
|
||||||
/* For new EH, all qcs are finished in one of three ways -
|
/*
|
||||||
|
* For new EH, all qcs are finished in one of three ways -
|
||||||
* normal completion, error completion, and SCSI timeout.
|
* normal completion, error completion, and SCSI timeout.
|
||||||
* Both completions can race against SCSI timeout. When normal
|
* Both completions can race against SCSI timeout. When normal
|
||||||
* completion wins, the qc never reaches EH. When error
|
* completion wins, the qc never reaches EH. When error
|
||||||
* completion wins, the qc has ATA_QCFLAG_FAILED set.
|
* completion wins, the qc has ATA_QCFLAG_EH set.
|
||||||
*
|
*
|
||||||
* When SCSI timeout wins, things are a bit more complex.
|
* When SCSI timeout wins, things are a bit more complex.
|
||||||
* Normal or error completion can occur after the timeout but
|
* Normal or error completion can occur after the timeout but
|
||||||
@ -584,19 +590,17 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
|
|||||||
* timed out iff its associated qc is active and not failed.
|
* timed out iff its associated qc is active and not failed.
|
||||||
*/
|
*/
|
||||||
spin_lock_irqsave(ap->lock, flags);
|
spin_lock_irqsave(ap->lock, flags);
|
||||||
if (ap->ops->error_handler) {
|
|
||||||
struct scsi_cmnd *scmd, *tmp;
|
|
||||||
int nr_timedout = 0;
|
|
||||||
|
|
||||||
/* This must occur under the ap->lock as we don't want
|
|
||||||
a polled recovery to race the real interrupt handler
|
|
||||||
|
|
||||||
The lost_interrupt handler checks for any completed but
|
|
||||||
non-notified command and completes much like an IRQ handler.
|
|
||||||
|
|
||||||
We then fall into the error recovery code which will treat
|
|
||||||
this as if normal completion won the race */
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This must occur under the ap->lock as we don't want
|
||||||
|
* a polled recovery to race the real interrupt handler
|
||||||
|
*
|
||||||
|
* The lost_interrupt handler checks for any completed but
|
||||||
|
* non-notified command and completes much like an IRQ handler.
|
||||||
|
*
|
||||||
|
* We then fall into the error recovery code which will treat
|
||||||
|
* this as if normal completion won the race
|
||||||
|
*/
|
||||||
if (ap->ops->lost_interrupt)
|
if (ap->ops->lost_interrupt)
|
||||||
ap->ops->lost_interrupt(ap);
|
ap->ops->lost_interrupt(ap);
|
||||||
|
|
||||||
@ -611,10 +615,10 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
|
|||||||
|
|
||||||
if (i < ATA_MAX_QUEUE) {
|
if (i < ATA_MAX_QUEUE) {
|
||||||
/* the scmd has an associated qc */
|
/* the scmd has an associated qc */
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED)) {
|
if (!(qc->flags & ATA_QCFLAG_EH)) {
|
||||||
/* which hasn't failed yet, timeout */
|
/* which hasn't failed yet, timeout */
|
||||||
qc->err_mask |= AC_ERR_TIMEOUT;
|
qc->err_mask |= AC_ERR_TIMEOUT;
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
nr_timedout++;
|
nr_timedout++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -627,21 +631,20 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we have timed out qcs. They belong to EH from
|
/*
|
||||||
|
* If we have timed out qcs. They belong to EH from
|
||||||
* this point but the state of the controller is
|
* this point but the state of the controller is
|
||||||
* unknown. Freeze the port to make sure the IRQ
|
* unknown. Freeze the port to make sure the IRQ
|
||||||
* handler doesn't diddle with those qcs. This must
|
* handler doesn't diddle with those qcs. This must
|
||||||
* be done atomically w.r.t. setting QCFLAG_FAILED.
|
* be done atomically w.r.t. setting ATA_QCFLAG_EH.
|
||||||
*/
|
*/
|
||||||
if (nr_timedout)
|
if (nr_timedout)
|
||||||
__ata_port_freeze(ap);
|
__ata_port_freeze(ap);
|
||||||
|
|
||||||
|
|
||||||
/* initialize eh_tries */
|
/* initialize eh_tries */
|
||||||
ap->eh_tries = ATA_EH_MAX_TRIES;
|
ap->eh_tries = ATA_EH_MAX_TRIES;
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(ap->lock, flags);
|
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(ap->lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ata_scsi_cmd_error_handler);
|
EXPORT_SYMBOL(ata_scsi_cmd_error_handler);
|
||||||
|
|
||||||
@ -911,12 +914,12 @@ void ata_qc_schedule_eh(struct ata_queued_cmd *qc)
|
|||||||
|
|
||||||
WARN_ON(!ap->ops->error_handler);
|
WARN_ON(!ap->ops->error_handler);
|
||||||
|
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
ata_eh_set_pending(ap, 1);
|
ata_eh_set_pending(ap, 1);
|
||||||
|
|
||||||
/* The following will fail if timeout has already expired.
|
/* The following will fail if timeout has already expired.
|
||||||
* ata_scsi_error() takes care of such scmds on EH entry.
|
* ata_scsi_error() takes care of such scmds on EH entry.
|
||||||
* Note that ATA_QCFLAG_FAILED is unconditionally set after
|
* Note that ATA_QCFLAG_EH is unconditionally set after
|
||||||
* this function completes.
|
* this function completes.
|
||||||
*/
|
*/
|
||||||
blk_abort_request(scsi_cmd_to_rq(qc->scsicmd));
|
blk_abort_request(scsi_cmd_to_rq(qc->scsicmd));
|
||||||
@ -994,7 +997,7 @@ static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link)
|
|||||||
/* include internal tag in iteration */
|
/* include internal tag in iteration */
|
||||||
ata_qc_for_each_with_internal(ap, qc, tag) {
|
ata_qc_for_each_with_internal(ap, qc, tag) {
|
||||||
if (qc && (!link || qc->dev->link == link)) {
|
if (qc && (!link || qc->dev->link == link)) {
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
ata_qc_complete(qc);
|
ata_qc_complete(qc);
|
||||||
nr_aborted++;
|
nr_aborted++;
|
||||||
}
|
}
|
||||||
@ -1954,7 +1957,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
|
|||||||
all_err_mask |= ehc->i.err_mask;
|
all_err_mask |= ehc->i.err_mask;
|
||||||
|
|
||||||
ata_qc_for_each_raw(ap, qc, tag) {
|
ata_qc_for_each_raw(ap, qc, tag) {
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
|
if (!(qc->flags & ATA_QCFLAG_EH) ||
|
||||||
qc->flags & ATA_QCFLAG_RETRY ||
|
qc->flags & ATA_QCFLAG_RETRY ||
|
||||||
ata_dev_phys_link(qc->dev) != link)
|
ata_dev_phys_link(qc->dev) != link)
|
||||||
continue;
|
continue;
|
||||||
@ -2232,7 +2235,7 @@ static void ata_eh_link_report(struct ata_link *link)
|
|||||||
desc = ehc->i.desc;
|
desc = ehc->i.desc;
|
||||||
|
|
||||||
ata_qc_for_each_raw(ap, qc, tag) {
|
ata_qc_for_each_raw(ap, qc, tag) {
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
|
if (!(qc->flags & ATA_QCFLAG_EH) ||
|
||||||
ata_dev_phys_link(qc->dev) != link ||
|
ata_dev_phys_link(qc->dev) != link ||
|
||||||
((qc->flags & ATA_QCFLAG_QUIET) &&
|
((qc->flags & ATA_QCFLAG_QUIET) &&
|
||||||
qc->err_mask == AC_ERR_DEV))
|
qc->err_mask == AC_ERR_DEV))
|
||||||
@ -2298,7 +2301,7 @@ static void ata_eh_link_report(struct ata_link *link)
|
|||||||
char data_buf[20] = "";
|
char data_buf[20] = "";
|
||||||
char cdb_buf[70] = "";
|
char cdb_buf[70] = "";
|
||||||
|
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
|
if (!(qc->flags & ATA_QCFLAG_EH) ||
|
||||||
ata_dev_phys_link(qc->dev) != link || !qc->err_mask)
|
ata_dev_phys_link(qc->dev) != link || !qc->err_mask)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -3802,7 +3805,7 @@ void ata_eh_finish(struct ata_port *ap)
|
|||||||
|
|
||||||
/* retry or finish qcs */
|
/* retry or finish qcs */
|
||||||
ata_qc_for_each_raw(ap, qc, tag) {
|
ata_qc_for_each_raw(ap, qc, tag) {
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED))
|
if (!(qc->flags & ATA_QCFLAG_EH))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (qc->err_mask) {
|
if (qc->err_mask) {
|
||||||
|
@ -655,6 +655,9 @@ int ata_qc_complete_multiple(struct ata_port *ap, u64 qc_active)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ap->ops->qc_ncq_fill_rtf)
|
||||||
|
ap->ops->qc_ncq_fill_rtf(ap, done_mask);
|
||||||
|
|
||||||
while (done_mask) {
|
while (done_mask) {
|
||||||
struct ata_queued_cmd *qc;
|
struct ata_queued_cmd *qc;
|
||||||
unsigned int tag = __ffs64(done_mask);
|
unsigned int tag = __ffs64(done_mask);
|
||||||
@ -1429,7 +1432,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
|
|||||||
|
|
||||||
/* has LLDD analyzed already? */
|
/* has LLDD analyzed already? */
|
||||||
ata_qc_for_each_raw(ap, qc, tag) {
|
ata_qc_for_each_raw(ap, qc, tag) {
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED))
|
if (!(qc->flags & ATA_QCFLAG_EH))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (qc->err_mask)
|
if (qc->err_mask)
|
||||||
@ -1477,7 +1480,7 @@ void ata_eh_analyze_ncq_error(struct ata_link *link)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ata_qc_for_each_raw(ap, qc, tag) {
|
ata_qc_for_each_raw(ap, qc, tag) {
|
||||||
if (!(qc->flags & ATA_QCFLAG_FAILED) ||
|
if (!(qc->flags & ATA_QCFLAG_EH) ||
|
||||||
ata_dev_phys_link(qc->dev) != link)
|
ata_dev_phys_link(qc->dev) != link)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1654,7 +1654,8 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
|
|||||||
struct ata_port *ap = qc->ap;
|
struct ata_port *ap = qc->ap;
|
||||||
struct scsi_cmnd *cmd = qc->scsicmd;
|
struct scsi_cmnd *cmd = qc->scsicmd;
|
||||||
u8 *cdb = cmd->cmnd;
|
u8 *cdb = cmd->cmnd;
|
||||||
int need_sense = (qc->err_mask != 0);
|
int need_sense = (qc->err_mask != 0) &&
|
||||||
|
!(qc->flags & ATA_QCFLAG_SENSE_VALID);
|
||||||
|
|
||||||
/* For ATA pass thru (SAT) commands, generate a sense block if
|
/* For ATA pass thru (SAT) commands, generate a sense block if
|
||||||
* user mandated it or if there's an error. Note that if we
|
* user mandated it or if there's an error. Note that if we
|
||||||
@ -1668,12 +1669,11 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc)
|
|||||||
if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
|
if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) &&
|
||||||
((cdb[2] & 0x20) || need_sense))
|
((cdb[2] & 0x20) || need_sense))
|
||||||
ata_gen_passthru_sense(qc);
|
ata_gen_passthru_sense(qc);
|
||||||
else if (qc->flags & ATA_QCFLAG_SENSE_VALID)
|
|
||||||
cmd->result = SAM_STAT_CHECK_CONDITION;
|
|
||||||
else if (need_sense)
|
else if (need_sense)
|
||||||
ata_gen_ata_sense(qc);
|
ata_gen_ata_sense(qc);
|
||||||
else
|
else
|
||||||
cmd->result = SAM_STAT_GOOD;
|
/* Keep the SCSI ML and status byte, clear host byte. */
|
||||||
|
cmd->result &= 0x0000ffff;
|
||||||
|
|
||||||
if (need_sense && !ap->ops->error_handler)
|
if (need_sense && !ap->ops->error_handler)
|
||||||
ata_dump_status(ap, &qc->result_tf);
|
ata_dump_status(ap, &qc->result_tf);
|
||||||
@ -2240,30 +2240,6 @@ static unsigned int ata_msense_rw_recovery(u8 *buf, bool changeable)
|
|||||||
return sizeof(def_rw_recovery_mpage);
|
return sizeof(def_rw_recovery_mpage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* We can turn this into a real blacklist if it's needed, for now just
|
|
||||||
* blacklist any Maxtor BANC1G10 revision firmware
|
|
||||||
*/
|
|
||||||
static int ata_dev_supports_fua(u16 *id)
|
|
||||||
{
|
|
||||||
unsigned char model[ATA_ID_PROD_LEN + 1], fw[ATA_ID_FW_REV_LEN + 1];
|
|
||||||
|
|
||||||
if (!libata_fua)
|
|
||||||
return 0;
|
|
||||||
if (!ata_id_has_fua(id))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ata_id_c_string(id, model, ATA_ID_PROD, sizeof(model));
|
|
||||||
ata_id_c_string(id, fw, ATA_ID_FW_REV, sizeof(fw));
|
|
||||||
|
|
||||||
if (strcmp(model, "Maxtor"))
|
|
||||||
return 1;
|
|
||||||
if (strcmp(fw, "BANC1G10"))
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0; /* blacklisted */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
|
* ata_scsiop_mode_sense - Simulate MODE SENSE 6, 10 commands
|
||||||
* @args: device IDENTIFY data / SCSI command of interest.
|
* @args: device IDENTIFY data / SCSI command of interest.
|
||||||
@ -2287,7 +2263,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
|
|||||||
};
|
};
|
||||||
u8 pg, spg;
|
u8 pg, spg;
|
||||||
unsigned int ebd, page_control, six_byte;
|
unsigned int ebd, page_control, six_byte;
|
||||||
u8 dpofua, bp = 0xff;
|
u8 dpofua = 0, bp = 0xff;
|
||||||
u16 fp;
|
u16 fp;
|
||||||
|
|
||||||
six_byte = (scsicmd[0] == MODE_SENSE);
|
six_byte = (scsicmd[0] == MODE_SENSE);
|
||||||
@ -2350,9 +2326,7 @@ static unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf)
|
|||||||
goto invalid_fld;
|
goto invalid_fld;
|
||||||
}
|
}
|
||||||
|
|
||||||
dpofua = 0;
|
if (dev->flags & ATA_DFLAG_FUA)
|
||||||
if (ata_dev_supports_fua(args->id) && (dev->flags & ATA_DFLAG_LBA48) &&
|
|
||||||
(!(dev->flags & ATA_DFLAG_PIO) || dev->multi_count))
|
|
||||||
dpofua = 1 << 4;
|
dpofua = 1 << 4;
|
||||||
|
|
||||||
if (six_byte) {
|
if (six_byte) {
|
||||||
@ -3266,11 +3240,12 @@ static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
|
|||||||
u8 supported = 0;
|
u8 supported = 0;
|
||||||
unsigned int err = 0;
|
unsigned int err = 0;
|
||||||
|
|
||||||
if (cdb[2] != 1) {
|
if (cdb[2] != 1 && cdb[2] != 3) {
|
||||||
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
|
ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
|
||||||
err = 2;
|
err = 2;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cdb[3]) {
|
switch (cdb[3]) {
|
||||||
case INQUIRY:
|
case INQUIRY:
|
||||||
case MODE_SENSE:
|
case MODE_SENSE:
|
||||||
|
@ -1377,14 +1377,10 @@ EXPORT_SYMBOL_GPL(ata_sff_qc_issue);
|
|||||||
*
|
*
|
||||||
* LOCKING:
|
* LOCKING:
|
||||||
* spin_lock_irqsave(host lock)
|
* spin_lock_irqsave(host lock)
|
||||||
*
|
|
||||||
* RETURNS:
|
|
||||||
* true indicating that result TF is successfully filled.
|
|
||||||
*/
|
*/
|
||||||
bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc)
|
void ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
qc->ap->ops->sff_tf_read(qc->ap, &qc->result_tf);
|
qc->ap->ops->sff_tf_read(qc->ap, &qc->result_tf);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
|
EXPORT_SYMBOL_GPL(ata_sff_qc_fill_rtf);
|
||||||
|
|
||||||
@ -2073,7 +2069,7 @@ void ata_sff_error_handler(struct ata_port *ap)
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
|
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
|
||||||
if (qc && !(qc->flags & ATA_QCFLAG_FAILED))
|
if (qc && !(qc->flags & ATA_QCFLAG_EH))
|
||||||
qc = NULL;
|
qc = NULL;
|
||||||
|
|
||||||
spin_lock_irqsave(ap->lock, flags);
|
spin_lock_irqsave(ap->lock, flags);
|
||||||
@ -2796,7 +2792,7 @@ void ata_bmdma_error_handler(struct ata_port *ap)
|
|||||||
bool thaw = false;
|
bool thaw = false;
|
||||||
|
|
||||||
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
|
qc = __ata_qc_from_tag(ap, ap->link.active_tag);
|
||||||
if (qc && !(qc->flags & ATA_QCFLAG_FAILED))
|
if (qc && !(qc->flags & ATA_QCFLAG_EH))
|
||||||
qc = NULL;
|
qc = NULL;
|
||||||
|
|
||||||
/* reset PIO HSM and stop DMA engine */
|
/* reset PIO HSM and stop DMA engine */
|
||||||
|
@ -142,7 +142,7 @@ libata_trace_parse_qc_flags(struct trace_seq *p, unsigned int qc_flags)
|
|||||||
trace_seq_printf(p, "QUIET ");
|
trace_seq_printf(p, "QUIET ");
|
||||||
if (qc_flags & ATA_QCFLAG_RETRY)
|
if (qc_flags & ATA_QCFLAG_RETRY)
|
||||||
trace_seq_printf(p, "RETRY ");
|
trace_seq_printf(p, "RETRY ");
|
||||||
if (qc_flags & ATA_QCFLAG_FAILED)
|
if (qc_flags & ATA_QCFLAG_EH)
|
||||||
trace_seq_printf(p, "FAILED ");
|
trace_seq_printf(p, "FAILED ");
|
||||||
if (qc_flags & ATA_QCFLAG_SENSE_VALID)
|
if (qc_flags & ATA_QCFLAG_SENSE_VALID)
|
||||||
trace_seq_printf(p, "SENSE_VALID ");
|
trace_seq_printf(p, "SENSE_VALID ");
|
||||||
|
141
drivers/ata/pata_parport/Kconfig
Normal file
141
drivers/ata/pata_parport/Kconfig
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
comment "Parallel IDE protocol modules"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
|
||||||
|
config PATA_PARPORT_ATEN
|
||||||
|
tristate "ATEN EH-100 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the ATEN EH-100 parallel port IDE
|
||||||
|
protocol. This protocol is used in some inexpensive low performance
|
||||||
|
parallel port kits made in Hong Kong.
|
||||||
|
|
||||||
|
config PATA_PARPORT_BPCK
|
||||||
|
tristate "MicroSolutions backpack (Series 5) protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the Micro Solutions BACKPACK
|
||||||
|
parallel port Series 5 IDE protocol. (Most BACKPACK drives made
|
||||||
|
before 1999 were Series 5) Series 5 drives will NOT always have the
|
||||||
|
Series noted on the bottom of the drive. Series 6 drivers will.
|
||||||
|
|
||||||
|
In other words, if your BACKPACK drive doesn't say "Series 6" on the
|
||||||
|
bottom, enable this option.
|
||||||
|
|
||||||
|
config PATA_PARPORT_BPCK6
|
||||||
|
tristate "MicroSolutions backpack (Series 6) protocol"
|
||||||
|
depends on (PATA_PARPORT) && !64BIT
|
||||||
|
help
|
||||||
|
This option enables support for the Micro Solutions BACKPACK
|
||||||
|
parallel port Series 6 IDE protocol. (Most BACKPACK drives made
|
||||||
|
after 1999 were Series 6) Series 6 drives will have the Series noted
|
||||||
|
on the bottom of the drive. Series 5 drivers don't always have it
|
||||||
|
noted.
|
||||||
|
|
||||||
|
In other words, if your BACKPACK drive says "Series 6" on the
|
||||||
|
bottom, enable this option.
|
||||||
|
|
||||||
|
config PATA_PARPORT_COMM
|
||||||
|
tristate "DataStor Commuter protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the Commuter parallel port IDE
|
||||||
|
protocol from DataStor.
|
||||||
|
|
||||||
|
config PATA_PARPORT_DSTR
|
||||||
|
tristate "DataStor EP-2000 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the EP-2000 parallel port IDE
|
||||||
|
protocol from DataStor
|
||||||
|
|
||||||
|
config PATA_PARPORT_FIT2
|
||||||
|
tristate "FIT TD-2000 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the TD-2000 parallel port IDE
|
||||||
|
protocol from Fidelity International Technology. This is a simple
|
||||||
|
(low speed) adapter that is used in some portable hard drives.
|
||||||
|
|
||||||
|
config PATA_PARPORT_FIT3
|
||||||
|
tristate "FIT TD-3000 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the TD-3000 parallel port IDE
|
||||||
|
protocol from Fidelity International Technology. This protocol is
|
||||||
|
used in newer models of their portable disk, CD-ROM and PD/CD
|
||||||
|
devices.
|
||||||
|
|
||||||
|
config PATA_PARPORT_EPAT
|
||||||
|
tristate "Shuttle EPAT/EPEZ protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the EPAT parallel port IDE protocol.
|
||||||
|
EPAT is a parallel port IDE adapter manufactured by Shuttle
|
||||||
|
Technology and widely used in devices from major vendors such as
|
||||||
|
Hewlett-Packard, SyQuest, Imation and Avatar.
|
||||||
|
|
||||||
|
config PATA_PARPORT_EPATC8
|
||||||
|
bool "Support c7/c8 chips"
|
||||||
|
depends on PATA_PARPORT_EPAT
|
||||||
|
help
|
||||||
|
This option enables support for the newer Shuttle EP1284 (aka c7 and
|
||||||
|
c8) chip. You need this if you are using any recent Imation SuperDisk
|
||||||
|
(LS-120) drive.
|
||||||
|
|
||||||
|
config PATA_PARPORT_EPIA
|
||||||
|
tristate "Shuttle EPIA protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the (obsolete) EPIA parallel port
|
||||||
|
IDE protocol from Shuttle Technology. This adapter can still be
|
||||||
|
found in some no-name kits.
|
||||||
|
|
||||||
|
config PATA_PARPORT_FRIQ
|
||||||
|
tristate "Freecom IQ ASIC-2 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for version 2 of the Freecom IQ parallel
|
||||||
|
port IDE adapter. This adapter is used by the Maxell Superdisk
|
||||||
|
drive.
|
||||||
|
|
||||||
|
config PATA_PARPORT_FRPW
|
||||||
|
tristate "FreeCom power protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the Freecom power parallel port IDE
|
||||||
|
protocol.
|
||||||
|
|
||||||
|
config PATA_PARPORT_KBIC
|
||||||
|
tristate "KingByte KBIC-951A/971A protocols"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the KBIC-951A and KBIC-971A parallel
|
||||||
|
port IDE protocols from KingByte Information Corp. KingByte's
|
||||||
|
adapters appear in many no-name portable disk and CD-ROM products,
|
||||||
|
especially in Europe.
|
||||||
|
|
||||||
|
config PATA_PARPORT_KTTI
|
||||||
|
tristate "KT PHd protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the "PHd" parallel port IDE protocol
|
||||||
|
from KT Technology. This is a simple (low speed) adapter that is
|
||||||
|
used in some 2.5" portable hard drives.
|
||||||
|
|
||||||
|
config PATA_PARPORT_ON20
|
||||||
|
tristate "OnSpec 90c20 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the (obsolete) 90c20 parallel port
|
||||||
|
IDE protocol from OnSpec (often marketed under the ValuStore brand
|
||||||
|
name).
|
||||||
|
|
||||||
|
config PATA_PARPORT_ON26
|
||||||
|
tristate "OnSpec 90c26 protocol"
|
||||||
|
depends on PATA_PARPORT
|
||||||
|
help
|
||||||
|
This option enables support for the 90c26 parallel port IDE protocol
|
||||||
|
from OnSpec Electronics (often marketed under the ValuStore brand
|
||||||
|
name).
|
19
drivers/ata/pata_parport/Makefile
Normal file
19
drivers/ata/pata_parport/Makefile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
obj-$(CONFIG_PATA_PARPORT) += pata_parport.o
|
||||||
|
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_ATEN) += aten.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_BPCK) += bpck.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_COMM) += comm.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_DSTR) += dstr.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_KBIC) += kbic.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_EPAT) += epat.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_EPIA) += epia.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_FRPW) += frpw.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_FRIQ) += friq.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_FIT2) += fit2.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_FIT3) += fit3.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_ON20) += on20.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_ON26) += on26.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_KTTI) += ktti.o
|
||||||
|
obj-$(CONFIG_PATA_PARPORT_BPCK6) += bpck6.o
|
@ -25,7 +25,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
|
#define j44(a,b) ((((a>>4)&0x0f)|(b&0xf0))^0x88)
|
||||||
|
|
@ -24,7 +24,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#undef r2
|
#undef r2
|
||||||
#undef w2
|
#undef w2
|
@ -31,7 +31,7 @@
|
|||||||
#include <linux/parport.h>
|
#include <linux/parport.h>
|
||||||
|
|
||||||
#include "ppc6lnx.c"
|
#include "ppc6lnx.c"
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
/* PARAMETERS */
|
/* PARAMETERS */
|
||||||
static bool verbose; /* set this to 1 to see debugging messages and whatnot */
|
static bool verbose; /* set this to 1 to see debugging messages and whatnot */
|
@ -24,7 +24,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
/* mode codes: 0 nybble reads, 8-bit writes
|
/* mode codes: 0 nybble reads, 8-bit writes
|
||||||
1 8-bit reads and writes
|
1 8-bit reads and writes
|
@ -23,7 +23,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
/* mode codes: 0 nybble reads, 8-bit writes
|
/* mode codes: 0 nybble reads, 8-bit writes
|
||||||
1 8-bit reads and writes
|
1 8-bit reads and writes
|
@ -26,7 +26,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
|
#define j44(a,b) (((a>>4)&0x0f)+(b&0xf0))
|
||||||
#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
|
#define j53(a,b) (((a>>3)&0x1f)+((b<<4)&0xe0))
|
@ -27,7 +27,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
/* mode codes: 0 nybble reads on port 1, 8-bit writes
|
/* mode codes: 0 nybble reads on port 1, 8-bit writes
|
||||||
1 5/3 reads on ports 1 & 2, 8-bit writes
|
1 5/3 reads on ports 1 & 2, 8-bit writes
|
@ -23,7 +23,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||||
|
|
@ -27,7 +27,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
|
#define j44(a,b) (((a>>3)&0x0f)|((b<<1)&0xf0))
|
||||||
|
|
@ -35,7 +35,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
|
#define CMD(x) w2(4);w0(0xff);w0(0xff);w0(0x73);w0(0x73);\
|
||||||
w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
|
w0(0xc9);w0(0xc9);w0(0x26);w0(0x26);w0(x);w0(x);
|
@ -33,7 +33,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
|
#define cec4 w2(0xc);w2(0xe);w2(0xe);w2(0xc);w2(4);w2(4);w2(4);
|
||||||
#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
|
#define j44(l,h) (((l>>4)&0x0f)|(h&0xf0))
|
@ -28,7 +28,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define r12w() (delay_p,inw(pi->port+1)&0xffff)
|
#define r12w() (delay_p,inw(pi->port+1)&0xffff)
|
||||||
|
|
@ -19,7 +19,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
#define j44(a,b) (((a>>4)&0x0f)|(b&0xf0))
|
||||||
|
|
@ -22,7 +22,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
|
#define op(f) w2(4);w0(f);w2(5);w2(0xd);w2(5);w2(0xd);w2(5);w2(4);
|
||||||
#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
|
#define vl(v) w2(4);w0(v);w2(5);w2(7);w2(5);w2(4);
|
@ -26,7 +26,7 @@
|
|||||||
#include <linux/wait.h>
|
#include <linux/wait.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
#include "paride.h"
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
/* mode codes: 0 nybble reads, 8-bit writes
|
/* mode codes: 0 nybble reads, 8-bit writes
|
||||||
1 8-bit reads and writes
|
1 8-bit reads and writes
|
761
drivers/ata/pata_parport/pata_parport.c
Normal file
761
drivers/ata/pata_parport/pata_parport.c
Normal file
@ -0,0 +1,761 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Copyright 2023 Ondrej Zary
|
||||||
|
* based on paride.c by Grant R. Guenther <grant@torque.net>
|
||||||
|
*/
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/parport.h>
|
||||||
|
#include <linux/pata_parport.h>
|
||||||
|
|
||||||
|
#define DRV_NAME "pata_parport"
|
||||||
|
|
||||||
|
static DEFINE_IDR(parport_list);
|
||||||
|
static DEFINE_IDR(protocols);
|
||||||
|
static DEFINE_IDA(pata_parport_bus_dev_ids);
|
||||||
|
static DEFINE_MUTEX(pi_mutex);
|
||||||
|
|
||||||
|
static bool probe = true;
|
||||||
|
module_param(probe, bool, 0644);
|
||||||
|
MODULE_PARM_DESC(probe, "Enable automatic device probing (0=off, 1=on [default])");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* libata drivers cannot sleep so this driver claims parport before activating
|
||||||
|
* the ata host and keeps it claimed (and protocol connected) until the ata
|
||||||
|
* host is removed. Unfortunately, this means that you cannot use any chained
|
||||||
|
* devices (neither other pata_parport devices nor a printer).
|
||||||
|
*/
|
||||||
|
static void pi_connect(struct pi_adapter *pi)
|
||||||
|
{
|
||||||
|
parport_claim_or_block(pi->pardev);
|
||||||
|
pi->proto->connect(pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pi_disconnect(struct pi_adapter *pi)
|
||||||
|
{
|
||||||
|
pi->proto->disconnect(pi);
|
||||||
|
parport_release(pi->pardev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_dev_select(struct ata_port *ap, unsigned int device)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
u8 tmp;
|
||||||
|
|
||||||
|
if (device == 0)
|
||||||
|
tmp = ATA_DEVICE_OBS;
|
||||||
|
else
|
||||||
|
tmp = ATA_DEVICE_OBS | ATA_DEV1;
|
||||||
|
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tmp);
|
||||||
|
ata_sff_pause(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pata_parport_devchk(struct ata_port *ap, unsigned int device)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
u8 nsect, lbal;
|
||||||
|
|
||||||
|
pata_parport_dev_select(ap, device);
|
||||||
|
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0x55);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
|
||||||
|
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 0xaa);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0x55);
|
||||||
|
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_NSECT, 055);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAL, 0xaa);
|
||||||
|
|
||||||
|
nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
|
||||||
|
lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
|
||||||
|
|
||||||
|
return (nsect == 0x55) && (lbal == 0xaa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pata_parport_bus_softreset(struct ata_port *ap, unsigned int devmask,
|
||||||
|
unsigned long deadline)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
/* software reset. causes dev0 to be selected */
|
||||||
|
pi->proto->write_regr(pi, 1, 6, ap->ctl);
|
||||||
|
udelay(20);
|
||||||
|
pi->proto->write_regr(pi, 1, 6, ap->ctl | ATA_SRST);
|
||||||
|
udelay(20);
|
||||||
|
pi->proto->write_regr(pi, 1, 6, ap->ctl);
|
||||||
|
ap->last_ctl = ap->ctl;
|
||||||
|
|
||||||
|
/* wait the port to become ready */
|
||||||
|
return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pata_parport_softreset(struct ata_link *link, unsigned int *classes,
|
||||||
|
unsigned long deadline)
|
||||||
|
{
|
||||||
|
struct ata_port *ap = link->ap;
|
||||||
|
unsigned int devmask = 0;
|
||||||
|
int rc;
|
||||||
|
u8 err;
|
||||||
|
|
||||||
|
/* determine if device 0/1 are present */
|
||||||
|
if (pata_parport_devchk(ap, 0))
|
||||||
|
devmask |= (1 << 0);
|
||||||
|
if (pata_parport_devchk(ap, 1))
|
||||||
|
devmask |= (1 << 1);
|
||||||
|
|
||||||
|
/* select device 0 again */
|
||||||
|
pata_parport_dev_select(ap, 0);
|
||||||
|
|
||||||
|
/* issue bus reset */
|
||||||
|
rc = pata_parport_bus_softreset(ap, devmask, deadline);
|
||||||
|
if (rc && rc != -ENODEV) {
|
||||||
|
ata_link_err(link, "SRST failed (errno=%d)\n", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* determine by signature whether we have ATA or ATAPI devices */
|
||||||
|
classes[0] = ata_sff_dev_classify(&link->device[0],
|
||||||
|
devmask & (1 << 0), &err);
|
||||||
|
if (err != 0x81)
|
||||||
|
classes[1] = ata_sff_dev_classify(&link->device[1],
|
||||||
|
devmask & (1 << 1), &err);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 pata_parport_check_status(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
return pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8 pata_parport_check_altstatus(struct ata_port *ap)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
return pi->proto->read_regr(pi, 1, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_tf_load(struct ata_port *ap,
|
||||||
|
const struct ata_taskfile *tf)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
if (tf->ctl != ap->last_ctl) {
|
||||||
|
pi->proto->write_regr(pi, 1, 6, tf->ctl);
|
||||||
|
ap->last_ctl = tf->ctl;
|
||||||
|
ata_wait_idle(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tf->flags & ATA_TFLAG_ISADDR) {
|
||||||
|
if (tf->flags & ATA_TFLAG_LBA48) {
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_FEATURE,
|
||||||
|
tf->hob_feature);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_NSECT,
|
||||||
|
tf->hob_nsect);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAL,
|
||||||
|
tf->hob_lbal);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAM,
|
||||||
|
tf->hob_lbam);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAH,
|
||||||
|
tf->hob_lbah);
|
||||||
|
}
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_FEATURE, tf->feature);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_NSECT, tf->nsect);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAL, tf->lbal);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAM, tf->lbam);
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_LBAH, tf->lbah);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tf->flags & ATA_TFLAG_DEVICE)
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_DEVICE, tf->device);
|
||||||
|
|
||||||
|
ata_wait_idle(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
tf->status = pi->proto->read_regr(pi, 0, ATA_REG_STATUS);
|
||||||
|
tf->error = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
|
||||||
|
tf->nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
|
||||||
|
tf->lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
|
||||||
|
tf->lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
|
||||||
|
tf->lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
|
||||||
|
tf->device = pi->proto->read_regr(pi, 0, ATA_REG_DEVICE);
|
||||||
|
|
||||||
|
if (tf->flags & ATA_TFLAG_LBA48) {
|
||||||
|
pi->proto->write_regr(pi, 1, 6, tf->ctl | ATA_HOB);
|
||||||
|
tf->hob_feature = pi->proto->read_regr(pi, 0, ATA_REG_ERR);
|
||||||
|
tf->hob_nsect = pi->proto->read_regr(pi, 0, ATA_REG_NSECT);
|
||||||
|
tf->hob_lbal = pi->proto->read_regr(pi, 0, ATA_REG_LBAL);
|
||||||
|
tf->hob_lbam = pi->proto->read_regr(pi, 0, ATA_REG_LBAM);
|
||||||
|
tf->hob_lbah = pi->proto->read_regr(pi, 0, ATA_REG_LBAH);
|
||||||
|
pi->proto->write_regr(pi, 1, 6, tf->ctl);
|
||||||
|
ap->last_ctl = tf->ctl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_exec_command(struct ata_port *ap,
|
||||||
|
const struct ata_taskfile *tf)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
pi->proto->write_regr(pi, 0, ATA_REG_CMD, tf->command);
|
||||||
|
ata_sff_pause(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int pata_parport_data_xfer(struct ata_queued_cmd *qc,
|
||||||
|
unsigned char *buf, unsigned int buflen, int rw)
|
||||||
|
{
|
||||||
|
struct ata_port *ap = qc->dev->link->ap;
|
||||||
|
struct pi_adapter *pi = ap->host->private_data;
|
||||||
|
|
||||||
|
if (rw == READ)
|
||||||
|
pi->proto->read_block(pi, buf, buflen);
|
||||||
|
else
|
||||||
|
pi->proto->write_block(pi, buf, buflen);
|
||||||
|
|
||||||
|
return buflen;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_drain_fifo(struct ata_queued_cmd *qc)
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
struct ata_port *ap;
|
||||||
|
struct pi_adapter *pi;
|
||||||
|
char junk[2];
|
||||||
|
|
||||||
|
/* We only need to flush incoming data when a command was running */
|
||||||
|
if (qc == NULL || qc->dma_dir == DMA_TO_DEVICE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ap = qc->ap;
|
||||||
|
pi = ap->host->private_data;
|
||||||
|
/* Drain up to 64K of data before we give up this recovery method */
|
||||||
|
for (count = 0; (pata_parport_check_status(ap) & ATA_DRQ)
|
||||||
|
&& count < 65536; count += 2) {
|
||||||
|
pi->proto->read_block(pi, junk, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count)
|
||||||
|
ata_port_dbg(ap, "drained %d bytes to clear DRQ\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ata_port_operations pata_parport_port_ops = {
|
||||||
|
.inherits = &ata_sff_port_ops,
|
||||||
|
|
||||||
|
.softreset = pata_parport_softreset,
|
||||||
|
.hardreset = NULL,
|
||||||
|
|
||||||
|
.sff_dev_select = pata_parport_dev_select,
|
||||||
|
.sff_check_status = pata_parport_check_status,
|
||||||
|
.sff_check_altstatus = pata_parport_check_altstatus,
|
||||||
|
.sff_tf_load = pata_parport_tf_load,
|
||||||
|
.sff_tf_read = pata_parport_tf_read,
|
||||||
|
.sff_exec_command = pata_parport_exec_command,
|
||||||
|
.sff_data_xfer = pata_parport_data_xfer,
|
||||||
|
.sff_drain_fifo = pata_parport_drain_fifo,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ata_port_info pata_parport_port_info = {
|
||||||
|
.flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
|
||||||
|
.pio_mask = ATA_PIO0,
|
||||||
|
/* No DMA */
|
||||||
|
.port_ops = &pata_parport_port_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pi_release(struct pi_adapter *pi)
|
||||||
|
{
|
||||||
|
parport_unregister_device(pi->pardev);
|
||||||
|
if (pi->proto->release_proto)
|
||||||
|
pi->proto->release_proto(pi);
|
||||||
|
module_put(pi->proto->owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int default_test_proto(struct pi_adapter *pi, char *scratch)
|
||||||
|
{
|
||||||
|
int j, k;
|
||||||
|
int e[2] = { 0, 0 };
|
||||||
|
|
||||||
|
pi->proto->connect(pi);
|
||||||
|
|
||||||
|
for (j = 0; j < 2; j++) {
|
||||||
|
pi->proto->write_regr(pi, 0, 6, 0xa0 + j * 0x10);
|
||||||
|
for (k = 0; k < 256; k++) {
|
||||||
|
pi->proto->write_regr(pi, 0, 2, k ^ 0xaa);
|
||||||
|
pi->proto->write_regr(pi, 0, 3, k ^ 0x55);
|
||||||
|
if (pi->proto->read_regr(pi, 0, 2) != (k ^ 0xaa))
|
||||||
|
e[j]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pi->proto->disconnect(pi);
|
||||||
|
|
||||||
|
dev_dbg(&pi->dev, "%s: port 0x%x, mode %d, test=(%d,%d)\n",
|
||||||
|
pi->proto->name, pi->port, pi->mode, e[0], e[1]);
|
||||||
|
|
||||||
|
return e[0] && e[1]; /* not here if both > 0 */
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pi_test_proto(struct pi_adapter *pi, char *scratch)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
parport_claim_or_block(pi->pardev);
|
||||||
|
if (pi->proto->test_proto)
|
||||||
|
res = pi->proto->test_proto(pi, scratch, 1);
|
||||||
|
else
|
||||||
|
res = default_test_proto(pi, scratch);
|
||||||
|
parport_release(pi->pardev);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pi_probe_mode(struct pi_adapter *pi, int max, char *scratch)
|
||||||
|
{
|
||||||
|
int best, range;
|
||||||
|
|
||||||
|
if (pi->mode != -1) {
|
||||||
|
if (pi->mode >= max)
|
||||||
|
return false;
|
||||||
|
range = 3;
|
||||||
|
if (pi->mode >= pi->proto->epp_first)
|
||||||
|
range = 8;
|
||||||
|
if (range == 8 && pi->port % 8)
|
||||||
|
return false;
|
||||||
|
return !pi_test_proto(pi, scratch);
|
||||||
|
}
|
||||||
|
best = -1;
|
||||||
|
for (pi->mode = 0; pi->mode < max; pi->mode++) {
|
||||||
|
range = 3;
|
||||||
|
if (pi->mode >= pi->proto->epp_first)
|
||||||
|
range = 8;
|
||||||
|
if (range == 8 && pi->port % 8)
|
||||||
|
break;
|
||||||
|
if (!pi_test_proto(pi, scratch))
|
||||||
|
best = pi->mode;
|
||||||
|
}
|
||||||
|
pi->mode = best;
|
||||||
|
return best > -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pi_probe_unit(struct pi_adapter *pi, int unit, char *scratch)
|
||||||
|
{
|
||||||
|
int max, s, e;
|
||||||
|
|
||||||
|
s = unit;
|
||||||
|
e = s + 1;
|
||||||
|
|
||||||
|
if (s == -1) {
|
||||||
|
s = 0;
|
||||||
|
e = pi->proto->max_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pi->proto->test_port) {
|
||||||
|
parport_claim_or_block(pi->pardev);
|
||||||
|
max = pi->proto->test_port(pi);
|
||||||
|
parport_release(pi->pardev);
|
||||||
|
} else {
|
||||||
|
max = pi->proto->max_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pi->proto->probe_unit) {
|
||||||
|
parport_claim_or_block(pi->pardev);
|
||||||
|
for (pi->unit = s; pi->unit < e; pi->unit++) {
|
||||||
|
if (pi->proto->probe_unit(pi)) {
|
||||||
|
parport_release(pi->pardev);
|
||||||
|
return pi_probe_mode(pi, max, scratch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parport_release(pi->pardev);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pi_probe_mode(pi, max, scratch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_dev_release(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
|
||||||
|
|
||||||
|
kfree(pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_bus_release(struct device *dev)
|
||||||
|
{
|
||||||
|
/* nothing to do here but required to avoid warning on device removal */
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct bus_type pata_parport_bus_type = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device pata_parport_bus = {
|
||||||
|
.init_name = DRV_NAME,
|
||||||
|
.release = pata_parport_bus_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct scsi_host_template pata_parport_sht = {
|
||||||
|
PATA_PARPORT_SHT("pata_parport")
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pi_device_match {
|
||||||
|
struct parport *parport;
|
||||||
|
struct pi_protocol *proto;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int pi_find_dev(struct device *dev, void *data)
|
||||||
|
{
|
||||||
|
struct pi_adapter *pi = container_of(dev, struct pi_adapter, dev);
|
||||||
|
struct pi_device_match *match = data;
|
||||||
|
|
||||||
|
return pi->pardev->port == match->parport && pi->proto == match->proto;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct pi_adapter *pi_init_one(struct parport *parport,
|
||||||
|
struct pi_protocol *pr, int mode, int unit, int delay)
|
||||||
|
{
|
||||||
|
struct pardev_cb par_cb = { };
|
||||||
|
char scratch[512];
|
||||||
|
const struct ata_port_info *ppi[] = { &pata_parport_port_info };
|
||||||
|
struct ata_host *host;
|
||||||
|
struct pi_adapter *pi;
|
||||||
|
struct pi_device_match match = { .parport = parport, .proto = pr };
|
||||||
|
int id;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Abort if there's a device already registered on the same parport
|
||||||
|
* using the same protocol.
|
||||||
|
*/
|
||||||
|
if (bus_for_each_dev(&pata_parport_bus_type, NULL, &match, pi_find_dev))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pi = kzalloc(sizeof(struct pi_adapter), GFP_KERNEL);
|
||||||
|
if (!pi)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* set up pi->dev before pi_probe_unit() so it can use dev_printk() */
|
||||||
|
pi->dev.parent = &pata_parport_bus;
|
||||||
|
pi->dev.bus = &pata_parport_bus_type;
|
||||||
|
pi->dev.driver = &pr->driver;
|
||||||
|
pi->dev.release = pata_parport_dev_release;
|
||||||
|
id = ida_alloc(&pata_parport_bus_dev_ids, GFP_KERNEL);
|
||||||
|
if (id < 0)
|
||||||
|
return NULL; /* pata_parport_dev_release will do kfree(pi) */
|
||||||
|
pi->dev.id = id;
|
||||||
|
dev_set_name(&pi->dev, "pata_parport.%u", pi->dev.id);
|
||||||
|
if (device_register(&pi->dev)) {
|
||||||
|
put_device(&pi->dev);
|
||||||
|
goto out_ida_free;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi->proto = pr;
|
||||||
|
|
||||||
|
if (!try_module_get(pi->proto->owner))
|
||||||
|
goto out_unreg_dev;
|
||||||
|
if (pi->proto->init_proto && pi->proto->init_proto(pi) < 0)
|
||||||
|
goto out_module_put;
|
||||||
|
|
||||||
|
pi->delay = (delay == -1) ? pi->proto->default_delay : delay;
|
||||||
|
pi->mode = mode;
|
||||||
|
pi->port = parport->base;
|
||||||
|
|
||||||
|
par_cb.private = pi;
|
||||||
|
pi->pardev = parport_register_dev_model(parport, DRV_NAME, &par_cb,
|
||||||
|
pi->dev.id);
|
||||||
|
if (!pi->pardev)
|
||||||
|
goto out_module_put;
|
||||||
|
|
||||||
|
if (!pi_probe_unit(pi, unit, scratch)) {
|
||||||
|
dev_info(&pi->dev, "Adapter not found\n");
|
||||||
|
goto out_unreg_parport;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi->proto->log_adapter(pi, scratch, 1);
|
||||||
|
|
||||||
|
host = ata_host_alloc_pinfo(&pi->pardev->dev, ppi, 1);
|
||||||
|
if (!host)
|
||||||
|
goto out_unreg_parport;
|
||||||
|
dev_set_drvdata(&pi->dev, host);
|
||||||
|
host->private_data = pi;
|
||||||
|
|
||||||
|
ata_port_desc(host->ports[0], "port %s", pi->pardev->port->name);
|
||||||
|
ata_port_desc(host->ports[0], "protocol %s", pi->proto->name);
|
||||||
|
|
||||||
|
pi_connect(pi);
|
||||||
|
if (ata_host_activate(host, 0, NULL, 0, &pata_parport_sht))
|
||||||
|
goto out_unreg_parport;
|
||||||
|
|
||||||
|
return pi;
|
||||||
|
|
||||||
|
out_unreg_parport:
|
||||||
|
pi_disconnect(pi);
|
||||||
|
parport_unregister_device(pi->pardev);
|
||||||
|
if (pi->proto->release_proto)
|
||||||
|
pi->proto->release_proto(pi);
|
||||||
|
out_module_put:
|
||||||
|
module_put(pi->proto->owner);
|
||||||
|
out_unreg_dev:
|
||||||
|
device_unregister(&pi->dev);
|
||||||
|
out_ida_free:
|
||||||
|
ida_free(&pata_parport_bus_dev_ids, pi->dev.id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pata_parport_register_driver(struct pi_protocol *pr)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
struct parport *parport;
|
||||||
|
int port_num;
|
||||||
|
|
||||||
|
pr->driver.bus = &pata_parport_bus_type;
|
||||||
|
pr->driver.name = pr->name;
|
||||||
|
error = driver_register(&pr->driver);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
error = idr_alloc(&protocols, pr, 0, 0, GFP_KERNEL);
|
||||||
|
if (error < 0) {
|
||||||
|
driver_unregister(&pr->driver);
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("pata_parport: protocol %s registered\n", pr->name);
|
||||||
|
|
||||||
|
if (probe) {
|
||||||
|
/* probe all parports using this protocol */
|
||||||
|
idr_for_each_entry(&parport_list, parport, port_num)
|
||||||
|
pi_init_one(parport, pr, -1, 0, -1);
|
||||||
|
}
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pata_parport_register_driver);
|
||||||
|
|
||||||
|
void pata_parport_unregister_driver(struct pi_protocol *pr)
|
||||||
|
{
|
||||||
|
struct pi_protocol *pr_iter;
|
||||||
|
int id = -1;
|
||||||
|
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
idr_for_each_entry(&protocols, pr_iter, id) {
|
||||||
|
if (pr_iter == pr)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
idr_remove(&protocols, id);
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
driver_unregister(&pr->driver);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(pata_parport_unregister_driver);
|
||||||
|
|
||||||
|
static ssize_t new_device_store(struct bus_type *bus, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
char port[12] = "auto";
|
||||||
|
char protocol[8] = "auto";
|
||||||
|
int mode = -1, unit = -1, delay = -1;
|
||||||
|
struct pi_protocol *pr, *pr_wanted;
|
||||||
|
struct device_driver *drv;
|
||||||
|
struct parport *parport;
|
||||||
|
int port_num, port_wanted, pr_num;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%11s %7s %d %d %d",
|
||||||
|
port, protocol, &mode, &unit, &delay) < 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (sscanf(port, "parport%u", &port_wanted) < 1) {
|
||||||
|
if (strcmp(port, "auto")) {
|
||||||
|
pr_err("invalid port name %s\n", port);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
port_wanted = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv = driver_find(protocol, &pata_parport_bus_type);
|
||||||
|
if (!drv) {
|
||||||
|
if (strcmp(protocol, "auto")) {
|
||||||
|
pr_err("protocol %s not found\n", protocol);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
pr_wanted = NULL;
|
||||||
|
} else {
|
||||||
|
pr_wanted = container_of(drv, struct pi_protocol, driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
/* walk all parports */
|
||||||
|
idr_for_each_entry(&parport_list, parport, port_num) {
|
||||||
|
if (port_num == port_wanted || port_wanted == -1) {
|
||||||
|
parport = parport_find_number(port_num);
|
||||||
|
if (!parport) {
|
||||||
|
pr_err("no such port %s\n", port);
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
/* walk all protocols */
|
||||||
|
idr_for_each_entry(&protocols, pr, pr_num) {
|
||||||
|
if (pr == pr_wanted || !pr_wanted)
|
||||||
|
if (pi_init_one(parport, pr, mode, unit,
|
||||||
|
delay))
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
parport_put_port(parport);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
if (!ok)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static BUS_ATTR_WO(new_device);
|
||||||
|
|
||||||
|
static void pi_remove_one(struct device *dev)
|
||||||
|
{
|
||||||
|
struct ata_host *host = dev_get_drvdata(dev);
|
||||||
|
struct pi_adapter *pi = host->private_data;
|
||||||
|
|
||||||
|
ata_host_detach(host);
|
||||||
|
pi_disconnect(pi);
|
||||||
|
pi_release(pi);
|
||||||
|
device_unregister(dev);
|
||||||
|
ida_free(&pata_parport_bus_dev_ids, dev->id);
|
||||||
|
/* pata_parport_dev_release will do kfree(pi) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t delete_device_store(struct bus_type *bus, const char *buf,
|
||||||
|
size_t count)
|
||||||
|
{
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
dev = bus_find_device_by_name(bus, NULL, buf);
|
||||||
|
if (!dev) {
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
pi_remove_one(dev);
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
static BUS_ATTR_WO(delete_device);
|
||||||
|
|
||||||
|
static void pata_parport_attach(struct parport *port)
|
||||||
|
{
|
||||||
|
struct pi_protocol *pr;
|
||||||
|
int pr_num, id;
|
||||||
|
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
id = idr_alloc(&parport_list, port, port->number, port->number,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (id < 0) {
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probe) {
|
||||||
|
/* probe this port using all protocols */
|
||||||
|
idr_for_each_entry(&protocols, pr, pr_num)
|
||||||
|
pi_init_one(port, pr, -1, 0, -1);
|
||||||
|
}
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pi_remove_port(struct device *dev, void *p)
|
||||||
|
{
|
||||||
|
struct ata_host *host = dev_get_drvdata(dev);
|
||||||
|
struct pi_adapter *pi = host->private_data;
|
||||||
|
|
||||||
|
if (pi->pardev->port == p)
|
||||||
|
pi_remove_one(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pata_parport_detach(struct parport *port)
|
||||||
|
{
|
||||||
|
mutex_lock(&pi_mutex);
|
||||||
|
bus_for_each_dev(&pata_parport_bus_type, NULL, port, pi_remove_port);
|
||||||
|
idr_remove(&parport_list, port->number);
|
||||||
|
mutex_unlock(&pi_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct parport_driver pata_parport_driver = {
|
||||||
|
.name = DRV_NAME,
|
||||||
|
.match_port = pata_parport_attach,
|
||||||
|
.detach = pata_parport_detach,
|
||||||
|
.devmodel = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static __init int pata_parport_init(void)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
|
||||||
|
error = bus_register(&pata_parport_bus_type);
|
||||||
|
if (error) {
|
||||||
|
pr_err("failed to register pata_parport bus, error: %d\n", error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = device_register(&pata_parport_bus);
|
||||||
|
if (error) {
|
||||||
|
pr_err("failed to register pata_parport bus, error: %d\n", error);
|
||||||
|
goto out_unregister_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = bus_create_file(&pata_parport_bus_type, &bus_attr_new_device);
|
||||||
|
if (error) {
|
||||||
|
pr_err("unable to create sysfs file, error: %d\n", error);
|
||||||
|
goto out_unregister_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = bus_create_file(&pata_parport_bus_type, &bus_attr_delete_device);
|
||||||
|
if (error) {
|
||||||
|
pr_err("unable to create sysfs file, error: %d\n", error);
|
||||||
|
goto out_remove_new;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = parport_register_driver(&pata_parport_driver);
|
||||||
|
if (error) {
|
||||||
|
pr_err("unable to register parport driver, error: %d\n", error);
|
||||||
|
goto out_remove_del;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_remove_del:
|
||||||
|
bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
|
||||||
|
out_remove_new:
|
||||||
|
bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
|
||||||
|
out_unregister_dev:
|
||||||
|
device_unregister(&pata_parport_bus);
|
||||||
|
out_unregister_bus:
|
||||||
|
bus_unregister(&pata_parport_bus_type);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __exit void pata_parport_exit(void)
|
||||||
|
{
|
||||||
|
parport_unregister_driver(&pata_parport_driver);
|
||||||
|
bus_remove_file(&pata_parport_bus_type, &bus_attr_new_device);
|
||||||
|
bus_remove_file(&pata_parport_bus_type, &bus_attr_delete_device);
|
||||||
|
device_unregister(&pata_parport_bus);
|
||||||
|
bus_unregister(&pata_parport_bus_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Ondrej Zary");
|
||||||
|
MODULE_DESCRIPTION("driver for parallel port ATA adapters");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("paride");
|
||||||
|
|
||||||
|
module_init(pata_parport_init);
|
||||||
|
module_exit(pata_parport_exit);
|
@ -566,7 +566,7 @@ static unsigned int sata_fsl_qc_issue(struct ata_queued_cmd *qc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct sata_fsl_port_priv *pp = qc->ap->private_data;
|
struct sata_fsl_port_priv *pp = qc->ap->private_data;
|
||||||
struct sata_fsl_host_priv *host_priv = qc->ap->host->private_data;
|
struct sata_fsl_host_priv *host_priv = qc->ap->host->private_data;
|
||||||
@ -577,7 +577,6 @@ static bool sata_fsl_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
cd = pp->cmdentry + tag;
|
cd = pp->cmdentry + tag;
|
||||||
|
|
||||||
ata_tf_from_fis(cd->sfis, &qc->result_tf);
|
ata_tf_from_fis(cd->sfis, &qc->result_tf);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sata_fsl_scr_write(struct ata_link *link,
|
static int sata_fsl_scr_write(struct ata_link *link,
|
||||||
@ -1042,7 +1041,7 @@ static void sata_fsl_error_handler(struct ata_port *ap)
|
|||||||
|
|
||||||
static void sata_fsl_post_internal_cmd(struct ata_queued_cmd *qc)
|
static void sata_fsl_post_internal_cmd(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
qc->err_mask |= AC_ERR_OTHER;
|
qc->err_mask |= AC_ERR_OTHER;
|
||||||
|
|
||||||
if (qc->err_mask) {
|
if (qc->err_mask) {
|
||||||
|
@ -566,7 +566,7 @@ static void inic_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
|
|||||||
tf->status = readb(port_base + PORT_TF_COMMAND);
|
tf->status = readb(port_base + PORT_TF_COMMAND);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void inic_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct ata_taskfile *rtf = &qc->result_tf;
|
struct ata_taskfile *rtf = &qc->result_tf;
|
||||||
struct ata_taskfile tf;
|
struct ata_taskfile tf;
|
||||||
@ -580,12 +580,10 @@ static bool inic_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
*/
|
*/
|
||||||
inic_tf_read(qc->ap, &tf);
|
inic_tf_read(qc->ap, &tf);
|
||||||
|
|
||||||
if (!(tf.status & ATA_ERR))
|
if (tf.status & ATA_ERR) {
|
||||||
return false;
|
|
||||||
|
|
||||||
rtf->status = tf.status;
|
rtf->status = tf.status;
|
||||||
rtf->error = tf.error;
|
rtf->error = tf.error;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void inic_freeze(struct ata_port *ap)
|
static void inic_freeze(struct ata_port *ap)
|
||||||
@ -672,7 +670,7 @@ static void inic_error_handler(struct ata_port *ap)
|
|||||||
static void inic_post_internal_cmd(struct ata_queued_cmd *qc)
|
static void inic_post_internal_cmd(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
/* make DMA engine forget about the failed command */
|
/* make DMA engine forget about the failed command */
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
inic_reset_port(inic_port_base(qc->ap));
|
inic_reset_port(inic_port_base(qc->ap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,7 +828,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
|
|||||||
struct ata_port *ap = qc->ap;
|
struct ata_port *ap = qc->ap;
|
||||||
|
|
||||||
/* make DMA engine forget about the failed command */
|
/* make DMA engine forget about the failed command */
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
pdc_reset_port(ap);
|
pdc_reset_port(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +328,7 @@ static int sil24_scr_write(struct ata_link *link, unsigned sc_reg, u32 val);
|
|||||||
static int sil24_qc_defer(struct ata_queued_cmd *qc);
|
static int sil24_qc_defer(struct ata_queued_cmd *qc);
|
||||||
static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc);
|
static enum ata_completion_errors sil24_qc_prep(struct ata_queued_cmd *qc);
|
||||||
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
|
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc);
|
||||||
static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc);
|
static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc);
|
||||||
static void sil24_pmp_attach(struct ata_port *ap);
|
static void sil24_pmp_attach(struct ata_port *ap);
|
||||||
static void sil24_pmp_detach(struct ata_port *ap);
|
static void sil24_pmp_detach(struct ata_port *ap);
|
||||||
static void sil24_freeze(struct ata_port *ap);
|
static void sil24_freeze(struct ata_port *ap);
|
||||||
@ -901,10 +901,9 @@ static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sil24_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void sil24_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
sil24_read_tf(qc->ap, qc->hw_tag, &qc->result_tf);
|
sil24_read_tf(qc->ap, qc->hw_tag, &qc->result_tf);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sil24_pmp_attach(struct ata_port *ap)
|
static void sil24_pmp_attach(struct ata_port *ap)
|
||||||
@ -1185,7 +1184,7 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
|
|||||||
struct ata_port *ap = qc->ap;
|
struct ata_port *ap = qc->ap;
|
||||||
|
|
||||||
/* make DMA engine forget about the failed command */
|
/* make DMA engine forget about the failed command */
|
||||||
if ((qc->flags & ATA_QCFLAG_FAILED) && sil24_init_port(ap))
|
if ((qc->flags & ATA_QCFLAG_EH) && sil24_init_port(ap))
|
||||||
ata_eh_freeze_port(ap);
|
ata_eh_freeze_port(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,7 +866,7 @@ static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
|
|||||||
struct ata_port *ap = qc->ap;
|
struct ata_port *ap = qc->ap;
|
||||||
|
|
||||||
/* make DMA engine forget about the failed command */
|
/* make DMA engine forget about the failed command */
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
pdc_reset_port(ap);
|
pdc_reset_port(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,35 +103,6 @@ config GDROM
|
|||||||
Most users will want to say "Y" here.
|
Most users will want to say "Y" here.
|
||||||
You can also build this as a module which will be called gdrom.
|
You can also build this as a module which will be called gdrom.
|
||||||
|
|
||||||
config PARIDE
|
|
||||||
tristate "Parallel port IDE device support"
|
|
||||||
depends on PARPORT_PC
|
|
||||||
help
|
|
||||||
There are many external CD-ROM and disk devices that connect through
|
|
||||||
your computer's parallel port. Most of them are actually IDE devices
|
|
||||||
using a parallel port IDE adapter. This option enables the PARIDE
|
|
||||||
subsystem which contains drivers for many of these external drives.
|
|
||||||
Read <file:Documentation/admin-guide/blockdev/paride.rst> for more information.
|
|
||||||
|
|
||||||
If you have said Y to the "Parallel-port support" configuration
|
|
||||||
option, you may share a single port between your printer and other
|
|
||||||
parallel port devices. Answer Y to build PARIDE support into your
|
|
||||||
kernel, or M if you would like to build it as a loadable module. If
|
|
||||||
your parallel port support is in a loadable module, you must build
|
|
||||||
PARIDE as a module. If you built PARIDE support into your kernel,
|
|
||||||
you may still build the individual protocol modules and high-level
|
|
||||||
drivers as loadable modules. If you build this support as a module,
|
|
||||||
it will be called paride.
|
|
||||||
|
|
||||||
To use the PARIDE support, you must say Y or M here and also to at
|
|
||||||
least one high-level driver (e.g. "Parallel port IDE disks",
|
|
||||||
"Parallel port ATAPI CD-ROMs", "Parallel port ATAPI disks" etc.) and
|
|
||||||
to at least one protocol driver (e.g. "ATEN EH-100 protocol",
|
|
||||||
"MicroSolutions backpack protocol", "DataStor Commuter protocol"
|
|
||||||
etc.).
|
|
||||||
|
|
||||||
source "drivers/block/paride/Kconfig"
|
|
||||||
|
|
||||||
source "drivers/block/mtip32xx/Kconfig"
|
source "drivers/block/mtip32xx/Kconfig"
|
||||||
|
|
||||||
source "drivers/block/zram/Kconfig"
|
source "drivers/block/zram/Kconfig"
|
||||||
|
@ -1,302 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
#
|
|
||||||
# PARIDE configuration
|
|
||||||
#
|
|
||||||
# PARIDE doesn't need PARPORT, but if PARPORT is configured as a module,
|
|
||||||
# PARIDE must also be a module.
|
|
||||||
# PARIDE only supports PC style parports. Tough for USB or other parports...
|
|
||||||
|
|
||||||
comment "Parallel IDE high-level drivers"
|
|
||||||
depends on PARIDE
|
|
||||||
|
|
||||||
config PARIDE_PD
|
|
||||||
tristate "Parallel port IDE disks"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables the high-level driver for IDE-type disk devices
|
|
||||||
connected through a parallel port. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
parallel port IDE driver, otherwise you should answer M to build
|
|
||||||
it as a loadable module. The module will be called pd. You
|
|
||||||
must also have at least one parallel port protocol driver in your
|
|
||||||
system. Among the devices supported by this driver are the SyQuest
|
|
||||||
EZ-135, EZ-230 and SparQ drives, the Avatar Shark and the backpack
|
|
||||||
hard drives from MicroSolutions.
|
|
||||||
|
|
||||||
config PARIDE_PCD
|
|
||||||
tristate "Parallel port ATAPI CD-ROMs"
|
|
||||||
depends on PARIDE
|
|
||||||
select CDROM
|
|
||||||
help
|
|
||||||
This option enables the high-level driver for ATAPI CD-ROM devices
|
|
||||||
connected through a parallel port. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
parallel port ATAPI CD-ROM driver, otherwise you should answer M to
|
|
||||||
build it as a loadable module. The module will be called pcd. You
|
|
||||||
must also have at least one parallel port protocol driver in your
|
|
||||||
system. Among the devices supported by this driver are the
|
|
||||||
MicroSolutions backpack CD-ROM drives and the Freecom Power CD. If
|
|
||||||
you have such a CD-ROM drive, you should also say Y or M to "ISO
|
|
||||||
9660 CD-ROM file system support" below, because that's the file
|
|
||||||
system used on CD-ROMs.
|
|
||||||
|
|
||||||
config PARIDE_PF
|
|
||||||
tristate "Parallel port ATAPI disks"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables the high-level driver for ATAPI disk devices
|
|
||||||
connected through a parallel port. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
parallel port ATAPI disk driver, otherwise you should answer M
|
|
||||||
to build it as a loadable module. The module will be called pf.
|
|
||||||
You must also have at least one parallel port protocol driver in
|
|
||||||
your system. Among the devices supported by this driver are the
|
|
||||||
MicroSolutions backpack PD/CD drive and the Imation Superdisk
|
|
||||||
LS-120 drive.
|
|
||||||
|
|
||||||
config PARIDE_PT
|
|
||||||
tristate "Parallel port ATAPI tapes"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables the high-level driver for ATAPI tape devices
|
|
||||||
connected through a parallel port. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
parallel port ATAPI disk driver, otherwise you should answer M
|
|
||||||
to build it as a loadable module. The module will be called pt.
|
|
||||||
You must also have at least one parallel port protocol driver in
|
|
||||||
your system. Among the devices supported by this driver is the
|
|
||||||
parallel port version of the HP 5GB drive.
|
|
||||||
|
|
||||||
config PARIDE_PG
|
|
||||||
tristate "Parallel port generic ATAPI devices"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables a special high-level driver for generic ATAPI
|
|
||||||
devices connected through a parallel port. The driver allows user
|
|
||||||
programs, such as cdrtools, to send ATAPI commands directly to a
|
|
||||||
device.
|
|
||||||
|
|
||||||
If you chose to build PARIDE support into your kernel, you may
|
|
||||||
answer Y here to build in the parallel port generic ATAPI driver,
|
|
||||||
otherwise you should answer M to build it as a loadable module. The
|
|
||||||
module will be called pg.
|
|
||||||
|
|
||||||
You must also have at least one parallel port protocol driver in
|
|
||||||
your system.
|
|
||||||
|
|
||||||
This driver implements an API loosely related to the generic SCSI
|
|
||||||
driver. See <file:include/linux/pg.h>. for details.
|
|
||||||
|
|
||||||
You can obtain the most recent version of cdrtools from
|
|
||||||
<ftp://ftp.berlios.de/pub/cdrecord/>. Versions 1.6.1a3 and
|
|
||||||
later fully support this driver.
|
|
||||||
|
|
||||||
comment "Parallel IDE protocol modules"
|
|
||||||
depends on PARIDE
|
|
||||||
|
|
||||||
config PARIDE_ATEN
|
|
||||||
tristate "ATEN EH-100 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the ATEN EH-100 parallel port IDE
|
|
||||||
protocol. This protocol is used in some inexpensive low performance
|
|
||||||
parallel port kits made in Hong Kong. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
protocol driver, otherwise you should answer M to build it as a
|
|
||||||
loadable module. The module will be called aten. You must also
|
|
||||||
have a high-level driver for the type of device that you want to
|
|
||||||
support.
|
|
||||||
|
|
||||||
config PARIDE_BPCK
|
|
||||||
tristate "MicroSolutions backpack (Series 5) protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the Micro Solutions BACKPACK
|
|
||||||
parallel port Series 5 IDE protocol. (Most BACKPACK drives made
|
|
||||||
before 1999 were Series 5) Series 5 drives will NOT always have the
|
|
||||||
Series noted on the bottom of the drive. Series 6 drivers will.
|
|
||||||
|
|
||||||
In other words, if your BACKPACK drive doesn't say "Series 6" on the
|
|
||||||
bottom, enable this option.
|
|
||||||
|
|
||||||
If you chose to build PARIDE support into your kernel, you may
|
|
||||||
answer Y here to build in the protocol driver, otherwise you should
|
|
||||||
answer M to build it as a loadable module. The module will be
|
|
||||||
called bpck. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_BPCK6
|
|
||||||
tristate "MicroSolutions backpack (Series 6) protocol"
|
|
||||||
depends on PARIDE && !64BIT
|
|
||||||
help
|
|
||||||
This option enables support for the Micro Solutions BACKPACK
|
|
||||||
parallel port Series 6 IDE protocol. (Most BACKPACK drives made
|
|
||||||
after 1999 were Series 6) Series 6 drives will have the Series noted
|
|
||||||
on the bottom of the drive. Series 5 drivers don't always have it
|
|
||||||
noted.
|
|
||||||
|
|
||||||
In other words, if your BACKPACK drive says "Series 6" on the
|
|
||||||
bottom, enable this option.
|
|
||||||
|
|
||||||
If you chose to build PARIDE support into your kernel, you may
|
|
||||||
answer Y here to build in the protocol driver, otherwise you should
|
|
||||||
answer M to build it as a loadable module. The module will be
|
|
||||||
called bpck6. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_COMM
|
|
||||||
tristate "DataStor Commuter protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the Commuter parallel port IDE
|
|
||||||
protocol from DataStor. If you chose to build PARIDE support
|
|
||||||
into your kernel, you may answer Y here to build in the protocol
|
|
||||||
driver, otherwise you should answer M to build it as a loadable
|
|
||||||
module. The module will be called comm. You must also have
|
|
||||||
a high-level driver for the type of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_DSTR
|
|
||||||
tristate "DataStor EP-2000 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the EP-2000 parallel port IDE
|
|
||||||
protocol from DataStor. If you chose to build PARIDE support
|
|
||||||
into your kernel, you may answer Y here to build in the protocol
|
|
||||||
driver, otherwise you should answer M to build it as a loadable
|
|
||||||
module. The module will be called dstr. You must also have
|
|
||||||
a high-level driver for the type of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_FIT2
|
|
||||||
tristate "FIT TD-2000 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the TD-2000 parallel port IDE
|
|
||||||
protocol from Fidelity International Technology. This is a simple
|
|
||||||
(low speed) adapter that is used in some portable hard drives. If
|
|
||||||
you chose to build PARIDE support into your kernel, you may answer Y
|
|
||||||
here to build in the protocol driver, otherwise you should answer M
|
|
||||||
to build it as a loadable module. The module will be called ktti.
|
|
||||||
You must also have a high-level driver for the type of device that
|
|
||||||
you want to support.
|
|
||||||
|
|
||||||
config PARIDE_FIT3
|
|
||||||
tristate "FIT TD-3000 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the TD-3000 parallel port IDE
|
|
||||||
protocol from Fidelity International Technology. This protocol is
|
|
||||||
used in newer models of their portable disk, CD-ROM and PD/CD
|
|
||||||
devices. If you chose to build PARIDE support into your kernel, you
|
|
||||||
may answer Y here to build in the protocol driver, otherwise you
|
|
||||||
should answer M to build it as a loadable module. The module will be
|
|
||||||
called fit3. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_EPAT
|
|
||||||
tristate "Shuttle EPAT/EPEZ protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the EPAT parallel port IDE protocol.
|
|
||||||
EPAT is a parallel port IDE adapter manufactured by Shuttle
|
|
||||||
Technology and widely used in devices from major vendors such as
|
|
||||||
Hewlett-Packard, SyQuest, Imation and Avatar. If you chose to build
|
|
||||||
PARIDE support into your kernel, you may answer Y here to build in
|
|
||||||
the protocol driver, otherwise you should answer M to build it as a
|
|
||||||
loadable module. The module will be called epat. You must also
|
|
||||||
have a high-level driver for the type of device that you want to
|
|
||||||
support.
|
|
||||||
|
|
||||||
config PARIDE_EPATC8
|
|
||||||
bool "Support c7/c8 chips"
|
|
||||||
depends on PARIDE_EPAT
|
|
||||||
help
|
|
||||||
This option enables support for the newer Shuttle EP1284 (aka c7 and
|
|
||||||
c8) chip. You need this if you are using any recent Imation SuperDisk
|
|
||||||
(LS-120) drive.
|
|
||||||
|
|
||||||
config PARIDE_EPIA
|
|
||||||
tristate "Shuttle EPIA protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the (obsolete) EPIA parallel port
|
|
||||||
IDE protocol from Shuttle Technology. This adapter can still be
|
|
||||||
found in some no-name kits. If you chose to build PARIDE support
|
|
||||||
into your kernel, you may answer Y here to build in the protocol
|
|
||||||
driver, otherwise you should answer M to build it as a loadable
|
|
||||||
module. The module will be called epia. You must also have a
|
|
||||||
high-level driver for the type of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_FRIQ
|
|
||||||
tristate "Freecom IQ ASIC-2 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for version 2 of the Freecom IQ parallel
|
|
||||||
port IDE adapter. This adapter is used by the Maxell Superdisk
|
|
||||||
drive. If you chose to build PARIDE support into your kernel, you
|
|
||||||
may answer Y here to build in the protocol driver, otherwise you
|
|
||||||
should answer M to build it as a loadable module. The module will be
|
|
||||||
called friq. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_FRPW
|
|
||||||
tristate "FreeCom power protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the Freecom power parallel port IDE
|
|
||||||
protocol. If you chose to build PARIDE support into your kernel, you
|
|
||||||
may answer Y here to build in the protocol driver, otherwise you
|
|
||||||
should answer M to build it as a loadable module. The module will be
|
|
||||||
called frpw. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_KBIC
|
|
||||||
tristate "KingByte KBIC-951A/971A protocols"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the KBIC-951A and KBIC-971A parallel
|
|
||||||
port IDE protocols from KingByte Information Corp. KingByte's
|
|
||||||
adapters appear in many no-name portable disk and CD-ROM products,
|
|
||||||
especially in Europe. If you chose to build PARIDE support into your
|
|
||||||
kernel, you may answer Y here to build in the protocol driver,
|
|
||||||
otherwise you should answer M to build it as a loadable module. The
|
|
||||||
module will be called kbic. You must also have a high-level driver
|
|
||||||
for the type of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_KTTI
|
|
||||||
tristate "KT PHd protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the "PHd" parallel port IDE protocol
|
|
||||||
from KT Technology. This is a simple (low speed) adapter that is
|
|
||||||
used in some 2.5" portable hard drives. If you chose to build PARIDE
|
|
||||||
support into your kernel, you may answer Y here to build in the
|
|
||||||
protocol driver, otherwise you should answer M to build it as a
|
|
||||||
loadable module. The module will be called ktti. You must also
|
|
||||||
have a high-level driver for the type of device that you want to
|
|
||||||
support.
|
|
||||||
|
|
||||||
config PARIDE_ON20
|
|
||||||
tristate "OnSpec 90c20 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the (obsolete) 90c20 parallel port
|
|
||||||
IDE protocol from OnSpec (often marketed under the ValuStore brand
|
|
||||||
name). If you chose to build PARIDE support into your kernel, you
|
|
||||||
may answer Y here to build in the protocol driver, otherwise you
|
|
||||||
should answer M to build it as a loadable module. The module will
|
|
||||||
be called on20. You must also have a high-level driver for the
|
|
||||||
type of device that you want to support.
|
|
||||||
|
|
||||||
config PARIDE_ON26
|
|
||||||
tristate "OnSpec 90c26 protocol"
|
|
||||||
depends on PARIDE
|
|
||||||
help
|
|
||||||
This option enables support for the 90c26 parallel port IDE protocol
|
|
||||||
from OnSpec Electronics (often marketed under the ValuStore brand
|
|
||||||
name). If you chose to build PARIDE support into your kernel, you
|
|
||||||
may answer Y here to build in the protocol driver, otherwise you
|
|
||||||
should answer M to build it as a loadable module. The module will be
|
|
||||||
called on26. You must also have a high-level driver for the type
|
|
||||||
of device that you want to support.
|
|
||||||
|
|
||||||
#
|
|
@ -1,29 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
#
|
|
||||||
# Makefile for Parallel port IDE device drivers.
|
|
||||||
#
|
|
||||||
# 7 October 2000, Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
|
|
||||||
# Rewritten to use lists instead of if-statements.
|
|
||||||
#
|
|
||||||
|
|
||||||
obj-$(CONFIG_PARIDE) += paride.o
|
|
||||||
obj-$(CONFIG_PARIDE_ATEN) += aten.o
|
|
||||||
obj-$(CONFIG_PARIDE_BPCK) += bpck.o
|
|
||||||
obj-$(CONFIG_PARIDE_COMM) += comm.o
|
|
||||||
obj-$(CONFIG_PARIDE_DSTR) += dstr.o
|
|
||||||
obj-$(CONFIG_PARIDE_KBIC) += kbic.o
|
|
||||||
obj-$(CONFIG_PARIDE_EPAT) += epat.o
|
|
||||||
obj-$(CONFIG_PARIDE_EPIA) += epia.o
|
|
||||||
obj-$(CONFIG_PARIDE_FRPW) += frpw.o
|
|
||||||
obj-$(CONFIG_PARIDE_FRIQ) += friq.o
|
|
||||||
obj-$(CONFIG_PARIDE_FIT2) += fit2.o
|
|
||||||
obj-$(CONFIG_PARIDE_FIT3) += fit3.o
|
|
||||||
obj-$(CONFIG_PARIDE_ON20) += on20.o
|
|
||||||
obj-$(CONFIG_PARIDE_ON26) += on26.o
|
|
||||||
obj-$(CONFIG_PARIDE_KTTI) += ktti.o
|
|
||||||
obj-$(CONFIG_PARIDE_BPCK6) += bpck6.o
|
|
||||||
obj-$(CONFIG_PARIDE_PD) += pd.o
|
|
||||||
obj-$(CONFIG_PARIDE_PCD) += pcd.o
|
|
||||||
obj-$(CONFIG_PARIDE_PF) += pf.o
|
|
||||||
obj-$(CONFIG_PARIDE_PT) += pt.o
|
|
||||||
obj-$(CONFIG_PARIDE_PG) += pg.o
|
|
@ -1,128 +0,0 @@
|
|||||||
Lemma 1:
|
|
||||||
If ps_tq is scheduled, ps_tq_active is 1. ps_tq_int() can be called
|
|
||||||
only when ps_tq_active is 1.
|
|
||||||
Proof: All assignments to ps_tq_active and all scheduling of ps_tq happen
|
|
||||||
under ps_spinlock. There are three places where that can happen:
|
|
||||||
one in ps_set_intr() (A) and two in ps_tq_int() (B and C).
|
|
||||||
Consider the sequnce of these events. A can not be preceded by
|
|
||||||
anything except B, since it is under if (!ps_tq_active) under
|
|
||||||
ps_spinlock. C is always preceded by B, since we can't reach it
|
|
||||||
other than through B and we don't drop ps_spinlock between them.
|
|
||||||
IOW, the sequence is A?(BA|BC|B)*. OTOH, number of B can not exceed
|
|
||||||
the sum of numbers of A and C, since each call of ps_tq_int() is
|
|
||||||
the result of ps_tq execution. Therefore, the sequence starts with
|
|
||||||
A and each B is preceded by either A or C. Moments when we enter
|
|
||||||
ps_tq_int() are sandwiched between {A,C} and B in that sequence,
|
|
||||||
since at any time number of B can not exceed the number of these
|
|
||||||
moments which, in turn, can not exceed the number of A and C.
|
|
||||||
In other words, the sequence of events is (A or C set ps_tq_active to
|
|
||||||
1 and schedule ps_tq, ps_tq is executed, ps_tq_int() is entered,
|
|
||||||
B resets ps_tq_active)*.
|
|
||||||
|
|
||||||
|
|
||||||
consider the following area:
|
|
||||||
* in do_pd_request1(): to calls of pi_do_claimed() and return in
|
|
||||||
case when pd_req is NULL.
|
|
||||||
* in next_request(): to call of do_pd_request1()
|
|
||||||
* in do_pd_read(): to call of ps_set_intr()
|
|
||||||
* in do_pd_read_start(): to calls of pi_do_claimed(), next_request()
|
|
||||||
and ps_set_intr()
|
|
||||||
* in do_pd_read_drq(): to calls of pi_do_claimed() and next_request()
|
|
||||||
* in do_pd_write(): to call of ps_set_intr()
|
|
||||||
* in do_pd_write_start(): to calls of pi_do_claimed(), next_request()
|
|
||||||
and ps_set_intr()
|
|
||||||
* in do_pd_write_done(): to calls of pi_do_claimed() and next_request()
|
|
||||||
* in ps_set_intr(): to check for ps_tq_active and to scheduling
|
|
||||||
ps_tq if ps_tq_active was 0.
|
|
||||||
* in ps_tq_int(): from the moment when we get ps_spinlock() to the
|
|
||||||
return, call of con() or scheduling ps_tq.
|
|
||||||
* in pi_schedule_claimed() when called from pi_do_claimed() called from
|
|
||||||
pd.c, everything until returning 1 or setting or setting ->claim_cont
|
|
||||||
on the path that returns 0
|
|
||||||
* in pi_do_claimed() when called from pd.c, everything until the call
|
|
||||||
of pi_do_claimed() plus the everything until the call of cont() if
|
|
||||||
pi_do_claimed() has returned 1.
|
|
||||||
* in pi_wake_up() called for PIA that belongs to pd.c, everything from
|
|
||||||
the moment when pi_spinlock has been acquired.
|
|
||||||
|
|
||||||
Lemma 2:
|
|
||||||
1) at any time at most one thread of execution can be in that area or
|
|
||||||
be preempted there.
|
|
||||||
2) When there is such a thread, pd_busy is set or pd_lock is held by
|
|
||||||
that thread.
|
|
||||||
3) When there is such a thread, ps_tq_active is 0 or ps_spinlock is
|
|
||||||
held by that thread.
|
|
||||||
4) When there is such a thread, all PIA belonging to pd.c have NULL
|
|
||||||
->claim_cont or pi_spinlock is held by thread in question.
|
|
||||||
|
|
||||||
Proof: consider the first moment when the above is not true.
|
|
||||||
|
|
||||||
(1) can become not true if some thread enters that area while another is there.
|
|
||||||
a) do_pd_request1() can be called from next_request() or do_pd_request()
|
|
||||||
In the first case the thread was already in the area. In the second,
|
|
||||||
the thread was holding pd_lock and found pd_busy not set, which would
|
|
||||||
mean that (2) was already not true.
|
|
||||||
b) ps_set_intr() and pi_schedule_claimed() can be called only from the
|
|
||||||
area.
|
|
||||||
c) pi_do_claimed() is called by pd.c only from the area.
|
|
||||||
d) ps_tq_int() can enter the area only when the thread is holding
|
|
||||||
ps_spinlock and ps_tq_active is 1 (due to Lemma 1). It means that
|
|
||||||
(3) was already not true.
|
|
||||||
e) do_pd_{read,write}* could be called only from the area. The only
|
|
||||||
case that needs consideration is call from pi_wake_up() and there
|
|
||||||
we would have to be called for the PIA that got ->claimed_cont
|
|
||||||
from pd.c. That could happen only if pi_do_claimed() had been
|
|
||||||
called from pd.c for that PIA, which happens only for PIA belonging
|
|
||||||
to pd.c.
|
|
||||||
f) pi_wake_up() can enter the area only when the thread is holding
|
|
||||||
pi_spinlock and ->claimed_cont is non-NULL for PIA belonging to
|
|
||||||
pd.c. It means that (4) was already not true.
|
|
||||||
|
|
||||||
(2) can become not true only when pd_lock is released by the thread in question.
|
|
||||||
Indeed, pd_busy is reset only in the area and thread that resets
|
|
||||||
it is holding pd_lock. The only place within the area where we
|
|
||||||
release pd_lock is in pd_next_buf() (called from within the area).
|
|
||||||
But that code does not reset pd_busy, so pd_busy would have to be
|
|
||||||
0 when pd_next_buf() had acquired pd_lock. If it become 0 while
|
|
||||||
we were acquiring the lock, (1) would be already false, since
|
|
||||||
the thread that had reset it would be in the area simulateously.
|
|
||||||
If it was 0 before we tried to acquire pd_lock, (2) would be
|
|
||||||
already false.
|
|
||||||
|
|
||||||
For similar reasons, (3) can become not true only when ps_spinlock is released
|
|
||||||
by the thread in question. However, all such places within the area are right
|
|
||||||
after resetting ps_tq_active to 0.
|
|
||||||
|
|
||||||
(4) is done the same way - all places where we release pi_spinlock within
|
|
||||||
the area are either after resetting ->claimed_cont to NULL while holding
|
|
||||||
pi_spinlock, or after not tocuhing ->claimed_cont since acquiring pi_spinlock
|
|
||||||
also in the area. The only place where ->claimed_cont is made non-NULL is
|
|
||||||
in the area, under pi_spinlock and we do not release it until after leaving
|
|
||||||
the area.
|
|
||||||
|
|
||||||
QED.
|
|
||||||
|
|
||||||
|
|
||||||
Corollary 1: ps_tq_active can be killed. Indeed, the only place where we
|
|
||||||
check its value is in ps_set_intr() and if it had been non-zero at that
|
|
||||||
point, we would have violated either (2.1) (if it was set while ps_set_intr()
|
|
||||||
was acquiring ps_spinlock) or (2.3) (if it was set when we started to
|
|
||||||
acquire ps_spinlock).
|
|
||||||
|
|
||||||
Corollary 2: ps_spinlock can be killed. Indeed, Lemma 1 and Lemma 2 show
|
|
||||||
that the only possible contention is between scheduling ps_tq followed by
|
|
||||||
immediate release of spinlock and beginning of execution of ps_tq on
|
|
||||||
another CPU.
|
|
||||||
|
|
||||||
Corollary 3: assignment to pd_busy in do_pd_read_start() and do_pd_write_start()
|
|
||||||
can be killed. Indeed, we are not holding pd_lock and thus pd_busy is already
|
|
||||||
1 here.
|
|
||||||
|
|
||||||
Corollary 4: in ps_tq_int() uses of con can be replaced with uses of
|
|
||||||
ps_continuation, since the latter is changed only from the area.
|
|
||||||
We don't need to reset it to NULL, since we are guaranteed that there
|
|
||||||
will be a call of ps_set_intr() before we look at ps_continuation again.
|
|
||||||
We can remove the check for ps_continuation being NULL for the same
|
|
||||||
reason - the value is guaranteed to be set by the last ps_set_intr() and
|
|
||||||
we never pass it NULL. Assignements in the beginning of ps_set_intr()
|
|
||||||
can be taken to callers as long as they remain within the area.
|
|
@ -1,31 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
#
|
|
||||||
# mkd -- a script to create the device special files for the PARIDE subsystem
|
|
||||||
#
|
|
||||||
# block devices: pd (45), pcd (46), pf (47)
|
|
||||||
# character devices: pt (96), pg (97)
|
|
||||||
#
|
|
||||||
function mkdev {
|
|
||||||
mknod $1 $2 $3 $4 ; chmod 0660 $1 ; chown root:disk $1
|
|
||||||
}
|
|
||||||
#
|
|
||||||
function pd {
|
|
||||||
D=$( printf \\$( printf "x%03x" $[ $1 + 97 ] ) )
|
|
||||||
mkdev pd$D b 45 $[ $1 * 16 ]
|
|
||||||
for P in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
||||||
do mkdev pd$D$P b 45 $[ $1 * 16 + $P ]
|
|
||||||
done
|
|
||||||
}
|
|
||||||
#
|
|
||||||
cd /dev
|
|
||||||
#
|
|
||||||
for u in 0 1 2 3 ; do pd $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pcd$u b 46 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pf$u b 47 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pt$u c 96 $u ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev npt$u c 96 $[ $u + 128 ] ; done
|
|
||||||
for u in 0 1 2 3 ; do mkdev pg$u c 97 $u ; done
|
|
||||||
#
|
|
||||||
# end of mkd
|
|
||||||
|
|
@ -1,479 +0,0 @@
|
|||||||
/*
|
|
||||||
paride.c (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
|
||||||
Under the terms of the GNU General Public License.
|
|
||||||
|
|
||||||
This is the base module for the family of device drivers
|
|
||||||
that support parallel port IDE devices.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Changes:
|
|
||||||
|
|
||||||
1.01 GRG 1998.05.03 Use spinlocks
|
|
||||||
1.02 GRG 1998.05.05 init_proto, release_proto, ktti
|
|
||||||
1.03 GRG 1998.08.15 eliminate compiler warning
|
|
||||||
1.04 GRG 1998.11.28 added support for FRIQ
|
|
||||||
1.05 TMW 2000.06.06 use parport_find_number instead of
|
|
||||||
parport_enumerate
|
|
||||||
1.06 TMW 2001.03.26 more sane parport-or-not resource management
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PI_VERSION "1.06"
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/kmod.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/ioport.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <linux/wait.h>
|
|
||||||
#include <linux/sched.h> /* TASK_* */
|
|
||||||
#include <linux/parport.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
|
|
||||||
#include "paride.h"
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
||||||
#define MAX_PROTOS 32
|
|
||||||
|
|
||||||
static struct pi_protocol *protocols[MAX_PROTOS];
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(pi_spinlock);
|
|
||||||
|
|
||||||
void pi_write_regr(PIA * pi, int cont, int regr, int val)
|
|
||||||
{
|
|
||||||
pi->proto->write_regr(pi, cont, regr, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_write_regr);
|
|
||||||
|
|
||||||
int pi_read_regr(PIA * pi, int cont, int regr)
|
|
||||||
{
|
|
||||||
return pi->proto->read_regr(pi, cont, regr);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_read_regr);
|
|
||||||
|
|
||||||
void pi_write_block(PIA * pi, char *buf, int count)
|
|
||||||
{
|
|
||||||
pi->proto->write_block(pi, buf, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_write_block);
|
|
||||||
|
|
||||||
void pi_read_block(PIA * pi, char *buf, int count)
|
|
||||||
{
|
|
||||||
pi->proto->read_block(pi, buf, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_read_block);
|
|
||||||
|
|
||||||
static void pi_wake_up(void *p)
|
|
||||||
{
|
|
||||||
PIA *pi = (PIA *) p;
|
|
||||||
unsigned long flags;
|
|
||||||
void (*cont) (void) = NULL;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&pi_spinlock, flags);
|
|
||||||
|
|
||||||
if (pi->claim_cont && !parport_claim(pi->pardev)) {
|
|
||||||
cont = pi->claim_cont;
|
|
||||||
pi->claim_cont = NULL;
|
|
||||||
pi->claimed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
|
||||||
|
|
||||||
wake_up(&(pi->parq));
|
|
||||||
|
|
||||||
if (cont)
|
|
||||||
cont();
|
|
||||||
}
|
|
||||||
|
|
||||||
int pi_schedule_claimed(PIA * pi, void (*cont) (void))
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&pi_spinlock, flags);
|
|
||||||
if (pi->pardev && parport_claim(pi->pardev)) {
|
|
||||||
pi->claim_cont = cont;
|
|
||||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pi->claimed = 1;
|
|
||||||
spin_unlock_irqrestore(&pi_spinlock, flags);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(pi_schedule_claimed);
|
|
||||||
|
|
||||||
void pi_do_claimed(PIA * pi, void (*cont) (void))
|
|
||||||
{
|
|
||||||
if (pi_schedule_claimed(pi, cont))
|
|
||||||
cont();
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_do_claimed);
|
|
||||||
|
|
||||||
static void pi_claim(PIA * pi)
|
|
||||||
{
|
|
||||||
if (pi->claimed)
|
|
||||||
return;
|
|
||||||
pi->claimed = 1;
|
|
||||||
if (pi->pardev)
|
|
||||||
wait_event(pi->parq,
|
|
||||||
!parport_claim((struct pardevice *) pi->pardev));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pi_unclaim(PIA * pi)
|
|
||||||
{
|
|
||||||
pi->claimed = 0;
|
|
||||||
if (pi->pardev)
|
|
||||||
parport_release((struct pardevice *) (pi->pardev));
|
|
||||||
}
|
|
||||||
|
|
||||||
void pi_connect(PIA * pi)
|
|
||||||
{
|
|
||||||
pi_claim(pi);
|
|
||||||
pi->proto->connect(pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_connect);
|
|
||||||
|
|
||||||
void pi_disconnect(PIA * pi)
|
|
||||||
{
|
|
||||||
pi->proto->disconnect(pi);
|
|
||||||
pi_unclaim(pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_disconnect);
|
|
||||||
|
|
||||||
static void pi_unregister_parport(PIA * pi)
|
|
||||||
{
|
|
||||||
if (pi->pardev) {
|
|
||||||
parport_unregister_device((struct pardevice *) (pi->pardev));
|
|
||||||
pi->pardev = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pi_release(PIA * pi)
|
|
||||||
{
|
|
||||||
pi_unregister_parport(pi);
|
|
||||||
if (pi->proto->release_proto)
|
|
||||||
pi->proto->release_proto(pi);
|
|
||||||
module_put(pi->proto->owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_release);
|
|
||||||
|
|
||||||
static int default_test_proto(PIA * pi, char *scratch, int verbose)
|
|
||||||
{
|
|
||||||
int j, k;
|
|
||||||
int e[2] = { 0, 0 };
|
|
||||||
|
|
||||||
pi->proto->connect(pi);
|
|
||||||
|
|
||||||
for (j = 0; j < 2; j++) {
|
|
||||||
pi_write_regr(pi, 0, 6, 0xa0 + j * 0x10);
|
|
||||||
for (k = 0; k < 256; k++) {
|
|
||||||
pi_write_regr(pi, 0, 2, k ^ 0xaa);
|
|
||||||
pi_write_regr(pi, 0, 3, k ^ 0x55);
|
|
||||||
if (pi_read_regr(pi, 0, 2) != (k ^ 0xaa))
|
|
||||||
e[j]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pi->proto->disconnect(pi);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
printk("%s: %s: port 0x%x, mode %d, test=(%d,%d)\n",
|
|
||||||
pi->device, pi->proto->name, pi->port,
|
|
||||||
pi->mode, e[0], e[1]);
|
|
||||||
|
|
||||||
return (e[0] && e[1]); /* not here if both > 0 */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pi_test_proto(PIA * pi, char *scratch, int verbose)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
|
|
||||||
pi_claim(pi);
|
|
||||||
if (pi->proto->test_proto)
|
|
||||||
res = pi->proto->test_proto(pi, scratch, verbose);
|
|
||||||
else
|
|
||||||
res = default_test_proto(pi, scratch, verbose);
|
|
||||||
pi_unclaim(pi);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int paride_register(PIP * pr)
|
|
||||||
{
|
|
||||||
int k;
|
|
||||||
|
|
||||||
for (k = 0; k < MAX_PROTOS; k++)
|
|
||||||
if (protocols[k] && !strcmp(pr->name, protocols[k]->name)) {
|
|
||||||
printk("paride: %s protocol already registered\n",
|
|
||||||
pr->name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
k = 0;
|
|
||||||
while ((k < MAX_PROTOS) && (protocols[k]))
|
|
||||||
k++;
|
|
||||||
if (k == MAX_PROTOS) {
|
|
||||||
printk("paride: protocol table full\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
protocols[k] = pr;
|
|
||||||
pr->index = k;
|
|
||||||
printk("paride: %s registered as protocol %d\n", pr->name, k);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(paride_register);
|
|
||||||
|
|
||||||
void paride_unregister(PIP * pr)
|
|
||||||
{
|
|
||||||
if (!pr)
|
|
||||||
return;
|
|
||||||
if (protocols[pr->index] != pr) {
|
|
||||||
printk("paride: %s not registered\n", pr->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
protocols[pr->index] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(paride_unregister);
|
|
||||||
|
|
||||||
static int pi_register_parport(PIA *pi, int verbose, int unit)
|
|
||||||
{
|
|
||||||
struct parport *port;
|
|
||||||
struct pardev_cb par_cb;
|
|
||||||
|
|
||||||
port = parport_find_base(pi->port);
|
|
||||||
if (!port)
|
|
||||||
return 0;
|
|
||||||
memset(&par_cb, 0, sizeof(par_cb));
|
|
||||||
par_cb.wakeup = pi_wake_up;
|
|
||||||
par_cb.private = (void *)pi;
|
|
||||||
pi->pardev = parport_register_dev_model(port, pi->device, &par_cb,
|
|
||||||
unit);
|
|
||||||
parport_put_port(port);
|
|
||||||
if (!pi->pardev)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
init_waitqueue_head(&pi->parq);
|
|
||||||
|
|
||||||
if (verbose)
|
|
||||||
printk("%s: 0x%x is %s\n", pi->device, pi->port, port->name);
|
|
||||||
|
|
||||||
pi->parname = (char *) port->name;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pi_probe_mode(PIA * pi, int max, char *scratch, int verbose)
|
|
||||||
{
|
|
||||||
int best, range;
|
|
||||||
|
|
||||||
if (pi->mode != -1) {
|
|
||||||
if (pi->mode >= max)
|
|
||||||
return 0;
|
|
||||||
range = 3;
|
|
||||||
if (pi->mode >= pi->proto->epp_first)
|
|
||||||
range = 8;
|
|
||||||
if ((range == 8) && (pi->port % 8))
|
|
||||||
return 0;
|
|
||||||
pi->reserved = range;
|
|
||||||
return (!pi_test_proto(pi, scratch, verbose));
|
|
||||||
}
|
|
||||||
best = -1;
|
|
||||||
for (pi->mode = 0; pi->mode < max; pi->mode++) {
|
|
||||||
range = 3;
|
|
||||||
if (pi->mode >= pi->proto->epp_first)
|
|
||||||
range = 8;
|
|
||||||
if ((range == 8) && (pi->port % 8))
|
|
||||||
break;
|
|
||||||
pi->reserved = range;
|
|
||||||
if (!pi_test_proto(pi, scratch, verbose))
|
|
||||||
best = pi->mode;
|
|
||||||
}
|
|
||||||
pi->mode = best;
|
|
||||||
return (best > -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pi_probe_unit(PIA * pi, int unit, char *scratch, int verbose)
|
|
||||||
{
|
|
||||||
int max, s, e;
|
|
||||||
|
|
||||||
s = unit;
|
|
||||||
e = s + 1;
|
|
||||||
|
|
||||||
if (s == -1) {
|
|
||||||
s = 0;
|
|
||||||
e = pi->proto->max_units;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pi_register_parport(pi, verbose, s))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (pi->proto->test_port) {
|
|
||||||
pi_claim(pi);
|
|
||||||
max = pi->proto->test_port(pi);
|
|
||||||
pi_unclaim(pi);
|
|
||||||
} else
|
|
||||||
max = pi->proto->max_mode;
|
|
||||||
|
|
||||||
if (pi->proto->probe_unit) {
|
|
||||||
pi_claim(pi);
|
|
||||||
for (pi->unit = s; pi->unit < e; pi->unit++)
|
|
||||||
if (pi->proto->probe_unit(pi)) {
|
|
||||||
pi_unclaim(pi);
|
|
||||||
if (pi_probe_mode(pi, max, scratch, verbose))
|
|
||||||
return 1;
|
|
||||||
pi_unregister_parport(pi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
pi_unclaim(pi);
|
|
||||||
pi_unregister_parport(pi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pi_probe_mode(pi, max, scratch, verbose)) {
|
|
||||||
pi_unregister_parport(pi);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int pi_init(PIA * pi, int autoprobe, int port, int mode,
|
|
||||||
int unit, int protocol, int delay, char *scratch,
|
|
||||||
int devtype, int verbose, char *device)
|
|
||||||
{
|
|
||||||
int p, k, s, e;
|
|
||||||
int lpts[7] = { 0x3bc, 0x378, 0x278, 0x268, 0x27c, 0x26c, 0 };
|
|
||||||
|
|
||||||
s = protocol;
|
|
||||||
e = s + 1;
|
|
||||||
|
|
||||||
if (!protocols[0])
|
|
||||||
request_module("paride_protocol");
|
|
||||||
|
|
||||||
if (autoprobe) {
|
|
||||||
s = 0;
|
|
||||||
e = MAX_PROTOS;
|
|
||||||
} else if ((s < 0) || (s >= MAX_PROTOS) || (port <= 0) ||
|
|
||||||
(!protocols[s]) || (unit < 0) ||
|
|
||||||
(unit >= protocols[s]->max_units)) {
|
|
||||||
printk("%s: Invalid parameters\n", device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (p = s; p < e; p++) {
|
|
||||||
struct pi_protocol *proto = protocols[p];
|
|
||||||
if (!proto)
|
|
||||||
continue;
|
|
||||||
/* still racy */
|
|
||||||
if (!try_module_get(proto->owner))
|
|
||||||
continue;
|
|
||||||
pi->proto = proto;
|
|
||||||
pi->private = 0;
|
|
||||||
if (proto->init_proto && proto->init_proto(pi) < 0) {
|
|
||||||
pi->proto = NULL;
|
|
||||||
module_put(proto->owner);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (delay == -1)
|
|
||||||
pi->delay = pi->proto->default_delay;
|
|
||||||
else
|
|
||||||
pi->delay = delay;
|
|
||||||
pi->devtype = devtype;
|
|
||||||
pi->device = device;
|
|
||||||
|
|
||||||
pi->parname = NULL;
|
|
||||||
pi->pardev = NULL;
|
|
||||||
init_waitqueue_head(&pi->parq);
|
|
||||||
pi->claimed = 0;
|
|
||||||
pi->claim_cont = NULL;
|
|
||||||
|
|
||||||
pi->mode = mode;
|
|
||||||
if (port != -1) {
|
|
||||||
pi->port = port;
|
|
||||||
if (pi_probe_unit(pi, unit, scratch, verbose))
|
|
||||||
break;
|
|
||||||
pi->port = 0;
|
|
||||||
} else {
|
|
||||||
k = 0;
|
|
||||||
while ((pi->port = lpts[k++]))
|
|
||||||
if (pi_probe_unit
|
|
||||||
(pi, unit, scratch, verbose))
|
|
||||||
break;
|
|
||||||
if (pi->port)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (pi->proto->release_proto)
|
|
||||||
pi->proto->release_proto(pi);
|
|
||||||
module_put(proto->owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pi->port) {
|
|
||||||
if (autoprobe)
|
|
||||||
printk("%s: Autoprobe failed\n", device);
|
|
||||||
else
|
|
||||||
printk("%s: Adapter not found\n", device);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pi->parname)
|
|
||||||
printk("%s: Sharing %s at 0x%x\n", pi->device,
|
|
||||||
pi->parname, pi->port);
|
|
||||||
|
|
||||||
pi->proto->log_adapter(pi, scratch, verbose);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPORT_SYMBOL(pi_init);
|
|
||||||
|
|
||||||
static int pi_probe(struct pardevice *par_dev)
|
|
||||||
{
|
|
||||||
struct device_driver *drv = par_dev->dev.driver;
|
|
||||||
int len = strlen(drv->name);
|
|
||||||
|
|
||||||
if (strncmp(par_dev->name, drv->name, len))
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *pi_register_driver(char *name)
|
|
||||||
{
|
|
||||||
struct parport_driver *parp_drv;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
parp_drv = kzalloc(sizeof(*parp_drv), GFP_KERNEL);
|
|
||||||
if (!parp_drv)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
parp_drv->name = name;
|
|
||||||
parp_drv->probe = pi_probe;
|
|
||||||
parp_drv->devmodel = true;
|
|
||||||
|
|
||||||
ret = parport_register_driver(parp_drv);
|
|
||||||
if (ret) {
|
|
||||||
kfree(parp_drv);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return (void *)parp_drv;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(pi_register_driver);
|
|
||||||
|
|
||||||
void pi_unregister_driver(void *_drv)
|
|
||||||
{
|
|
||||||
struct parport_driver *drv = _drv;
|
|
||||||
|
|
||||||
parport_unregister_driver(drv);
|
|
||||||
kfree(drv);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(pi_unregister_driver);
|
|
@ -1,172 +0,0 @@
|
|||||||
#ifndef __DRIVERS_PARIDE_H__
|
|
||||||
#define __DRIVERS_PARIDE_H__
|
|
||||||
|
|
||||||
/*
|
|
||||||
paride.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
|
||||||
Under the terms of the GPL.
|
|
||||||
|
|
||||||
This file defines the interface between the high-level parallel
|
|
||||||
IDE device drivers (pd, pf, pcd, pt) and the adapter chips.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Changes:
|
|
||||||
|
|
||||||
1.01 GRG 1998.05.05 init_proto, release_proto
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PARIDE_H_VERSION "1.01"
|
|
||||||
|
|
||||||
/* Some adapters need to know what kind of device they are in
|
|
||||||
|
|
||||||
Values for devtype:
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PI_PD 0 /* IDE disk */
|
|
||||||
#define PI_PCD 1 /* ATAPI CDrom */
|
|
||||||
#define PI_PF 2 /* ATAPI disk */
|
|
||||||
#define PI_PT 3 /* ATAPI tape */
|
|
||||||
#define PI_PG 4 /* ATAPI generic */
|
|
||||||
|
|
||||||
/* The paride module contains no state, instead the drivers allocate
|
|
||||||
a pi_adapter data structure and pass it to paride in every operation.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct pi_adapter {
|
|
||||||
|
|
||||||
struct pi_protocol *proto; /* adapter protocol */
|
|
||||||
int port; /* base address of parallel port */
|
|
||||||
int mode; /* transfer mode in use */
|
|
||||||
int delay; /* adapter delay setting */
|
|
||||||
int devtype; /* device type: PI_PD etc. */
|
|
||||||
char *device; /* name of driver */
|
|
||||||
int unit; /* unit number for chained adapters */
|
|
||||||
int saved_r0; /* saved port state */
|
|
||||||
int saved_r2; /* saved port state */
|
|
||||||
int reserved; /* number of ports reserved */
|
|
||||||
unsigned long private; /* for protocol module */
|
|
||||||
|
|
||||||
wait_queue_head_t parq; /* semaphore for parport sharing */
|
|
||||||
void *pardev; /* pointer to pardevice */
|
|
||||||
char *parname; /* parport name */
|
|
||||||
int claimed; /* parport has already been claimed */
|
|
||||||
void (*claim_cont)(void); /* continuation for parport wait */
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct pi_adapter PIA;
|
|
||||||
|
|
||||||
/* functions exported by paride to the high level drivers */
|
|
||||||
|
|
||||||
extern int pi_init(PIA *pi,
|
|
||||||
int autoprobe, /* 1 to autoprobe */
|
|
||||||
int port, /* base port address */
|
|
||||||
int mode, /* -1 for autoprobe */
|
|
||||||
int unit, /* unit number, if supported */
|
|
||||||
int protocol, /* protocol to use */
|
|
||||||
int delay, /* -1 to use adapter specific default */
|
|
||||||
char * scratch, /* address of 512 byte buffer */
|
|
||||||
int devtype, /* device type: PI_PD, PI_PCD, etc ... */
|
|
||||||
int verbose, /* log verbose data while probing */
|
|
||||||
char *device /* name of the driver */
|
|
||||||
); /* returns 0 on failure, 1 on success */
|
|
||||||
|
|
||||||
extern void pi_release(PIA *pi);
|
|
||||||
|
|
||||||
/* registers are addressed as (cont,regr)
|
|
||||||
|
|
||||||
cont: 0 for command register file, 1 for control register(s)
|
|
||||||
regr: 0-7 for register number.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
extern void pi_write_regr(PIA *pi, int cont, int regr, int val);
|
|
||||||
|
|
||||||
extern int pi_read_regr(PIA *pi, int cont, int regr);
|
|
||||||
|
|
||||||
extern void pi_write_block(PIA *pi, char * buf, int count);
|
|
||||||
|
|
||||||
extern void pi_read_block(PIA *pi, char * buf, int count);
|
|
||||||
|
|
||||||
extern void pi_connect(PIA *pi);
|
|
||||||
|
|
||||||
extern void pi_disconnect(PIA *pi);
|
|
||||||
|
|
||||||
extern void pi_do_claimed(PIA *pi, void (*cont)(void));
|
|
||||||
extern int pi_schedule_claimed(PIA *pi, void (*cont)(void));
|
|
||||||
|
|
||||||
/* macros and functions exported to the protocol modules */
|
|
||||||
|
|
||||||
#define delay_p (pi->delay?udelay(pi->delay):(void)0)
|
|
||||||
#define out_p(offs,byte) outb(byte,pi->port+offs); delay_p;
|
|
||||||
#define in_p(offs) (delay_p,inb(pi->port+offs))
|
|
||||||
|
|
||||||
#define w0(byte) {out_p(0,byte);}
|
|
||||||
#define r0() (in_p(0) & 0xff)
|
|
||||||
#define w1(byte) {out_p(1,byte);}
|
|
||||||
#define r1() (in_p(1) & 0xff)
|
|
||||||
#define w2(byte) {out_p(2,byte);}
|
|
||||||
#define r2() (in_p(2) & 0xff)
|
|
||||||
#define w3(byte) {out_p(3,byte);}
|
|
||||||
#define w4(byte) {out_p(4,byte);}
|
|
||||||
#define r4() (in_p(4) & 0xff)
|
|
||||||
#define w4w(data) {outw(data,pi->port+4); delay_p;}
|
|
||||||
#define w4l(data) {outl(data,pi->port+4); delay_p;}
|
|
||||||
#define r4w() (delay_p,inw(pi->port+4)&0xffff)
|
|
||||||
#define r4l() (delay_p,inl(pi->port+4)&0xffffffff)
|
|
||||||
|
|
||||||
static inline u16 pi_swab16( char *b, int k)
|
|
||||||
|
|
||||||
{ union { u16 u; char t[2]; } r;
|
|
||||||
|
|
||||||
r.t[0]=b[2*k+1]; r.t[1]=b[2*k];
|
|
||||||
return r.u;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u32 pi_swab32( char *b, int k)
|
|
||||||
|
|
||||||
{ union { u32 u; char f[4]; } r;
|
|
||||||
|
|
||||||
r.f[0]=b[4*k+1]; r.f[1]=b[4*k];
|
|
||||||
r.f[2]=b[4*k+3]; r.f[3]=b[4*k+2];
|
|
||||||
return r.u;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct pi_protocol {
|
|
||||||
|
|
||||||
char name[8]; /* name for this protocol */
|
|
||||||
int index; /* index into protocol table */
|
|
||||||
|
|
||||||
int max_mode; /* max mode number */
|
|
||||||
int epp_first; /* modes >= this use 8 ports */
|
|
||||||
|
|
||||||
int default_delay; /* delay parameter if not specified */
|
|
||||||
int max_units; /* max chained units probed for */
|
|
||||||
|
|
||||||
void (*write_regr)(PIA *,int,int,int);
|
|
||||||
int (*read_regr)(PIA *,int,int);
|
|
||||||
void (*write_block)(PIA *,char *,int);
|
|
||||||
void (*read_block)(PIA *,char *,int);
|
|
||||||
|
|
||||||
void (*connect)(PIA *);
|
|
||||||
void (*disconnect)(PIA *);
|
|
||||||
|
|
||||||
int (*test_port)(PIA *);
|
|
||||||
int (*probe_unit)(PIA *);
|
|
||||||
int (*test_proto)(PIA *,char *,int);
|
|
||||||
void (*log_adapter)(PIA *,char *,int);
|
|
||||||
|
|
||||||
int (*init_proto)(PIA *);
|
|
||||||
void (*release_proto)(PIA *);
|
|
||||||
struct module *owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct pi_protocol PIP;
|
|
||||||
|
|
||||||
extern int paride_register( PIP * );
|
|
||||||
extern void paride_unregister ( PIP * );
|
|
||||||
void *pi_register_driver(char *);
|
|
||||||
void pi_unregister_driver(void *);
|
|
||||||
|
|
||||||
#endif /* __DRIVERS_PARIDE_H__ */
|
|
||||||
/* end of paride.h */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,734 +0,0 @@
|
|||||||
/*
|
|
||||||
pg.c (c) 1998 Grant R. Guenther <grant@torque.net>
|
|
||||||
Under the terms of the GNU General Public License.
|
|
||||||
|
|
||||||
The pg driver provides a simple character device interface for
|
|
||||||
sending ATAPI commands to a device. With the exception of the
|
|
||||||
ATAPI reset operation, all operations are performed by a pair
|
|
||||||
of read and write operations to the appropriate /dev/pgN device.
|
|
||||||
A write operation delivers a command and any outbound data in
|
|
||||||
a single buffer. Normally, the write will succeed unless the
|
|
||||||
device is offline or malfunctioning, or there is already another
|
|
||||||
command pending. If the write succeeds, it should be followed
|
|
||||||
immediately by a read operation, to obtain any returned data and
|
|
||||||
status information. A read will fail if there is no operation
|
|
||||||
in progress.
|
|
||||||
|
|
||||||
As a special case, the device can be reset with a write operation,
|
|
||||||
and in this case, no following read is expected, or permitted.
|
|
||||||
|
|
||||||
There are no ioctl() operations. Any single operation
|
|
||||||
may transfer at most PG_MAX_DATA bytes. Note that the driver must
|
|
||||||
copy the data through an internal buffer. In keeping with all
|
|
||||||
current ATAPI devices, command packets are assumed to be exactly
|
|
||||||
12 bytes in length.
|
|
||||||
|
|
||||||
To permit future changes to this interface, the headers in the
|
|
||||||
read and write buffers contain a single character "magic" flag.
|
|
||||||
Currently this flag must be the character "P".
|
|
||||||
|
|
||||||
By default, the driver will autoprobe for a single parallel
|
|
||||||
port ATAPI device, but if their individual parameters are
|
|
||||||
specified, the driver can handle up to 4 devices.
|
|
||||||
|
|
||||||
To use this device, you must have the following device
|
|
||||||
special files defined:
|
|
||||||
|
|
||||||
/dev/pg0 c 97 0
|
|
||||||
/dev/pg1 c 97 1
|
|
||||||
/dev/pg2 c 97 2
|
|
||||||
/dev/pg3 c 97 3
|
|
||||||
|
|
||||||
(You'll need to change the 97 to something else if you use
|
|
||||||
the 'major' parameter to install the driver on a different
|
|
||||||
major number.)
|
|
||||||
|
|
||||||
The behaviour of the pg driver can be altered by setting
|
|
||||||
some parameters from the insmod command line. The following
|
|
||||||
parameters are adjustable:
|
|
||||||
|
|
||||||
drive0 These four arguments can be arrays of
|
|
||||||
drive1 1-6 integers as follows:
|
|
||||||
drive2
|
|
||||||
drive3 <prt>,<pro>,<uni>,<mod>,<slv>,<dly>
|
|
||||||
|
|
||||||
Where,
|
|
||||||
|
|
||||||
<prt> is the base of the parallel port address for
|
|
||||||
the corresponding drive. (required)
|
|
||||||
|
|
||||||
<pro> is the protocol number for the adapter that
|
|
||||||
supports this drive. These numbers are
|
|
||||||
logged by 'paride' when the protocol modules
|
|
||||||
are initialised. (0 if not given)
|
|
||||||
|
|
||||||
<uni> for those adapters that support chained
|
|
||||||
devices, this is the unit selector for the
|
|
||||||
chain of devices on the given port. It should
|
|
||||||
be zero for devices that don't support chaining.
|
|
||||||
(0 if not given)
|
|
||||||
|
|
||||||
<mod> this can be -1 to choose the best mode, or one
|
|
||||||
of the mode numbers supported by the adapter.
|
|
||||||
(-1 if not given)
|
|
||||||
|
|
||||||
<slv> ATAPI devices can be jumpered to master or slave.
|
|
||||||
Set this to 0 to choose the master drive, 1 to
|
|
||||||
choose the slave, -1 (the default) to choose the
|
|
||||||
first drive found.
|
|
||||||
|
|
||||||
<dly> some parallel ports require the driver to
|
|
||||||
go more slowly. -1 sets a default value that
|
|
||||||
should work with the chosen protocol. Otherwise,
|
|
||||||
set this to a small integer, the larger it is
|
|
||||||
the slower the port i/o. In some cases, setting
|
|
||||||
this to zero will speed up the device. (default -1)
|
|
||||||
|
|
||||||
major You may use this parameter to override the
|
|
||||||
default major number (97) that this driver
|
|
||||||
will use. Be sure to change the device
|
|
||||||
name as well.
|
|
||||||
|
|
||||||
name This parameter is a character string that
|
|
||||||
contains the name the kernel will use for this
|
|
||||||
device (in /proc output, for instance).
|
|
||||||
(default "pg").
|
|
||||||
|
|
||||||
verbose This parameter controls the amount of logging
|
|
||||||
that is done by the driver. Set it to 0 for
|
|
||||||
quiet operation, to 1 to enable progress
|
|
||||||
messages while the driver probes for devices,
|
|
||||||
or to 2 for full debug logging. (default 0)
|
|
||||||
|
|
||||||
If this driver is built into the kernel, you can use
|
|
||||||
the following command line parameters, with the same values
|
|
||||||
as the corresponding module parameters listed above:
|
|
||||||
|
|
||||||
pg.drive0
|
|
||||||
pg.drive1
|
|
||||||
pg.drive2
|
|
||||||
pg.drive3
|
|
||||||
|
|
||||||
In addition, you can use the parameter pg.disable to disable
|
|
||||||
the driver entirely.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Changes:
|
|
||||||
|
|
||||||
1.01 GRG 1998.06.16 Bug fixes
|
|
||||||
1.02 GRG 1998.09.24 Added jumbo support
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PG_VERSION "1.02"
|
|
||||||
#define PG_MAJOR 97
|
|
||||||
#define PG_NAME "pg"
|
|
||||||
#define PG_UNITS 4
|
|
||||||
|
|
||||||
#ifndef PI_PG
|
|
||||||
#define PI_PG 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
|
||||||
/* Here are things one can override from the insmod command.
|
|
||||||
Most are autoprobed by paride unless set here. Verbose is 0
|
|
||||||
by default.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int verbose;
|
|
||||||
static int major = PG_MAJOR;
|
|
||||||
static char *name = PG_NAME;
|
|
||||||
static int disable = 0;
|
|
||||||
|
|
||||||
static int drive0[6] = { 0, 0, 0, -1, -1, -1 };
|
|
||||||
static int drive1[6] = { 0, 0, 0, -1, -1, -1 };
|
|
||||||
static int drive2[6] = { 0, 0, 0, -1, -1, -1 };
|
|
||||||
static int drive3[6] = { 0, 0, 0, -1, -1, -1 };
|
|
||||||
|
|
||||||
static int (*drives[4])[6] = {&drive0, &drive1, &drive2, &drive3};
|
|
||||||
static int pg_drive_count;
|
|
||||||
|
|
||||||
enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY};
|
|
||||||
|
|
||||||
/* end of parameters */
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/delay.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/mtio.h>
|
|
||||||
#include <linux/pg.h>
|
|
||||||
#include <linux/device.h>
|
|
||||||
#include <linux/sched.h> /* current, TASK_* */
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
|
|
||||||
module_param(verbose, int, 0644);
|
|
||||||
module_param(major, int, 0);
|
|
||||||
module_param(name, charp, 0);
|
|
||||||
module_param_array(drive0, int, NULL, 0);
|
|
||||||
module_param_array(drive1, int, NULL, 0);
|
|
||||||
module_param_array(drive2, int, NULL, 0);
|
|
||||||
module_param_array(drive3, int, NULL, 0);
|
|
||||||
|
|
||||||
#include "paride.h"
|
|
||||||
|
|
||||||
#define PG_SPIN_DEL 50 /* spin delay in micro-seconds */
|
|
||||||
#define PG_SPIN 200
|
|
||||||
#define PG_TMO HZ
|
|
||||||
#define PG_RESET_TMO 10*HZ
|
|
||||||
|
|
||||||
#define STAT_ERR 0x01
|
|
||||||
#define STAT_INDEX 0x02
|
|
||||||
#define STAT_ECC 0x04
|
|
||||||
#define STAT_DRQ 0x08
|
|
||||||
#define STAT_SEEK 0x10
|
|
||||||
#define STAT_WRERR 0x20
|
|
||||||
#define STAT_READY 0x40
|
|
||||||
#define STAT_BUSY 0x80
|
|
||||||
|
|
||||||
#define ATAPI_IDENTIFY 0x12
|
|
||||||
|
|
||||||
static DEFINE_MUTEX(pg_mutex);
|
|
||||||
static int pg_open(struct inode *inode, struct file *file);
|
|
||||||
static int pg_release(struct inode *inode, struct file *file);
|
|
||||||
static ssize_t pg_read(struct file *filp, char __user *buf,
|
|
||||||
size_t count, loff_t * ppos);
|
|
||||||
static ssize_t pg_write(struct file *filp, const char __user *buf,
|
|
||||||
size_t count, loff_t * ppos);
|
|
||||||
static int pg_detect(void);
|
|
||||||
|
|
||||||
#define PG_NAMELEN 8
|
|
||||||
|
|
||||||
struct pg {
|
|
||||||
struct pi_adapter pia; /* interface to paride layer */
|
|
||||||
struct pi_adapter *pi;
|
|
||||||
int busy; /* write done, read expected */
|
|
||||||
int start; /* jiffies at command start */
|
|
||||||
int dlen; /* transfer size requested */
|
|
||||||
unsigned long timeout; /* timeout requested */
|
|
||||||
int status; /* last sense key */
|
|
||||||
int drive; /* drive */
|
|
||||||
unsigned long access; /* count of active opens ... */
|
|
||||||
int present; /* device present ? */
|
|
||||||
char *bufptr;
|
|
||||||
char name[PG_NAMELEN]; /* pg0, pg1, ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pg devices[PG_UNITS];
|
|
||||||
|
|
||||||
static int pg_identify(struct pg *dev, int log);
|
|
||||||
|
|
||||||
static char pg_scratch[512]; /* scratch block buffer */
|
|
||||||
|
|
||||||
static struct class *pg_class;
|
|
||||||
static void *par_drv; /* reference of parport driver */
|
|
||||||
|
|
||||||
/* kernel glue structures */
|
|
||||||
|
|
||||||
static const struct file_operations pg_fops = {
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.read = pg_read,
|
|
||||||
.write = pg_write,
|
|
||||||
.open = pg_open,
|
|
||||||
.release = pg_release,
|
|
||||||
.llseek = noop_llseek,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pg_init_units(void)
|
|
||||||
{
|
|
||||||
int unit;
|
|
||||||
|
|
||||||
pg_drive_count = 0;
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
|
||||||
int *parm = *drives[unit];
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
dev->pi = &dev->pia;
|
|
||||||
clear_bit(0, &dev->access);
|
|
||||||
dev->busy = 0;
|
|
||||||
dev->present = 0;
|
|
||||||
dev->bufptr = NULL;
|
|
||||||
dev->drive = parm[D_SLV];
|
|
||||||
snprintf(dev->name, PG_NAMELEN, "%s%c", name, 'a'+unit);
|
|
||||||
if (parm[D_PRT])
|
|
||||||
pg_drive_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int status_reg(struct pg *dev)
|
|
||||||
{
|
|
||||||
return pi_read_regr(dev->pi, 1, 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int read_reg(struct pg *dev, int reg)
|
|
||||||
{
|
|
||||||
return pi_read_regr(dev->pi, 0, reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void write_reg(struct pg *dev, int reg, int val)
|
|
||||||
{
|
|
||||||
pi_write_regr(dev->pi, 0, reg, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline u8 DRIVE(struct pg *dev)
|
|
||||||
{
|
|
||||||
return 0xa0+0x10*dev->drive;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pg_sleep(int cs)
|
|
||||||
{
|
|
||||||
schedule_timeout_interruptible(cs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_wait(struct pg *dev, int go, int stop, unsigned long tmo, char *msg)
|
|
||||||
{
|
|
||||||
int j, r, e, s, p, to;
|
|
||||||
|
|
||||||
dev->status = 0;
|
|
||||||
|
|
||||||
j = 0;
|
|
||||||
while ((((r = status_reg(dev)) & go) || (stop && (!(r & stop))))
|
|
||||||
&& time_before(jiffies, tmo)) {
|
|
||||||
if (j++ < PG_SPIN)
|
|
||||||
udelay(PG_SPIN_DEL);
|
|
||||||
else
|
|
||||||
pg_sleep(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
to = time_after_eq(jiffies, tmo);
|
|
||||||
|
|
||||||
if ((r & (STAT_ERR & stop)) || to) {
|
|
||||||
s = read_reg(dev, 7);
|
|
||||||
e = read_reg(dev, 1);
|
|
||||||
p = read_reg(dev, 2);
|
|
||||||
if (verbose > 1)
|
|
||||||
printk("%s: %s: stat=0x%x err=0x%x phase=%d%s\n",
|
|
||||||
dev->name, msg, s, e, p, to ? " timeout" : "");
|
|
||||||
if (to)
|
|
||||||
e |= 0x100;
|
|
||||||
dev->status = (e >> 4) & 0xff;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_command(struct pg *dev, char *cmd, int dlen, unsigned long tmo)
|
|
||||||
{
|
|
||||||
int k;
|
|
||||||
|
|
||||||
pi_connect(dev->pi);
|
|
||||||
|
|
||||||
write_reg(dev, 6, DRIVE(dev));
|
|
||||||
|
|
||||||
if (pg_wait(dev, STAT_BUSY | STAT_DRQ, 0, tmo, "before command"))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
write_reg(dev, 4, dlen % 256);
|
|
||||||
write_reg(dev, 5, dlen / 256);
|
|
||||||
write_reg(dev, 7, 0xa0); /* ATAPI packet command */
|
|
||||||
|
|
||||||
if (pg_wait(dev, STAT_BUSY, STAT_DRQ, tmo, "command DRQ"))
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
if (read_reg(dev, 2) != 1) {
|
|
||||||
printk("%s: command phase error\n", dev->name);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
pi_write_block(dev->pi, cmd, 12);
|
|
||||||
|
|
||||||
if (verbose > 1) {
|
|
||||||
printk("%s: Command sent, dlen=%d packet= ", dev->name, dlen);
|
|
||||||
for (k = 0; k < 12; k++)
|
|
||||||
printk("%02x ", cmd[k] & 0xff);
|
|
||||||
printk("\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
fail:
|
|
||||||
pi_disconnect(dev->pi);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_completion(struct pg *dev, char *buf, unsigned long tmo)
|
|
||||||
{
|
|
||||||
int r, d, n, p;
|
|
||||||
|
|
||||||
r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
|
|
||||||
tmo, "completion");
|
|
||||||
|
|
||||||
dev->dlen = 0;
|
|
||||||
|
|
||||||
while (read_reg(dev, 7) & STAT_DRQ) {
|
|
||||||
d = (read_reg(dev, 4) + 256 * read_reg(dev, 5));
|
|
||||||
n = ((d + 3) & 0xfffc);
|
|
||||||
p = read_reg(dev, 2) & 3;
|
|
||||||
if (p == 0)
|
|
||||||
pi_write_block(dev->pi, buf, n);
|
|
||||||
if (p == 2)
|
|
||||||
pi_read_block(dev->pi, buf, n);
|
|
||||||
if (verbose > 1)
|
|
||||||
printk("%s: %s %d bytes\n", dev->name,
|
|
||||||
p ? "Read" : "Write", n);
|
|
||||||
dev->dlen += (1 - p) * d;
|
|
||||||
buf += d;
|
|
||||||
r = pg_wait(dev, STAT_BUSY, STAT_DRQ | STAT_READY | STAT_ERR,
|
|
||||||
tmo, "completion");
|
|
||||||
}
|
|
||||||
|
|
||||||
pi_disconnect(dev->pi);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_reset(struct pg *dev)
|
|
||||||
{
|
|
||||||
int i, k, err;
|
|
||||||
int expect[5] = { 1, 1, 1, 0x14, 0xeb };
|
|
||||||
int got[5];
|
|
||||||
|
|
||||||
pi_connect(dev->pi);
|
|
||||||
write_reg(dev, 6, DRIVE(dev));
|
|
||||||
write_reg(dev, 7, 8);
|
|
||||||
|
|
||||||
pg_sleep(20 * HZ / 1000);
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
while ((k++ < PG_RESET_TMO) && (status_reg(dev) & STAT_BUSY))
|
|
||||||
pg_sleep(1);
|
|
||||||
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
got[i] = read_reg(dev, i + 1);
|
|
||||||
|
|
||||||
err = memcmp(expect, got, sizeof(got)) ? -1 : 0;
|
|
||||||
|
|
||||||
if (verbose) {
|
|
||||||
printk("%s: Reset (%d) signature = ", dev->name, k);
|
|
||||||
for (i = 0; i < 5; i++)
|
|
||||||
printk("%3x", got[i]);
|
|
||||||
if (err)
|
|
||||||
printk(" (incorrect)");
|
|
||||||
printk("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
pi_disconnect(dev->pi);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xs(char *buf, char *targ, int len)
|
|
||||||
{
|
|
||||||
char l = '\0';
|
|
||||||
int k;
|
|
||||||
|
|
||||||
for (k = 0; k < len; k++) {
|
|
||||||
char c = *buf++;
|
|
||||||
if (c != ' ' && c != l)
|
|
||||||
l = *targ++ = c;
|
|
||||||
}
|
|
||||||
if (l == ' ')
|
|
||||||
targ--;
|
|
||||||
*targ = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_identify(struct pg *dev, int log)
|
|
||||||
{
|
|
||||||
int s;
|
|
||||||
char *ms[2] = { "master", "slave" };
|
|
||||||
char mf[10], id[18];
|
|
||||||
char id_cmd[12] = { ATAPI_IDENTIFY, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0 };
|
|
||||||
char buf[36];
|
|
||||||
|
|
||||||
s = pg_command(dev, id_cmd, 36, jiffies + PG_TMO);
|
|
||||||
if (s)
|
|
||||||
return -1;
|
|
||||||
s = pg_completion(dev, buf, jiffies + PG_TMO);
|
|
||||||
if (s)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (log) {
|
|
||||||
xs(buf + 8, mf, 8);
|
|
||||||
xs(buf + 16, id, 16);
|
|
||||||
printk("%s: %s %s, %s\n", dev->name, mf, id, ms[dev->drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* returns 0, with id set if drive is detected
|
|
||||||
* -1, if drive detection failed
|
|
||||||
*/
|
|
||||||
static int pg_probe(struct pg *dev)
|
|
||||||
{
|
|
||||||
if (dev->drive == -1) {
|
|
||||||
for (dev->drive = 0; dev->drive <= 1; dev->drive++)
|
|
||||||
if (!pg_reset(dev))
|
|
||||||
return pg_identify(dev, 1);
|
|
||||||
} else {
|
|
||||||
if (!pg_reset(dev))
|
|
||||||
return pg_identify(dev, 1);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_detect(void)
|
|
||||||
{
|
|
||||||
struct pg *dev = &devices[0];
|
|
||||||
int k, unit;
|
|
||||||
|
|
||||||
printk("%s: %s version %s, major %d\n", name, name, PG_VERSION, major);
|
|
||||||
|
|
||||||
par_drv = pi_register_driver(name);
|
|
||||||
if (!par_drv) {
|
|
||||||
pr_err("failed to register %s driver\n", name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
k = 0;
|
|
||||||
if (pg_drive_count == 0) {
|
|
||||||
if (pi_init(dev->pi, 1, -1, -1, -1, -1, -1, pg_scratch,
|
|
||||||
PI_PG, verbose, dev->name)) {
|
|
||||||
if (!pg_probe(dev)) {
|
|
||||||
dev->present = 1;
|
|
||||||
k++;
|
|
||||||
} else
|
|
||||||
pi_release(dev->pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++, dev++) {
|
|
||||||
int *parm = *drives[unit];
|
|
||||||
if (!parm[D_PRT])
|
|
||||||
continue;
|
|
||||||
if (pi_init(dev->pi, 0, parm[D_PRT], parm[D_MOD],
|
|
||||||
parm[D_UNI], parm[D_PRO], parm[D_DLY],
|
|
||||||
pg_scratch, PI_PG, verbose, dev->name)) {
|
|
||||||
if (!pg_probe(dev)) {
|
|
||||||
dev->present = 1;
|
|
||||||
k++;
|
|
||||||
} else
|
|
||||||
pi_release(dev->pi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (k)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
pi_unregister_driver(par_drv);
|
|
||||||
printk("%s: No ATAPI device detected\n", name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int unit = iminor(inode) & 0x7f;
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
mutex_lock(&pg_mutex);
|
|
||||||
if ((unit >= PG_UNITS) || (!dev->present)) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_and_set_bit(0, &dev->access)) {
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->busy) {
|
|
||||||
pg_reset(dev);
|
|
||||||
dev->busy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_identify(dev, (verbose > 1));
|
|
||||||
|
|
||||||
dev->bufptr = kmalloc(PG_MAX_DATA, GFP_KERNEL);
|
|
||||||
if (dev->bufptr == NULL) {
|
|
||||||
clear_bit(0, &dev->access);
|
|
||||||
printk("%s: buffer allocation failed\n", dev->name);
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
file->private_data = dev;
|
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&pg_mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pg_release(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct pg *dev = file->private_data;
|
|
||||||
|
|
||||||
kfree(dev->bufptr);
|
|
||||||
dev->bufptr = NULL;
|
|
||||||
clear_bit(0, &dev->access);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t pg_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
struct pg *dev = filp->private_data;
|
|
||||||
struct pg_write_hdr hdr;
|
|
||||||
int hs = sizeof (hdr);
|
|
||||||
|
|
||||||
if (dev->busy)
|
|
||||||
return -EBUSY;
|
|
||||||
if (count < hs)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (copy_from_user(&hdr, buf, hs))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if (hdr.magic != PG_MAGIC)
|
|
||||||
return -EINVAL;
|
|
||||||
if (hdr.dlen < 0 || hdr.dlen > PG_MAX_DATA)
|
|
||||||
return -EINVAL;
|
|
||||||
if ((count - hs) > PG_MAX_DATA)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (hdr.func == PG_RESET) {
|
|
||||||
if (count != hs)
|
|
||||||
return -EINVAL;
|
|
||||||
if (pg_reset(dev))
|
|
||||||
return -EIO;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hdr.func != PG_COMMAND)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
dev->start = jiffies;
|
|
||||||
dev->timeout = hdr.timeout * HZ + HZ / 2 + jiffies;
|
|
||||||
|
|
||||||
if (pg_command(dev, hdr.packet, hdr.dlen, jiffies + PG_TMO)) {
|
|
||||||
if (dev->status & 0x10)
|
|
||||||
return -ETIME;
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->busy = 1;
|
|
||||||
|
|
||||||
if (copy_from_user(dev->bufptr, buf + hs, count - hs))
|
|
||||||
return -EFAULT;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t pg_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
struct pg *dev = filp->private_data;
|
|
||||||
struct pg_read_hdr hdr;
|
|
||||||
int hs = sizeof (hdr);
|
|
||||||
int copy;
|
|
||||||
|
|
||||||
if (!dev->busy)
|
|
||||||
return -EINVAL;
|
|
||||||
if (count < hs)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
dev->busy = 0;
|
|
||||||
|
|
||||||
if (pg_completion(dev, dev->bufptr, dev->timeout))
|
|
||||||
if (dev->status & 0x10)
|
|
||||||
return -ETIME;
|
|
||||||
|
|
||||||
memset(&hdr, 0, sizeof(hdr));
|
|
||||||
hdr.magic = PG_MAGIC;
|
|
||||||
hdr.dlen = dev->dlen;
|
|
||||||
copy = 0;
|
|
||||||
|
|
||||||
if (hdr.dlen < 0) {
|
|
||||||
hdr.dlen = -1 * hdr.dlen;
|
|
||||||
copy = hdr.dlen;
|
|
||||||
if (copy > (count - hs))
|
|
||||||
copy = count - hs;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdr.duration = (jiffies - dev->start + HZ / 2) / HZ;
|
|
||||||
hdr.scsi = dev->status & 0x0f;
|
|
||||||
|
|
||||||
if (copy_to_user(buf, &hdr, hs))
|
|
||||||
return -EFAULT;
|
|
||||||
if (copy > 0)
|
|
||||||
if (copy_to_user(buf + hs, dev->bufptr, copy))
|
|
||||||
return -EFAULT;
|
|
||||||
return copy + hs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init pg_init(void)
|
|
||||||
{
|
|
||||||
int unit;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (disable){
|
|
||||||
err = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
pg_init_units();
|
|
||||||
|
|
||||||
if (pg_detect()) {
|
|
||||||
err = -ENODEV;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = register_chrdev(major, name, &pg_fops);
|
|
||||||
if (err < 0) {
|
|
||||||
printk("pg_init: unable to get major number %d\n", major);
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
if (dev->present)
|
|
||||||
pi_release(dev->pi);
|
|
||||||
}
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
major = err; /* In case the user specified `major=0' (dynamic) */
|
|
||||||
pg_class = class_create(THIS_MODULE, "pg");
|
|
||||||
if (IS_ERR(pg_class)) {
|
|
||||||
err = PTR_ERR(pg_class);
|
|
||||||
goto out_chrdev;
|
|
||||||
}
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
if (dev->present)
|
|
||||||
device_create(pg_class, NULL, MKDEV(major, unit), NULL,
|
|
||||||
"pg%u", unit);
|
|
||||||
}
|
|
||||||
err = 0;
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
out_chrdev:
|
|
||||||
unregister_chrdev(major, "pg");
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit pg_exit(void)
|
|
||||||
{
|
|
||||||
int unit;
|
|
||||||
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
if (dev->present)
|
|
||||||
device_destroy(pg_class, MKDEV(major, unit));
|
|
||||||
}
|
|
||||||
class_destroy(pg_class);
|
|
||||||
unregister_chrdev(major, name);
|
|
||||||
|
|
||||||
for (unit = 0; unit < PG_UNITS; unit++) {
|
|
||||||
struct pg *dev = &devices[unit];
|
|
||||||
if (dev->present)
|
|
||||||
pi_release(dev->pi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
module_init(pg_init)
|
|
||||||
module_exit(pg_exit)
|
|
@ -1,102 +0,0 @@
|
|||||||
/*
|
|
||||||
pseudo.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
|
||||||
Under the terms of the GNU General Public License.
|
|
||||||
|
|
||||||
This is the "pseudo-interrupt" logic for parallel port drivers.
|
|
||||||
|
|
||||||
This module is #included into each driver. It makes one
|
|
||||||
function available:
|
|
||||||
|
|
||||||
ps_set_intr( void (*continuation)(void),
|
|
||||||
int (*ready)(void),
|
|
||||||
int timeout,
|
|
||||||
int nice )
|
|
||||||
|
|
||||||
Which will arrange for ready() to be evaluated frequently and
|
|
||||||
when either it returns true, or timeout jiffies have passed,
|
|
||||||
continuation() will be invoked.
|
|
||||||
|
|
||||||
If nice is 1, the test will done approximately once a
|
|
||||||
jiffy. If nice is 0, the test will also be done whenever
|
|
||||||
the scheduler runs (by adding it to a task queue). If
|
|
||||||
nice is greater than 1, the test will be done once every
|
|
||||||
(nice-1) jiffies.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Changes:
|
|
||||||
|
|
||||||
1.01 1998.05.03 Switched from cli()/sti() to spinlocks
|
|
||||||
1.02 1998.12.14 Added support for nice > 1
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PS_VERSION "1.02"
|
|
||||||
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
|
|
||||||
static void ps_tq_int(struct work_struct *work);
|
|
||||||
|
|
||||||
static void (* ps_continuation)(void);
|
|
||||||
static int (* ps_ready)(void);
|
|
||||||
static unsigned long ps_timeout;
|
|
||||||
static int ps_tq_active = 0;
|
|
||||||
static int ps_nice = 0;
|
|
||||||
|
|
||||||
static DEFINE_SPINLOCK(ps_spinlock __attribute__((unused)));
|
|
||||||
|
|
||||||
static DECLARE_DELAYED_WORK(ps_tq, ps_tq_int);
|
|
||||||
|
|
||||||
static void ps_set_intr(void (*continuation)(void),
|
|
||||||
int (*ready)(void),
|
|
||||||
int timeout, int nice)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ps_spinlock,flags);
|
|
||||||
|
|
||||||
ps_continuation = continuation;
|
|
||||||
ps_ready = ready;
|
|
||||||
ps_timeout = jiffies + timeout;
|
|
||||||
ps_nice = nice;
|
|
||||||
|
|
||||||
if (!ps_tq_active) {
|
|
||||||
ps_tq_active = 1;
|
|
||||||
if (!ps_nice)
|
|
||||||
schedule_delayed_work(&ps_tq, 0);
|
|
||||||
else
|
|
||||||
schedule_delayed_work(&ps_tq, ps_nice-1);
|
|
||||||
}
|
|
||||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ps_tq_int(struct work_struct *work)
|
|
||||||
{
|
|
||||||
void (*con)(void);
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ps_spinlock,flags);
|
|
||||||
|
|
||||||
con = ps_continuation;
|
|
||||||
ps_tq_active = 0;
|
|
||||||
|
|
||||||
if (!con) {
|
|
||||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!ps_ready || ps_ready() || time_after_eq(jiffies, ps_timeout)) {
|
|
||||||
ps_continuation = NULL;
|
|
||||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
|
||||||
con();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ps_tq_active = 1;
|
|
||||||
if (!ps_nice)
|
|
||||||
schedule_delayed_work(&ps_tq, 0);
|
|
||||||
else
|
|
||||||
schedule_delayed_work(&ps_tq, ps_nice-1);
|
|
||||||
spin_unlock_irqrestore(&ps_spinlock,flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* end of pseudo.h */
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -5370,9 +5370,9 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
ipr_cmd->done = ipr_sata_eh_done;
|
ipr_cmd->done = ipr_sata_eh_done;
|
||||||
if (!(ipr_cmd->qc->flags & ATA_QCFLAG_FAILED)) {
|
if (!(ipr_cmd->qc->flags & ATA_QCFLAG_EH)) {
|
||||||
ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
|
ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT;
|
||||||
ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED;
|
ipr_cmd->qc->flags |= ATA_QCFLAG_EH;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7142,11 +7142,8 @@ static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc)
|
|||||||
/**
|
/**
|
||||||
* ipr_qc_fill_rtf - Read result TF
|
* ipr_qc_fill_rtf - Read result TF
|
||||||
* @qc: ATA queued command
|
* @qc: ATA queued command
|
||||||
*
|
|
||||||
* Return value:
|
|
||||||
* true
|
|
||||||
**/
|
**/
|
||||||
static bool ipr_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void ipr_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct ipr_sata_port *sata_port = qc->ap->private_data;
|
struct ipr_sata_port *sata_port = qc->ap->private_data;
|
||||||
struct ipr_ioasa_gata *g = &sata_port->ioasa;
|
struct ipr_ioasa_gata *g = &sata_port->ioasa;
|
||||||
@ -7163,8 +7160,6 @@ static bool ipr_qc_fill_rtf(struct ata_queued_cmd *qc)
|
|||||||
tf->hob_lbal = g->hob_lbal;
|
tf->hob_lbal = g->hob_lbal;
|
||||||
tf->hob_lbam = g->hob_lbam;
|
tf->hob_lbam = g->hob_lbam;
|
||||||
tf->hob_lbah = g->hob_lbah;
|
tf->hob_lbah = g->hob_lbah;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ata_port_operations ipr_sata_ops = {
|
static struct ata_port_operations ipr_sata_ops = {
|
||||||
|
@ -125,7 +125,7 @@ static void sas_ata_task_done(struct sas_task *task)
|
|||||||
} else {
|
} else {
|
||||||
link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]);
|
link->eh_info.err_mask |= ac_err_mask(dev->sata_dev.fis[2]);
|
||||||
if (unlikely(link->eh_info.err_mask))
|
if (unlikely(link->eh_info.err_mask))
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ac = sas_to_ata_err(stat);
|
ac = sas_to_ata_err(stat);
|
||||||
@ -136,7 +136,7 @@ static void sas_ata_task_done(struct sas_task *task)
|
|||||||
qc->err_mask = ac;
|
qc->err_mask = ac;
|
||||||
} else {
|
} else {
|
||||||
link->eh_info.err_mask |= AC_ERR_DEV;
|
link->eh_info.err_mask |= AC_ERR_DEV;
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */
|
dev->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */
|
||||||
@ -226,12 +226,11 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
|
static void sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
struct domain_device *dev = qc->ap->private_data;
|
struct domain_device *dev = qc->ap->private_data;
|
||||||
|
|
||||||
ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf);
|
ata_tf_from_fis(dev->sata_dev.fis, &qc->result_tf);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
|
static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
|
||||||
@ -476,7 +475,7 @@ static void sas_ata_internal_abort(struct sas_task *task)
|
|||||||
|
|
||||||
static void sas_ata_post_internal(struct ata_queued_cmd *qc)
|
static void sas_ata_post_internal(struct ata_queued_cmd *qc)
|
||||||
{
|
{
|
||||||
if (qc->flags & ATA_QCFLAG_FAILED)
|
if (qc->flags & ATA_QCFLAG_EH)
|
||||||
qc->err_mask |= AC_ERR_OTHER;
|
qc->err_mask |= AC_ERR_OTHER;
|
||||||
|
|
||||||
if (qc->err_mask) {
|
if (qc->err_mask) {
|
||||||
@ -631,7 +630,7 @@ void sas_ata_task_abort(struct sas_task *task)
|
|||||||
|
|
||||||
/* Internal command, fake a timeout and complete. */
|
/* Internal command, fake a timeout and complete. */
|
||||||
qc->flags &= ~ATA_QCFLAG_ACTIVE;
|
qc->flags &= ~ATA_QCFLAG_ACTIVE;
|
||||||
qc->flags |= ATA_QCFLAG_FAILED;
|
qc->flags |= ATA_QCFLAG_EH;
|
||||||
qc->err_mask |= AC_ERR_TIMEOUT;
|
qc->err_mask |= AC_ERR_TIMEOUT;
|
||||||
waiting = qc->private_data;
|
waiting = qc->private_data;
|
||||||
complete(waiting);
|
complete(waiting);
|
||||||
|
@ -90,32 +90,32 @@ enum {
|
|||||||
ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */
|
ATA_DFLAG_ACPI_FAILED = (1 << 6), /* ACPI on devcfg has failed */
|
||||||
ATA_DFLAG_AN = (1 << 7), /* AN configured */
|
ATA_DFLAG_AN = (1 << 7), /* AN configured */
|
||||||
ATA_DFLAG_TRUSTED = (1 << 8), /* device supports trusted send/recv */
|
ATA_DFLAG_TRUSTED = (1 << 8), /* device supports trusted send/recv */
|
||||||
|
ATA_DFLAG_FUA = (1 << 9), /* device supports FUA */
|
||||||
ATA_DFLAG_DMADIR = (1 << 10), /* device requires DMADIR */
|
ATA_DFLAG_DMADIR = (1 << 10), /* device requires DMADIR */
|
||||||
ATA_DFLAG_CFG_MASK = (1 << 12) - 1,
|
ATA_DFLAG_NCQ_SEND_RECV = (1 << 11), /* device supports NCQ SEND and RECV */
|
||||||
|
ATA_DFLAG_NCQ_PRIO = (1 << 12), /* device supports NCQ priority */
|
||||||
|
ATA_DFLAG_CFG_MASK = (1 << 13) - 1,
|
||||||
|
|
||||||
ATA_DFLAG_PIO = (1 << 12), /* device limited to PIO mode */
|
ATA_DFLAG_PIO = (1 << 13), /* device limited to PIO mode */
|
||||||
ATA_DFLAG_NCQ_OFF = (1 << 13), /* device limited to non-NCQ mode */
|
ATA_DFLAG_NCQ_OFF = (1 << 14), /* device limited to non-NCQ mode */
|
||||||
ATA_DFLAG_SLEEPING = (1 << 15), /* device is sleeping */
|
ATA_DFLAG_SLEEPING = (1 << 15), /* device is sleeping */
|
||||||
ATA_DFLAG_DUBIOUS_XFER = (1 << 16), /* data transfer not verified */
|
ATA_DFLAG_DUBIOUS_XFER = (1 << 16), /* data transfer not verified */
|
||||||
ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */
|
ATA_DFLAG_NO_UNLOAD = (1 << 17), /* device doesn't support unload */
|
||||||
ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */
|
ATA_DFLAG_UNLOCK_HPA = (1 << 18), /* unlock HPA */
|
||||||
ATA_DFLAG_NCQ_SEND_RECV = (1 << 19), /* device supports NCQ SEND and RECV */
|
ATA_DFLAG_INIT_MASK = (1 << 19) - 1,
|
||||||
ATA_DFLAG_NCQ_PRIO = (1 << 20), /* device supports NCQ priority */
|
|
||||||
ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 21), /* Priority cmds sent to dev */
|
|
||||||
ATA_DFLAG_INIT_MASK = (1 << 24) - 1,
|
|
||||||
|
|
||||||
|
ATA_DFLAG_NCQ_PRIO_ENABLED = (1 << 19), /* Priority cmds sent to dev */
|
||||||
ATA_DFLAG_DETACH = (1 << 24),
|
ATA_DFLAG_DETACH = (1 << 24),
|
||||||
ATA_DFLAG_DETACHED = (1 << 25),
|
ATA_DFLAG_DETACHED = (1 << 25),
|
||||||
|
|
||||||
ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */
|
ATA_DFLAG_DA = (1 << 26), /* device supports Device Attention */
|
||||||
ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */
|
ATA_DFLAG_DEVSLP = (1 << 27), /* device supports Device Sleep */
|
||||||
ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */
|
ATA_DFLAG_ACPI_DISABLED = (1 << 28), /* ACPI for the device is disabled */
|
||||||
ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */
|
ATA_DFLAG_D_SENSE = (1 << 29), /* Descriptor sense requested */
|
||||||
ATA_DFLAG_ZAC = (1 << 30), /* ZAC device */
|
ATA_DFLAG_ZAC = (1 << 30), /* ZAC device */
|
||||||
|
|
||||||
ATA_DFLAG_FEATURES_MASK = ATA_DFLAG_TRUSTED | ATA_DFLAG_DA | \
|
ATA_DFLAG_FEATURES_MASK = (ATA_DFLAG_TRUSTED | ATA_DFLAG_DA | \
|
||||||
ATA_DFLAG_DEVSLP | ATA_DFLAG_NCQ_SEND_RECV | \
|
ATA_DFLAG_DEVSLP | ATA_DFLAG_NCQ_SEND_RECV | \
|
||||||
ATA_DFLAG_NCQ_PRIO,
|
ATA_DFLAG_NCQ_PRIO | ATA_DFLAG_FUA),
|
||||||
|
|
||||||
ATA_DEV_UNKNOWN = 0, /* unknown device */
|
ATA_DEV_UNKNOWN = 0, /* unknown device */
|
||||||
ATA_DEV_ATA = 1, /* ATA device */
|
ATA_DEV_ATA = 1, /* ATA device */
|
||||||
@ -200,13 +200,14 @@ enum {
|
|||||||
/* struct ata_queued_cmd flags */
|
/* struct ata_queued_cmd flags */
|
||||||
ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */
|
ATA_QCFLAG_ACTIVE = (1 << 0), /* cmd not yet ack'd to scsi lyer */
|
||||||
ATA_QCFLAG_DMAMAP = (1 << 1), /* SG table is DMA mapped */
|
ATA_QCFLAG_DMAMAP = (1 << 1), /* SG table is DMA mapped */
|
||||||
|
ATA_QCFLAG_RTF_FILLED = (1 << 2), /* result TF has been filled */
|
||||||
ATA_QCFLAG_IO = (1 << 3), /* standard IO command */
|
ATA_QCFLAG_IO = (1 << 3), /* standard IO command */
|
||||||
ATA_QCFLAG_RESULT_TF = (1 << 4), /* result TF requested */
|
ATA_QCFLAG_RESULT_TF = (1 << 4), /* result TF requested */
|
||||||
ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */
|
ATA_QCFLAG_CLEAR_EXCL = (1 << 5), /* clear excl_link on completion */
|
||||||
ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */
|
ATA_QCFLAG_QUIET = (1 << 6), /* don't report device error */
|
||||||
ATA_QCFLAG_RETRY = (1 << 7), /* retry after failure */
|
ATA_QCFLAG_RETRY = (1 << 7), /* retry after failure */
|
||||||
|
|
||||||
ATA_QCFLAG_FAILED = (1 << 16), /* cmd failed and is owned by EH */
|
ATA_QCFLAG_EH = (1 << 16), /* cmd aborted and owned by EH */
|
||||||
ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */
|
ATA_QCFLAG_SENSE_VALID = (1 << 17), /* sense data valid */
|
||||||
ATA_QCFLAG_EH_SCHEDULED = (1 << 18), /* EH scheduled (obsolete) */
|
ATA_QCFLAG_EH_SCHEDULED = (1 << 18), /* EH scheduled (obsolete) */
|
||||||
|
|
||||||
@ -381,6 +382,7 @@ enum {
|
|||||||
ATA_HORKAGE_NO_NCQ_ON_ATI = (1 << 27), /* Disable NCQ on ATI chipset */
|
ATA_HORKAGE_NO_NCQ_ON_ATI = (1 << 27), /* Disable NCQ on ATI chipset */
|
||||||
ATA_HORKAGE_NO_ID_DEV_LOG = (1 << 28), /* Identify device log missing */
|
ATA_HORKAGE_NO_ID_DEV_LOG = (1 << 28), /* Identify device log missing */
|
||||||
ATA_HORKAGE_NO_LOG_DIR = (1 << 29), /* Do not read log directory */
|
ATA_HORKAGE_NO_LOG_DIR = (1 << 29), /* Do not read log directory */
|
||||||
|
ATA_HORKAGE_NO_FUA = (1 << 30), /* Do not use FUA */
|
||||||
|
|
||||||
/* DMA mask for user DMA control: User visible values; DO NOT
|
/* DMA mask for user DMA control: User visible values; DO NOT
|
||||||
renumber */
|
renumber */
|
||||||
@ -876,7 +878,8 @@ struct ata_port_operations {
|
|||||||
int (*check_atapi_dma)(struct ata_queued_cmd *qc);
|
int (*check_atapi_dma)(struct ata_queued_cmd *qc);
|
||||||
enum ata_completion_errors (*qc_prep)(struct ata_queued_cmd *qc);
|
enum ata_completion_errors (*qc_prep)(struct ata_queued_cmd *qc);
|
||||||
unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
|
unsigned int (*qc_issue)(struct ata_queued_cmd *qc);
|
||||||
bool (*qc_fill_rtf)(struct ata_queued_cmd *qc);
|
void (*qc_fill_rtf)(struct ata_queued_cmd *qc);
|
||||||
|
void (*qc_ncq_fill_rtf)(struct ata_port *ap, u64 done_mask);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configuration and exception handling
|
* Configuration and exception handling
|
||||||
@ -1690,21 +1693,35 @@ extern struct ata_device *ata_dev_next(struct ata_device *dev,
|
|||||||
(dev) = ata_dev_next((dev), (link), ATA_DITER_##mode))
|
(dev) = ata_dev_next((dev), (link), ATA_DITER_##mode))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ata_ncq_enabled - Test whether NCQ is enabled
|
* ata_ncq_supported - Test whether NCQ is supported
|
||||||
* @dev: ATA device to test for
|
* @dev: ATA device to test
|
||||||
*
|
*
|
||||||
* LOCKING:
|
* LOCKING:
|
||||||
* spin_lock_irqsave(host lock)
|
* spin_lock_irqsave(host lock)
|
||||||
*
|
*
|
||||||
* RETURNS:
|
* RETURNS:
|
||||||
* 1 if NCQ is enabled for @dev, 0 otherwise.
|
* true if @dev supports NCQ, false otherwise.
|
||||||
*/
|
*/
|
||||||
static inline int ata_ncq_enabled(struct ata_device *dev)
|
static inline bool ata_ncq_supported(struct ata_device *dev)
|
||||||
{
|
{
|
||||||
if (!IS_ENABLED(CONFIG_SATA_HOST))
|
if (!IS_ENABLED(CONFIG_SATA_HOST))
|
||||||
return 0;
|
return false;
|
||||||
return (dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF |
|
return (dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ;
|
||||||
ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ata_ncq_enabled - Test whether NCQ is enabled
|
||||||
|
* @dev: ATA device to test
|
||||||
|
*
|
||||||
|
* LOCKING:
|
||||||
|
* spin_lock_irqsave(host lock)
|
||||||
|
*
|
||||||
|
* RETURNS:
|
||||||
|
* true if NCQ is enabled for @dev, false otherwise.
|
||||||
|
*/
|
||||||
|
static inline bool ata_ncq_enabled(struct ata_device *dev)
|
||||||
|
{
|
||||||
|
return ata_ncq_supported(dev) && !(dev->flags & ATA_DFLAG_NCQ_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool ata_fpdma_dsm_supported(struct ata_device *dev)
|
static inline bool ata_fpdma_dsm_supported(struct ata_device *dev)
|
||||||
@ -1756,7 +1773,7 @@ static inline struct ata_queued_cmd *ata_qc_from_tag(struct ata_port *ap,
|
|||||||
return qc;
|
return qc;
|
||||||
|
|
||||||
if ((qc->flags & (ATA_QCFLAG_ACTIVE |
|
if ((qc->flags & (ATA_QCFLAG_ACTIVE |
|
||||||
ATA_QCFLAG_FAILED)) == ATA_QCFLAG_ACTIVE)
|
ATA_QCFLAG_EH)) == ATA_QCFLAG_ACTIVE)
|
||||||
return qc;
|
return qc;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -1936,7 +1953,7 @@ extern void ata_sff_queue_delayed_work(struct delayed_work *dwork,
|
|||||||
unsigned long delay);
|
unsigned long delay);
|
||||||
extern void ata_sff_queue_pio_task(struct ata_link *link, unsigned long delay);
|
extern void ata_sff_queue_pio_task(struct ata_link *link, unsigned long delay);
|
||||||
extern unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc);
|
extern unsigned int ata_sff_qc_issue(struct ata_queued_cmd *qc);
|
||||||
extern bool ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc);
|
extern void ata_sff_qc_fill_rtf(struct ata_queued_cmd *qc);
|
||||||
extern unsigned int ata_sff_port_intr(struct ata_port *ap,
|
extern unsigned int ata_sff_port_intr(struct ata_port *ap,
|
||||||
struct ata_queued_cmd *qc);
|
struct ata_queued_cmd *qc);
|
||||||
extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance);
|
extern irqreturn_t ata_sff_interrupt(int irq, void *dev_instance);
|
||||||
|
111
include/linux/pata_parport.h
Normal file
111
include/linux/pata_parport.h
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* pata_parport.h (c) 1997-8 Grant R. Guenther <grant@torque.net>
|
||||||
|
* Under the terms of the GPL.
|
||||||
|
*
|
||||||
|
* This file defines the interface for parallel port IDE adapter chip drivers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LINUX_PATA_PARPORT_H
|
||||||
|
#define LINUX_PATA_PARPORT_H
|
||||||
|
|
||||||
|
#include <linux/libata.h>
|
||||||
|
|
||||||
|
#define PI_PCD 1 /* dummy for paride protocol modules */
|
||||||
|
|
||||||
|
struct pi_adapter {
|
||||||
|
struct device dev;
|
||||||
|
struct pi_protocol *proto; /* adapter protocol */
|
||||||
|
int port; /* base address of parallel port */
|
||||||
|
int mode; /* transfer mode in use */
|
||||||
|
int delay; /* adapter delay setting */
|
||||||
|
int devtype; /* dummy for paride protocol modules */
|
||||||
|
char *device; /* dummy for paride protocol modules */
|
||||||
|
int unit; /* unit number for chained adapters */
|
||||||
|
int saved_r0; /* saved port state */
|
||||||
|
int saved_r2; /* saved port state */
|
||||||
|
unsigned long private; /* for protocol module */
|
||||||
|
struct pardevice *pardev; /* pointer to pardevice */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct pi_adapter PIA; /* for paride protocol modules */
|
||||||
|
|
||||||
|
/* registers are addressed as (cont,regr)
|
||||||
|
* cont: 0 for command register file, 1 for control register(s)
|
||||||
|
* regr: 0-7 for register number.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* macros and functions exported to the protocol modules */
|
||||||
|
#define delay_p (pi->delay ? udelay(pi->delay) : (void)0)
|
||||||
|
#define out_p(offs, byte) do { outb(byte, pi->port + offs); delay_p; } while (0)
|
||||||
|
#define in_p(offs) (delay_p, inb(pi->port + offs))
|
||||||
|
|
||||||
|
#define w0(byte) out_p(0, byte)
|
||||||
|
#define r0() in_p(0)
|
||||||
|
#define w1(byte) out_p(1, byte)
|
||||||
|
#define r1() in_p(1)
|
||||||
|
#define w2(byte) out_p(2, byte)
|
||||||
|
#define r2() in_p(2)
|
||||||
|
#define w3(byte) out_p(3, byte)
|
||||||
|
#define w4(byte) out_p(4, byte)
|
||||||
|
#define r4() in_p(4)
|
||||||
|
#define w4w(data) do { outw(data, pi->port + 4); delay_p; } while (0)
|
||||||
|
#define w4l(data) do { outl(data, pi->port + 4); delay_p; } while (0)
|
||||||
|
#define r4w() (delay_p, inw(pi->port + 4))
|
||||||
|
#define r4l() (delay_p, inl(pi->port + 4))
|
||||||
|
|
||||||
|
static inline u16 pi_swab16(char *b, int k)
|
||||||
|
{
|
||||||
|
union { u16 u; char t[2]; } r;
|
||||||
|
|
||||||
|
r.t[0] = b[2 * k + 1]; r.t[1] = b[2 * k];
|
||||||
|
return r.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 pi_swab32(char *b, int k)
|
||||||
|
{
|
||||||
|
union { u32 u; char f[4]; } r;
|
||||||
|
|
||||||
|
r.f[0] = b[4 * k + 1]; r.f[1] = b[4 * k];
|
||||||
|
r.f[2] = b[4 * k + 3]; r.f[3] = b[4 * k + 2];
|
||||||
|
return r.u;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pi_protocol {
|
||||||
|
char name[8];
|
||||||
|
|
||||||
|
int max_mode;
|
||||||
|
int epp_first; /* modes >= this use 8 ports */
|
||||||
|
|
||||||
|
int default_delay;
|
||||||
|
int max_units; /* max chained units probed for */
|
||||||
|
|
||||||
|
void (*write_regr)(struct pi_adapter *pi, int cont, int regr, int val);
|
||||||
|
int (*read_regr)(struct pi_adapter *pi, int cont, int regr);
|
||||||
|
void (*write_block)(struct pi_adapter *pi, char *buf, int count);
|
||||||
|
void (*read_block)(struct pi_adapter *pi, char *buf, int count);
|
||||||
|
|
||||||
|
void (*connect)(struct pi_adapter *pi);
|
||||||
|
void (*disconnect)(struct pi_adapter *pi);
|
||||||
|
|
||||||
|
int (*test_port)(struct pi_adapter *pi);
|
||||||
|
int (*probe_unit)(struct pi_adapter *pi);
|
||||||
|
int (*test_proto)(struct pi_adapter *pi, char *scratch, int verbose);
|
||||||
|
void (*log_adapter)(struct pi_adapter *pi, char *scratch, int verbose);
|
||||||
|
|
||||||
|
int (*init_proto)(struct pi_adapter *pi);
|
||||||
|
void (*release_proto)(struct pi_adapter *pi);
|
||||||
|
struct module *owner;
|
||||||
|
struct device_driver driver;
|
||||||
|
struct scsi_host_template sht;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PATA_PARPORT_SHT ATA_PIO_SHT
|
||||||
|
|
||||||
|
int pata_parport_register_driver(struct pi_protocol *pr);
|
||||||
|
void pata_parport_unregister_driver(struct pi_protocol *pr);
|
||||||
|
/* defines for old paride protocol modules */
|
||||||
|
#define paride_register pata_parport_register_driver
|
||||||
|
#define paride_unregister pata_parport_unregister_driver
|
||||||
|
|
||||||
|
#endif /* LINUX_PATA_PARPORT_H */
|
Loading…
Reference in New Issue
Block a user