// SPDX-License-Identifier: GPL-2.0-or-later /* * AppliedMicro X-Gene SoC SATA Host Controller Driver * * Copyright (c) 2014, Applied Micro Circuits Corporation * Author: Loc Ho <[email protected]> * Tuan Phan <[email protected]> * Suman Tripathi <[email protected]> * * NOTE: PM support is not currently available. */ #include <linux/acpi.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/ahci_platform.h> #include <linux/of.h> #include <linux/phy/phy.h> #include "ahci.h" #define DRV_NAME … /* Max # of disk per a controller */ #define MAX_AHCI_CHN_PERCTR … /* MUX CSR */ #define SATA_ENET_CONFIG_REG … #define CFG_SATA_ENET_SELECT_MASK … /* SATA core host controller CSR */ #define SLVRDERRATTRIBUTES … #define SLVWRERRATTRIBUTES … #define MSTRDERRATTRIBUTES … #define MSTWRERRATTRIBUTES … #define BUSCTLREG … #define IOFMSTRWAUX … #define INTSTATUSMASK … #define ERRINTSTATUS … #define ERRINTSTATUSMASK … /* SATA host AHCI CSR */ #define PORTCFG … #define PORTADDR_SET(dst, src) … #define PORTPHY1CFG … #define PORTPHY1CFG_FRCPHYRDY_SET(dst, src) … #define PORTPHY2CFG … #define PORTPHY3CFG … #define PORTPHY4CFG … #define PORTPHY5CFG … #define SCTL0 … #define PORTPHY5CFG_RTCHG_SET(dst, src) … #define PORTAXICFG_EN_CONTEXT_SET(dst, src) … #define PORTAXICFG … #define PORTAXICFG_OUTTRANS_SET(dst, src) … #define PORTRANSCFG … #define PORTRANSCFG_RXWM_SET(dst, src) … /* SATA host controller AXI CSR */ #define INT_SLV_TMOMASK … /* SATA diagnostic CSR */ #define CFG_MEM_RAM_SHUTDOWN … #define BLOCK_MEM_RDY … /* Max retry for link down */ #define MAX_LINK_DOWN_RETRY … enum xgene_ahci_version { … }; struct xgene_ahci_context { … }; static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) { … } /** * xgene_ahci_poll_reg_val- Poll a register on a specific value. * @ap : ATA port of interest. * @reg : Register of interest. * @val : Value to be attained. * @interval : waiting interval for polling. * @timeout : timeout for achieving the value. */ static int xgene_ahci_poll_reg_val(struct ata_port *ap, void __iomem *reg, unsigned int val, unsigned int interval, unsigned int timeout) { … } /** * xgene_ahci_restart_engine - Restart the dma engine. * @ap : ATA port of interest * * Waits for completion of multiple commands and restarts * the DMA engine inside the controller. */ static int xgene_ahci_restart_engine(struct ata_port *ap) { … } /** * xgene_ahci_qc_issue - Issue commands to the device * @qc: Command to issue * * Due to Hardware errata for IDENTIFY DEVICE command, the controller cannot * clear the BSY bit after receiving the PIO setup FIS. This results in the dma * state machine goes into the CMFatalErrorUpdate state and locks up. By * restarting the dma engine, it removes the controller out of lock up state. * * Due to H/W errata, the controller is unable to save the PMP * field fetched from command header before sending the H2D FIS. * When the device returns the PMP port field in the D2H FIS, there is * a mismatch and results in command completion failure. The * workaround is to write the pmp value to PxFBS.DEV field before issuing * any command to PMP. */ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) { … } static bool xgene_ahci_is_memram_inited(struct xgene_ahci_context *ctx) { … } /** * xgene_ahci_read_id - Read ID data from the specified device * @dev: device * @tf: proposed taskfile * @id: data buffer * * This custom read ID function is required due to the fact that the HW * does not support DEVSLP. */ static unsigned int xgene_ahci_read_id(struct ata_device *dev, struct ata_taskfile *tf, __le16 *id) { … } static void xgene_ahci_set_phy_cfg(struct xgene_ahci_context *ctx, int channel) { … } /** * xgene_ahci_do_hardreset - Issue the actual COMRESET * @link: link to reset * @deadline: deadline jiffies for the operation * @online: Return value to indicate if device online * * Due to the limitation of the hardware PHY, a difference set of setting is * required for each supported disk speed - Gen3 (6.0Gbps), Gen2 (3.0Gbps), * and Gen1 (1.5Gbps). Otherwise during long IO stress test, the PHY will * report disparity error and etc. In addition, during COMRESET, there can * be error reported in the register PORT_SCR_ERR. For SERR_DISPARITY and * SERR_10B_8B_ERR, the PHY receiver line must be reseted. Also during long * reboot cycle regression, sometimes the PHY reports link down even if the * device is present because of speed negotiation failure. so need to retry * the COMRESET to get the link up. The following algorithm is followed to * proper configure the hardware PHY during COMRESET: * * Alg Part 1: * 1. Start the PHY at Gen3 speed (default setting) * 2. Issue the COMRESET * 3. If no link, go to Alg Part 3 * 4. If link up, determine if the negotiated speed matches the PHY * configured speed * 5. If they matched, go to Alg Part 2 * 6. If they do not matched and first time, configure the PHY for the linked * up disk speed and repeat step 2 * 7. Go to Alg Part 2 * * Alg Part 2: * 1. On link up, if there are any SERR_DISPARITY and SERR_10B_8B_ERR error * reported in the register PORT_SCR_ERR, then reset the PHY receiver line * 2. Go to Alg Part 4 * * Alg Part 3: * 1. Check the PORT_SCR_STAT to see whether device presence detected but PHY * communication establishment failed and maximum link down attempts are * less than Max attempts 3 then goto Alg Part 1. * 2. Go to Alg Part 4. * * Alg Part 4: * 1. Clear any pending from register PORT_SCR_ERR. * * NOTE: For the initial version, we will NOT support Gen1/Gen2. In addition * and until the underlying PHY supports an method to reset the receiver * line, on detection of SERR_DISPARITY or SERR_10B_8B_ERR errors, * an warning message will be printed. */ static int xgene_ahci_do_hardreset(struct ata_link *link, unsigned long deadline, bool *online) { … } static int xgene_ahci_hardreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { … } static void xgene_ahci_host_stop(struct ata_host *host) { … } /** * xgene_ahci_pmp_softreset - Issue the softreset to the drives connected * to Port Multiplier. * @link: link to reset * @class: Return value to indicate class of device * @deadline: deadline jiffies for the operation * * Due to H/W errata, the controller is unable to save the PMP * field fetched from command header before sending the H2D FIS. * When the device returns the PMP port field in the D2H FIS, there is * a mismatch and results in command completion failure. The workaround * is to write the pmp value to PxFBS.DEV field before issuing any command * to PMP. */ static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { … } /** * xgene_ahci_softreset - Issue the softreset to the drive. * @link: link to reset * @class: Return value to indicate class of device * @deadline: deadline jiffies for the operation * * Due to H/W errata, the controller is unable to save the PMP * field fetched from command header before sending the H2D FIS. * When the device returns the PMP port field in the D2H FIS, there is * a mismatch and results in command completion failure. The workaround * is to write the pmp value to PxFBS.DEV field before issuing any command * to PMP. Here is the algorithm to detect PMP : * * 1. Save the PxFBS value * 2. Program PxFBS.DEV with pmp value send by framework. Framework sends * 0xF for both PMP/NON-PMP initially * 3. Issue softreset * 4. If signature class is PMP goto 6 * 5. restore the original PxFBS and goto 3 * 6. return */ static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class, unsigned long deadline) { … } /** * xgene_ahci_handle_broken_edge_irq - Handle the broken irq. * @host: Host that recieved the irq * @irq_masked: HOST_IRQ_STAT value * * For hardware with broken edge trigger latch * the HOST_IRQ_STAT register misses the edge interrupt * when clearing of HOST_IRQ_STAT register and hardware * reporting the PORT_IRQ_STAT register at the * same clock cycle. * As such, the algorithm below outlines the workaround. * * 1. Read HOST_IRQ_STAT register and save the state. * 2. Clear the HOST_IRQ_STAT register. * 3. Read back the HOST_IRQ_STAT register. * 4. If HOST_IRQ_STAT register equals to zero, then * traverse the rest of port's PORT_IRQ_STAT register * to check if an interrupt is triggered at that point else * go to step 6. * 5. If PORT_IRQ_STAT register of rest ports is not equal to zero * then update the state of HOST_IRQ_STAT saved in step 1. * 6. Handle port interrupts. * 7. Exit */ static int xgene_ahci_handle_broken_edge_irq(struct ata_host *host, u32 irq_masked) { … } static irqreturn_t xgene_ahci_irq_intr(int irq, void *dev_instance) { … } static struct ata_port_operations xgene_ahci_v1_ops = …; static const struct ata_port_info xgene_ahci_v1_port_info = …; static struct ata_port_operations xgene_ahci_v2_ops = …; static const struct ata_port_info xgene_ahci_v2_port_info = …; static int xgene_ahci_hw_init(struct ahci_host_priv *hpriv) { … } static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx) { … } static const struct scsi_host_template ahci_platform_sht = …; #ifdef CONFIG_ACPI static const struct acpi_device_id xgene_ahci_acpi_match[] = …; MODULE_DEVICE_TABLE(acpi, xgene_ahci_acpi_match); #endif static const struct of_device_id xgene_ahci_of_match[] = …; MODULE_DEVICE_TABLE(of, xgene_ahci_of_match); static int xgene_ahci_probe(struct platform_device *pdev) { … } static struct platform_driver xgene_ahci_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …; MODULE_VERSION(…) …;