Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
linux-elphel
Commits
2ca997e5
Commit
2ca997e5
authored
Feb 26, 2016
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Plain Diff
merging
parents
67bbf1dd
b2a72326
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
5233 additions
and
35 deletions
+5233
-35
Kconfig
src/drivers/ata/Kconfig
+1027
-0
Makefile
src/drivers/ata/Makefile
+120
-0
ahci_elphel.c
src/drivers/ata/ahci_elphel.c
+86
-35
libahci.c
src/drivers/ata/libahci.c
+3003
-0
libahci_debug.c
src/drivers/ata/libahci_debug.c
+864
-0
libahci_debug.h
src/drivers/ata/libahci_debug.h
+133
-0
No files found.
src/drivers/ata/Kconfig
0 → 100644
View file @
2ca997e5
#
# SATA/PATA driver configuration
#
config HAVE_PATA_PLATFORM
bool
help
This is an internal configuration node for any machine that
uses pata-platform driver to enable the relevant driver in the
configuration structure without having to submit endless patches
to update the PATA_PLATFORM entry.
menuconfig ATA
tristate "Serial ATA and Parallel ATA drivers (libata)"
depends on HAS_IOMEM
depends on BLOCK
depends on !(M32R || M68K || S390) || BROKEN
select SCSI
select GLOB
---help---
If you want to use an ATA hard disk, ATA tape drive, ATA CD-ROM or
any other ATA device under Linux, say Y and make sure that you know
the name of your ATA host adapter (the card inside your computer
that "speaks" the ATA protocol, also called ATA controller),
because you will be asked for it.
NOTE: ATA enables basic SCSI support; *however*,
'SCSI disk support', 'SCSI tape support', or
'SCSI CDROM support' may also be needed,
depending on your hardware configuration.
if ATA
config ATA_NONSTANDARD
bool
default n
config ATA_VERBOSE_ERROR
bool "Verbose ATA error reporting"
default y
help
This option adds parsing of ATA command descriptions and error bits
in libata kernel output, making it easier to interpret.
This option will enlarge the kernel by approx. 6KB. Disable it only
if kernel size is more important than ease of debugging.
If unsure, say Y.
config ATA_ACPI
bool "ATA ACPI Support"
depends on ACPI && PCI
default y
help
This option adds support for ATA-related ACPI objects.
These ACPI objects add the ability to retrieve taskfiles
from the ACPI BIOS and write them to the disk controller.
These objects may be related to performance, security,
power management, or other areas.
You can disable this at kernel boot time by using the
option libata.noacpi=1
config SATA_ZPODD
bool "SATA Zero Power Optical Disc Drive (ZPODD) support"
depends on ATA_ACPI && PM
default n
help
This option adds support for SATA Zero Power Optical Disc
Drive (ZPODD). It requires both the ODD and the platform
support, and if enabled, will automatically power on/off the
ODD when certain condition is satisfied. This does not impact
end user's experience of the ODD, only power is saved when
the ODD is not in use (i.e. no disc inside).
If unsure, say N.
config SATA_PMP
bool "SATA Port Multiplier support"
default y
help
This option adds support for SATA Port Multipliers
(the SATA version of an ethernet hub, or SAS expander).
comment "Controllers with non-SFF native interface"
config SATA_AHCI
tristate "AHCI SATA support"
depends on PCI
help
This option enables support for AHCI Serial ATA.
If unsure, say N.
config SATA_AHCI_PLATFORM
tristate "Platform AHCI SATA support"
help
This option enables support for Platform AHCI Serial ATA
controllers.
If unsure, say N.
config AHCI_DA850
tristate "DaVinci DA850 AHCI SATA support"
depends on ARCH_DAVINCI_DA850
help
This option enables support for the DaVinci DA850 SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_ST
tristate "ST AHCI SATA support"
depends on ARCH_STI
help
This option enables support for ST AHCI SATA controller.
If unsure, say N.
config AHCI_IMX
tristate "Freescale i.MX AHCI SATA support"
depends on MFD_SYSCON && (ARCH_MXC || COMPILE_TEST)
help
This option enables support for the Freescale i.MX SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_CEVA
tristate "Ceva AHCI SATA support"
depends on OF
help
This option enables support for the Xilinx Zynq
UltraScale+ MPSoC's onboard Ceva AHCI SATA.
If unsure, say N.
config AHCI_MVEBU
tristate "Marvell EBU AHCI SATA support"
depends on ARCH_MVEBU
help
This option enables support for the Marvebu EBU SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_SUNXI
tristate "Allwinner sunxi AHCI SATA support"
depends on ARCH_SUNXI
help
This option enables support for the Allwinner sunxi SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_TEGRA
tristate "NVIDIA Tegra124 AHCI SATA support"
depends on ARCH_TEGRA
help
This option enables support for the NVIDIA Tegra124 SoC's
onboard AHCI SATA.
If unsure, say N.
config AHCI_XGENE
tristate "APM X-Gene 6.0Gbps AHCI SATA host controller support"
depends on PHY_XGENE
help
This option enables support for APM X-Gene SoC SATA host controller.
config AHCI_ELPHEL
tristate "Elphel AHCI SATA driver support for elphel393 camera series"
depends on ARM
default m if ARM
help
This option enables support for Elphel AHCI SATA controller in elphel393
series cameras.
If unsure, say N.
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
help
This option enables support for Freescale 3.0Gbps SATA controller.
It can be found on MPC837x and MPC8315.
If unsure, say N.
config SATA_INIC162X
tristate "Initio 162x SATA support (Very Experimental)"
depends on PCI
help
This option enables support for Initio 162x Serial ATA.
config SATA_ACARD_AHCI
tristate "ACard AHCI variant (ATP 8620)"
depends on PCI
help
This option enables support for Acard.
If unsure, say N.
config SATA_SIL24
tristate "Silicon Image 3124/3132 SATA support"
depends on PCI
help
This option enables support for Silicon Image 3124/3132 Serial ATA.
If unsure, say N.
config ATA_SFF
bool "ATA SFF support (for legacy IDE and PATA)"
default y
help
This option adds support for ATA controllers with SFF
compliant or similar programming interface.
SFF is the legacy IDE interface that has been around since
the dawn of time. Almost all PATA controllers have an
SFF interface. Many SATA controllers have an SFF interface
when configured into a legacy compatibility mode.
For users with exclusively modern controllers like AHCI,
Silicon Image 3124, or Marvell 6440, you may choose to
disable this unneeded SFF support.
If unsure, say Y.
if ATA_SFF
comment "SFF controllers with custom DMA interface"
config PDC_ADMA
tristate "Pacific Digital ADMA support"
depends on PCI
help
This option enables support for Pacific Digital ADMA controllers
If unsure, say N.
config PATA_OCTEON_CF
tristate "OCTEON Boot Bus Compact Flash support"
depends on CAVIUM_OCTEON_SOC
help
This option enables a polled compact flash driver for use with
compact flash cards attached to the OCTEON boot bus.
If unsure, say N.
config SATA_QSTOR
tristate "Pacific Digital SATA QStor support"
depends on PCI
help
This option enables support for Pacific Digital Serial ATA QStor.
If unsure, say N.
config SATA_SX4
tristate "Promise SATA SX4 support (Experimental)"
depends on PCI
help
This option enables support for Promise Serial ATA SX4.
If unsure, say N.
config ATA_BMDMA
bool "ATA BMDMA support"
default y
help
This option adds support for SFF ATA controllers with BMDMA
capability. BMDMA stands for bus-master DMA and is the
de facto DMA interface for SFF controllers.
If unsure, say Y.
if ATA_BMDMA
comment "SATA SFF controllers with BMDMA"
config ATA_PIIX
tristate "Intel ESB, ICH, PIIX3, PIIX4 PATA/SATA support"
depends on PCI
help
This option enables support for ICH5/6/7/8 Serial ATA
and support for PATA on the Intel ESB/ICH/PIIX3/PIIX4 series
host controllers.
If unsure, say N.
config SATA_DWC
tristate "DesignWare Cores SATA support"
depends on 460EX
help
This option enables support for the on-chip SATA controller of the
AppliedMicro processor 460EX.
If unsure, say N.
config SATA_DWC_DEBUG
bool "Debugging driver version"
depends on SATA_DWC
help
This option enables debugging output in the driver.
config SATA_DWC_VDEBUG
bool "Verbose debug output"
depends on SATA_DWC_DEBUG
help
This option enables the taskfile dumping and NCQ debugging.
config SATA_HIGHBANK
tristate "Calxeda Highbank SATA support"
depends on ARCH_HIGHBANK || COMPILE_TEST
help
This option enables support for the Calxeda Highbank SoC's
onboard SATA.
If unsure, say N.
config SATA_MV
tristate "Marvell SATA support"
depends on PCI || ARCH_DOVE || ARCH_MV78XX0 || \
ARCH_MVEBU || ARCH_ORION5X || COMPILE_TEST
select GENERIC_PHY
help
This option enables support for the Marvell Serial ATA family.
Currently supports 88SX[56]0[48][01] PCI(-X) chips,
as well as the newer [67]042 PCI-X/PCIe and SOC devices.
If unsure, say N.
config SATA_NV
tristate "NVIDIA SATA support"
depends on PCI
help
This option enables support for NVIDIA Serial ATA.
If unsure, say N.
config SATA_PROMISE
tristate "Promise SATA TX2/TX4 support"
depends on PCI
help
This option enables support for Promise Serial ATA TX2/TX4.
If unsure, say N.
config SATA_RCAR
tristate "Renesas R-Car SATA support"
depends on ARCH_SHMOBILE || COMPILE_TEST
help
This option enables support for Renesas R-Car Serial ATA.
If unsure, say N.
config SATA_SIL
tristate "Silicon Image SATA support"
depends on PCI
help
This option enables support for Silicon Image Serial ATA.
If unsure, say N.
config SATA_SIS
tristate "SiS 964/965/966/180 SATA support"
depends on PCI
select PATA_SIS
help
This option enables support for SiS Serial ATA on
SiS 964/965/966/180 and Parallel ATA on SiS 180.
The PATA support for SiS 180 requires additionally to
enable the PATA_SIS driver in the config.
If unsure, say N.
config SATA_SVW
tristate "ServerWorks Frodo / Apple K2 SATA support"
depends on PCI
help
This option enables support for Broadcom/Serverworks/Apple K2
SATA support.
If unsure, say N.
config SATA_ULI
tristate "ULi Electronics SATA support"
depends on PCI
help
This option enables support for ULi Electronics SATA.
If unsure, say N.
config SATA_VIA
tristate "VIA SATA support"
depends on PCI
help
This option enables support for VIA Serial ATA.
If unsure, say N.
config SATA_VITESSE
tristate "VITESSE VSC-7174 / INTEL 31244 SATA support"
depends on PCI
help
This option enables support for Vitesse VSC7174 and Intel 31244 Serial ATA.
If unsure, say N.
comment "PATA SFF controllers with BMDMA"
config PATA_ALI
tristate "ALi PATA support"
depends on PCI
help
This option enables support for the ALi ATA interfaces
found on the many ALi chipsets.
If unsure, say N.
config PATA_AMD
tristate "AMD/NVidia PATA support"
depends on PCI
help
This option enables support for the AMD and NVidia PATA
interfaces found on the chipsets for Athlon/Athlon64.
If unsure, say N.
config PATA_ARASAN_CF
tristate "ARASAN CompactFlash PATA Controller Support"
depends on ARCH_SPEAR13XX || COMPILE_TEST
depends on DMADEVICES
select DMA_ENGINE
help
Say Y here to support the ARASAN CompactFlash PATA controller
config PATA_ARTOP
tristate "ARTOP 6210/6260 PATA support"
depends on PCI
help
This option enables support for ARTOP PATA controllers.
If unsure, say N.
config PATA_ATIIXP
tristate "ATI PATA support"
depends on PCI
help
This option enables support for the ATI ATA interfaces
found on the many ATI chipsets.
If unsure, say N.
config PATA_ATP867X
tristate "ARTOP/Acard ATP867X PATA support"
depends on PCI
help
This option enables support for ARTOP/Acard ATP867X PATA
controllers.
If unsure, say N.
config PATA_BF54X
tristate "Blackfin 54x ATAPI support"
depends on BF542 || BF548 || BF549
help
This option enables support for the built-in ATAPI controller on
Blackfin 54x family chips.
If unsure, say N.
config PATA_CMD64X
tristate "CMD64x PATA support"
depends on PCI
help
This option enables support for the CMD64x series chips
except for the CMD640.
If unsure, say N.
config PATA_CS5520
tristate "CS5510/5520 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the Cyrix 5510/5520
companion chip used with the MediaGX/Geode processor family.
If unsure, say N.
config PATA_CS5530
tristate "CS5530 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the Cyrix/NatSemi/AMD CS5530
companion chip used with the MediaGX/Geode processor family.
If unsure, say N.
config PATA_CS5535
tristate "CS5535 PATA support (Experimental)"
depends on PCI && X86_32
help
This option enables support for the NatSemi/AMD CS5535
companion chip used with the Geode processor family.
If unsure, say N.
config PATA_CS5536
tristate "CS5536 PATA support"
depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
help
This option enables support for the AMD CS5536
companion chip used with the Geode LX processor family.
If unsure, say N.
config PATA_CYPRESS
tristate "Cypress CY82C693 PATA support (Very Experimental)"
depends on PCI
help
This option enables support for the Cypress/Contaq CY82C693
chipset found in some Alpha systems
If unsure, say N.
config PATA_EFAR
tristate "EFAR SLC90E66 support"
depends on PCI
help
This option enables support for the EFAR SLC90E66
IDE controller found on some older machines.
If unsure, say N.
config PATA_EP93XX
tristate "Cirrus Logic EP93xx PATA support"
depends on ARCH_EP93XX
help
This option enables support for the PATA controller in
the Cirrus Logic EP9312 and EP9315 ARM CPU.
If unsure, say N.
config PATA_HPT366
tristate "HPT 366/368 PATA support"
depends on PCI
help
This option enables support for the HPT 366 and 368
PATA controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT37X
tristate "HPT 370/370A/371/372/374/302 PATA support"
depends on PCI
help
This option enables support for the majority of the later HPT
PATA controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT3X2N
tristate "HPT 371N/372N/302N PATA support"
depends on PCI
help
This option enables support for the N variant HPT PATA
controllers via the new ATA layer.
If unsure, say N.
config PATA_HPT3X3
tristate "HPT 343/363 PATA support"
depends on PCI
help
This option enables support for the HPT 343/363
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_HPT3X3_DMA
bool "HPT 343/363 DMA support"
depends on PATA_HPT3X3
help
This option enables DMA support for the HPT343/363
controllers. Enable with care as there are still some
problems with DMA on this chipset.
config PATA_ICSIDE
tristate "Acorn ICS PATA support"
depends on ARM && ARCH_ACORN
help
On Acorn systems, say Y here if you wish to use the ICS PATA
interface card. This is not required for ICS partition support.
If you are unsure, say N to this.
config PATA_IMX
tristate "PATA support for Freescale iMX"
depends on ARCH_MXC
help
This option enables support for the PATA host available on Freescale
iMX SoCs.
If unsure, say N.
config PATA_IT8213
tristate "IT8213 PATA support (Experimental)"
depends on PCI
help
This option enables support for the ITE 821 PATA
controllers via the new ATA layer.
If unsure, say N.
config PATA_IT821X
tristate "IT8211/2 PATA support"
depends on PCI
help
This option enables support for the ITE 8211 and 8212
PATA controllers via the new ATA layer, including RAID
mode.
If unsure, say N.
config PATA_JMICRON
tristate "JMicron PATA support"
depends on PCI
help
Enable support for the JMicron IDE controller, via the new
ATA layer.
If unsure, say N.
config PATA_MACIO
tristate "Apple PowerMac/PowerBook internal 'MacIO' IDE"
depends on PPC_PMAC
help
Most IDE capable PowerMacs have IDE busses driven by a variant
of this controller which is part of the Apple chipset used on
most PowerMac models. Some models have multiple busses using
different chipsets, though generally, MacIO is one of them.
config PATA_MARVELL
tristate "Marvell PATA support via legacy mode"
depends on PCI
help
This option enables limited support for the Marvell 88SE61xx ATA
controllers. If you wish to use only the SATA ports then select
the AHCI driver alone. If you wish to the use the PATA port or
both SATA and PATA include this driver.
If unsure, say N.
config PATA_MPC52xx
tristate "Freescale MPC52xx SoC internal IDE"
depends on PPC_MPC52xx && PPC_BESTCOMM
select PPC_BESTCOMM_ATA
help
This option enables support for integrated IDE controller
of the Freescale MPC52xx SoC.
If unsure, say N.
config PATA_NETCELL
tristate "NETCELL Revolution RAID support"
depends on PCI
help
This option enables support for the Netcell Revolution RAID
PATA controller.
If unsure, say N.
config PATA_NINJA32
tristate "Ninja32/Delkin Cardbus ATA support"
depends on PCI
help
This option enables support for the Ninja32, Delkin and
possibly other brands of Cardbus ATA adapter
If unsure, say N.
config PATA_NS87415
tristate "Nat Semi NS87415 PATA support"
depends on PCI
help
This option enables support for the National Semiconductor
NS87415 PCI-IDE controller.
If unsure, say N.
config PATA_OLDPIIX
tristate "Intel PATA old PIIX support"
depends on PCI
help
This option enables support for early PIIX PATA support.
If unsure, say N.
config PATA_OPTIDMA
tristate "OPTI FireStar PATA support (Very Experimental)"
depends on PCI
help
This option enables DMA/PIO support for the later OPTi
controllers found on some old motherboards and in some
laptops.
If unsure, say N.
config PATA_PDC2027X
tristate "Promise PATA 2027x support"
depends on PCI
help
This option enables support for Promise PATA pdc20268 to pdc20277 host adapters.
If unsure, say N.
config PATA_PDC_OLD
tristate "Older Promise PATA controller support"
depends on PCI
help
This option enables support for the Promise 20246, 20262, 20263,
20265 and 20267 adapters.
If unsure, say N.
config PATA_RADISYS
tristate "RADISYS 82600 PATA support (Experimental)"
depends on PCI
help
This option enables support for the RADISYS 82600
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_RDC
tristate "RDC PATA support"
depends on PCI
help
This option enables basic support for the later RDC PATA controllers
controllers via the new ATA layer. For the RDC 1010, you need to
enable the IT821X driver instead.
If unsure, say N.
config PATA_SC1200
tristate "SC1200 PATA support"
depends on PCI && (X86_32 || COMPILE_TEST)
help
This option enables support for the NatSemi/AMD SC1200 SoC
companion chip used with the Geode processor family.
If unsure, say N.
config PATA_SCC
tristate "Toshiba's Cell Reference Set IDE support"
depends on PCI && PPC_CELLEB
help
This option enables support for the built-in IDE controller on
Toshiba Cell Reference Board.
If unsure, say N.
config PATA_SCH
tristate "Intel SCH PATA support"
depends on PCI
help
This option enables support for Intel SCH PATA on the Intel
SCH (US15W, US15L, UL11L) series host controllers.
If unsure, say N.
config PATA_SERVERWORKS
tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support"
depends on PCI
help
This option enables support for the Serverworks OSB4/CSB5/CSB6 and
HT1000 PATA controllers, via the new ATA layer.
If unsure, say N.
config PATA_SIL680
tristate "CMD / Silicon Image 680 PATA support"
depends on PCI
help
This option enables support for CMD / Silicon Image 680 PATA.
If unsure, say N.
config PATA_SIS
tristate "SiS PATA support"
depends on PCI
help
This option enables support for SiS PATA controllers
If unsure, say N.
config PATA_TOSHIBA
tristate "Toshiba Piccolo support (Experimental)"
depends on PCI
help
Support for the Toshiba Piccolo controllers. Currently only the
primary channel is supported by this driver.
If unsure, say N.
config PATA_TRIFLEX
tristate "Compaq Triflex PATA support"
depends on PCI
help
Enable support for the Compaq 'Triflex' IDE controller as found
on many Compaq Pentium-Pro systems, via the new ATA layer.
If unsure, say N.
config PATA_VIA
tristate "VIA PATA support"
depends on PCI
help
This option enables support for the VIA PATA interfaces
found on the many VIA chipsets.
If unsure, say N.
config PATA_PXA
tristate "PXA DMA-capable PATA support"
depends on ARCH_PXA
help
This option enables support for harddrive attached to PXA CPU's bus.
NOTE: This driver utilizes PXA DMA controller, in case your hardware
is not capable of doing MWDMA, use pata_platform instead.
If unsure, say N.
config PATA_WINBOND
tristate "Winbond SL82C105 PATA support"
depends on PCI
help
This option enables support for SL82C105 PATA devices found in the
Netwinder and some other systems
If unsure, say N.
endif # ATA_BMDMA
comment "PIO-only SFF controllers"
config PATA_AT32
tristate "Atmel AVR32 PATA support (Experimental)"
depends on AVR32 && PLATFORM_AT32AP
help
This option enables support for the IDE devices on the
Atmel AT32AP platform.
If unsure, say N.
config PATA_AT91
tristate "PATA support for AT91SAM9260"
depends on ARM && SOC_AT91SAM9
depends on !ARCH_MULTIPLATFORM
help
This option enables support for IDE devices on the Atmel AT91SAM9260 SoC.
If unsure, say N.
config PATA_CMD640_PCI
tristate "CMD640 PCI PATA support (Experimental)"
depends on PCI
help
This option enables support for the CMD640 PCI IDE
interface chip. Only the primary channel is currently
supported.
If unsure, say N.
config PATA_ISAPNP
tristate "ISA Plug and Play PATA support"
depends on ISAPNP
help
This option enables support for ISA plug & play ATA
controllers such as those found on old soundcards.
If unsure, say N.
config PATA_IXP4XX_CF
tristate "IXP4XX Compact Flash support"
depends on ARCH_IXP4XX
help
This option enables support for a Compact Flash connected on
the ixp4xx expansion bus. This driver had been written for
Loft/Avila boards in mind but can work with others.
If unsure, say N.
config PATA_MPIIX
tristate "Intel PATA MPIIX support"
depends on PCI
help
This option enables support for MPIIX PATA support.
If unsure, say N.
config PATA_NS87410
tristate "Nat Semi NS87410 PATA support"
depends on PCI
help
This option enables support for the National Semiconductor
NS87410 PCI-IDE controller.
If unsure, say N.
config PATA_OPTI
tristate "OPTI621/6215 PATA support (Very Experimental)"
depends on PCI
help
This option enables full PIO support for the early Opti ATA
controllers found on some old motherboards.
If unsure, say N.
config PATA_PALMLD
tristate "Palm LifeDrive PATA support"
depends on MACH_PALMLD
help
This option enables support for Palm LifeDrive's internal ATA
port via the new ATA layer.
If unsure, say N.
config PATA_PCMCIA
tristate "PCMCIA PATA support"
depends on PCMCIA
help
This option enables support for PCMCIA ATA interfaces, including
compact flash card adapters via the new ATA layer.
If unsure, say N.
config PATA_PLATFORM
tristate "Generic platform device PATA support"
depends on EXPERT || PPC || HAVE_PATA_PLATFORM
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems.
If unsure, say N.
config PATA_OF_PLATFORM
tristate "OpenFirmware platform device PATA support"
depends on PATA_PLATFORM && OF
help
This option enables support for generic directly connected ATA
devices commonly found on embedded systems with OpenFirmware
bindings.
If unsure, say N.
config PATA_QDI
tristate "QDI VLB PATA support"
depends on ISA
select PATA_LEGACY
help
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
config PATA_RB532
tristate "RouterBoard 532 PATA CompactFlash support"
depends on MIKROTIK_RB532
help
This option enables support for the RouterBoard 532
PATA CompactFlash controller.
If unsure, say N.
config PATA_RZ1000
tristate "PC Tech RZ1000 PATA support"
depends on PCI
help
This option enables basic support for the PC Tech RZ1000/1
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_SAMSUNG_CF
tristate "Samsung SoC PATA support"
depends on SAMSUNG_DEV_IDE
help
This option enables basic support for Samsung's S3C/S5P board
PATA controllers via the new ATA layer
If unsure, say N.
config PATA_WINBOND_VLB
tristate "Winbond W83759A VLB PATA support (Experimental)"
depends on ISA
select PATA_LEGACY
help
Support for the Winbond W83759A controller on Vesa Local Bus
systems.
comment "Generic fallback / legacy drivers"
config PATA_ACPI
tristate "ACPI firmware driver for PATA"
depends on ATA_ACPI && ATA_BMDMA
help
This option enables an ACPI method driver which drives
motherboard PATA controller interfaces through the ACPI
firmware in the BIOS. This driver can sometimes handle
otherwise unsupported hardware.
config ATA_GENERIC
tristate "Generic ATA support"
depends on PCI && ATA_BMDMA
help
This option enables support for generic BIOS configured
ATA controllers via the new ATA layer
If unsure, say N.
config PATA_LEGACY
tristate "Legacy ISA PATA support (Experimental)"
depends on (ISA || PCI)
help
This option enables support for ISA/VLB/PCI bus legacy PATA
ports and allows them to be accessed via the new ATA layer.
If unsure, say N.
endif # ATA_SFF
endif # ATA
src/drivers/ata/Makefile
0 → 100644
View file @
2ca997e5
obj-$(CONFIG_ATA)
+=
libata.o
# non-SFF interface
obj-$(CONFIG_SATA_AHCI)
+=
ahci.o
libahci.o
libahci_debug.o
obj-$(CONFIG_SATA_ACARD_AHCI)
+=
acard-ahci.o
libahci.o
obj-$(CONFIG_SATA_AHCI_PLATFORM)
+=
ahci_platform.o
libahci.o
libahci_platform.o
libahci_debug.o
obj-$(CONFIG_AHCI_ELPHEL)
+=
ahci_elphel.o
libahci.o
libahci_platform.o
libahci_debug.o
obj-$(CONFIG_SATA_FSL)
+=
sata_fsl.o
obj-$(CONFIG_SATA_INIC162X)
+=
sata_inic162x.o
obj-$(CONFIG_SATA_SIL24)
+=
sata_sil24.o
obj-$(CONFIG_SATA_DWC)
+=
sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK)
+=
sata_highbank.o
libahci.o
obj-$(CONFIG_AHCI_CEVA)
+=
ahci_ceva.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_DA850)
+=
ahci_da850.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_IMX)
+=
ahci_imx.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_MVEBU)
+=
ahci_mvebu.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_SUNXI)
+=
ahci_sunxi.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_ST)
+=
ahci_st.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_TEGRA)
+=
ahci_tegra.o
libahci.o
libahci_platform.o
obj-$(CONFIG_AHCI_XGENE)
+=
ahci_xgene.o
libahci.o
libahci_platform.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA)
+=
pdc_adma.o
obj-$(CONFIG_PATA_ARASAN_CF)
+=
pata_arasan_cf.o
obj-$(CONFIG_PATA_OCTEON_CF)
+=
pata_octeon_cf.o
obj-$(CONFIG_SATA_QSTOR)
+=
sata_qstor.o
obj-$(CONFIG_SATA_SX4)
+=
sata_sx4.o
# SFF SATA w/ BMDMA
obj-$(CONFIG_ATA_PIIX)
+=
ata_piix.o
obj-$(CONFIG_SATA_MV)
+=
sata_mv.o
obj-$(CONFIG_SATA_NV)
+=
sata_nv.o
obj-$(CONFIG_SATA_PROMISE)
+=
sata_promise.o
obj-$(CONFIG_SATA_RCAR)
+=
sata_rcar.o
obj-$(CONFIG_SATA_SIL)
+=
sata_sil.o
obj-$(CONFIG_SATA_SIS)
+=
sata_sis.o
obj-$(CONFIG_SATA_SVW)
+=
sata_svw.o
obj-$(CONFIG_SATA_ULI)
+=
sata_uli.o
obj-$(CONFIG_SATA_VIA)
+=
sata_via.o
obj-$(CONFIG_SATA_VITESSE)
+=
sata_vsc.o
# SFF PATA w/ BMDMA
obj-$(CONFIG_PATA_ALI)
+=
pata_ali.o
obj-$(CONFIG_PATA_AMD)
+=
pata_amd.o
obj-$(CONFIG_PATA_ARTOP)
+=
pata_artop.o
obj-$(CONFIG_PATA_ATIIXP)
+=
pata_atiixp.o
obj-$(CONFIG_PATA_ATP867X)
+=
pata_atp867x.o
obj-$(CONFIG_PATA_BF54X)
+=
pata_bf54x.o
obj-$(CONFIG_PATA_CMD64X)
+=
pata_cmd64x.o
obj-$(CONFIG_PATA_CS5520)
+=
pata_cs5520.o
obj-$(CONFIG_PATA_CS5530)
+=
pata_cs5530.o
obj-$(CONFIG_PATA_CS5535)
+=
pata_cs5535.o
obj-$(CONFIG_PATA_CS5536)
+=
pata_cs5536.o
obj-$(CONFIG_PATA_CYPRESS)
+=
pata_cypress.o
obj-$(CONFIG_PATA_EFAR)
+=
pata_efar.o
obj-$(CONFIG_PATA_EP93XX)
+=
pata_ep93xx.o
obj-$(CONFIG_PATA_HPT366)
+=
pata_hpt366.o
obj-$(CONFIG_PATA_HPT37X)
+=
pata_hpt37x.o
obj-$(CONFIG_PATA_HPT3X2N)
+=
pata_hpt3x2n.o
obj-$(CONFIG_PATA_HPT3X3)
+=
pata_hpt3x3.o
obj-$(CONFIG_PATA_ICSIDE)
+=
pata_icside.o
obj-$(CONFIG_PATA_IMX)
+=
pata_imx.o
obj-$(CONFIG_PATA_IT8213)
+=
pata_it8213.o
obj-$(CONFIG_PATA_IT821X)
+=
pata_it821x.o
obj-$(CONFIG_PATA_JMICRON)
+=
pata_jmicron.o
obj-$(CONFIG_PATA_MACIO)
+=
pata_macio.o
obj-$(CONFIG_PATA_MARVELL)
+=
pata_marvell.o
obj-$(CONFIG_PATA_MPC52xx)
+=
pata_mpc52xx.o
obj-$(CONFIG_PATA_NETCELL)
+=
pata_netcell.o
obj-$(CONFIG_PATA_NINJA32)
+=
pata_ninja32.o
obj-$(CONFIG_PATA_NS87415)
+=
pata_ns87415.o
obj-$(CONFIG_PATA_OLDPIIX)
+=
pata_oldpiix.o
obj-$(CONFIG_PATA_OPTIDMA)
+=
pata_optidma.o
obj-$(CONFIG_PATA_PDC2027X)
+=
pata_pdc2027x.o
obj-$(CONFIG_PATA_PDC_OLD)
+=
pata_pdc202xx_old.o
obj-$(CONFIG_PATA_RADISYS)
+=
pata_radisys.o
obj-$(CONFIG_PATA_RDC)
+=
pata_rdc.o
obj-$(CONFIG_PATA_SC1200)
+=
pata_sc1200.o
obj-$(CONFIG_PATA_SCC)
+=
pata_scc.o
obj-$(CONFIG_PATA_SCH)
+=
pata_sch.o
obj-$(CONFIG_PATA_SERVERWORKS)
+=
pata_serverworks.o
obj-$(CONFIG_PATA_SIL680)
+=
pata_sil680.o
obj-$(CONFIG_PATA_SIS)
+=
pata_sis.o
obj-$(CONFIG_PATA_TOSHIBA)
+=
pata_piccolo.o
obj-$(CONFIG_PATA_TRIFLEX)
+=
pata_triflex.o
obj-$(CONFIG_PATA_VIA)
+=
pata_via.o
obj-$(CONFIG_PATA_WINBOND)
+=
pata_sl82c105.o
# SFF PIO only
obj-$(CONFIG_PATA_AT32)
+=
pata_at32.o
obj-$(CONFIG_PATA_AT91)
+=
pata_at91.o
obj-$(CONFIG_PATA_CMD640_PCI)
+=
pata_cmd640.o
obj-$(CONFIG_PATA_ISAPNP)
+=
pata_isapnp.o
obj-$(CONFIG_PATA_IXP4XX_CF)
+=
pata_ixp4xx_cf.o
obj-$(CONFIG_PATA_MPIIX)
+=
pata_mpiix.o
obj-$(CONFIG_PATA_NS87410)
+=
pata_ns87410.o
obj-$(CONFIG_PATA_OPTI)
+=
pata_opti.o
obj-$(CONFIG_PATA_PCMCIA)
+=
pata_pcmcia.o
obj-$(CONFIG_PATA_PALMLD)
+=
pata_palmld.o
obj-$(CONFIG_PATA_PLATFORM)
+=
pata_platform.o
obj-$(CONFIG_PATA_OF_PLATFORM)
+=
pata_of_platform.o
obj-$(CONFIG_PATA_RB532)
+=
pata_rb532_cf.o
obj-$(CONFIG_PATA_RZ1000)
+=
pata_rz1000.o
obj-$(CONFIG_PATA_SAMSUNG_CF)
+=
pata_samsung_cf.o
obj-$(CONFIG_PATA_PXA)
+=
pata_pxa.o
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI)
+=
pata_acpi.o
# Should be last but one libata driver
obj-$(CONFIG_ATA_GENERIC)
+=
ata_generic.o
# Should be last libata driver
obj-$(CONFIG_PATA_LEGACY)
+=
pata_legacy.o
libata-y
:=
libata-core.o libata-scsi.o libata-eh.o libata-transport.o
libata-$(CONFIG_ATA_SFF)
+=
libata-sff.o
libata-$(CONFIG_SATA_PMP)
+=
libata-pmp.o
libata-$(CONFIG_ATA_ACPI)
+=
libata-acpi.o
libata-$(CONFIG_SATA_ZPODD)
+=
libata-zpodd.o
src/drivers/ata/ahci_elphel.c
View file @
2ca997e5
...
@@ -19,13 +19,14 @@
...
@@ -19,13 +19,14 @@
#include <linux/ahci_platform.h>
#include <linux/ahci_platform.h>
#include <linux/of.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include "ahci.h"
#include "ahci.h"
#include "libahci_debug.h"
#define DRV_NAME "elphel-ahci"
#define DRV_NAME "elphel-ahci"
#define MARKER "+"
/* Property names from device tree, these are specific for the controller */
/* Property names from device tree, these are specific for the controller */
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_CLB_OFFS "clb_offs"
...
@@ -39,6 +40,7 @@ static const struct of_device_id ahci_elphel_of_match[];
...
@@ -39,6 +40,7 @@ static const struct of_device_id ahci_elphel_of_match[];
struct
elphel_ahci_priv
{
struct
elphel_ahci_priv
{
u32
clb_offs
;
u32
clb_offs
;
u32
fb_offs
;
u32
fb_offs
;
u32
base_addr
;
};
};
static
int
elphel_port_start
(
struct
ata_port
*
ap
)
static
int
elphel_port_start
(
struct
ata_port
*
ap
)
...
@@ -50,8 +52,12 @@ static int elphel_port_start(struct ata_port *ap)
...
@@ -50,8 +52,12 @@ static int elphel_port_start(struct ata_port *ap)
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
const
struct
elphel_ahci_priv
*
dpriv
=
hpriv
->
plat_data
;
const
struct
elphel_ahci_priv
*
dpriv
=
hpriv
->
plat_data
;
libahci_debug_init
(
ap
->
host
);
dev_info
(
dev
,
"starting port %d"
,
ap
->
port_no
);
dev_info
(
dev
,
"starting port %d"
,
ap
->
port_no
);
/*pp = devm_kzalloc(dev, sizeof(struct ahci_port_priv), GFP_KERNEL);
libahci_debug_wait_flag
();
pp
=
devm_kzalloc
(
dev
,
sizeof
(
struct
ahci_port_priv
),
GFP_KERNEL
);
if
(
!
pp
)
if
(
!
pp
)
return
-
ENOMEM
;
return
-
ENOMEM
;
...
@@ -60,34 +66,47 @@ static int elphel_port_start(struct ata_port *ap)
...
@@ -60,34 +66,47 @@ static int elphel_port_start(struct ata_port *ap)
return
-
ENOMEM
;
return
-
ENOMEM
;
memset
(
mem
,
0
,
AHCI_CMD_TBL_AR_SZ
);
memset
(
mem
,
0
,
AHCI_CMD_TBL_AR_SZ
);
pp
->
cmd_tbl
=
mem
;
pp
->
cmd_tbl
=
mem
;
pp->cmd_tbl_dma = mem_dma;
*/
pp
->
cmd_tbl_dma
=
mem_dma
;
/*
/*
* Set predefined addresses
* Set predefined addresses
*/
*/
/*pp->cmd_slot = hpriv->mmio + dpriv->clb_offs;
pp
->
cmd_slot
=
hpriv
->
mmio
+
dpriv
->
clb_offs
;
pp->cmd_slot_dma = virt_to_phys(pp->cmd_slot);
//pp->cmd_slot_dma = virt_to_phys(pp->cmd_slot);
pp
->
cmd_slot_dma
=
0x80000000
+
dpriv
->
clb_offs
;
pp
->
rx_fis
=
hpriv
->
mmio
+
dpriv
->
fb_offs
;
pp
->
rx_fis
=
hpriv
->
mmio
+
dpriv
->
fb_offs
;
pp->rx_fis_dma = virt_to_phys(pp->rx_fis);*/
//pp->rx_fis_dma = virt_to_phys(pp->rx_fis);
pp
->
rx_fis_dma
=
0x80000000
+
dpriv
->
fb_offs
;
/*dev_info(dev, "cmd_slot: 0x%p", pp->cmd_slot);
dev_info(dev, "cmd_slot_dma: 0x%08u", pp->cmd_slot_dma);
dev_info(dev, "rx_fis: 0x%p", pp->rx_fis);
dev_info(dev, "rx_fis_dma: 0x%08u", pp->rx_fis_dma);
dev_info(dev, "base_addr: 0x%08u", dpriv->base_addr);*/
/*
/*
* Save off initial list of interrupts to be enabled.
* Save off initial list of interrupts to be enabled.
* This could be changed later
* This could be changed later
*/
*/
/*
pp->intr_mask = DEF_PORT_IRQ;
pp
->
intr_mask
=
DEF_PORT_IRQ
;
ap
->
private_data
=
pp
;
ap
->
private_data
=
pp
;
return ahci_port_resume(ap);*/
libahci_debug_state_dump
(
ap
);
return
0
;
libahci_debug_state_dump
(
ap
);
return
ahci_port_resume
(
ap
);
}
}
static
int
elphel_parse_prop
(
const
struct
device_node
*
devn
,
static
int
elphel_parse_prop
(
const
struct
device_node
*
devn
,
struct
device
*
dev
,
struct
device
*
dev
,
struct
elphel_ahci_priv
*
dpriv
)
struct
elphel_ahci_priv
*
dpriv
)
{
{
u64
size
;
unsigned
int
flags
;
const
__be32
*
val
;
const
__be32
*
val
;
struct
resource
res
;
if
(
!
devn
)
{
if
(
!
devn
)
{
dev_err
(
dev
,
"device tree node is not found"
);
dev_err
(
dev
,
"device tree node is not found"
);
...
@@ -98,6 +117,14 @@ static int elphel_parse_prop(const struct device_node *devn,
...
@@ -98,6 +117,14 @@ static int elphel_parse_prop(const struct device_node *devn,
dpriv
->
clb_offs
=
be32_to_cpup
(
val
);
dpriv
->
clb_offs
=
be32_to_cpup
(
val
);
val
=
of_get_property
(
devn
,
PROP_NAME_FB_OFFS
,
NULL
);
val
=
of_get_property
(
devn
,
PROP_NAME_FB_OFFS
,
NULL
);
dpriv
->
fb_offs
=
be32_to_cpup
(
val
);
dpriv
->
fb_offs
=
be32_to_cpup
(
val
);
val
=
of_get_address
(
devn
,
0
,
NULL
,
NULL
);
if
(
val
!=
NULL
)
{
dpriv
->
base_addr
=
be32_to_cpu
(
val
);
dev_info
(
dev
,
"base_addr: 0x%08u"
,
dpriv
->
base_addr
);
}
else
{
dev_err
(
dev
,
"can not get register address"
);
}
//of_address_to_resource(devn, 0, &res);
return
0
;
return
0
;
}
}
...
@@ -109,11 +136,12 @@ static int elphel_drv_probe(struct platform_device *pdev)
...
@@ -109,11 +136,12 @@ static int elphel_drv_probe(struct platform_device *pdev)
struct
elphel_ahci_priv
*
drv_priv
;
struct
elphel_ahci_priv
*
drv_priv
;
struct
device
*
dev
=
&
pdev
->
dev
;
struct
device
*
dev
=
&
pdev
->
dev
;
const
struct
of_device_id
*
match
;
const
struct
of_device_id
*
match
;
void
__iomem
*
mmio
=
NULL
;
struct
resource
*
res
;
struct
resource
*
res
;
unsigned
int
reg_val
;
dev_info
(
&
pdev
->
dev
,
"probing Elphel AHCI driver"
);
dev_info
(
&
pdev
->
dev
,
"probing Elphel AHCI driver"
);
drv_priv
=
devm_kzalloc
(
dev
,
sizeof
(
struct
elphel_ahci_priv
),
GFP_KERNEL
);
drv_priv
=
devm_kzalloc
(
dev
,
sizeof
(
struct
elphel_ahci_priv
),
GFP_KERNEL
);
if
(
!
drv_priv
)
if
(
!
drv_priv
)
return
-
ENOMEM
;
return
-
ENOMEM
;
...
@@ -126,39 +154,44 @@ static int elphel_drv_probe(struct platform_device *pdev)
...
@@ -126,39 +154,44 @@ static int elphel_drv_probe(struct platform_device *pdev)
if
(
ret
!=
0
)
if
(
ret
!=
0
)
return
ret
;
return
ret
;
/*
hpriv = ahci_platform_get_resources(pdev);
hpriv
=
ahci_platform_get_resources
(
pdev
);
if
(
IS_ERR
(
hpriv
))
if
(
IS_ERR
(
hpriv
))
return
PTR_ERR
(
hpriv
);
return
PTR_ERR
(
hpriv
);
hpriv->plat_data = drv_priv;
dev_info(dev, "ahci platform resources set");*/
dev_info
(
dev
,
"get IORESOURCE_MEM"
);
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res
)
{
dev_err
(
dev
,
"can not get resourse"
);
return
-
EINVAL
;
}
dev_info
(
dev
,
"resource name: %s"
,
res
->
name
);
dev_info
(
dev
,
"resource start: 0x%08x"
,
res
->
start
);
dev_info
(
dev
,
"resource end: 0x%08x"
,
res
->
end
);
/*mmio = hpriv->mmio;
hpriv
->
plat_data
=
drv_priv
;
if (!mmio) {
dev_err(dev, "mmio pointer is not initialized");
ahci_platform_disable_resources(hpriv);
return -EINVAL;
}
dev_info(dev, "mmio pointer: 0x%08p", mmio);
dev_info(dev, "HOST CAP register: 0x%08x", readl(mmio + HOST_CAP));
ahci_platform_disable_resources(hpriv);*/
/*ret = ahci_platform_init_host(pdev, hpriv, &ahci_elphel_port_info,
reg_val
=
readl
(
hpriv
->
mmio
+
HOST_CAP
);
dev_info
(
dev
,
"HOST CAP register: 0x%08x"
,
reg_val
);
reg_val
=
readl
(
hpriv
->
mmio
+
HOST_CTL
);
dev_info
(
dev
,
"HOST GHC register: 0x%08x"
,
reg_val
);
reg_val
=
readl
(
hpriv
->
mmio
+
HOST_IRQ_STAT
);
dev_info
(
dev
,
"HOST IS register: 0x%08x"
,
reg_val
);
reg_val
=
readl
(
hpriv
->
mmio
+
HOST_PORTS_IMPL
);
dev_info
(
dev
,
"HOST PI register: 0x%08x"
,
reg_val
);
reg_val
=
readl
(
hpriv
->
mmio
+
HOST_VERSION
);
dev_info
(
dev
,
"HOST VS register: 0x%08x"
,
reg_val
);
phys_addr_t
paddr
=
virt_to_phys
(
hpriv
->
mmio
);
void
*
vaddr
=
phys_to_virt
(
paddr
);
dev_err
(
dev
,
"current mmio virt addr: 0x%p
\n
"
,
hpriv
->
mmio
);
dev_err
(
dev
,
"current mmio virt addr as uint: 0x%08x
\n
"
,
hpriv
->
mmio
);
dev_err
(
dev
,
"mmio phys addr: 0x%08x
\n
"
,
paddr
);
dev_err
(
dev
,
"mmio phys addr as tr: 0x%p
\n
"
,
paddr
);
dev_err
(
dev
,
"back converted mmio virt addr: 0x%p
\n
"
,
vaddr
);
//printk(KERN_DEBUG, "current mmio virt addr: %p\n", hpriv->mmio);
//printk(KERN_DEBUG, "mmio phys addr: %u\n", paddr);
//printk(KERN_DEBUG, "back converted mmio virt addr: %p\n", vaddr);
printk
(
KERN_DEBUG
"======"
);
ret
=
ahci_platform_init_host
(
pdev
,
hpriv
,
&
ahci_elphel_port_info
,
&
ahci_platform_sht
);
&
ahci_platform_sht
);
if
(
ret
)
{
if
(
ret
)
{
dev_err
(
dev
,
"can not initialize platform host"
);
ahci_platform_disable_resources
(
hpriv
);
ahci_platform_disable_resources
(
hpriv
);
return
ret
;
return
ret
;
}
}
dev_info(dev, "ahci platform host initialized");
*/
dev_info
(
dev
,
"ahci platform host initialized"
);
return
0
;
return
0
;
}
}
...
@@ -166,12 +199,30 @@ static int elphel_drv_probe(struct platform_device *pdev)
...
@@ -166,12 +199,30 @@ static int elphel_drv_probe(struct platform_device *pdev)
static
int
elphel_drv_remove
(
struct
platform_device
*
pdev
)
static
int
elphel_drv_remove
(
struct
platform_device
*
pdev
)
{
{
dev_info
(
&
pdev
->
dev
,
"removing Elphel AHCI driver"
);
dev_info
(
&
pdev
->
dev
,
"removing Elphel AHCI driver"
);
//ata_platform_remove_one(pdev);
ata_platform_remove_one
(
pdev
);
libahci_debug_exit
();
return
0
;
}
static
unsigned
int
elphel_read_id
(
struct
ata_device
*
dev
,
struct
ata_taskfile
*
tf
,
u16
*
id
)
{
u32
err_mask
;
struct
device
*
d
=
&
dev
->
tdev
;
err_mask
=
ata_do_dev_read_id
(
dev
,
tf
,
id
);
if
(
err_mask
)
return
err_mask
;
dev_info
(
d
,
"issue identify command"
);
return
0
;
}
}
static
struct
ata_port_operations
ahci_elphel_ops
=
{
static
struct
ata_port_operations
ahci_elphel_ops
=
{
.
inherits
=
&
ahci_ops
,
.
inherits
=
&
ahci_ops
,
.
port_start
=
elphel_port_start
,
.
port_start
=
elphel_port_start
,
.
read_id
=
elphel_read_id
,
};
};
static
const
struct
ata_port_info
ahci_elphel_port_info
=
{
static
const
struct
ata_port_info
ahci_elphel_port_info
=
{
...
...
src/drivers/ata/libahci.c
0 → 100644
View file @
2ca997e5
/*
* libahci.c - Common AHCI SATA low-level routines
*
* Maintained by: Tejun Heo <tj@kernel.org>
* Please ALWAYS copy linux-ide@vger.kernel.org
* on emails.
*
* Copyright 2004-2005 Red Hat, Inc.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
*
* libata documentation is available via 'make {ps|pdf}docs',
* as Documentation/DocBook/libata.*
*
* AHCI hardware documentation:
* http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
* http://www.intel.com/technology/serialata/pdf/rev1_1.pdf
*
*/
#include <linux/kernel.h>
#include <linux/gfp.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <linux/libata.h>
#include "ahci.h"
#include "libahci_debug.h"
#include "libata.h"
static
int
ahci_skip_host_reset
;
int
ahci_ignore_sss
;
EXPORT_SYMBOL_GPL
(
ahci_ignore_sss
);
module_param_named
(
skip_host_reset
,
ahci_skip_host_reset
,
int
,
0444
);
MODULE_PARM_DESC
(
skip_host_reset
,
"skip global host reset (0=don't skip, 1=skip)"
);
module_param_named
(
ignore_sss
,
ahci_ignore_sss
,
int
,
0444
);
MODULE_PARM_DESC
(
ignore_sss
,
"Ignore staggered spinup flag (0=don't ignore, 1=ignore)"
);
static
int
ahci_set_lpm
(
struct
ata_link
*
link
,
enum
ata_lpm_policy
policy
,
unsigned
hints
);
static
ssize_t
ahci_led_show
(
struct
ata_port
*
ap
,
char
*
buf
);
static
ssize_t
ahci_led_store
(
struct
ata_port
*
ap
,
const
char
*
buf
,
size_t
size
);
static
ssize_t
ahci_transmit_led_message
(
struct
ata_port
*
ap
,
u32
state
,
ssize_t
size
);
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
bool
ahci_qc_fill_rtf
(
struct
ata_queued_cmd
*
qc
);
static
int
ahci_port_start
(
struct
ata_port
*
ap
);
static
void
ahci_port_stop
(
struct
ata_port
*
ap
);
static
void
ahci_qc_prep
(
struct
ata_queued_cmd
*
qc
);
static
int
ahci_pmp_qc_defer
(
struct
ata_queued_cmd
*
qc
);
static
void
ahci_freeze
(
struct
ata_port
*
ap
);
static
void
ahci_thaw
(
struct
ata_port
*
ap
);
static
void
ahci_set_aggressive_devslp
(
struct
ata_port
*
ap
,
bool
sleep
);
static
void
ahci_enable_fbs
(
struct
ata_port
*
ap
);
static
void
ahci_disable_fbs
(
struct
ata_port
*
ap
);
static
void
ahci_pmp_attach
(
struct
ata_port
*
ap
);
static
void
ahci_pmp_detach
(
struct
ata_port
*
ap
);
static
int
ahci_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
);
static
int
ahci_pmp_retry_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
);
static
int
ahci_hardreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
);
static
void
ahci_postreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
);
static
void
ahci_post_internal_cmd
(
struct
ata_queued_cmd
*
qc
);
static
void
ahci_dev_config
(
struct
ata_device
*
dev
);
#ifdef CONFIG_PM
static
int
ahci_port_suspend
(
struct
ata_port
*
ap
,
pm_message_t
mesg
);
#endif
static
ssize_t
ahci_activity_show
(
struct
ata_device
*
dev
,
char
*
buf
);
static
ssize_t
ahci_activity_store
(
struct
ata_device
*
dev
,
enum
sw_activity
val
);
static
void
ahci_init_sw_activity
(
struct
ata_link
*
link
);
static
ssize_t
ahci_show_host_caps
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
ahci_show_host_cap2
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
ahci_show_host_version
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
ahci_show_port_cmd
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
ahci_read_em_buffer
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
ssize_t
ahci_store_em_buffer
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
);
static
ssize_t
ahci_show_em_supported
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
static
DEVICE_ATTR
(
ahci_host_caps
,
S_IRUGO
,
ahci_show_host_caps
,
NULL
);
static
DEVICE_ATTR
(
ahci_host_cap2
,
S_IRUGO
,
ahci_show_host_cap2
,
NULL
);
static
DEVICE_ATTR
(
ahci_host_version
,
S_IRUGO
,
ahci_show_host_version
,
NULL
);
static
DEVICE_ATTR
(
ahci_port_cmd
,
S_IRUGO
,
ahci_show_port_cmd
,
NULL
);
static
DEVICE_ATTR
(
em_buffer
,
S_IWUSR
|
S_IRUGO
,
ahci_read_em_buffer
,
ahci_store_em_buffer
);
static
DEVICE_ATTR
(
em_message_supported
,
S_IRUGO
,
ahci_show_em_supported
,
NULL
);
struct
device_attribute
*
ahci_shost_attrs
[]
=
{
&
dev_attr_link_power_management_policy
,
&
dev_attr_em_message_type
,
&
dev_attr_em_message
,
&
dev_attr_ahci_host_caps
,
&
dev_attr_ahci_host_cap2
,
&
dev_attr_ahci_host_version
,
&
dev_attr_ahci_port_cmd
,
&
dev_attr_em_buffer
,
&
dev_attr_em_message_supported
,
NULL
};
EXPORT_SYMBOL_GPL
(
ahci_shost_attrs
);
struct
device_attribute
*
ahci_sdev_attrs
[]
=
{
&
dev_attr_sw_activity
,
&
dev_attr_unload_heads
,
NULL
};
EXPORT_SYMBOL_GPL
(
ahci_sdev_attrs
);
struct
ata_port_operations
ahci_ops
=
{
.
inherits
=
&
sata_pmp_port_ops
,
.
qc_defer
=
ahci_pmp_qc_defer
,
.
qc_prep
=
ahci_qc_prep
,
.
qc_issue
=
ahci_qc_issue
,
.
qc_fill_rtf
=
ahci_qc_fill_rtf
,
.
freeze
=
ahci_freeze
,
.
thaw
=
ahci_thaw
,
.
softreset
=
ahci_softreset
,
.
hardreset
=
ahci_hardreset
,
.
postreset
=
ahci_postreset
,
.
pmp_softreset
=
ahci_softreset
,
.
error_handler
=
ahci_error_handler
,
.
post_internal_cmd
=
ahci_post_internal_cmd
,
.
dev_config
=
ahci_dev_config
,
.
scr_read
=
ahci_scr_read
,
.
scr_write
=
ahci_scr_write
,
.
pmp_attach
=
ahci_pmp_attach
,
.
pmp_detach
=
ahci_pmp_detach
,
.
set_lpm
=
ahci_set_lpm
,
.
em_show
=
ahci_led_show
,
.
em_store
=
ahci_led_store
,
.
sw_activity_show
=
ahci_activity_show
,
.
sw_activity_store
=
ahci_activity_store
,
.
transmit_led_message
=
ahci_transmit_led_message
,
#ifdef CONFIG_PM
.
port_suspend
=
ahci_port_suspend
,
.
port_resume
=
ahci_port_resume
,
#endif
.
port_start
=
ahci_port_start
,
.
port_stop
=
ahci_port_stop
,
};
EXPORT_SYMBOL_GPL
(
ahci_ops
);
struct
ata_port_operations
ahci_pmp_retry_srst_ops
=
{
.
inherits
=
&
ahci_ops
,
.
softreset
=
ahci_pmp_retry_softreset
,
};
EXPORT_SYMBOL_GPL
(
ahci_pmp_retry_srst_ops
);
static
bool
ahci_em_messages
__read_mostly
=
true
;
EXPORT_SYMBOL_GPL
(
ahci_em_messages
);
module_param
(
ahci_em_messages
,
bool
,
0444
);
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC
(
ahci_em_messages
,
"AHCI Enclosure Management Message control (0 = off, 1 = on)"
);
/* device sleep idle timeout in ms */
static
int
devslp_idle_timeout
__read_mostly
=
1000
;
module_param
(
devslp_idle_timeout
,
int
,
0644
);
MODULE_PARM_DESC
(
devslp_idle_timeout
,
"device sleep idle timeout"
);
static
void
ahci_enable_ahci
(
void
__iomem
*
mmio
)
{
int
i
;
u32
tmp
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
int
len
;
/* turn on AHCI_EN */
tmp
=
readl
(
mmio
+
HOST_CTL
);
if
(
tmp
&
HOST_AHCI_EN
)
return
;
/* Some controllers need AHCI_EN to be written multiple times.
* Try a few times before giving up.
*/
for
(
i
=
0
;
i
<
5
;
i
++
)
{
tmp
|=
HOST_AHCI_EN
;
writel
(
tmp
,
mmio
+
HOST_CTL
);
tmp
=
readl
(
mmio
+
HOST_CTL
);
/* flush && sanity check */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"write HOST_AHCI_EN to HOST_CTL register"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
tmp
&
HOST_AHCI_EN
)
return
;
msleep
(
10
);
}
WARN_ON
(
1
);
}
static
ssize_t
ahci_show_host_caps
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
return
sprintf
(
buf
,
"%x
\n
"
,
hpriv
->
cap
);
}
static
ssize_t
ahci_show_host_cap2
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
return
sprintf
(
buf
,
"%x
\n
"
,
hpriv
->
cap2
);
}
static
ssize_t
ahci_show_host_version
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
return
sprintf
(
buf
,
"%x
\n
"
,
readl
(
mmio
+
HOST_VERSION
));
}
static
ssize_t
ahci_show_port_cmd
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
return
sprintf
(
buf
,
"%x
\n
"
,
readl
(
port_mmio
+
PORT_CMD
));
}
static
ssize_t
ahci_read_em_buffer
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
void
__iomem
*
em_mmio
=
mmio
+
hpriv
->
em_loc
;
u32
em_ctl
,
msg
;
unsigned
long
flags
;
size_t
count
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read EM buffer"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
spin_lock_irqsave
(
ap
->
lock
,
flags
);
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
if
(
!
(
ap
->
flags
&
ATA_FLAG_EM
)
||
em_ctl
&
EM_CTL_XMT
||
!
(
hpriv
->
em_msg_type
&
EM_MSG_TYPE_SGPIO
))
{
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
-
EINVAL
;
}
if
(
!
(
em_ctl
&
EM_CTL_MR
))
{
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
-
EAGAIN
;
}
if
(
!
(
em_ctl
&
EM_CTL_SMB
))
em_mmio
+=
hpriv
->
em_buf_sz
;
count
=
hpriv
->
em_buf_sz
;
/* the count should not be larger than PAGE_SIZE */
if
(
count
>
PAGE_SIZE
)
{
if
(
printk_ratelimit
())
ata_port_warn
(
ap
,
"EM read buffer size too large: "
"buffer size %u, page size %lu
\n
"
,
hpriv
->
em_buf_sz
,
PAGE_SIZE
);
count
=
PAGE_SIZE
;
}
for
(
i
=
0
;
i
<
count
;
i
+=
4
)
{
msg
=
readl
(
em_mmio
+
i
);
buf
[
i
]
=
msg
&
0xff
;
buf
[
i
+
1
]
=
(
msg
>>
8
)
&
0xff
;
buf
[
i
+
2
]
=
(
msg
>>
16
)
&
0xff
;
buf
[
i
+
3
]
=
(
msg
>>
24
)
&
0xff
;
}
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
i
;
}
static
ssize_t
ahci_store_em_buffer
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buf
,
size_t
size
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
void
__iomem
*
em_mmio
=
mmio
+
hpriv
->
em_loc
;
const
unsigned
char
*
msg_buf
=
buf
;
u32
em_ctl
,
msg
;
unsigned
long
flags
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"transmit message in EM buffer"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* check size validity */
if
(
!
(
ap
->
flags
&
ATA_FLAG_EM
)
||
!
(
hpriv
->
em_msg_type
&
EM_MSG_TYPE_SGPIO
)
||
size
%
4
||
size
>
hpriv
->
em_buf_sz
)
return
-
EINVAL
;
spin_lock_irqsave
(
ap
->
lock
,
flags
);
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
if
(
em_ctl
&
EM_CTL_TM
)
{
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
-
EBUSY
;
}
for
(
i
=
0
;
i
<
size
;
i
+=
4
)
{
msg
=
msg_buf
[
i
]
|
msg_buf
[
i
+
1
]
<<
8
|
msg_buf
[
i
+
2
]
<<
16
|
msg_buf
[
i
+
3
]
<<
24
;
writel
(
msg
,
em_mmio
+
i
);
}
writel
(
em_ctl
|
EM_CTL_TM
,
mmio
+
HOST_EM_CTL
);
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
size
;
}
static
ssize_t
ahci_show_em_supported
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
)
{
struct
Scsi_Host
*
shost
=
class_to_shost
(
dev
);
struct
ata_port
*
ap
=
ata_shost_to_port
(
shost
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
em_ctl
;
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
return
sprintf
(
buf
,
"%s%s%s%s
\n
"
,
em_ctl
&
EM_CTL_LED
?
"led "
:
""
,
em_ctl
&
EM_CTL_SAFTE
?
"saf-te "
:
""
,
em_ctl
&
EM_CTL_SES
?
"ses-2 "
:
""
,
em_ctl
&
EM_CTL_SGPIO
?
"sgpio "
:
""
);
}
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
* @hpriv: host private area to store config values
*
* Some registers containing configuration info might be setup by
* BIOS and might be cleared on reset. This function saves the
* initial values of those registers into @hpriv such that they
* can be restored after controller reset.
*
* If inconsistent, config values are fixed up by this function.
*
* If it is not set already this function sets hpriv->start_engine to
* ahci_start_engine.
*
* LOCKING:
* None.
*/
void
ahci_save_initial_config
(
struct
device
*
dev
,
struct
ahci_host_priv
*
hpriv
)
{
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
cap
,
cap2
,
vers
,
port_map
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read and save host capabilities"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* make sure AHCI mode is enabled before accessing CAP */
ahci_enable_ahci
(
mmio
);
/* Values prefixed with saved_ are written back to host after
* reset. Values without are used for driver operation.
*/
hpriv
->
saved_cap
=
cap
=
readl
(
mmio
+
HOST_CAP
);
hpriv
->
saved_port_map
=
port_map
=
readl
(
mmio
+
HOST_PORTS_IMPL
);
/* CAP2 register is only defined for AHCI 1.2 and later */
vers
=
readl
(
mmio
+
HOST_VERSION
);
if
((
vers
>>
16
)
>
1
||
((
vers
>>
16
)
==
1
&&
(
vers
&
0xFFFF
)
>=
0x200
))
hpriv
->
saved_cap2
=
cap2
=
readl
(
mmio
+
HOST_CAP2
);
else
hpriv
->
saved_cap2
=
cap2
=
0
;
/* some chips have errata preventing 64bit use */
if
((
cap
&
HOST_CAP_64
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_32BIT_ONLY
))
{
dev_info
(
dev
,
"controller can't do 64bit DMA, forcing 32bit
\n
"
);
cap
&=
~
HOST_CAP_64
;
}
if
((
cap
&
HOST_CAP_NCQ
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_NCQ
))
{
dev_info
(
dev
,
"controller can't do NCQ, turning off CAP_NCQ
\n
"
);
cap
&=
~
HOST_CAP_NCQ
;
}
if
(
!
(
cap
&
HOST_CAP_NCQ
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_YES_NCQ
))
{
dev_info
(
dev
,
"controller can do NCQ, turning on CAP_NCQ
\n
"
);
cap
|=
HOST_CAP_NCQ
;
}
if
((
cap
&
HOST_CAP_PMP
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_PMP
))
{
dev_info
(
dev
,
"controller can't do PMP, turning off CAP_PMP
\n
"
);
cap
&=
~
HOST_CAP_PMP
;
}
if
((
cap
&
HOST_CAP_SNTF
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_SNTF
))
{
dev_info
(
dev
,
"controller can't do SNTF, turning off CAP_SNTF
\n
"
);
cap
&=
~
HOST_CAP_SNTF
;
}
if
((
cap2
&
HOST_CAP2_SDS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_DEVSLP
))
{
dev_info
(
dev
,
"controller can't do DEVSLP, turning off
\n
"
);
cap2
&=
~
HOST_CAP2_SDS
;
cap2
&=
~
HOST_CAP2_SADM
;
}
if
(
!
(
cap
&
HOST_CAP_FBS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_YES_FBS
))
{
dev_info
(
dev
,
"controller can do FBS, turning on CAP_FBS
\n
"
);
cap
|=
HOST_CAP_FBS
;
}
if
((
cap
&
HOST_CAP_FBS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_FBS
))
{
dev_info
(
dev
,
"controller can't do FBS, turning off CAP_FBS
\n
"
);
cap
&=
~
HOST_CAP_FBS
;
}
if
(
hpriv
->
force_port_map
&&
port_map
!=
hpriv
->
force_port_map
)
{
dev_info
(
dev
,
"forcing port_map 0x%x -> 0x%x
\n
"
,
port_map
,
hpriv
->
force_port_map
);
port_map
=
hpriv
->
force_port_map
;
}
if
(
hpriv
->
mask_port_map
)
{
dev_warn
(
dev
,
"masking port_map 0x%x -> 0x%x
\n
"
,
port_map
,
port_map
&
hpriv
->
mask_port_map
);
port_map
&=
hpriv
->
mask_port_map
;
}
/* cross check port_map and cap.n_ports */
if
(
port_map
)
{
int
map_ports
=
0
;
for
(
i
=
0
;
i
<
AHCI_MAX_PORTS
;
i
++
)
if
(
port_map
&
(
1
<<
i
))
map_ports
++
;
/* If PI has more ports than n_ports, whine, clear
* port_map and let it be generated from n_ports.
*/
if
(
map_ports
>
ahci_nr_ports
(
cap
))
{
dev_warn
(
dev
,
"implemented port map (0x%x) contains more ports than nr_ports (%u), using nr_ports
\n
"
,
port_map
,
ahci_nr_ports
(
cap
));
port_map
=
0
;
}
}
/* fabricate port_map from cap.nr_ports */
if
(
!
port_map
)
{
port_map
=
(
1
<<
ahci_nr_ports
(
cap
))
-
1
;
dev_warn
(
dev
,
"forcing PORTS_IMPL to 0x%x
\n
"
,
port_map
);
/* write the fixed up value to the PI register */
hpriv
->
saved_port_map
=
port_map
;
}
/* record values to use during operation */
hpriv
->
cap
=
cap
;
hpriv
->
cap2
=
cap2
;
hpriv
->
port_map
=
port_map
;
if
(
!
hpriv
->
start_engine
)
hpriv
->
start_engine
=
ahci_start_engine
;
}
EXPORT_SYMBOL_GPL
(
ahci_save_initial_config
);
/**
* ahci_restore_initial_config - Restore initial config
* @host: target ATA host
*
* Restore initial config stored by ahci_save_initial_config().
*
* LOCKING:
* None.
*/
static
void
ahci_restore_initial_config
(
struct
ata_host
*
host
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"restore host capabilities"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
writel
(
hpriv
->
saved_cap
,
mmio
+
HOST_CAP
);
if
(
hpriv
->
saved_cap2
)
writel
(
hpriv
->
saved_cap2
,
mmio
+
HOST_CAP2
);
writel
(
hpriv
->
saved_port_map
,
mmio
+
HOST_PORTS_IMPL
);
(
void
)
readl
(
mmio
+
HOST_PORTS_IMPL
);
/* flush */
}
static
unsigned
ahci_scr_offset
(
struct
ata_port
*
ap
,
unsigned
int
sc_reg
)
{
static
const
int
offset
[]
=
{
[
SCR_STATUS
]
=
PORT_SCR_STAT
,
[
SCR_CONTROL
]
=
PORT_SCR_CTL
,
[
SCR_ERROR
]
=
PORT_SCR_ERR
,
[
SCR_ACTIVE
]
=
PORT_SCR_ACT
,
[
SCR_NOTIFICATION
]
=
PORT_SCR_NTF
,
};
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
if
(
sc_reg
<
ARRAY_SIZE
(
offset
)
&&
(
sc_reg
!=
SCR_NOTIFICATION
||
(
hpriv
->
cap
&
HOST_CAP_SNTF
)))
return
offset
[
sc_reg
];
return
0
;
}
static
int
ahci_scr_read
(
struct
ata_link
*
link
,
unsigned
int
sc_reg
,
u32
*
val
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
link
->
ap
);
int
offset
=
ahci_scr_offset
(
link
->
ap
,
sc_reg
);
int
len
;
u32
tmp
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u SATA status and control registers"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
if
(
offset
)
{
*
val
=
readl
(
port_mmio
+
offset
);
if
(
msg_str
!=
NULL
)
{
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
port %u offset: 0x%x, value: 0x%x, PxCMD: 0x%08x"
,
link
->
ap
->
port_no
,
offset
,
*
val
,
tmp
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
return
0
;
}
if
(
msg_str
!=
NULL
)
kfree
(
msg_str
);
return
-
EINVAL
;
}
static
int
ahci_scr_write
(
struct
ata_link
*
link
,
unsigned
int
sc_reg
,
u32
val
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
link
->
ap
);
int
offset
=
ahci_scr_offset
(
link
->
ap
,
sc_reg
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"write port %u SATA status and control registers"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
if
(
offset
)
{
writel
(
val
,
port_mmio
+
offset
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
port %u offset: 0x%x, value: 0x%x"
,
link
->
ap
->
port_no
,
offset
,
val
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
return
0
;
}
if
(
msg_str
!=
NULL
)
kfree
(
msg_str
);
return
-
EINVAL
;
}
void
ahci_start_engine
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u command list DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* start DMA */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
|=
PORT_CMD_START
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
EXPORT_SYMBOL_GPL
(
ahci_start_engine
);
int
ahci_stop_engine
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u command list DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
/* check if the HBA is idle */
if
((
tmp
&
(
PORT_CMD_START
|
PORT_CMD_LIST_ON
))
==
0
)
return
0
;
/* setting HBA to idle */
tmp
&=
~
PORT_CMD_START
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* wait for engine to stop. This could be as long as 500 msec */
tmp
=
ata_wait_register
(
ap
,
port_mmio
+
PORT_CMD
,
PORT_CMD_LIST_ON
,
PORT_CMD_LIST_ON
,
1
,
500
);
if
(
tmp
&
PORT_CMD_LIST_ON
)
return
-
EIO
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_stop_engine
);
void
ahci_start_fis_rx
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u FIS RX reception"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* set FIS registers */
if
(
hpriv
->
cap
&
HOST_CAP_64
)
writel
((
pp
->
cmd_slot_dma
>>
16
)
>>
16
,
port_mmio
+
PORT_LST_ADDR_HI
);
writel
(
pp
->
cmd_slot_dma
&
0xffffffff
,
port_mmio
+
PORT_LST_ADDR
);
if
(
hpriv
->
cap
&
HOST_CAP_64
)
writel
((
pp
->
rx_fis_dma
>>
16
)
>>
16
,
port_mmio
+
PORT_FIS_ADDR_HI
);
writel
(
pp
->
rx_fis_dma
&
0xffffffff
,
port_mmio
+
PORT_FIS_ADDR
);
/* enable FIS reception */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
|=
PORT_CMD_FIS_RX
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
/* flush */
readl
(
port_mmio
+
PORT_CMD
);
}
EXPORT_SYMBOL_GPL
(
ahci_start_fis_rx
);
static
int
ahci_stop_fis_rx
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u FIS RX reception"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* disable FIS reception */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
&=
~
PORT_CMD_FIS_RX
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
/* wait for completion, spec says 500ms, give it 1000 */
tmp
=
ata_wait_register
(
ap
,
port_mmio
+
PORT_CMD
,
PORT_CMD_FIS_ON
,
PORT_CMD_FIS_ON
,
10
,
1000
);
if
(
tmp
&
PORT_CMD_FIS_ON
)
return
-
EBUSY
;
return
0
;
}
static
void
ahci_power_up
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
cmd
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u wake up link"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
cmd
=
readl
(
port_mmio
+
PORT_CMD
)
&
~
PORT_CMD_ICC_MASK
;
/* spin up device */
if
(
hpriv
->
cap
&
HOST_CAP_SSS
)
{
cmd
|=
PORT_CMD_SPIN_UP
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
}
/* wake up link */
writel
(
cmd
|
PORT_CMD_ICC_ACTIVE
,
port_mmio
+
PORT_CMD
);
}
static
int
ahci_set_lpm
(
struct
ata_link
*
link
,
enum
ata_lpm_policy
policy
,
unsigned
int
hints
)
{
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"set port %u link power management"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
policy
!=
ATA_LPM_MAX_POWER
)
{
/*
* Disable interrupts on Phy Ready. This keeps us from
* getting woken up due to spurious phy ready
* interrupts.
*/
pp
->
intr_mask
&=
~
PORT_IRQ_PHYRDY
;
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
sata_link_scr_lpm
(
link
,
policy
,
false
);
}
if
(
hpriv
->
cap
&
HOST_CAP_ALPM
)
{
u32
cmd
=
readl
(
port_mmio
+
PORT_CMD
);
if
(
policy
==
ATA_LPM_MAX_POWER
||
!
(
hints
&
ATA_LPM_HIPM
))
{
cmd
&=
~
(
PORT_CMD_ASP
|
PORT_CMD_ALPE
);
cmd
|=
PORT_CMD_ICC_ACTIVE
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* wait 10ms to be sure we've come out of LPM state */
ata_msleep
(
ap
,
10
);
}
else
{
cmd
|=
PORT_CMD_ALPE
;
if
(
policy
==
ATA_LPM_MIN_POWER
)
cmd
|=
PORT_CMD_ASP
;
/* write out new cmd value */
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
}
}
/* set aggressive device sleep */
if
((
hpriv
->
cap2
&
HOST_CAP2_SDS
)
&&
(
hpriv
->
cap2
&
HOST_CAP2_SADM
)
&&
(
link
->
device
->
flags
&
ATA_DFLAG_DEVSLP
))
{
if
(
policy
==
ATA_LPM_MIN_POWER
)
ahci_set_aggressive_devslp
(
ap
,
true
);
else
ahci_set_aggressive_devslp
(
ap
,
false
);
}
if
(
policy
==
ATA_LPM_MAX_POWER
)
{
sata_link_scr_lpm
(
link
,
policy
,
false
);
/* turn PHYRDY IRQ back on */
pp
->
intr_mask
|=
PORT_IRQ_PHYRDY
;
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
return
0
;
}
#ifdef CONFIG_PM
static
void
ahci_power_down
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
cmd
,
scontrol
;
if
(
!
(
hpriv
->
cap
&
HOST_CAP_SSS
))
return
;
/* put device into listen mode, first set PxSCTL.DET to 0 */
scontrol
=
readl
(
port_mmio
+
PORT_SCR_CTL
);
scontrol
&=
~
0xf
;
writel
(
scontrol
,
port_mmio
+
PORT_SCR_CTL
);
/* then set PxCMD.SUD to 0 */
cmd
=
readl
(
port_mmio
+
PORT_CMD
)
&
~
PORT_CMD_ICC_MASK
;
cmd
&=
~
PORT_CMD_SPIN_UP
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
}
#endif
static
void
ahci_start_port
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ata_link
*
link
;
struct
ahci_em_priv
*
emp
;
ssize_t
rc
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* enable FIS reception */
ahci_start_fis_rx
(
ap
);
/* enable DMA */
if
(
!
(
hpriv
->
flags
&
AHCI_HFLAG_DELAY_ENGINE
))
hpriv
->
start_engine
(
ap
);
/* turn on LEDs */
if
(
ap
->
flags
&
ATA_FLAG_EM
)
{
ata_for_each_link
(
link
,
ap
,
EDGE
)
{
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
/* EM Transmit bit maybe busy during init */
for
(
i
=
0
;
i
<
EM_MAX_RETRY
;
i
++
)
{
rc
=
ap
->
ops
->
transmit_led_message
(
ap
,
emp
->
led_state
,
4
);
/*
* If busy, give a breather but do not
* release EH ownership by using msleep()
* instead of ata_msleep(). EM Transmit
* bit is busy for the whole host and
* releasing ownership will cause other
* ports to fail the same way.
*/
if
(
rc
==
-
EBUSY
)
msleep
(
1
);
else
break
;
}
}
}
if
(
ap
->
flags
&
ATA_FLAG_SW_ACTIVITY
)
ata_for_each_link
(
link
,
ap
,
EDGE
)
ahci_init_sw_activity
(
link
);
}
static
int
ahci_deinit_port
(
struct
ata_port
*
ap
,
const
char
**
emsg
)
{
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* disable DMA */
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
{
*
emsg
=
"failed to stop engine"
;
return
rc
;
}
/* disable FIS reception */
rc
=
ahci_stop_fis_rx
(
ap
);
if
(
rc
)
{
*
emsg
=
"failed stop FIS RX"
;
return
rc
;
}
return
0
;
}
int
ahci_reset_controller
(
struct
ata_host
*
host
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"trying to reset host controller"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* we must be in AHCI mode, before using anything
* AHCI-specific, such as HOST_RESET.
*/
ahci_enable_ahci
(
mmio
);
/* global controller reset */
if
(
!
ahci_skip_host_reset
)
{
tmp
=
readl
(
mmio
+
HOST_CTL
);
if
((
tmp
&
HOST_RESET
)
==
0
)
{
writel
(
tmp
|
HOST_RESET
,
mmio
+
HOST_CTL
);
readl
(
mmio
+
HOST_CTL
);
/* flush */
}
/*
* to perform host reset, OS should set HOST_RESET
* and poll until this bit is read to be "0".
* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
tmp
=
ata_wait_register
(
NULL
,
mmio
+
HOST_CTL
,
HOST_RESET
,
HOST_RESET
,
10
,
1000
);
if
(
tmp
&
HOST_RESET
)
{
dev_err
(
host
->
dev
,
"controller reset failed (0x%x)
\n
"
,
tmp
);
return
-
EIO
;
}
/* turn on AHCI mode */
ahci_enable_ahci
(
mmio
);
/* Some registers might be cleared on reset. Restore
* initial values.
*/
ahci_restore_initial_config
(
host
);
}
else
dev_info
(
host
->
dev
,
"skipping global host reset
\n
"
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_reset_controller
);
static
void
ahci_sw_activity
(
struct
ata_link
*
link
)
{
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
if
(
!
(
link
->
flags
&
ATA_LFLAG_SW_ACTIVITY
))
return
;
emp
->
activity
++
;
if
(
!
timer_pending
(
&
emp
->
timer
))
mod_timer
(
&
emp
->
timer
,
jiffies
+
msecs_to_jiffies
(
10
));
}
static
void
ahci_sw_activity_blink
(
unsigned
long
arg
)
{
struct
ata_link
*
link
=
(
struct
ata_link
*
)
arg
;
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
unsigned
long
led_message
=
emp
->
led_state
;
u32
activity_led_state
;
unsigned
long
flags
;
led_message
&=
EM_MSG_LED_VALUE
;
led_message
|=
ap
->
port_no
|
(
link
->
pmp
<<
8
);
/* check to see if we've had activity. If so,
* toggle state of LED and reset timer. If not,
* turn LED to desired idle state.
*/
spin_lock_irqsave
(
ap
->
lock
,
flags
);
if
(
emp
->
saved_activity
!=
emp
->
activity
)
{
emp
->
saved_activity
=
emp
->
activity
;
/* get the current LED state */
activity_led_state
=
led_message
&
EM_MSG_LED_VALUE_ON
;
if
(
activity_led_state
)
activity_led_state
=
0
;
else
activity_led_state
=
1
;
/* clear old state */
led_message
&=
~
EM_MSG_LED_VALUE_ACTIVITY
;
/* toggle state */
led_message
|=
(
activity_led_state
<<
16
);
mod_timer
(
&
emp
->
timer
,
jiffies
+
msecs_to_jiffies
(
100
));
}
else
{
/* switch to idle */
led_message
&=
~
EM_MSG_LED_VALUE_ACTIVITY
;
if
(
emp
->
blink_policy
==
BLINK_OFF
)
led_message
|=
(
1
<<
16
);
}
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
ap
->
ops
->
transmit_led_message
(
ap
,
led_message
,
4
);
}
static
void
ahci_init_sw_activity
(
struct
ata_link
*
link
)
{
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
/* init activity stats, setup timer */
emp
->
saved_activity
=
emp
->
activity
=
0
;
setup_timer
(
&
emp
->
timer
,
ahci_sw_activity_blink
,
(
unsigned
long
)
link
);
/* check our blink policy and set flag for link if it's enabled */
if
(
emp
->
blink_policy
)
link
->
flags
|=
ATA_LFLAG_SW_ACTIVITY
;
}
int
ahci_reset_em
(
struct
ata_host
*
host
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
em_ctl
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"reset EM message logic"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
if
((
em_ctl
&
EM_CTL_TM
)
||
(
em_ctl
&
EM_CTL_RST
))
return
-
EINVAL
;
writel
(
em_ctl
|
EM_CTL_RST
,
mmio
+
HOST_EM_CTL
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_reset_em
);
static
ssize_t
ahci_transmit_led_message
(
struct
ata_port
*
ap
,
u32
state
,
ssize_t
size
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
em_ctl
;
u32
message
[]
=
{
0
,
0
};
unsigned
long
flags
;
int
pmp
;
struct
ahci_em_priv
*
emp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"send port %u LED message"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* get the slot number from the message */
pmp
=
(
state
&
EM_MSG_LED_PMP_SLOT
)
>>
8
;
if
(
pmp
<
EM_MAX_SLOTS
)
emp
=
&
pp
->
em_priv
[
pmp
];
else
return
-
EINVAL
;
spin_lock_irqsave
(
ap
->
lock
,
flags
);
/*
* if we are still busy transmitting a previous message,
* do not allow
*/
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
if
(
em_ctl
&
EM_CTL_TM
)
{
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
-
EBUSY
;
}
if
(
hpriv
->
em_msg_type
&
EM_MSG_TYPE_LED
)
{
/*
* create message header - this is all zero except for
* the message size, which is 4 bytes.
*/
message
[
0
]
|=
(
4
<<
8
);
/* ignore 0:4 of byte zero, fill in port info yourself */
message
[
1
]
=
((
state
&
~
EM_MSG_LED_HBA_PORT
)
|
ap
->
port_no
);
/* write message to EM_LOC */
writel
(
message
[
0
],
mmio
+
hpriv
->
em_loc
);
writel
(
message
[
1
],
mmio
+
hpriv
->
em_loc
+
4
);
/*
* tell hardware to transmit the message
*/
writel
(
em_ctl
|
EM_CTL_TM
,
mmio
+
HOST_EM_CTL
);
}
/* save off new led state for port/slot */
emp
->
led_state
=
state
;
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
size
;
}
static
ssize_t
ahci_led_show
(
struct
ata_port
*
ap
,
char
*
buf
)
{
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ata_link
*
link
;
struct
ahci_em_priv
*
emp
;
int
rc
=
0
;
ata_for_each_link
(
link
,
ap
,
EDGE
)
{
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
rc
+=
sprintf
(
buf
,
"%lx
\n
"
,
emp
->
led_state
);
}
return
rc
;
}
static
ssize_t
ahci_led_store
(
struct
ata_port
*
ap
,
const
char
*
buf
,
size_t
size
)
{
unsigned
int
state
;
int
pmp
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
;
if
(
kstrtouint
(
buf
,
0
,
&
state
)
<
0
)
return
-
EINVAL
;
/* get the slot number from the message */
pmp
=
(
state
&
EM_MSG_LED_PMP_SLOT
)
>>
8
;
if
(
pmp
<
EM_MAX_SLOTS
)
emp
=
&
pp
->
em_priv
[
pmp
];
else
return
-
EINVAL
;
/* mask off the activity bits if we are in sw_activity
* mode, user should turn off sw_activity before setting
* activity led through em_message
*/
if
(
emp
->
blink_policy
)
state
&=
~
EM_MSG_LED_VALUE_ACTIVITY
;
return
ap
->
ops
->
transmit_led_message
(
ap
,
state
,
size
);
}
static
ssize_t
ahci_activity_store
(
struct
ata_device
*
dev
,
enum
sw_activity
val
)
{
struct
ata_link
*
link
=
dev
->
link
;
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
u32
port_led_state
=
emp
->
led_state
;
/* save the desired Activity LED behavior */
if
(
val
==
OFF
)
{
/* clear LFLAG */
link
->
flags
&=
~
(
ATA_LFLAG_SW_ACTIVITY
);
/* set the LED to OFF */
port_led_state
&=
EM_MSG_LED_VALUE_OFF
;
port_led_state
|=
(
ap
->
port_no
|
(
link
->
pmp
<<
8
));
ap
->
ops
->
transmit_led_message
(
ap
,
port_led_state
,
4
);
}
else
{
link
->
flags
|=
ATA_LFLAG_SW_ACTIVITY
;
if
(
val
==
BLINK_OFF
)
{
/* set LED to ON for idle */
port_led_state
&=
EM_MSG_LED_VALUE_OFF
;
port_led_state
|=
(
ap
->
port_no
|
(
link
->
pmp
<<
8
));
port_led_state
|=
EM_MSG_LED_VALUE_ON
;
/* check this */
ap
->
ops
->
transmit_led_message
(
ap
,
port_led_state
,
4
);
}
}
emp
->
blink_policy
=
val
;
return
0
;
}
static
ssize_t
ahci_activity_show
(
struct
ata_device
*
dev
,
char
*
buf
)
{
struct
ata_link
*
link
=
dev
->
link
;
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
=
&
pp
->
em_priv
[
link
->
pmp
];
/* display the saved value of activity behavior for this
* disk.
*/
return
sprintf
(
buf
,
"%d
\n
"
,
emp
->
blink_policy
);
}
static
void
ahci_port_init
(
struct
device
*
dev
,
struct
ata_port
*
ap
,
int
port_no
,
void
__iomem
*
mmio
,
void
__iomem
*
port_mmio
)
{
const
char
*
emsg
=
NULL
;
int
rc
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u init"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* make sure port is not active */
rc
=
ahci_deinit_port
(
ap
,
&
emsg
);
if
(
rc
)
dev_warn
(
dev
,
"%s (%d)
\n
"
,
emsg
,
rc
);
/* clear SError */
tmp
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
VPRINTK
(
"PORT_SCR_ERR 0x%x
\n
"
,
tmp
);
writel
(
tmp
,
port_mmio
+
PORT_SCR_ERR
);
/* clear port IRQ */
tmp
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
VPRINTK
(
"PORT_IRQ_STAT 0x%x
\n
"
,
tmp
);
if
(
tmp
)
writel
(
tmp
,
port_mmio
+
PORT_IRQ_STAT
);
writel
(
1
<<
port_no
,
mmio
+
HOST_IRQ_STAT
);
}
void
ahci_init_controller
(
struct
ata_host
*
host
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
int
i
;
void
__iomem
*
port_mmio
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"init AHCI controller"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
{
struct
ata_port
*
ap
=
host
->
ports
[
i
];
port_mmio
=
ahci_port_base
(
ap
);
if
(
ata_port_is_dummy
(
ap
))
continue
;
ahci_port_init
(
host
->
dev
,
ap
,
i
,
mmio
,
port_mmio
);
}
tmp
=
readl
(
mmio
+
HOST_CTL
);
VPRINTK
(
"HOST_CTL 0x%x
\n
"
,
tmp
);
writel
(
tmp
|
HOST_IRQ_EN
,
mmio
+
HOST_CTL
);
tmp
=
readl
(
mmio
+
HOST_CTL
);
VPRINTK
(
"HOST_CTL 0x%x
\n
"
,
tmp
);
}
EXPORT_SYMBOL_GPL
(
ahci_init_controller
);
static
void
ahci_dev_config
(
struct
ata_device
*
dev
)
{
struct
ahci_host_priv
*
hpriv
=
dev
->
link
->
ap
->
host
->
private_data
;
if
(
hpriv
->
flags
&
AHCI_HFLAG_SECT255
)
{
dev
->
max_sectors
=
255
;
ata_dev_info
(
dev
,
"SB600 AHCI: limiting to 255 sectors per cmd
\n
"
);
}
}
unsigned
int
ahci_dev_classify
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ata_taskfile
tf
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
tmp
=
readl
(
port_mmio
+
PORT_SIG
);
tf
.
lbah
=
(
tmp
>>
24
)
&
0xff
;
tf
.
lbam
=
(
tmp
>>
16
)
&
0xff
;
tf
.
lbal
=
(
tmp
>>
8
)
&
0xff
;
tf
.
nsect
=
(
tmp
)
&
0xff
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"reading port %u signature: PxSIG = 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
return
ata_dev_classify
(
&
tf
);
}
EXPORT_SYMBOL_GPL
(
ahci_dev_classify
);
void
ahci_fill_cmd_slot
(
struct
ahci_port_priv
*
pp
,
unsigned
int
tag
,
u32
opts
)
{
dma_addr_t
cmd_tbl_dma
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
cmd_tbl_dma
=
pp
->
cmd_tbl_dma
+
tag
*
AHCI_CMD_TBL_SZ
;
pp
->
cmd_slot
[
tag
].
opts
=
cpu_to_le32
(
opts
);
pp
->
cmd_slot
[
tag
].
status
=
0
;
pp
->
cmd_slot
[
tag
].
tbl_addr
=
cpu_to_le32
(
cmd_tbl_dma
&
0xffffffff
);
pp
->
cmd_slot
[
tag
].
tbl_addr_hi
=
cpu_to_le32
((
cmd_tbl_dma
>>
16
)
>>
16
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
fill command slot %u: DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x, DW3 = 0x%08x"
,
tag
,
pp
->
cmd_slot
[
tag
].
opts
,
pp
->
cmd_slot
[
tag
].
status
,
pp
->
cmd_slot
[
tag
].
tbl_addr
,
pp
->
cmd_slot
[
tag
].
tbl_addr_hi
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
EXPORT_SYMBOL_GPL
(
ahci_fill_cmd_slot
);
int
ahci_kick_engine
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
u8
status
=
readl
(
port_mmio
+
PORT_TFDATA
)
&
0xFF
;
u32
tmp
;
int
busy
,
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"kick port %u DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* stop engine */
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
goto
out_restart
;
/* need to do CLO?
* always do CLO if PMP is attached (AHCI-1.3 9.2)
*/
busy
=
status
&
(
ATA_BUSY
|
ATA_DRQ
);
if
(
!
busy
&&
!
sata_pmp_attached
(
ap
))
{
rc
=
0
;
goto
out_restart
;
}
if
(
!
(
hpriv
->
cap
&
HOST_CAP_CLO
))
{
rc
=
-
EOPNOTSUPP
;
goto
out_restart
;
}
/* perform CLO */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
|=
PORT_CMD_CLO
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
rc
=
0
;
tmp
=
ata_wait_register
(
ap
,
port_mmio
+
PORT_CMD
,
PORT_CMD_CLO
,
PORT_CMD_CLO
,
1
,
500
);
if
(
tmp
&
PORT_CMD_CLO
)
rc
=
-
EIO
;
/* restart engine */
out_restart:
hpriv
->
start_engine
(
ap
);
kfree
(
msg_str
);
return
rc
;
}
EXPORT_SYMBOL_GPL
(
ahci_kick_engine
);
static
int
ahci_exec_polled_cmd
(
struct
ata_port
*
ap
,
int
pmp
,
struct
ata_taskfile
*
tf
,
int
is_cmd
,
u16
flags
,
unsigned
long
timeout_msec
)
{
const
u32
cmd_fis_len
=
5
;
/* five dwords */
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u8
*
fis
=
pp
->
cmd_tbl
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u command issue"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* prep the command */
ata_tf_to_fis
(
tf
,
pmp
,
is_cmd
,
fis
);
ahci_fill_cmd_slot
(
pp
,
0
,
cmd_fis_len
|
flags
|
(
pmp
<<
12
));
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)
fis
,
cmd_fis_len
,
"
\t
write H2D register FIS; dump: "
);
/* issue & wait */
writel
(
1
,
port_mmio
+
PORT_CMD_ISSUE
);
if
(
timeout_msec
)
{
tmp
=
ata_wait_register
(
ap
,
port_mmio
+
PORT_CMD_ISSUE
,
0x1
,
0x1
,
1
,
timeout_msec
);
if
(
tmp
&
0x1
)
{
ahci_kick_engine
(
ap
);
return
-
EBUSY
;
}
}
else
readl
(
port_mmio
+
PORT_CMD_ISSUE
);
/* flush */
return
0
;
}
int
ahci_do_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
int
pmp
,
unsigned
long
deadline
,
int
(
*
check_ready
)(
struct
ata_link
*
link
))
{
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
const
char
*
reason
=
NULL
;
unsigned
long
now
,
msecs
;
struct
ata_taskfile
tf
;
bool
fbs_disabled
=
false
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u softreset"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
DPRINTK
(
"ENTER
\n
"
);
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc
=
ahci_kick_engine
(
ap
);
if
(
rc
&&
rc
!=
-
EOPNOTSUPP
)
ata_link_warn
(
link
,
"failed to reset engine (errno=%d)
\n
"
,
rc
);
/*
* According to AHCI-1.2 9.3.9: if FBS is enable, software shall
* clear PxFBS.EN to '0' prior to issuing software reset to devices
* that is attached to port multiplier.
*/
if
(
!
ata_is_host_link
(
link
)
&&
pp
->
fbs_enabled
)
{
ahci_disable_fbs
(
ap
);
fbs_disabled
=
true
;
}
ata_tf_init
(
link
->
device
,
&
tf
);
/* issue the first D2H Register FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"issue the first H2D Register FIS; set ATA_SRST bit"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
msecs
=
0
;
now
=
jiffies
;
if
(
time_after
(
deadline
,
now
))
msecs
=
jiffies_to_msecs
(
deadline
-
now
);
tf
.
ctl
|=
ATA_SRST
;
if
(
ahci_exec_polled_cmd
(
ap
,
pmp
,
&
tf
,
0
,
AHCI_CMD_RESET
|
AHCI_CMD_CLR_BUSY
,
msecs
))
{
rc
=
-
EIO
;
reason
=
"1st FIS failed"
;
goto
fail
;
}
/* spec says at least 5us, but be generous and sleep for 1ms */
ata_msleep
(
ap
,
1
);
/* issue the second D2H Register FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"issue the second H2D Register FIS; reset ATA_SRST"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
tf
.
ctl
&=
~
ATA_SRST
;
ahci_exec_polled_cmd
(
ap
,
pmp
,
&
tf
,
0
,
0
,
0
);
/* wait for link to become ready */
rc
=
ata_wait_after_reset
(
link
,
deadline
,
check_ready
);
if
(
rc
==
-
EBUSY
&&
hpriv
->
flags
&
AHCI_HFLAG_SRST_TOUT_IS_OFFLINE
)
{
/*
* Workaround for cases where link online status can't
* be trusted. Treat device readiness timeout as link
* offline.
*/
ata_link_info
(
link
,
"device not ready, treating as offline
\n
"
);
*
class
=
ATA_DEV_NONE
;
}
else
if
(
rc
)
{
/* link occupied, -ENODEV too is an error */
reason
=
"device not ready"
;
goto
fail
;
}
else
*
class
=
ahci_dev_classify
(
ap
);
/* re-enable FBS if disabled before */
if
(
fbs_disabled
)
ahci_enable_fbs
(
ap
);
if
(
msg_str
!=
NULL
)
{
kfree
(
msg_str
);
}
DPRINTK
(
"EXIT, class=%u
\n
"
,
*
class
);
return
0
;
fail:
ata_link_err
(
link
,
"softreset failed (%s)
\n
"
,
reason
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
softreset failed, reson code: %s"
,
reason
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
return
rc
;
}
int
ahci_check_ready
(
struct
ata_link
*
link
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
link
->
ap
);
u8
status
=
readl
(
port_mmio
+
PORT_TFDATA
)
&
0xFF
;
int
len
;
char
*
msg_str
;
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Task File Data: PxTFD = 0x%02x "
,
link
->
ap
->
port_no
,
status
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
return
ata_check_ready
(
status
);
}
EXPORT_SYMBOL_GPL
(
ahci_check_ready
);
static
int
ahci_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
)
{
int
pmp
=
sata_srst_pmp
(
link
);
DPRINTK
(
"ENTER
\n
"
);
return
ahci_do_softreset
(
link
,
class
,
pmp
,
deadline
,
ahci_check_ready
);
}
EXPORT_SYMBOL_GPL
(
ahci_do_softreset
);
static
int
ahci_bad_pmp_check_ready
(
struct
ata_link
*
link
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
link
->
ap
);
u8
status
=
readl
(
port_mmio
+
PORT_TFDATA
)
&
0xFF
;
u32
irq_status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
/*
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
* which can save timeout delay.
*/
if
(
irq_status
&
PORT_IRQ_BAD_PMP
)
return
-
EIO
;
return
ata_check_ready
(
status
);
}
static
int
ahci_pmp_retry_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
)
{
struct
ata_port
*
ap
=
link
->
ap
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
pmp
=
sata_srst_pmp
(
link
);
int
rc
;
u32
irq_sts
;
DPRINTK
(
"ENTER
\n
"
);
rc
=
ahci_do_softreset
(
link
,
class
,
pmp
,
deadline
,
ahci_bad_pmp_check_ready
);
/*
* Soft reset fails with IPMS set when PMP is enabled but
* SATA HDD/ODD is connected to SATA port, do soft reset
* again to port 0.
*/
if
(
rc
==
-
EIO
)
{
irq_sts
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
if
(
irq_sts
&
PORT_IRQ_BAD_PMP
)
{
ata_link_warn
(
link
,
"applying PMP SRST workaround "
"and retrying
\n
"
);
rc
=
ahci_do_softreset
(
link
,
class
,
0
,
deadline
,
ahci_check_ready
);
}
}
return
rc
;
}
static
int
ahci_hardreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
)
{
const
unsigned
long
*
timing
=
sata_ehc_deb_timing
(
&
link
->
eh_context
);
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
u8
*
d2h_fis
=
pp
->
rx_fis
+
RX_FIS_D2H_REG
;
struct
ata_taskfile
tf
;
bool
online
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u hardreset"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
DPRINTK
(
"ENTER
\n
"
);
ahci_stop_engine
(
ap
);
/* clear D2H reception area to properly wait for D2H FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
clear RX FIS area"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
ata_tf_init
(
link
->
device
,
&
tf
);
tf
.
command
=
ATA_BUSY
;
ata_tf_to_fis
(
&
tf
,
0
,
0
,
d2h_fis
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
proceed to sata_link_hardreset"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
rc
=
sata_link_hardreset
(
link
,
timing
,
deadline
,
&
online
,
ahci_check_ready
);
hpriv
->
start_engine
(
ap
);
if
(
online
)
{
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
link is online"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
*
class
=
ahci_dev_classify
(
ap
);
}
if
(
msg_str
!=
NULL
)
{
kfree
(
msg_str
);
}
DPRINTK
(
"EXIT, rc=%d, class=%u
\n
"
,
rc
,
*
class
);
return
rc
;
}
static
void
ahci_postreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
)
{
struct
ata_port
*
ap
=
link
->
ap
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
new_tmp
,
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u postreset actions"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
ata_std_postreset
(
link
,
class
);
/* Make sure port's ATAPI bit is set appropriately */
new_tmp
=
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
if
(
*
class
==
ATA_DEV_ATAPI
)
new_tmp
|=
PORT_CMD_ATAPI
;
else
new_tmp
&=
~
PORT_CMD_ATAPI
;
if
(
new_tmp
!=
tmp
)
{
writel
(
new_tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
}
}
static
unsigned
int
ahci_fill_sg
(
struct
ata_queued_cmd
*
qc
,
void
*
cmd_tbl
)
{
struct
scatterlist
*
sg
;
struct
ahci_sg
*
ahci_sg
=
cmd_tbl
+
AHCI_CMD_TBL_HDR_SZ
;
unsigned
int
si
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
VPRINTK
(
"ENTER
\n
"
);
/*
* Next, the S/G list.
*/
for_each_sg
(
qc
->
sg
,
sg
,
qc
->
n_elem
,
si
)
{
dma_addr_t
addr
=
sg_dma_address
(
sg
);
u32
sg_len
=
sg_dma_len
(
sg
);
ahci_sg
[
si
].
addr
=
cpu_to_le32
(
addr
&
0xffffffff
);
ahci_sg
[
si
].
addr_hi
=
cpu_to_le32
((
addr
>>
16
)
>>
16
);
ahci_sg
[
si
].
flags_size
=
cpu_to_le32
(
sg_len
-
1
);
}
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
fill S/G list for port %u: %u PRD(s) written"
,
qc
->
ap
->
port_no
,
si
);
libahci_debug_event
(
qc
->
ap
,
msg_str
,
len
);
libahci_debug_dump_sg
(
qc
,
"reading data pointed by S/G list; dump: "
);
kfree
(
msg_str
);
}
return
si
;
}
static
int
ahci_pmp_qc_defer
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"process qc_defer ata command for port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
sata_pmp_attached
(
ap
)
||
pp
->
fbs_enabled
)
return
ata_std_qc_defer
(
qc
);
else
return
sata_pmp_qc_defer_cmd_switch
(
qc
);
}
static
void
ahci_qc_prep
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
int
is_atapi
=
ata_is_atapi
(
qc
->
tf
.
protocol
);
void
*
cmd_tbl
;
u32
opts
;
const
u32
cmd_fis_len
=
5
;
/* five dwords */
unsigned
int
n_elem
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"prepare command table information for port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
*/
cmd_tbl
=
pp
->
cmd_tbl
+
qc
->
tag
*
AHCI_CMD_TBL_SZ
;
ata_tf_to_fis
(
&
qc
->
tf
,
qc
->
dev
->
link
->
pmp
,
1
,
cmd_tbl
);
if
(
is_atapi
)
{
memset
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
0
,
32
);
memcpy
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
qc
->
cdb
,
qc
->
dev
->
cdb_len
);
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)(
cmd_tbl
+
AHCI_CMD_TBL_CDB
),
4
,
"
\t
this is ATAPI comman, dump ACMD region: "
);
}
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)
cmd_tbl
,
cmd_fis_len
,
"
\t
write H2D register FIS; dump: "
);
n_elem
=
0
;
if
(
qc
->
flags
&
ATA_QCFLAG_DMAMAP
)
n_elem
=
ahci_fill_sg
(
qc
,
cmd_tbl
);
/*
* Fill in command slot information.
*/
opts
=
cmd_fis_len
|
n_elem
<<
16
|
(
qc
->
dev
->
link
->
pmp
<<
12
);
if
(
qc
->
tf
.
flags
&
ATA_TFLAG_WRITE
)
opts
|=
AHCI_CMD_WRITE
;
if
(
is_atapi
)
opts
|=
AHCI_CMD_ATAPI
|
AHCI_CMD_PREFETCH
;
ahci_fill_cmd_slot
(
pp
,
qc
->
tag
,
opts
);
}
static
void
ahci_fbs_dec_intr
(
struct
ata_port
*
ap
)
{
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
int
retries
=
3
;
DPRINTK
(
"ENTER
\n
"
);
BUG_ON
(
!
pp
->
fbs_enabled
);
/* time to wait for DEC is not specified by AHCI spec,
* add a retry loop for safety.
*/
writel
(
fbs
|
PORT_FBS_DEC
,
port_mmio
+
PORT_FBS
);
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
while
((
fbs
&
PORT_FBS_DEC
)
&&
retries
--
)
{
udelay
(
1
);
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
}
if
(
fbs
&
PORT_FBS_DEC
)
dev_err
(
ap
->
host
->
dev
,
"failed to clear device error
\n
"
);
}
static
void
ahci_error_intr
(
struct
ata_port
*
ap
,
u32
irq_stat
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ata_eh_info
*
host_ehi
=
&
ap
->
link
.
eh_info
;
struct
ata_link
*
link
=
NULL
;
struct
ata_queued_cmd
*
active_qc
;
struct
ata_eh_info
*
active_ehi
;
bool
fbs_need_dec
=
false
;
u32
serror
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
/* determine active link with error */
if
(
pp
->
fbs_enabled
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
int
pmp
=
fbs
>>
PORT_FBS_DWE_OFFSET
;
if
((
fbs
&
PORT_FBS_SDE
)
&&
(
pmp
<
ap
->
nr_pmp_links
))
{
link
=
&
ap
->
pmp_link
[
pmp
];
fbs_need_dec
=
true
;
}
}
else
ata_for_each_link
(
link
,
ap
,
EDGE
)
if
(
ata_link_active
(
link
))
break
;
if
(
!
link
)
link
=
&
ap
->
link
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"handle port %u error"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
active_qc
=
ata_qc_from_tag
(
ap
,
link
->
active_tag
);
active_ehi
=
&
link
->
eh_info
;
/* record irq stat */
ata_ehi_clear_desc
(
host_ehi
);
ata_ehi_push_desc
(
host_ehi
,
"irq_stat 0x%08x"
,
irq_stat
);
/* AHCI needs SError cleared; otherwise, it might lock up */
ahci_scr_read
(
&
ap
->
link
,
SCR_ERROR
,
&
serror
);
ahci_scr_write
(
&
ap
->
link
,
SCR_ERROR
,
serror
);
host_ehi
->
serror
|=
serror
;
/* some controllers set IRQ_IF_ERR on device errors, ignore it */
if
(
hpriv
->
flags
&
AHCI_HFLAG_IGN_IRQ_IF_ERR
)
irq_stat
&=
~
PORT_IRQ_IF_ERR
;
if
(
irq_stat
&
PORT_IRQ_TF_ERR
)
{
/* If qc is active, charge it; otherwise, the active
* link. There's no active qc on NCQ errors. It will
* be determined by EH by reading log page 10h.
*/
if
(
active_qc
)
active_qc
->
err_mask
|=
AC_ERR_DEV
;
else
active_ehi
->
err_mask
|=
AC_ERR_DEV
;
if
(
hpriv
->
flags
&
AHCI_HFLAG_IGN_SERR_INTERNAL
)
host_ehi
->
serror
&=
~
SERR_INTERNAL
;
}
if
(
irq_stat
&
PORT_IRQ_UNK_FIS
)
{
u32
*
unk
=
pp
->
rx_fis
+
RX_FIS_UNK
;
active_ehi
->
err_mask
|=
AC_ERR_HSM
;
active_ehi
->
action
|=
ATA_EH_RESET
;
ata_ehi_push_desc
(
active_ehi
,
"unknown FIS %08x %08x %08x %08x"
,
unk
[
0
],
unk
[
1
],
unk
[
2
],
unk
[
3
]);
}
if
(
sata_pmp_attached
(
ap
)
&&
(
irq_stat
&
PORT_IRQ_BAD_PMP
))
{
active_ehi
->
err_mask
|=
AC_ERR_HSM
;
active_ehi
->
action
|=
ATA_EH_RESET
;
ata_ehi_push_desc
(
active_ehi
,
"incorrect PMP"
);
}
if
(
irq_stat
&
(
PORT_IRQ_HBUS_ERR
|
PORT_IRQ_HBUS_DATA_ERR
))
{
host_ehi
->
err_mask
|=
AC_ERR_HOST_BUS
;
host_ehi
->
action
|=
ATA_EH_RESET
;
ata_ehi_push_desc
(
host_ehi
,
"host bus error"
);
}
if
(
irq_stat
&
PORT_IRQ_IF_ERR
)
{
if
(
fbs_need_dec
)
active_ehi
->
err_mask
|=
AC_ERR_DEV
;
else
{
host_ehi
->
err_mask
|=
AC_ERR_ATA_BUS
;
host_ehi
->
action
|=
ATA_EH_RESET
;
}
ata_ehi_push_desc
(
host_ehi
,
"interface fatal error"
);
}
if
(
irq_stat
&
(
PORT_IRQ_CONNECT
|
PORT_IRQ_PHYRDY
))
{
ata_ehi_hotplugged
(
host_ehi
);
ata_ehi_push_desc
(
host_ehi
,
"%s"
,
irq_stat
&
PORT_IRQ_CONNECT
?
"connection status changed"
:
"PHY RDY changed"
);
}
/* okay, let's hand over to EH */
if
(
irq_stat
&
PORT_IRQ_FREEZE
)
ata_port_freeze
(
ap
);
else
if
(
fbs_need_dec
)
{
ata_link_abort
(
link
);
ahci_fbs_dec_intr
(
ap
);
}
else
ata_port_abort
(
ap
);
}
static
void
ahci_handle_port_interrupt
(
struct
ata_port
*
ap
,
void
__iomem
*
port_mmio
,
u32
status
)
{
struct
ata_eh_info
*
ehi
=
&
ap
->
link
.
eh_info
;
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
;
int
rc
;
int
len
;
char
*
msg
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u interrupt handler, PxIS: 0x%x"
,
ap
->
port_no
,
status
);
libahci_debug_event
(
ap
,
msg
,
len
);
libahci_debug_dump_irq
(
status
);
libahci_debug_irq_notify
(
ap
);
}
/* ignore BAD_PMP while resetting */
if
(
unlikely
(
resetting
))
status
&=
~
PORT_IRQ_BAD_PMP
;
/* if LPM is enabled, PHYRDY doesn't mean anything */
if
(
ap
->
link
.
lpm_policy
>
ATA_LPM_MAX_POWER
)
{
status
&=
~
PORT_IRQ_PHYRDY
;
ahci_scr_write
(
&
ap
->
link
,
SCR_ERROR
,
SERR_PHYRDY_CHG
);
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"resetting PORT_IRQ_PHYRDY, new PxIS: 0x%x"
,
status
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
}
if
(
unlikely
(
status
&
PORT_IRQ_ERROR
))
{
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"processing PORT_IRQ_ERROR"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
ahci_error_intr
(
ap
,
status
);
return
;
}
if
(
status
&
PORT_IRQ_SDB_FIS
)
{
/* If SNotification is available, leave notification
* handling to sata_async_notification(). If not,
* emulate it by snooping SDB FIS RX area.
*
* Snooping FIS RX area is probably cheaper than
* poking SNotification but some constrollers which
* implement SNotification, ICH9 for example, don't
* store AN SDB FIS into receive area.
*/
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"processing PORT_IRQ_SDB_FIS, further processing will be done on sata layer"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
if
(
hpriv
->
cap
&
HOST_CAP_SNTF
)
{
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"host supports SNotification register, proceed to 'sata_async_notification'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
sata_async_notification
(
ap
);
}
else
{
/* If the 'N' bit in word 0 of the FIS is set,
* we just received asynchronous notification.
* Tell libata about it.
*
* Lack of SNotification should not appear in
* ahci 1.2, so the workaround is unnecessary
* when FBS is enabled.
*/
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"host DOES NOT support SNotification register, snoop FIS RX area and proceed to 'sata_async_notification'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
if
(
pp
->
fbs_enabled
)
WARN_ON_ONCE
(
1
);
else
{
const
__le32
*
f
=
pp
->
rx_fis
+
RX_FIS_SDB
;
u32
f0
=
le32_to_cpu
(
f
[
0
]);
if
(
f0
&
(
1
<<
15
))
sata_async_notification
(
ap
);
}
}
}
/* 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
);
}
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Serial ATA Active register, PxSACT: 0x%x"
,
ap
->
port_no
,
qc_active
);
libahci_debug_event
(
ap
,
msg
,
len
);
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"proceed to 'ata_qc_complete_multiple'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
//libahci_debug_irq_notify(ap);
kfree
(
msg
);
}
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
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
status
;
status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
status
,
port_mmio
+
PORT_IRQ_STAT
);
ahci_handle_port_interrupt
(
ap
,
port_mmio
,
status
);
}
static
irqreturn_t
ahci_port_thread_fn
(
int
irq
,
void
*
dev_instance
)
{
struct
ata_port
*
ap
=
dev_instance
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
status
;
status
=
atomic_xchg
(
&
pp
->
intr_status
,
0
);
if
(
!
status
)
return
IRQ_NONE
;
spin_lock_bh
(
ap
->
lock
);
ahci_handle_port_interrupt
(
ap
,
port_mmio
,
status
);
spin_unlock_bh
(
ap
->
lock
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
ahci_multi_irqs_intr
(
int
irq
,
void
*
dev_instance
)
{
struct
ata_port
*
ap
=
dev_instance
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
status
;
VPRINTK
(
"ENTER
\n
"
);
status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
status
,
port_mmio
+
PORT_IRQ_STAT
);
atomic_or
(
status
,
&
pp
->
intr_status
);
VPRINTK
(
"EXIT
\n
"
);
return
IRQ_WAKE_THREAD
;
}
static
irqreturn_t
ahci_single_irq_intr
(
int
irq
,
void
*
dev_instance
)
{
struct
ata_host
*
host
=
dev_instance
;
struct
ahci_host_priv
*
hpriv
;
unsigned
int
i
,
handled
=
0
;
void
__iomem
*
mmio
;
u32
irq_stat
,
irq_masked
;
VPRINTK
(
"ENTER
\n
"
);
hpriv
=
host
->
private_data
;
mmio
=
hpriv
->
mmio
;
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat
=
readl
(
mmio
+
HOST_IRQ_STAT
);
if
(
!
irq_stat
)
return
IRQ_NONE
;
irq_masked
=
irq_stat
&
hpriv
->
port_map
;
spin_lock
(
&
host
->
lock
);
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
{
struct
ata_port
*
ap
;
if
(
!
(
irq_masked
&
(
1
<<
i
)))
continue
;
ap
=
host
->
ports
[
i
];
if
(
ap
)
{
ahci_port_intr
(
ap
);
VPRINTK
(
"port %u
\n
"
,
i
);
}
else
{
VPRINTK
(
"port %u (no irq)
\n
"
,
i
);
if
(
ata_ratelimit
())
dev_warn
(
host
->
dev
,
"interrupt on disabled port %u
\n
"
,
i
);
}
handled
=
1
;
}
/* HOST_IRQ_STAT behaves as level triggered latch meaning that
* it should be cleared after all the port events are cleared;
* otherwise, it will raise a spurious interrupt after each
* valid one. Please read section 10.6.2 of ahci 1.1 for more
* information.
*
* Also, use the unmasked value to clear interrupt as spurious
* pending event on a dummy port might cause screaming IRQ.
*/
writel
(
irq_stat
,
mmio
+
HOST_IRQ_STAT
);
spin_unlock
(
&
host
->
lock
);
VPRINTK
(
"EXIT
\n
"
);
return
IRQ_RETVAL
(
handled
);
}
unsigned
int
ahci_qc_issue
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u queued command issue"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* Keep track of the currently active link. It will be used
* in completion path to determine whether NCQ phase is in
* progress.
*/
pp
->
active_link
=
qc
->
dev
->
link
;
if
(
qc
->
tf
.
protocol
==
ATA_PROT_NCQ
)
{
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxSACT, value: 0x%08x"
,
ap
->
port_no
,
(
1
<<
qc
->
tag
));
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
writel
(
1
<<
qc
->
tag
,
port_mmio
+
PORT_SCR_ACT
);
}
if
(
pp
->
fbs_enabled
&&
pp
->
fbs_last_dev
!=
qc
->
dev
->
link
->
pmp
)
{
u32
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
fbs
&=
~
(
PORT_FBS_DEV_MASK
|
PORT_FBS_DEC
);
fbs
|=
qc
->
dev
->
link
->
pmp
<<
PORT_FBS_DEV_OFFSET
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxFBS, value: 0x%08x"
,
ap
->
port_no
,
fbs
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
writel
(
fbs
,
port_mmio
+
PORT_FBS
);
pp
->
fbs_last_dev
=
qc
->
dev
->
link
->
pmp
;
}
//libahci_debug_wait_flag();
writel
(
1
<<
qc
->
tag
,
port_mmio
+
PORT_CMD_ISSUE
);
ahci_sw_activity
(
qc
->
dev
->
link
);
kfree
(
msg_str
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_qc_issue
);
static
bool
ahci_qc_fill_rtf
(
struct
ata_queued_cmd
*
qc
)
{
struct
ahci_port_priv
*
pp
=
qc
->
ap
->
private_data
;
u8
*
rx_fis
=
pp
->
rx_fis
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Received FIS and copy to Task File"
,
qc
->
ap
->
port_no
);
libahci_debug_event
(
qc
->
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
pp
->
fbs_enabled
)
rx_fis
+=
qc
->
dev
->
link
->
pmp
*
AHCI_RX_FIS_SZ
;
/*
* After a successful execution of an ATA PIO data-in command,
* the device doesn't send D2H Reg FIS to update the TF and
* the host should take TF and E_Status from the preceding PIO
* Setup FIS.
*/
if
(
qc
->
tf
.
protocol
==
ATA_PROT_PIO
&&
qc
->
dma_dir
==
DMA_FROM_DEVICE
&&
!
(
qc
->
flags
&
ATA_QCFLAG_FAILED
))
{
ata_tf_from_fis
(
rx_fis
+
RX_FIS_PIO_SETUP
,
&
qc
->
result_tf
);
qc
->
result_tf
.
command
=
(
rx_fis
+
RX_FIS_PIO_SETUP
)[
15
];
libahci_debug_dump_region
(
qc
->
ap
,
(
const
u32
*
)(
rx_fis
+
RX_FIS_PIO_SETUP
),
5
,
"
\t
read PIO Setup FIS; dump: "
);
libahci_debug_dump_sg
(
qc
,
"reading data pointed by S/G list; dump: "
);
}
else
{
ata_tf_from_fis
(
rx_fis
+
RX_FIS_D2H_REG
,
&
qc
->
result_tf
);
libahci_debug_dump_region
(
qc
->
ap
,
(
const
u32
*
)(
rx_fis
+
RX_FIS_D2H_REG
),
5
,
"
\t
read D2H Register FIS; dump: "
);
}
return
true
;
}
static
void
ahci_freeze
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"freeze port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* turn IRQ off */
writel
(
0
,
port_mmio
+
PORT_IRQ_MASK
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxIE, value: 0x0"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
static
void
ahci_thaw
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"thaw port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* clear IRQ */
tmp
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
tmp
,
port_mmio
+
PORT_IRQ_STAT
);
writel
(
1
<<
ap
->
port_no
,
mmio
+
HOST_IRQ_STAT
);
/* turn IRQ back on */
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
void
ahci_error_handler
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u error handler"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
{
/* restart engine */
ahci_stop_engine
(
ap
);
hpriv
->
start_engine
(
ap
);
}
sata_pmp_error_handler
(
ap
);
if
(
!
ata_dev_enabled
(
ap
->
link
.
device
))
ahci_stop_engine
(
ap
);
}
EXPORT_SYMBOL_GPL
(
ahci_error_handler
);
static
void
ahci_post_internal_cmd
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u post internal command"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* make DMA engine forget about the failed command */
if
(
qc
->
flags
&
ATA_QCFLAG_FAILED
)
ahci_kick_engine
(
ap
);
}
static
void
ahci_set_aggressive_devslp
(
struct
ata_port
*
ap
,
bool
sleep
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ata_device
*
dev
=
ap
->
link
.
device
;
u32
devslp
,
dm
,
dito
,
mdat
,
deto
;
int
rc
;
unsigned
int
err_mask
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"set aggressive port %u device sleep"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
devslp
=
readl
(
port_mmio
+
PORT_DEVSLP
);
if
(
!
(
devslp
&
PORT_DEVSLP_DSP
))
{
dev_info
(
ap
->
host
->
dev
,
"port does not support device sleep
\n
"
);
return
;
}
/* disable device sleep */
if
(
!
sleep
)
{
if
(
devslp
&
PORT_DEVSLP_ADSE
)
{
writel
(
devslp
&
~
PORT_DEVSLP_ADSE
,
port_mmio
+
PORT_DEVSLP
);
err_mask
=
ata_dev_set_feature
(
dev
,
SETFEATURES_SATA_DISABLE
,
SATA_DEVSLP
);
if
(
err_mask
&&
err_mask
!=
AC_ERR_DEV
)
ata_dev_warn
(
dev
,
"failed to disable DEVSLP
\n
"
);
}
return
;
}
/* device sleep was already enabled */
if
(
devslp
&
PORT_DEVSLP_ADSE
)
return
;
/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
return
;
dm
=
(
devslp
&
PORT_DEVSLP_DM_MASK
)
>>
PORT_DEVSLP_DM_OFFSET
;
dito
=
devslp_idle_timeout
/
(
dm
+
1
);
if
(
dito
>
0x3ff
)
dito
=
0x3ff
;
/* Use the nominal value 10 ms if the read MDAT is zero,
* the nominal value of DETO is 20 ms.
*/
if
(
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_VALID
]
&
ATA_LOG_DEVSLP_VALID_MASK
)
{
mdat
=
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_MDAT
]
&
ATA_LOG_DEVSLP_MDAT_MASK
;
if
(
!
mdat
)
mdat
=
10
;
deto
=
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_DETO
];
if
(
!
deto
)
deto
=
20
;
}
else
{
mdat
=
10
;
deto
=
20
;
}
devslp
|=
((
dito
<<
PORT_DEVSLP_DITO_OFFSET
)
|
(
mdat
<<
PORT_DEVSLP_MDAT_OFFSET
)
|
(
deto
<<
PORT_DEVSLP_DETO_OFFSET
)
|
PORT_DEVSLP_ADSE
);
writel
(
devslp
,
port_mmio
+
PORT_DEVSLP
);
hpriv
->
start_engine
(
ap
);
/* enable device sleep feature for the drive */
err_mask
=
ata_dev_set_feature
(
dev
,
SETFEATURES_SATA_ENABLE
,
SATA_DEVSLP
);
if
(
err_mask
&&
err_mask
!=
AC_ERR_DEV
)
ata_dev_warn
(
dev
,
"failed to enable DEVSLP
\n
"
);
}
static
void
ahci_enable_fbs
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
&&
pp
->
fbs_supported
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"enable port %u FIS-based switching"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
pp
->
fbs_supported
)
return
;
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
if
(
fbs
&
PORT_FBS_EN
)
{
pp
->
fbs_enabled
=
true
;
pp
->
fbs_last_dev
=
-
1
;
/* initialization */
return
;
}
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
return
;
writel
(
fbs
|
PORT_FBS_EN
,
port_mmio
+
PORT_FBS
);
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
if
(
fbs
&
PORT_FBS_EN
)
{
dev_info
(
ap
->
host
->
dev
,
"FBS is enabled
\n
"
);
pp
->
fbs_enabled
=
true
;
pp
->
fbs_last_dev
=
-
1
;
/* initialization */
}
else
dev_err
(
ap
->
host
->
dev
,
"Failed to enable FBS
\n
"
);
hpriv
->
start_engine
(
ap
);
}
static
void
ahci_disable_fbs
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
&&
pp
->
fbs_supported
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"disable port %u FIS-based switching"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
pp
->
fbs_supported
)
return
;
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
if
((
fbs
&
PORT_FBS_EN
)
==
0
)
{
pp
->
fbs_enabled
=
false
;
return
;
}
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
return
;
writel
(
fbs
&
~
PORT_FBS_EN
,
port_mmio
+
PORT_FBS
);
fbs
=
readl
(
port_mmio
+
PORT_FBS
);
if
(
fbs
&
PORT_FBS_EN
)
dev_err
(
ap
->
host
->
dev
,
"Failed to disable FBS
\n
"
);
else
{
dev_info
(
ap
->
host
->
dev
,
"FBS is disabled
\n
"
);
pp
->
fbs_enabled
=
false
;
}
hpriv
->
start_engine
(
ap
);
}
static
void
ahci_pmp_attach
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
cmd
;
cmd
=
readl
(
port_mmio
+
PORT_CMD
);
cmd
|=
PORT_CMD_PMP
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
ahci_enable_fbs
(
ap
);
pp
->
intr_mask
|=
PORT_IRQ_BAD_PMP
;
/*
* We must not change the port interrupt mask register if the
* port is marked frozen, the value in pp->intr_mask will be
* restored later when the port is thawed.
*
* Note that during initialization, the port is marked as
* frozen since the irq handler is not yet registered.
*/
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
static
void
ahci_pmp_detach
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
cmd
;
ahci_disable_fbs
(
ap
);
cmd
=
readl
(
port_mmio
+
PORT_CMD
);
cmd
&=
~
PORT_CMD_PMP
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
pp
->
intr_mask
&=
~
PORT_IRQ_BAD_PMP
;
/* see comment above in ahci_pmp_attach() */
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
int
ahci_port_resume
(
struct
ata_port
*
ap
)
{
ahci_power_up
(
ap
);
ahci_start_port
(
ap
);
if
(
sata_pmp_attached
(
ap
))
ahci_pmp_attach
(
ap
);
else
ahci_pmp_detach
(
ap
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_port_resume
);
#ifdef CONFIG_PM
static
int
ahci_port_suspend
(
struct
ata_port
*
ap
,
pm_message_t
mesg
)
{
const
char
*
emsg
=
NULL
;
int
rc
;
rc
=
ahci_deinit_port
(
ap
,
&
emsg
);
if
(
rc
==
0
)
ahci_power_down
(
ap
);
else
{
ata_port_err
(
ap
,
"%s (%d)
\n
"
,
emsg
,
rc
);
ata_port_freeze
(
ap
);
}
return
rc
;
}
#endif
static
int
ahci_port_start
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
device
*
dev
=
ap
->
host
->
dev
;
struct
ahci_port_priv
*
pp
;
void
*
mem
;
dma_addr_t
mem_dma
;
size_t
dma_sz
,
rx_fis_sz
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"starting port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
pp
=
devm_kzalloc
(
dev
,
sizeof
(
*
pp
),
GFP_KERNEL
);
if
(
!
pp
)
return
-
ENOMEM
;
if
(
ap
->
host
->
n_ports
>
1
)
{
pp
->
irq_desc
=
devm_kzalloc
(
dev
,
8
,
GFP_KERNEL
);
if
(
!
pp
->
irq_desc
)
{
devm_kfree
(
dev
,
pp
);
return
-
ENOMEM
;
}
snprintf
(
pp
->
irq_desc
,
8
,
"%s%d"
,
dev_driver_string
(
dev
),
ap
->
port_no
);
}
/* check FBS capability */
if
((
hpriv
->
cap
&
HOST_CAP_FBS
)
&&
sata_pmp_supported
(
ap
))
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
cmd
=
readl
(
port_mmio
+
PORT_CMD
);
if
(
cmd
&
PORT_CMD_FBSCP
)
pp
->
fbs_supported
=
true
;
else
if
(
hpriv
->
flags
&
AHCI_HFLAG_YES_FBS
)
{
dev_info
(
dev
,
"port %d can do FBS, forcing FBSCP
\n
"
,
ap
->
port_no
);
pp
->
fbs_supported
=
true
;
}
else
dev_warn
(
dev
,
"port %d is not capable of FBS
\n
"
,
ap
->
port_no
);
}
if
(
pp
->
fbs_supported
)
{
dma_sz
=
AHCI_PORT_PRIV_FBS_DMA_SZ
;
rx_fis_sz
=
AHCI_RX_FIS_SZ
*
16
;
}
else
{
dma_sz
=
AHCI_PORT_PRIV_DMA_SZ
;
rx_fis_sz
=
AHCI_RX_FIS_SZ
;
}
mem
=
dmam_alloc_coherent
(
dev
,
dma_sz
,
&
mem_dma
,
GFP_KERNEL
);
if
(
!
mem
)
return
-
ENOMEM
;
memset
(
mem
,
0
,
dma_sz
);
/*
* First item in chunk of DMA memory: 32-slot command table,
* 32 bytes each in size
*/
pp
->
cmd_slot
=
mem
;
pp
->
cmd_slot_dma
=
mem_dma
;
mem
+=
AHCI_CMD_SLOT_SZ
;
mem_dma
+=
AHCI_CMD_SLOT_SZ
;
/*
* Second item: Received-FIS area
*/
pp
->
rx_fis
=
mem
;
pp
->
rx_fis_dma
=
mem_dma
;
mem
+=
rx_fis_sz
;
mem_dma
+=
rx_fis_sz
;
/*
* Third item: data area for storing a single command
* and its scatter-gather table
*/
pp
->
cmd_tbl
=
mem
;
pp
->
cmd_tbl_dma
=
mem_dma
;
/*
* Save off initial list of interrupts to be enabled.
* This could be changed later
*/
pp
->
intr_mask
=
DEF_PORT_IRQ
;
/*
* Switch to per-port locking in case each port has its own MSI vector.
*/
if
((
hpriv
->
flags
&
AHCI_HFLAG_MULTI_MSI
))
{
spin_lock_init
(
&
pp
->
lock
);
ap
->
lock
=
&
pp
->
lock
;
}
ap
->
private_data
=
pp
;
/* engage engines, captain */
return
ahci_port_resume
(
ap
);
}
static
void
ahci_port_stop
(
struct
ata_port
*
ap
)
{
const
char
*
emsg
=
NULL
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stopping port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* de-initialize port */
rc
=
ahci_deinit_port
(
ap
,
&
emsg
);
if
(
rc
)
ata_port_warn
(
ap
,
"%s (%d)
\n
"
,
emsg
,
rc
);
}
void
ahci_print_info
(
struct
ata_host
*
host
,
const
char
*
scc_s
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
vers
,
cap
,
cap2
,
impl
,
speed
;
const
char
*
speed_s
;
vers
=
readl
(
mmio
+
HOST_VERSION
);
cap
=
hpriv
->
cap
;
cap2
=
hpriv
->
cap2
;
impl
=
hpriv
->
port_map
;
speed
=
(
cap
>>
20
)
&
0xf
;
if
(
speed
==
1
)
speed_s
=
"1.5"
;
else
if
(
speed
==
2
)
speed_s
=
"3"
;
else
if
(
speed
==
3
)
speed_s
=
"6"
;
else
speed_s
=
"?"
;
dev_info
(
host
->
dev
,
"AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl %s mode
\n
"
,
(
vers
>>
24
)
&
0xff
,
(
vers
>>
16
)
&
0xff
,
(
vers
>>
8
)
&
0xff
,
vers
&
0xff
,
((
cap
>>
8
)
&
0x1f
)
+
1
,
(
cap
&
0x1f
)
+
1
,
speed_s
,
impl
,
scc_s
);
dev_info
(
host
->
dev
,
"flags: "
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s%s%s%s%s%s"
"%s%s
\n
"
,
cap
&
HOST_CAP_64
?
"64bit "
:
""
,
cap
&
HOST_CAP_NCQ
?
"ncq "
:
""
,
cap
&
HOST_CAP_SNTF
?
"sntf "
:
""
,
cap
&
HOST_CAP_MPS
?
"ilck "
:
""
,
cap
&
HOST_CAP_SSS
?
"stag "
:
""
,
cap
&
HOST_CAP_ALPM
?
"pm "
:
""
,
cap
&
HOST_CAP_LED
?
"led "
:
""
,
cap
&
HOST_CAP_CLO
?
"clo "
:
""
,
cap
&
HOST_CAP_ONLY
?
"only "
:
""
,
cap
&
HOST_CAP_PMP
?
"pmp "
:
""
,
cap
&
HOST_CAP_FBS
?
"fbs "
:
""
,
cap
&
HOST_CAP_PIO_MULTI
?
"pio "
:
""
,
cap
&
HOST_CAP_SSC
?
"slum "
:
""
,
cap
&
HOST_CAP_PART
?
"part "
:
""
,
cap
&
HOST_CAP_CCC
?
"ccc "
:
""
,
cap
&
HOST_CAP_EMS
?
"ems "
:
""
,
cap
&
HOST_CAP_SXS
?
"sxs "
:
""
,
cap2
&
HOST_CAP2_DESO
?
"deso "
:
""
,
cap2
&
HOST_CAP2_SADM
?
"sadm "
:
""
,
cap2
&
HOST_CAP2_SDS
?
"sds "
:
""
,
cap2
&
HOST_CAP2_APST
?
"apst "
:
""
,
cap2
&
HOST_CAP2_NVMHCI
?
"nvmp "
:
""
,
cap2
&
HOST_CAP2_BOH
?
"boh "
:
""
);
}
EXPORT_SYMBOL_GPL
(
ahci_print_info
);
void
ahci_set_em_messages
(
struct
ahci_host_priv
*
hpriv
,
struct
ata_port_info
*
pi
)
{
u8
messages
;
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
em_loc
=
readl
(
mmio
+
HOST_EM_LOC
);
u32
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"store EM registers"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
ahci_em_messages
||
!
(
hpriv
->
cap
&
HOST_CAP_EMS
))
return
;
messages
=
(
em_ctl
&
EM_CTRL_MSG_TYPE
)
>>
16
;
if
(
messages
)
{
/* store em_loc */
hpriv
->
em_loc
=
((
em_loc
>>
16
)
*
4
);
hpriv
->
em_buf_sz
=
((
em_loc
&
0xff
)
*
4
);
hpriv
->
em_msg_type
=
messages
;
pi
->
flags
|=
ATA_FLAG_EM
;
if
(
!
(
em_ctl
&
EM_CTL_ALHD
))
pi
->
flags
|=
ATA_FLAG_SW_ACTIVITY
;
}
}
EXPORT_SYMBOL_GPL
(
ahci_set_em_messages
);
static
int
ahci_host_activate_multi_irqs
(
struct
ata_host
*
host
,
int
irq
,
struct
scsi_host_template
*
sht
)
{
int
i
,
rc
;
rc
=
ata_host_start
(
host
);
if
(
rc
)
return
rc
;
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
{
struct
ahci_port_priv
*
pp
=
host
->
ports
[
i
]
->
private_data
;
/* Do not receive interrupts sent by dummy ports */
if
(
!
pp
)
{
disable_irq
(
irq
+
i
);
continue
;
}
rc
=
devm_request_threaded_irq
(
host
->
dev
,
irq
+
i
,
ahci_multi_irqs_intr
,
ahci_port_thread_fn
,
IRQF_SHARED
,
pp
->
irq_desc
,
host
->
ports
[
i
]);
if
(
rc
)
goto
out_free_irqs
;
}
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
ata_port_desc
(
host
->
ports
[
i
],
"irq %d"
,
irq
+
i
);
rc
=
ata_host_register
(
host
,
sht
);
if
(
rc
)
goto
out_free_all_irqs
;
return
0
;
out_free_all_irqs:
i
=
host
->
n_ports
;
out_free_irqs:
for
(
i
--
;
i
>=
0
;
i
--
)
devm_free_irq
(
host
->
dev
,
irq
+
i
,
host
->
ports
[
i
]);
return
rc
;
}
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
* @irq: base IRQ number to request
* @sht: scsi_host_template to use when registering the host
*
* Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
* when multiple MSIs were allocated. That is one MSI per port, starting
* from @irq.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int
ahci_host_activate
(
struct
ata_host
*
host
,
int
irq
,
struct
scsi_host_template
*
sht
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"activate AHCI host"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
hpriv
->
flags
&
AHCI_HFLAG_MULTI_MSI
)
rc
=
ahci_host_activate_multi_irqs
(
host
,
irq
,
sht
);
else
rc
=
ata_host_activate
(
host
,
irq
,
ahci_single_irq_intr
,
IRQF_SHARED
,
sht
);
return
rc
;
}
EXPORT_SYMBOL_GPL
(
ahci_host_activate
);
MODULE_AUTHOR
(
"Jeff Garzik"
);
MODULE_DESCRIPTION
(
"Common AHCI SATA low-level routines"
);
MODULE_LICENSE
(
"GPL"
);
src/drivers/ata/libahci_debug.c
0 → 100644
View file @
2ca997e5
/*
* libahci_debug.c
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <asm/outercache.h>
#include <asm/cacheflush.h>
#include "libahci_debug.h"
static
struct
dentry
*
debug_root
=
NULL
;
static
struct
libahci_debug_list
debug_list
=
{.
debug
=
0
};
static
struct
ahci_cmd
cmd
;
static
bool
load_flag
=
false
;
static
struct
mem_buffer
mem_buff
;
/*
* Print PxIS (0x10) analysis
*/
void
libahci_debug_dump_irq
(
u32
status
)
{
int
len
=
0
;
int
pos
;
char
*
str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
!
str
)
return
;
len
=
snprintf
(
str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
interrupt analysis: "
);
pos
=
len
;
if
(
status
&
PORT_IRQ_D2H_REG_FIS
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"D2H Register FIS * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_PIOS_FIS
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
" PIO Setup FIS * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_DMAS_FIS
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"DMA Setup FIS * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_SDB_FIS
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Set Device Bits FIS * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_UNK_FIS
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Unknown FIS * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_SG_DONE
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Descriptor processed * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_CONNECT
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Port connect change status * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_DEV_ILCK
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Device interlock * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_PHYRDY
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"PhyRdy change status * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_BAD_PMP
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Incorrect port multiplier * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_OVERFLOW
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Overflow * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_IF_NONFATAL
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Iface nonfatal error * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_IF_ERR
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Iface fatal error * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_HBUS_DATA_ERR
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Host bus data error * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_HBUS_ERR
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Host bus fatal error * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_TF_ERR
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Task file error * "
);
pos
+=
len
;
}
if
(
status
&
PORT_IRQ_COLD_PRES
)
{
len
=
snprintf
(
&
str
[
pos
],
LIBAHCI_DEBUG_BUFSZ
-
pos
,
"Cold port detect * "
);
pos
+=
len
;
}
libahci_debug_event
(
NULL
,
str
,
pos
);
kfree
(
str
);
}
EXPORT_SYMBOL_GPL
(
libahci_debug_dump_irq
);
/*
* Read memory region pointed to by buff and dump its content
*/
void
libahci_debug_dump_region
(
const
struct
ata_port
*
ap
,
const
u32
*
buff
,
size_t
buff_sz
,
const
char
*
prefix
)
{
int
i
;
int
len
,
sz
;
char
*
str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
!
str
)
return
;
len
=
strlen
(
prefix
);
if
(
len
<
LIBAHCI_DEBUG_BUFSZ
)
{
strncpy
(
str
,
prefix
,
len
);
}
else
{
len
=
0
;
}
for
(
i
=
0
;
i
<
buff_sz
;
i
++
)
{
sz
=
snprintf
(
&
str
[
len
],
LIBAHCI_DEBUG_BUFSZ
-
len
,
"0x%08x "
,
buff
[
i
]);
len
+=
sz
;
}
libahci_debug_event
(
ap
,
str
,
len
);
kfree
(
str
);
}
EXPORT_SYMBOL_GPL
(
libahci_debug_dump_region
);
/*
* Copy data from S/G list to linear buffer and dump the data
*/
void
libahci_debug_dump_sg
(
const
struct
ata_queued_cmd
*
qc
,
const
char
*
prefix
)
{
struct
scatterlist
*
sg
;
int
si
;
int
i
;
int
len
;
int
sz
;
int
line_brk
;
u32
buff_sz
=
0
;
u32
buff_ptr
=
0
;
char
*
buff
;
char
*
str
;
u32
*
buff_map
;
// Calculate the amount of memory needed
for_each_sg
(
qc
->
sg
,
sg
,
qc
->
n_elem
,
si
)
{
buff_sz
+=
sg_dma_len
(
sg
);
}
buff
=
kzalloc
(
buff_sz
,
GFP_KERNEL
);
if
(
!
buff
)
{
return
;
}
str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
!
str
)
{
kfree
(
buff
);
return
;
}
// Copy data from all DMA buffers
dma_sync_sg_for_cpu
(
&
qc
->
dev
->
tdev
,
qc
->
sg
,
qc
->
n_elem
,
qc
->
dma_dir
);
for_each_sg
(
qc
->
sg
,
sg
,
qc
->
n_elem
,
si
)
{
u32
sg_len
=
sg_dma_len
(
sg
);
sz
=
sg_copy_to_buffer
(
sg
,
1
,
buff
+
buff_ptr
,
sg_len
);
buff_ptr
+=
sz
;
}
dma_sync_sg_for_device
(
&
qc
->
dev
->
tdev
,
qc
->
sg
,
qc
->
n_elem
,
qc
->
dma_dir
);
// Print the content of DMA buffers
buff_map
=
(
u32
*
)
buff
;
len
=
snprintf
(
str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
%s
\t
%u bytes
\n\t
"
,
prefix
,
buff_ptr
);
for
(
i
=
0
,
line_brk
=
0
;
i
<
buff_ptr
/
4
;
i
++
)
{
sz
=
snprintf
(
&
str
[
len
],
LIBAHCI_DEBUG_BUFSZ
-
len
,
"0x%08x "
,
buff_map
[
i
]);
len
+=
sz
;
line_brk
++
;
if
(
line_brk
>=
8
)
{
libahci_debug_event
(
qc
->
ap
,
str
,
len
);
line_brk
=
0
;
len
=
snprintf
(
str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
"
);
}
}
if
(
line_brk
!=
0
)
{
libahci_debug_event
(
qc
->
ap
,
str
,
len
);
}
//printk(KERN_DEBUG "%s\tdump S/G list", MARKER);
kfree
(
buff
);
kfree
(
str
);
}
EXPORT_SYMBOL_GPL
(
libahci_debug_dump_sg
);
static
void
libahci_debug_read_host_regs
(
struct
ata_host
*
host
,
struct
host_regs
*
regs
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
void
__iomem
*
host_mmio
=
hpriv
->
mmio
;
regs
->
CAP
=
readl
(
host_mmio
+
HOST_CAP
);
regs
->
CAP2
=
readl
(
host_mmio
+
HOST_CAP2
);
regs
->
GHC
=
readl
(
host_mmio
+
HOST_CTL
);
regs
->
IS
=
readl
(
host_mmio
+
HOST_IRQ_STAT
);
regs
->
PI
=
readl
(
host_mmio
+
HOST_PORTS_IMPL
);
regs
->
VS
=
readl
(
host_mmio
+
HOST_VERSION
);
regs
->
CCC_CTL
=
readl
(
host_mmio
+
0x14
);
regs
->
CCC_PORTS
=
readl
(
host_mmio
+
0x18
);
regs
->
EM_CTL
=
readl
(
host_mmio
+
HOST_EM_CTL
);
regs
->
EM_LOC
=
readl
(
host_mmio
+
HOST_EM_LOC
);
regs
->
BOHC
=
readl
(
host_mmio
+
0x28
);
}
static
void
libahci_debug_read_port_regs
(
struct
ata_port
*
ap
,
struct
port_regs
*
pr
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
i
;
pr
->
PxCLB
=
readl
(
port_mmio
+
PORT_LST_ADDR
);
pr
->
PxCLBU
=
readl
(
port_mmio
+
PORT_LST_ADDR_HI
);
pr
->
PxFB
=
readl
(
port_mmio
+
PORT_FIS_ADDR
);
pr
->
PxFBU
=
readl
(
port_mmio
+
PORT_FIS_ADDR_HI
);
pr
->
PxIS
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
pr
->
PxIE
=
readl
(
port_mmio
+
PORT_IRQ_MASK
);
pr
->
PxCMD
=
readl
(
port_mmio
+
PORT_CMD
);
//pr->reserved_1 = readl(port_mmio + PORT_RESERVED_1);
pr
->
PxTFD
=
readl
(
port_mmio
+
PORT_TFDATA
);
pr
->
PxSIG
=
readl
(
port_mmio
+
PORT_SIG
);
pr
->
PxSSTS
=
readl
(
port_mmio
+
PORT_SCR_STAT
);
pr
->
PxSCTL
=
readl
(
port_mmio
+
PORT_SCR_CTL
);
pr
->
PxSERR
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
pr
->
PxSACT
=
readl
(
port_mmio
+
PORT_SCR_ACT
);
pr
->
PxCI
=
readl
(
port_mmio
+
PORT_CMD_ISSUE
);
pr
->
PxSNTF
=
readl
(
port_mmio
+
PORT_SCR_NTF
);
pr
->
PxFBS
=
readl
(
port_mmio
+
PORT_FBS
);
pr
->
PxDEVSLP
=
readl
(
port_mmio
+
PORT_DEVSLP
);
for
(
i
=
0
;
i
<
PORT_VENDOR_BYTES
;
i
++
)
{
pr
->
reserved_2
[
i
]
=
readb
(
port_mmio
+
0x70
+
i
);
}
}
static
int
libahci_debug_host_show
(
struct
seq_file
*
f
,
void
*
p
)
{
struct
ata_host
*
host
=
f
->
private
;
struct
host_regs
hr
=
{
0
};
libahci_debug_read_host_regs
(
host
,
&
hr
);
seq_printf
(
f
,
"CAP:
\t\t
0x%08X
\n
"
,
hr
.
CAP
);
seq_printf
(
f
,
"CAP2:
\t\t
0x%08X
\n
"
,
hr
.
CAP2
);
seq_printf
(
f
,
"GHC:
\t\t
0x%08X
\n
"
,
hr
.
GHC
);
seq_printf
(
f
,
"IS:
\t\t
0x%08X
\n
"
,
hr
.
IS
);
seq_printf
(
f
,
"PI:
\t\t
0x%08X
\n
"
,
hr
.
PI
);
seq_printf
(
f
,
"VS:
\t\t
0x%08X
\n
"
,
hr
.
VS
);
seq_printf
(
f
,
"CCC_CTL:
\t
0x%08X
\n
"
,
hr
.
CCC_CTL
);
seq_printf
(
f
,
"CCC_PORTS:
\t
0x%08X
\n
"
,
hr
.
CCC_PORTS
);
seq_printf
(
f
,
"EM_LOC:
\t\t
0x%08X
\n
"
,
hr
.
EM_LOC
);
seq_printf
(
f
,
"EM_CTL:
\t\t
0x%08X
\n
"
,
hr
.
EM_CTL
);
seq_printf
(
f
,
"BOHC:
\t\t
0x%08X
\n
"
,
hr
.
BOHC
);
seq_printf
(
f
,
"
\n
buffer location:
\t\t
0x%08X
\n
"
,
mem_buff
.
paddr
);
return
0
;
}
static
int
libahci_debug_rdesc_show
(
struct
seq_file
*
f
,
void
*
p
)
{
struct
ata_port
*
ap
=
f
->
private
;
struct
port_regs
pr
=
{
0
};
int
i
;
libahci_debug_read_port_regs
(
ap
,
&
pr
);
seq_printf
(
f
,
"PxCLB:
\t\t
0x%08X
\n
"
,
pr
.
PxCLB
);
seq_printf
(
f
,
"PxCLBU:
\t\t
0x%08X
\n
"
,
pr
.
PxCLBU
);
seq_printf
(
f
,
"PxFB:
\t\t
x0%08X
\n
"
,
pr
.
PxFB
);
seq_printf
(
f
,
"PxFBU:
\t\t
0x%08X
\n
"
,
pr
.
PxFBU
);
seq_printf
(
f
,
"PxIS:
\t\t
0x%08X
\n
"
,
pr
.
PxIS
);
seq_printf
(
f
,
"PxIE:
\t\t
0x%08X
\n
"
,
pr
.
PxIE
);
seq_printf
(
f
,
"PxCMD:
\t\t
0x%08X
\n
"
,
pr
.
PxCMD
);
seq_printf
(
f
,
"reserved:
\t
0x%08X
\n
"
,
pr
.
reserved_1
);
seq_printf
(
f
,
"PxTFD:
\t\t
0x%08X
\n
"
,
pr
.
PxTFD
);
seq_printf
(
f
,
"PxSIG:
\t\t
0x%08X
\n
"
,
pr
.
PxSIG
);
seq_printf
(
f
,
"PxSSTS:
\t\t
0x%08X
\n
"
,
pr
.
PxSSTS
);
seq_printf
(
f
,
"PxSCTL:
\t\t
0x%08X
\n
"
,
pr
.
PxSCTL
);
seq_printf
(
f
,
"PxSERR:
\t\t
0x%08X
\n
"
,
pr
.
PxSERR
);
seq_printf
(
f
,
"PxSACT:
\t\t
0x%08X
\n
"
,
pr
.
PxSACT
);
seq_printf
(
f
,
"PxCI:
\t\t
0x%08X
\n
"
,
pr
.
PxCI
);
seq_printf
(
f
,
"PxSNTF:
\t\t
0x%08X
\n
"
,
pr
.
PxSNTF
);
seq_printf
(
f
,
"PxFBS:
\t\t
0x%08X
\n
"
,
pr
.
PxFBS
);
seq_printf
(
f
,
"PxDEVSLP:
\t
0x%08X
\n
"
,
pr
.
PxDEVSLP
);
seq_printf
(
f
,
"reserved area:
\t
"
);
for
(
i
=
0
;
i
<
PORT_RESERVED_2
;
i
++
)
{
seq_printf
(
f
,
"0x%02X "
,
pr
.
reserved_2
[
i
]);
}
seq_printf
(
f
,
"
\n
Vendor specific bytes:
\t
"
);
for
(
i
=
0
;
i
<
PORT_VENDOR_BYTES
;
i
++
)
{
seq_printf
(
f
,
"0x%02X "
,
pr
.
PxVS
[
i
]);
}
seq_printf
(
f
,
"
\n
"
);
return
0
;
}
static
int
libahci_debug_host_open
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
return
single_open
(
f
,
libahci_debug_host_show
,
i_node
->
i_private
);
}
static
int
libahci_debug_rdesc_open
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
return
single_open
(
f
,
libahci_debug_rdesc_show
,
i_node
->
i_private
);
}
static
int
libahci_debug_events_open
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
int
err
=
0
;
unsigned
long
flags
;
struct
libahci_debug_list
*
list
;
struct
ata_port
*
port
=
i_node
->
i_private
;
debug_list
.
debug
=
1
;
//Create event buffer for current port
list
=
kzalloc
(
sizeof
(
struct
libahci_debug_list
),
GFP_KERNEL
);
if
(
!
list
)
{
err
=
-
ENOMEM
;
goto
fail_handler
;
}
if
(
!
(
list
->
libahci_debug_buf
=
kzalloc
(
sizeof
(
char
)
*
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
)))
{
err
=
-
ENOMEM
;
kfree
(
list
);
goto
fail_handler
;
}
list
->
port_n
=
port
->
port_no
;
mutex_init
(
&
list
->
read_mutex
);
init_waitqueue_head
(
&
list
->
debug_wait
);
spin_lock_irqsave
(
&
list
->
debug_list_lock
,
flags
);
list_add_tail
(
&
list
->
node
,
&
debug_list
.
node
);
spin_unlock_irqrestore
(
&
list
->
debug_list_lock
,
flags
);
// Associate debug list entry with corresponding file
f
->
private_data
=
list
;
fail_handler:
return
err
;
}
static
ssize_t
libahci_debug_events_read
(
struct
file
*
f
,
char
__user
*
buff
,
size_t
sz
,
loff_t
*
pos
)
{
int
ret
=
0
,
len
;
struct
libahci_debug_list
*
list
=
f
->
private_data
;
DECLARE_WAITQUEUE
(
wait
,
current
);
mutex_lock
(
&
list
->
read_mutex
);
while
(
ret
==
0
)
{
if
(
list
->
head
==
list
->
tail
)
{
// Buffer is empty, put the queue in sleep mode
add_wait_queue
(
&
list
->
debug_wait
,
&
wait
);
set_current_state
(
TASK_INTERRUPTIBLE
);
while
(
list
->
head
==
list
->
tail
)
{
if
(
f
->
f_flags
&
O_NONBLOCK
)
{
ret
=
-
EAGAIN
;
break
;
}
if
(
signal_pending
(
current
))
{
ret
=
-
ERESTARTSYS
;
break
;
}
mutex_unlock
(
&
list
->
read_mutex
);
schedule
();
mutex_lock
(
&
list
->
read_mutex
);
set_current_state
(
TASK_INTERRUPTIBLE
);
}
set_current_state
(
TASK_RUNNING
);
remove_wait_queue
(
&
list
->
debug_wait
,
&
wait
);
}
copy_rest:
//printk(KERN_DEBUG "%s Read event", MARKER);
if
(
list
->
head
!=
list
->
tail
&&
ret
==
0
)
{
if
(
list
->
tail
>
list
->
head
)
{
len
=
list
->
tail
-
list
->
head
;
if
(
copy_to_user
(
buff
+
ret
,
&
list
->
libahci_debug_buf
[
list
->
head
],
len
))
{
ret
=
-
EFAULT
;
}
else
{
ret
+=
len
;
list
->
head
+=
len
;
}
}
else
{
len
=
LIBAHCI_DEBUG_BUFSZ
-
list
->
head
;
if
(
copy_to_user
(
buff
,
&
list
->
libahci_debug_buf
[
list
->
head
],
len
))
{
ret
=
-
EFAULT
;
}
else
{
ret
+=
len
;
list
->
head
=
0
;
goto
copy_rest
;
}
}
}
//printk(KERN_DEBUG "%s\thead now is %u", MARKER, list->head);
}
mutex_unlock
(
&
list
->
read_mutex
);
return
ret
;
}
static
unsigned
int
libahci_debug_events_poll
(
struct
file
*
f
,
struct
poll_table_struct
*
wait
)
{
struct
libahci_debug_list
*
list
=
f
->
private_data
;
poll_wait
(
f
,
&
list
->
debug_wait
,
wait
);
if
(
list
->
head
!=
list
->
tail
)
{
return
POLLIN
|
POLLRDNORM
;
}
return
0
;
}
static
int
libahci_debug_events_release
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
struct
libahci_debug_list
*
list
=
f
->
private_data
;
unsigned
long
flags
;
debug_list
.
debug
=
0
;
spin_lock_irqsave
(
&
list
->
debug_list_lock
,
flags
);
list_del
(
&
list
->
node
);
spin_unlock_irqrestore
(
&
list
->
debug_list_lock
,
flags
);
kfree
(
list
->
libahci_debug_buf
);
kfree
(
list
);
return
0
;
}
void
libahci_debug_event
(
const
struct
ata_port
*
port
,
char
*
msg
,
size_t
msg_sz
)
{
int
len
;
int
i
;
char
*
format_msg
=
NULL
;
unsigned
long
flags
;
unsigned
int
port_index
=
(
port
==
NULL
)
?
0
:
port
->
port_no
;
struct
libahci_debug_list
*
list
=
NULL
,
*
pos
=
NULL
;
//printk(KERN_DEBUG "%s Write event: %s", MARKER, msg);
if
(
debug_list
.
debug
)
{
format_msg
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
format_msg
!=
NULL
)
{
// Find buffer which this event is addressed to
list_for_each_entry
(
list
,
&
debug_list
.
node
,
node
)
{
if
(
list
->
port_n
==
port_index
)
{
pos
=
list
;
}
}
if
(
pos
!=
NULL
)
{
//i = libahci_debug_state_dump(port);
spin_lock_irqsave
(
&
pos
->
debug_list_lock
,
flags
);
len
=
snprintf
(
format_msg
,
LIBAHCI_DEBUG_BUFSZ
,
"%s [%08u] %s
\n
"
,
EVT_MARKER
,
i
,
msg
);
for
(
i
=
0
;
i
<
len
;
i
++
)
{
pos
->
libahci_debug_buf
[(
pos
->
tail
+
i
)
%
LIBAHCI_DEBUG_BUFSZ
]
=
format_msg
[
i
];
}
pos
->
tail
=
(
pos
->
tail
+
len
)
%
LIBAHCI_DEBUG_BUFSZ
;
spin_unlock_irqrestore
(
&
pos
->
debug_list_lock
,
flags
);
//printk(KERN_DEBUG "%s\ttail is now %u", MARKER, pos->tail);
// Wake up the queue which should be sleeping
wake_up_interruptible
(
&
pos
->
debug_wait
);
}
kfree
(
format_msg
);
}
}
}
EXPORT_SYMBOL_GPL
(
libahci_debug_event
);
static
void
libahci_debug_prep_cfis
(
struct
ahci_cmd_fis
*
cmd
,
u32
*
fis
,
u8
pmp
)
{
fis
[
0
]
=
cmd
->
dw0
;
fis
[
1
]
=
cmd
->
dw1
;
fis
[
2
]
=
cmd
->
dw2
;
fis
[
3
]
=
cmd
->
dw3
;
fis
[
4
]
=
cmd
->
dw4
;
}
void
libahci_debug_exec_cmd
(
struct
ata_port
*
ap
)
{
void
*
cmd_tbl
;
dma_addr_t
cmd_tbl_dma
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_sg
*
ahci_sg
;
struct
ahci_cmd_hdr
*
data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
unsigned
int
slot
;
printk
(
KERN_DEBUG
"%s Executing command for port %u"
,
MARKER
,
ap
->
port_no
);
slot
=
readl
(
port_mmio
+
PORT_CMD_ISSUE
);
printk
(
KERN_DEBUG
"%s PxCI: 0x%08x"
,
MARKER
,
slot
);
slot
=
ffz
(
slot
);
printk
(
KERN_DEBUG
"%s Preparing command; using slot %u"
,
MARKER
,
slot
);
cmd_tbl
=
pp
->
cmd_tbl
+
slot
*
AHCI_CMD_TBL_SZ
;
libahci_debug_prep_cfis
(
&
cmd
.
fis
,
cmd_tbl
,
ap
->
link
.
pmp
);
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)
cmd_tbl
,
5
,
"
\t
cfis data dump: "
);
/*printk(KERN_DEBUG "%s\tcfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x",
MARKER, cmd.fis.dw0, cmd.fis.dw1, cmd.fis.dw2, cmd.fis.dw3, cmd.fis.dw4);*/
printk
(
KERN_DEBUG
"%s Preparing one S/G list"
,
MARKER
);
ahci_sg
=
cmd_tbl
+
AHCI_CMD_TBL_HDR_SZ
;
ahci_sg
->
addr
=
cpu_to_le32
(
sg_dma_address
(
&
cmd
.
sg
)
&
0xffffffff
);
ahci_sg
->
addr_hi
=
cpu_to_le32
((
sg_dma_address
(
&
cmd
.
sg
)
>>
16
)
>>
16
);
ahci_sg
->
flags_size
=
cpu_to_le32
(
sg_dma_len
(
&
cmd
.
sg
));
printk
(
KERN_DEBUG
"%s Preparing command header"
,
MARKER
);
cmd_tbl_dma
=
pp
->
cmd_tbl_dma
+
slot
*
AHCI_CMD_TBL_SZ
;
pp
->
cmd_slot
[
slot
].
opts
=
cpu_to_le32
(
cmd
.
hdr
.
opts
);
pp
->
cmd_slot
[
slot
].
status
=
0
;
pp
->
cmd_slot
[
slot
].
tbl_addr
=
cpu_to_le32
(
cmd_tbl_dma
&
0xffffffff
);
pp
->
cmd_slot
[
slot
].
tbl_addr_hi
=
cpu_to_le32
((
cmd_tbl_dma
>>
16
)
>>
16
);
data
=
&
pp
->
cmd_slot
[
slot
];
printk
(
KERN_DEBUG
"%s
\t
chdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x"
,
MARKER
,
data
->
opts
,
data
->
status
,
data
->
tbl_addr
,
data
->
tbl_addr_hi
);
printk
(
KERN_DEBUG
"%s Issuing command"
,
MARKER
);
writel
(
1
<<
slot
,
port_mmio
+
PORT_CMD_ISSUE
);
cmd
.
cmd_sent
=
true
;
}
EXPORT_SYMBOL_GPL
(
libahci_debug_exec_cmd
);
void
libahci_debug_irq_notify
(
const
struct
ata_port
*
ap
)
{
int
i
,
sz
,
line_brk
,
ptr
;
size_t
len
;
char
*
buff
=
kzalloc
(
CMD_DMA_BUFSZ
,
GFP_KERNEL
);
char
*
str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
u32
*
buff_map
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
*
rx_fis
=
pp
->
rx_fis
;
void
__iomem
*
port_mmio
=
ahci_port_base
((
struct
ata_port
*
)
ap
);
u32
tmp
;
if
(
!
buff
||
!
str
)
return
;
printk
(
KERN_DEBUG
"%s IRQ notify event, flag: %d"
,
MARKER
,
cmd
.
cmd_sent
);
if
(
cmd
.
cmd_sent
)
{
tmp
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
printk
(
KERN_DEBUG
"%s PxSERR: 0x%08x"
,
MARKER
,
tmp
);
// Read RX FIS memory
libahci_debug_dump_region
(
ap
,
rx_fis
+
RX_FIS_PIO_SETUP
,
5
,
"
\t
read PIO SETUP FIS region; dump: "
);
libahci_debug_dump_region
(
ap
,
rx_fis
+
RX_FIS_D2H_REG
,
5
,
"
\t
read D2H Register FIS; dump: "
);
dma_sync_sg_for_cpu
(
ap
->
dev
,
&
cmd
.
sg
,
1
,
DMA_BIDIRECTIONAL
);
len
=
sg_copy_to_buffer
(
&
cmd
.
sg
,
1
,
buff
,
CMD_DMA_BUFSZ
);
dma_sync_sg_for_device
(
ap
->
dev
,
&
cmd
.
sg
,
1
,
DMA_BIDIRECTIONAL
);
printk
(
KERN_DEBUG
"%s %u bytes copied from DMA buffer"
,
MARKER
,
len
);
// Print the content of DMA buffers
buff_map
=
(
u32
*
)
buff
;
for
(
i
=
0
,
line_brk
=
0
,
ptr
=
0
;
i
<
len
/
4
;
i
++
)
{
sz
=
snprintf
(
&
str
[
ptr
],
LIBAHCI_DEBUG_BUFSZ
-
ptr
,
"0x%08x "
,
buff_map
[
i
]);
ptr
+=
sz
;
line_brk
++
;
if
(
line_brk
>=
8
)
{
libahci_debug_event
(
ap
,
str
,
ptr
);
line_brk
=
0
;
ptr
=
snprintf
(
str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
"
);
}
}
if
(
line_brk
!=
0
)
{
libahci_debug_event
(
ap
,
str
,
ptr
);
}
// Command processed, clear flag
cmd
.
cmd_sent
=
false
;
}
}
EXPORT_SYMBOL_GPL
(
libahci_debug_irq_notify
);
static
int
libahci_debug_fis_open
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
struct
ata_port
*
ap
=
i_node
->
i_private
;
const
char
*
name
=
f
->
f_path
.
dentry
->
d_name
.
name
;
void
*
buff
=
NULL
;
/*if (strncmp(name, FILE_NAME_CFIS, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_fis), GFP_KERNEL);
} else if (strncmp(name, FILE_NAME_CHDR, 5) == 0) {
buff = kzalloc(sizeof(struct ahci_cmd_hdr), GFP_KERNEL);
}
if (!buff)
return -ENOMEM;*/
f
->
private_data
=
ap
;
return
0
;
}
static
ssize_t
libahci_debug_cfis_write
(
struct
file
*
f
,
const
char
__user
*
buff
,
size_t
buff_sz
,
loff_t
*
ppos
)
{
struct
ahci_cmd_fis
*
data
=
&
cmd
.
fis
;
if
(
buff_sz
!=
CMD_FIS_SZ
)
{
return
-
EINVAL
;
}
data
->
dw0
=
(((
buff
[
3
]
&
0xff
)
<<
24
)
|
((
buff
[
2
]
&
0xff
)
<<
16
)
|
((
buff
[
1
]
&
0xff
)
<<
8
)
|
(
buff
[
0
]
&
0xff
));
data
->
dw1
=
(((
buff
[
7
]
&
0xff
)
<<
24
)
|
((
buff
[
6
]
&
0xff
)
<<
16
)
|
((
buff
[
5
]
&
0xff
)
<<
8
)
|
(
buff
[
4
]
&
0xff
));
data
->
dw2
=
(((
buff
[
11
]
&
0xff
)
<<
24
)
|
((
buff
[
10
]
&
0xff
)
<<
16
)
|
((
buff
[
9
]
&
0xff
)
<<
8
)
|
(
buff
[
8
]
&
0xff
));
data
->
dw3
=
(((
buff
[
15
]
&
0xff
)
<<
24
)
|
((
buff
[
14
]
&
0xff
)
<<
16
)
|
((
buff
[
13
]
&
0xff
)
<<
8
)
|
(
buff
[
12
]
&
0xff
));
data
->
dw4
=
(((
buff
[
19
]
&
0xff
)
<<
24
)
|
((
buff
[
18
]
&
0xff
)
<<
16
)
|
((
buff
[
17
]
&
0xff
)
<<
8
)
|
(
buff
[
16
]
&
0xff
));
//printk(KERN_DEBUG "%s cfis data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x DW4 = 0x%08x, pos = %lld",
// MARKER, data->dw0, data->dw1, data->dw2, data->dw3, data->dw4, *ppos);
return
buff_sz
;
}
static
ssize_t
libahci_debug_chdr_write
(
struct
file
*
f
,
const
char
__user
*
buff
,
size_t
buff_sz
,
loff_t
*
ppos
)
{
struct
ahci_cmd_hdr
*
data
=
&
cmd
.
hdr
;
if
(
buff_sz
!=
CMD_HDR_SZ
)
{
return
-
EINVAL
;
}
data
->
opts
=
(((
buff
[
3
]
&
0xff
)
<<
24
)
|
((
buff
[
2
]
&
0xff
)
<<
16
)
|
((
buff
[
1
]
&
0xff
)
<<
8
)
|
(
buff
[
0
]
&
0xff
));
data
->
status
=
(((
buff
[
7
]
&
0xff
)
<<
24
)
|
((
buff
[
6
]
&
0xff
)
<<
16
)
|
((
buff
[
5
]
&
0xff
)
<<
8
)
|
(
buff
[
4
]
&
0xff
));
data
->
tbl_addr
=
(((
buff
[
11
]
&
0xff
)
<<
24
)
|
((
buff
[
10
]
&
0xff
)
<<
16
)
|
((
buff
[
9
]
&
0xff
)
<<
8
)
|
(
buff
[
8
]
&
0xff
));
data
->
tbl_addr_hi
=
(((
buff
[
15
]
&
0xff
)
<<
24
)
|
((
buff
[
14
]
&
0xff
)
<<
16
)
|
((
buff
[
13
]
&
0xff
)
<<
8
)
|
(
buff
[
12
]
&
0xff
));
//printk(KERN_DEBUG "%s chdr data: DW0 = 0x%08x DW1 = 0x%08x DW2 = 0x%08x DW3 = 0x%08x, pos = %lld", MARKER, data->opts,
// data->status, data->tbl_addr, data->tbl_addr_hi, *ppos);
libahci_debug_exec_cmd
(
f
->
private_data
);
return
buff_sz
;
}
static
int
libahci_debug_fis_release
(
struct
inode
*
i_node
,
struct
file
*
f
)
{
return
0
;
}
static
ssize_t
libahci_debug_load
(
struct
file
*
f
,
const
char
__user
*
buff
,
size_t
buff_sz
,
loff_t
*
ppos
)
{
load_flag
=
true
;
return
buff_sz
;
}
/*
* This function waits until the loading flag is set through debugfs file.
* The state of the flag is checked every 100ms.
*/
void
libahci_debug_wait_flag
(
void
)
{
printk
(
KERN_DEBUG
"%s Waiting for flag to be written to debugfs"
,
MARKER
);
while
(
load_flag
==
false
)
{
/*set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(msecs_to_jiffies(100));*/
msleep
(
500
);
}
load_flag
=
false
;
}
EXPORT_SYMBOL_GPL
(
libahci_debug_wait_flag
);
static
void
libahci_debug_buff_line
(
void
*
mem
,
u32
cntr
)
{
int
i
;
u32
*
mem_ptr
=
mem
;
mem_ptr
[
0
]
=
cntr
;
for
(
i
=
1
;
i
<
MARKER_LEN
;
i
++
)
{
mem_ptr
[
i
]
=
0xa5a5a5a5
;
}
}
/*
* Copy controller registers to buffer memory and return the record number
*/
unsigned
int
libahci_debug_state_dump
(
struct
ata_port
*
ap
)
{
static
u32
page_cntr
;
int
i
;
u32
tmp
;
u32
ptr
;
struct
device
*
dev
=
ap
->
dev
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
host_mmio
=
hpriv
->
mmio
;
struct
ahci_port_priv
*
ppriv
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
if
(
!
ap
)
return
0
;
if
(
!
mem_buff
.
vaddr
)
{
dev_err
(
dev
,
"dump buffer has not been allocated"
);
return
0
;
}
dev_info
(
dev
,
"dump page num: %u"
,
page_cntr
);
ptr
=
page_cntr
*
DUMP_LEN
;
if
(
ptr
+
DUMP_LEN
>
SEGMENT_SIZE
)
ptr
=
0
;
dev_info
(
dev
,
"current ptr: %u"
,
ptr
);
for
(
i
=
0
;
i
<
GHC_SZ
;
i
++
)
{
tmp
=
ioread32
(
host_mmio
+
4
*
i
);
mem_buff
.
vaddr
[
ptr
++
]
=
tmp
;
}
for
(
i
=
0
;
i
<
PORT_REG_SZ
;
i
++
)
{
tmp
=
ioread32
(
port_mmio
+
4
*
i
);
mem_buff
.
vaddr
[
ptr
++
]
=
tmp
;
}
for
(
i
=
0
;
i
<
CLB_SZ
;
i
++
)
{
tmp
=
ioread32
(
ppriv
->
cmd_slot
);
mem_buff
.
vaddr
[
ptr
++
]
=
tmp
;
}
for
(
i
=
0
;
i
<
FIS_SZ
;
i
++
)
{
tmp
=
ioread32
(
ppriv
->
rx_fis
);
mem_buff
.
vaddr
[
ptr
++
]
=
tmp
;
}
libahci_debug_buff_line
(
&
mem_buff
.
vaddr
[
ptr
+
ALIGN_OFFSET
],
page_cntr
);
//__cpuc_flush_kern_all();
//outer_flush_all();
page_cntr
++
;
return
page_cntr
;
}
EXPORT_SYMBOL_GPL
(
libahci_debug_state_dump
);
static
void
libahci_debug_buff_init
(
struct
device
*
dev
)
{
mem_buff
.
vaddr
=
dmam_alloc_coherent
(
dev
,
SEGMENT_SIZE
,
&
mem_buff
.
paddr
,
GFP_KERNEL
);
if
(
!
mem_buff
.
vaddr
)
dev_err
(
dev
,
"unable to allocate memory"
);
else
dev_info
(
dev
,
"dump buffer allocated"
);
}
static
const
struct
file_operations
libahci_debug_host_ops
=
{
.
open
=
libahci_debug_host_open
,
.
read
=
seq_read
,
.
llseek
=
seq_lseek
,
.
release
=
single_release
,
};
static
const
struct
file_operations
libahci_debug_rdesc_ops
=
{
.
open
=
libahci_debug_rdesc_open
,
.
read
=
seq_read
,
.
llseek
=
seq_lseek
,
.
release
=
single_release
,
};
static
const
struct
file_operations
libahci_debug_events_fops
=
{
.
owner
=
THIS_MODULE
,
.
open
=
libahci_debug_events_open
,
.
read
=
libahci_debug_events_read
,
.
poll
=
libahci_debug_events_poll
,
.
release
=
libahci_debug_events_release
,
.
llseek
=
noop_llseek
,
};
static
const
struct
file_operations
libahci_debug_cfis_ops
=
{
.
open
=
libahci_debug_fis_open
,
.
write
=
libahci_debug_cfis_write
,
.
release
=
libahci_debug_fis_release
,
};
static
const
struct
file_operations
libahci_debug_chdr_ops
=
{
.
open
=
libahci_debug_fis_open
,
.
write
=
libahci_debug_chdr_write
,
.
release
=
libahci_debug_fis_release
,
};
static
const
struct
file_operations
libahci_debug_load_ops
=
{
.
write
=
libahci_debug_load
,
};
static
int
libahci_debug_init_sg
(
void
)
{
cmd
.
sg_buff
=
kzalloc
(
CMD_DMA_BUFSZ
,
GFP_KERNEL
);
// mark the area
memset
(
cmd
.
sg_buff
,
0xa5
,
CMD_DMA_BUFSZ
);
if
(
!
cmd
.
sg_buff
)
{
return
-
ENOMEM
;
}
else
{
sg_init_one
(
&
cmd
.
sg
,
cmd
.
sg_buff
,
CMD_DMA_BUFSZ
);
}
return
0
;
}
int
libahci_debug_init
(
struct
ata_host
*
host
)
{
int
i
;
char
port_n
[]
=
"port00"
;
struct
dentry
*
node
;
INIT_LIST_HEAD
(
&
debug_list
.
node
);
printk
(
KERN_DEBUG
"%s Loading debug AHCI driver"
,
MARKER
);
debug_root
=
debugfs_create_dir
(
ROOT_DIR_NAME
,
NULL
);
if
(
!
debug_root
)
{
goto
fail_handler
;
}
debugfs_create_file
(
"rdesc_host"
,
0644
,
debug_root
,
host
,
&
libahci_debug_host_ops
);
debugfs_create_file
(
"loading"
,
0222
,
debug_root
,
host
,
&
libahci_debug_load_ops
);
/* Create subdir for each port and add there several files:
* one for port registers, one for port events log and
* two files for working with FISes
*/
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
{
snprintf
(
port_n
,
7
,
"port%02d"
,
i
);
node
=
debugfs_create_dir
(
port_n
,
debug_root
);
debugfs_create_file
(
"rdesc_port"
,
0644
,
node
,
host
->
ports
[
i
],
&
libahci_debug_rdesc_ops
);
debugfs_create_file
(
"events"
,
0644
,
node
,
host
->
ports
[
i
],
&
libahci_debug_events_fops
);
debugfs_create_file
(
FILE_NAME_CFIS
,
0222
,
node
,
host
->
ports
[
i
],
&
libahci_debug_cfis_ops
);
debugfs_create_file
(
FILE_NAME_CHDR
,
0222
,
node
,
host
->
ports
[
i
],
&
libahci_debug_chdr_ops
);
}
if
(
libahci_debug_init_sg
()
!=
0
)
{
goto
fail_handler
;
}
libahci_debug_buff_init
(
host
->
dev
);
return
0
;
fail_handler:
debugfs_remove_recursive
(
debug_root
);
printk
(
KERN_DEBUG
"%s Unable to create debugfs file structure"
,
MARKER
);
return
-
ENOENT
;
}
EXPORT_SYMBOL_GPL
(
libahci_debug_init
);
void
libahci_debug_exit
(
void
)
{
kfree
(
cmd
.
sg_buff
);
debugfs_remove_recursive
(
debug_root
);
}
EXPORT_SYMBOL_GPL
(
libahci_debug_exit
);
MODULE_AUTHOR
(
"Jeff Garzik"
);
MODULE_DESCRIPTION
(
"Debug AHCI SATA low-level routines"
);
MODULE_LICENSE
(
"GPL"
);
src/drivers/ata/libahci_debug.h
0 → 100644
View file @
2ca997e5
/*
* libahci_debug.h
*
* Created on: Jan 20, 2016
* Author: mk
*/
#include "ahci.h"
#ifndef _LIBAHCI_DEBUG_H_
#define _LIBAHCI_DEBUG_H_
#define ROOT_DIR_NAME "ahci_exp"
#define FILE_NAME_CFIS "cfis"
#define FILE_NAME_CHDR "chdr"
#define MARKER "++"
#define EVT_MARKER ">"
#define CMD_FIS_SZ 20
#define CMD_HDR_SZ 16
#define CMD_DMA_BUFSZ 512
#define PORT_RESERVED_2 40
#define PORT_VENDOR_BYTES 16
#define LIBAHCI_DEBUG_BUFSZ 16384
/* Total size of dump buffer in bytes*/
#define SEGMENT_SIZE 0x10000
/* The sizes below are in DWORDs*/
#define GHC_SZ 0x0B
#define PORT_REG_SZ 0x20
#define CLB_SZ 0x08
#define FIS_SZ 0x40
#define DUMP_LEN 0x240
/* The length of delimiter line in memory */
#define MARKER_LEN 0x10
/* Offset to the end of nearest 16 DWORD string */
#define ALIGN_OFFSET 0x0d
struct
libahci_debug_list
{
unsigned
int
debug
;
unsigned
int
port_n
;
char
*
libahci_debug_buf
;
int
head
;
int
tail
;
struct
list_head
node
;
struct
mutex
read_mutex
;
wait_queue_head_t
debug_wait
;
spinlock_t
debug_list_lock
;
};
struct
ahci_cmd_fis
{
__le32
dw0
;
__le32
dw1
;
__le32
dw2
;
__le32
dw3
;
__le32
dw4
;
};
struct
ahci_cmd
{
struct
ahci_cmd_hdr
hdr
;
struct
ahci_cmd_fis
fis
;
struct
scatterlist
sg
;
char
*
sg_buff
;
int
cmd_sent
;
};
struct
mem_buffer
{
volatile
u32
*
vaddr
;
dma_addr_t
paddr
;
ssize_t
size
;
};
struct
dump_record
{
u32
reg_ghc
[
GHC_SZ
];
u32
reg_port
[
PORT_REG_SZ
];
u32
reg_clb
[
CLB_SZ
];
u32
reg_fis
[
FIS_SZ
];
u32
cntr
;
};
// AHCI Port registers
struct
port_regs
{
// Port command list base address
u32
PxCLB
;
// Port command list based address upper 32-bits
u32
PxCLBU
;
// Port FIS base address
u32
PxFB
;
// Port FIS base address upper 32-bits
u32
PxFBU
;
u32
PxIS
;
u32
PxIE
;
u32
PxCMD
;
u32
reserved_1
;
u32
PxTFD
;
u32
PxSIG
;
u32
PxSSTS
;
u32
PxSCTL
;
u32
PxSERR
;
u32
PxSACT
;
u32
PxCI
;
u32
PxSNTF
;
u32
PxFBS
;
u32
PxDEVSLP
;
char
reserved_2
[
PORT_RESERVED_2
];
char
PxVS
[
PORT_VENDOR_BYTES
];
};
struct
host_regs
{
u32
CAP
;
u32
GHC
;
u32
IS
;
u32
PI
;
u32
VS
;
u32
CCC_CTL
;
u32
CCC_PORTS
;
u32
EM_LOC
;
u32
EM_CTL
;
u32
CAP2
;
u32
BOHC
;
};
int
libahci_debug_init
(
struct
ata_host
*
host
);
void
libahci_debug_exit
(
void
);
void
libahci_debug_event
(
const
struct
ata_port
*
port
,
char
*
msg
,
size_t
msg_sz
);
void
libahci_debug_dump_region
(
const
struct
ata_port
*
ap
,
const
u32
*
buf
,
size_t
buff_sz
,
const
char
*
prefix
);
void
libahci_debug_dump_irq
(
u32
status
);
void
libahci_debug_dump_sg
(
const
struct
ata_queued_cmd
*
qc
,
const
char
*
prefix
);
void
libahci_debug_irq_notify
(
const
struct
ata_port
*
ap
);
void
libahci_debug_exec_cmd
(
struct
ata_port
*
ap
);
void
libahci_debug_wait_flag
(
void
);
unsigned
int
libahci_debug_state_dump
(
struct
ata_port
*
ap
);
#endif
/* _LIBAHCI_DEBUG_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment