linux/drivers/ata/ahci_xgene.c

// 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();