/** @file
  Header file for AHCI mode of ATA host controller.

  Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#ifndef __ATA_HC_AHCI_MODE_H__
#define __ATA_HC_AHCI_MODE_H__

#define EFI_AHCI_BAR_INDEX                     0x05

#define EFI_AHCI_CAPABILITY_OFFSET             0x0000
#define   EFI_AHCI_CAP_SAM                     BIT18
#define   EFI_AHCI_CAP_SSS                     BIT27
#define   EFI_AHCI_CAP_S64A                    BIT31
#define EFI_AHCI_GHC_OFFSET                    0x0004
#define   EFI_AHCI_GHC_RESET                   BIT0
#define   EFI_AHCI_GHC_IE                      BIT1
#define   EFI_AHCI_GHC_ENABLE                  BIT31
#define EFI_AHCI_IS_OFFSET                     0x0008
#define EFI_AHCI_PI_OFFSET                     0x000C

#define EFI_AHCI_MAX_PORTS                     32

#define AHCI_CAPABILITY2_OFFSET                0x0024
#define   AHCI_CAP2_SDS                        BIT3
#define   AHCI_CAP2_SADM                       BIT4

typedef struct {
  UINT32  Lower32;
  UINT32  Upper32;
} DATA_32;

typedef union {
  DATA_32   Uint32;
  UINT64    Uint64;
} DATA_64;

//
// Refer SATA1.0a spec section 5.2, the Phy detection time should be less than 10ms.
// Add a bit of margin for robustness.
//
#define  EFI_AHCI_BUS_PHY_DETECT_TIMEOUT       15
//
// Refer SATA1.0a spec, the FIS enable time should be less than 500ms.
//
#define  EFI_AHCI_PORT_CMD_FR_CLEAR_TIMEOUT    EFI_TIMER_PERIOD_MILLISECONDS(500)
//
// Refer SATA1.0a spec, the bus reset time should be less than 1s.
//
#define  EFI_AHCI_BUS_RESET_TIMEOUT            EFI_TIMER_PERIOD_SECONDS(1)

#define  EFI_AHCI_ATAPI_DEVICE_SIG             0xEB140000
#define  EFI_AHCI_ATA_DEVICE_SIG               0x00000000
#define  EFI_AHCI_PORT_MULTIPLIER_SIG          0x96690000
#define  EFI_AHCI_ATAPI_SIG_MASK               0xFFFF0000

//
// Each PRDT entry can point to a memory block up to 4M byte
//
#define EFI_AHCI_MAX_DATA_PER_PRDT             0x400000

#define EFI_AHCI_FIS_REGISTER_H2D              0x27      //Register FIS - Host to Device
#define   EFI_AHCI_FIS_REGISTER_H2D_LENGTH     20
#define EFI_AHCI_FIS_REGISTER_D2H              0x34      //Register FIS - Device to Host
#define   EFI_AHCI_FIS_REGISTER_D2H_LENGTH     20
#define EFI_AHCI_FIS_DMA_ACTIVATE              0x39      //DMA Activate FIS - Device to Host
#define   EFI_AHCI_FIS_DMA_ACTIVATE_LENGTH     4
#define EFI_AHCI_FIS_DMA_SETUP                 0x41      //DMA Setup FIS - Bi-directional
#define   EFI_AHCI_FIS_DMA_SETUP_LENGTH        28
#define EFI_AHCI_FIS_DATA                      0x46      //Data FIS - Bi-directional
#define EFI_AHCI_FIS_BIST                      0x58      //BIST Activate FIS - Bi-directional
#define   EFI_AHCI_FIS_BIST_LENGTH             12
#define EFI_AHCI_FIS_PIO_SETUP                 0x5F      //PIO Setup FIS - Device to Host
#define   EFI_AHCI_FIS_PIO_SETUP_LENGTH        20
#define EFI_AHCI_FIS_SET_DEVICE                0xA1      //Set Device Bits FIS - Device to Host
#define   EFI_AHCI_FIS_SET_DEVICE_LENGTH       8

#define EFI_AHCI_D2H_FIS_OFFSET                0x40
#define EFI_AHCI_DMA_FIS_OFFSET                0x00
#define EFI_AHCI_PIO_FIS_OFFSET                0x20
#define EFI_AHCI_SDB_FIS_OFFSET                0x58
#define EFI_AHCI_FIS_TYPE_MASK                 0xFF
#define EFI_AHCI_U_FIS_OFFSET                  0x60

//
// Port register
//
#define EFI_AHCI_PORT_START                    0x0100
#define EFI_AHCI_PORT_REG_WIDTH                0x0080
#define EFI_AHCI_PORT_CLB                      0x0000
#define EFI_AHCI_PORT_CLBU                     0x0004
#define EFI_AHCI_PORT_FB                       0x0008
#define EFI_AHCI_PORT_FBU                      0x000C
#define EFI_AHCI_PORT_IS                       0x0010
#define   EFI_AHCI_PORT_IS_DHRS                BIT0
#define   EFI_AHCI_PORT_IS_PSS                 BIT1
#define   EFI_AHCI_PORT_IS_DSS                 BIT2
#define   EFI_AHCI_PORT_IS_SDBS                BIT3
#define   EFI_AHCI_PORT_IS_UFS                 BIT4
#define   EFI_AHCI_PORT_IS_DPS                 BIT5
#define   EFI_AHCI_PORT_IS_PCS                 BIT6
#define   EFI_AHCI_PORT_IS_DIS                 BIT7
#define   EFI_AHCI_PORT_IS_PRCS                BIT22
#define   EFI_AHCI_PORT_IS_IPMS                BIT23
#define   EFI_AHCI_PORT_IS_OFS                 BIT24
#define   EFI_AHCI_PORT_IS_INFS                BIT26
#define   EFI_AHCI_PORT_IS_IFS                 BIT27
#define   EFI_AHCI_PORT_IS_HBDS                BIT28
#define   EFI_AHCI_PORT_IS_HBFS                BIT29
#define   EFI_AHCI_PORT_IS_TFES                BIT30
#define   EFI_AHCI_PORT_IS_CPDS                BIT31
#define   EFI_AHCI_PORT_IS_CLEAR               0xFFFFFFFF
#define   EFI_AHCI_PORT_IS_FIS_CLEAR           0x0000000F
#define   EFI_AHCI_PORT_IS_ERROR_MASK          (EFI_AHCI_PORT_IS_INFS | EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_HBDS | EFI_AHCI_PORT_IS_HBFS | EFI_AHCI_PORT_IS_TFES)
#define   EFI_AHCI_PORT_IS_FATAL_ERROR_MASK    (EFI_AHCI_PORT_IS_IFS | EFI_AHCI_PORT_IS_HBDS | EFI_AHCI_PORT_IS_HBFS | EFI_AHCI_PORT_IS_TFES)

#define EFI_AHCI_PORT_IE                       0x0014
#define EFI_AHCI_PORT_CMD                      0x0018
#define   EFI_AHCI_PORT_CMD_ST_MASK            0xFFFFFFFE
#define   EFI_AHCI_PORT_CMD_ST                 BIT0
#define   EFI_AHCI_PORT_CMD_SUD                BIT1
#define   EFI_AHCI_PORT_CMD_POD                BIT2
#define   EFI_AHCI_PORT_CMD_CLO                BIT3
#define   EFI_AHCI_PORT_CMD_FRE                BIT4
#define   EFI_AHCI_PORT_CMD_CCS_MASK           (BIT8 | BIT9 | BIT10 | BIT11 | BIT12)
#define   EFI_AHCI_PORT_CMD_CCS_SHIFT          8
#define   EFI_AHCI_PORT_CMD_FR                 BIT14
#define   EFI_AHCI_PORT_CMD_CR                 BIT15
#define   EFI_AHCI_PORT_CMD_MASK               ~(EFI_AHCI_PORT_CMD_ST | EFI_AHCI_PORT_CMD_FRE | EFI_AHCI_PORT_CMD_COL)
#define   EFI_AHCI_PORT_CMD_PMA                BIT17
#define   EFI_AHCI_PORT_CMD_HPCP               BIT18
#define   EFI_AHCI_PORT_CMD_MPSP               BIT19
#define   EFI_AHCI_PORT_CMD_CPD                BIT20
#define   EFI_AHCI_PORT_CMD_ESP                BIT21
#define   EFI_AHCI_PORT_CMD_ATAPI              BIT24
#define   EFI_AHCI_PORT_CMD_DLAE               BIT25
#define   EFI_AHCI_PORT_CMD_ALPE               BIT26
#define   EFI_AHCI_PORT_CMD_ASP                BIT27
#define   EFI_AHCI_PORT_CMD_ICC_MASK           (BIT28 | BIT29 | BIT30 | BIT31)
#define   EFI_AHCI_PORT_CMD_ACTIVE             (1 << 28 )
#define EFI_AHCI_PORT_TFD                      0x0020
#define   EFI_AHCI_PORT_TFD_MASK               (BIT7 | BIT3 | BIT0)
#define   EFI_AHCI_PORT_TFD_BSY                BIT7
#define   EFI_AHCI_PORT_TFD_DRQ                BIT3
#define   EFI_AHCI_PORT_TFD_ERR                BIT0
#define   EFI_AHCI_PORT_TFD_ERR_MASK           0x00FF00
#define EFI_AHCI_PORT_SIG                      0x0024
#define EFI_AHCI_PORT_SSTS                     0x0028
#define   EFI_AHCI_PORT_SSTS_DET_MASK          0x000F
#define   EFI_AHCI_PORT_SSTS_DET               0x0001
#define   EFI_AHCI_PORT_SSTS_DET_PCE           0x0003
#define   EFI_AHCI_PORT_SSTS_SPD_MASK          0x00F0
#define EFI_AHCI_PORT_SCTL                     0x002C
#define   EFI_AHCI_PORT_SCTL_DET_MASK          0x000F
#define   EFI_AHCI_PORT_SCTL_MASK              (~EFI_AHCI_PORT_SCTL_DET_MASK)
#define   EFI_AHCI_PORT_SCTL_DET_INIT          0x0001
#define   EFI_AHCI_PORT_SCTL_DET_PHYCOMM       0x0003
#define   EFI_AHCI_PORT_SCTL_SPD_MASK          0x00F0
#define   EFI_AHCI_PORT_SCTL_IPM_MASK          0x0F00
#define   EFI_AHCI_PORT_SCTL_IPM_INIT          0x0300
#define   EFI_AHCI_PORT_SCTL_IPM_PSD           0x0100
#define   EFI_AHCI_PORT_SCTL_IPM_SSD           0x0200
#define EFI_AHCI_PORT_SERR                     0x0030
#define   EFI_AHCI_PORT_SERR_RDIE              BIT0
#define   EFI_AHCI_PORT_SERR_RCE               BIT1
#define   EFI_AHCI_PORT_SERR_TDIE              BIT8
#define   EFI_AHCI_PORT_SERR_PCDIE             BIT9
#define   EFI_AHCI_PORT_SERR_PE                BIT10
#define   EFI_AHCI_PORT_SERR_IE                BIT11
#define   EFI_AHCI_PORT_SERR_PRC               BIT16
#define   EFI_AHCI_PORT_SERR_PIE               BIT17
#define   EFI_AHCI_PORT_SERR_CW                BIT18
#define   EFI_AHCI_PORT_SERR_BDE               BIT19
#define   EFI_AHCI_PORT_SERR_DE                BIT20
#define   EFI_AHCI_PORT_SERR_CRCE              BIT21
#define   EFI_AHCI_PORT_SERR_HE                BIT22
#define   EFI_AHCI_PORT_SERR_LSE               BIT23
#define   EFI_AHCI_PORT_SERR_TSTE              BIT24
#define   EFI_AHCI_PORT_SERR_UFT               BIT25
#define   EFI_AHCI_PORT_SERR_EX                BIT26
#define   EFI_AHCI_PORT_ERR_CLEAR              0xFFFFFFFF
#define EFI_AHCI_PORT_SACT                     0x0034
#define EFI_AHCI_PORT_CI                       0x0038
#define EFI_AHCI_PORT_SNTF                     0x003C
#define AHCI_PORT_DEVSLP                       0x0044
#define   AHCI_PORT_DEVSLP_ADSE                BIT0
#define   AHCI_PORT_DEVSLP_DSP                 BIT1
#define   AHCI_PORT_DEVSLP_DETO_MASK           0x000003FC
#define   AHCI_PORT_DEVSLP_MDAT_MASK           0x00007C00
#define   AHCI_PORT_DEVSLP_DITO_MASK           0x01FF8000
#define   AHCI_PORT_DEVSLP_DM_MASK             0x1E000000

#define AHCI_COMMAND_RETRIES  5

#pragma pack(1)
//
// Command List structure includes total 32 entries.
// The entry data structure is listed at the following.
//
typedef struct {
  UINT32   AhciCmdCfl:5;      //Command FIS Length
  UINT32   AhciCmdA:1;        //ATAPI
  UINT32   AhciCmdW:1;        //Write
  UINT32   AhciCmdP:1;        //Prefetchable
  UINT32   AhciCmdR:1;        //Reset
  UINT32   AhciCmdB:1;        //BIST
  UINT32   AhciCmdC:1;        //Clear Busy upon R_OK
  UINT32   AhciCmdRsvd:1;
  UINT32   AhciCmdPmp:4;      //Port Multiplier Port
  UINT32   AhciCmdPrdtl:16;   //Physical Region Descriptor Table Length
  UINT32   AhciCmdPrdbc;      //Physical Region Descriptor Byte Count
  UINT32   AhciCmdCtba;       //Command Table Descriptor Base Address
  UINT32   AhciCmdCtbau;      //Command Table Descriptor Base Address Upper 32-BITs
  UINT32   AhciCmdRsvd1[4];
} EFI_AHCI_COMMAND_LIST;

//
// This is a software constructed FIS.
// For data transfer operations, this is the H2D Register FIS format as
// specified in the Serial ATA Revision 2.6 specification.
//
typedef struct {
  UINT8    AhciCFisType;
  UINT8    AhciCFisPmNum:4;
  UINT8    AhciCFisRsvd:1;
  UINT8    AhciCFisRsvd1:1;
  UINT8    AhciCFisRsvd2:1;
  UINT8    AhciCFisCmdInd:1;
  UINT8    AhciCFisCmd;
  UINT8    AhciCFisFeature;
  UINT8    AhciCFisSecNum;
  UINT8    AhciCFisClyLow;
  UINT8    AhciCFisClyHigh;
  UINT8    AhciCFisDevHead;
  UINT8    AhciCFisSecNumExp;
  UINT8    AhciCFisClyLowExp;
  UINT8    AhciCFisClyHighExp;
  UINT8    AhciCFisFeatureExp;
  UINT8    AhciCFisSecCount;
  UINT8    AhciCFisSecCountExp;
  UINT8    AhciCFisRsvd3;
  UINT8    AhciCFisControl;
  UINT8    AhciCFisRsvd4[4];
  UINT8    AhciCFisRsvd5[44];
} EFI_AHCI_COMMAND_FIS;

typedef enum {
  SataFisD2H = 0,
  SataFisPioSetup,
  SataFisDmaSetup
} SATA_FIS_TYPE;

//
// ACMD: ATAPI command (12 or 16 bytes)
//
typedef struct {
  UINT8    AtapiCmd[0x10];
} EFI_AHCI_ATAPI_COMMAND;

//
// Physical Region Descriptor Table includes up to 65535 entries
// The entry data structure is listed at the following.
// the actual entry number comes from the PRDTL field in the command
// list entry for this command slot.
//
typedef struct {
  UINT32   AhciPrdtDba;       //Data Base Address
  UINT32   AhciPrdtDbau;      //Data Base Address Upper 32-BITs
  UINT32   AhciPrdtRsvd;
  UINT32   AhciPrdtDbc:22;    //Data Byte Count
  UINT32   AhciPrdtRsvd1:9;
  UINT32   AhciPrdtIoc:1;     //Interrupt on Completion
} EFI_AHCI_COMMAND_PRDT;

//
// Command table data structure which is pointed to by the entry in the command list
//
typedef struct {
  EFI_AHCI_COMMAND_FIS      CommandFis;       // A software constructed FIS.
  EFI_AHCI_ATAPI_COMMAND    AtapiCmd;         // 12 or 16 bytes ATAPI cmd.
  UINT8                     Reserved[0x30];
  EFI_AHCI_COMMAND_PRDT     PrdtTable[65535];     // The scatter/gather list for data transfer
} EFI_AHCI_COMMAND_TABLE;

//
// Received FIS structure
//
typedef struct {
  UINT8    AhciDmaSetupFis[0x1C];         // Dma Setup Fis: offset 0x00
  UINT8    AhciDmaSetupFisRsvd[0x04];
  UINT8    AhciPioSetupFis[0x14];         // Pio Setup Fis: offset 0x20
  UINT8    AhciPioSetupFisRsvd[0x0C];
  UINT8    AhciD2HRegisterFis[0x14];      // D2H Register Fis: offset 0x40
  UINT8    AhciD2HRegisterFisRsvd[0x04];
  UINT64   AhciSetDeviceBitsFis;          // Set Device Bits Fix: offset 0x58
  UINT8    AhciUnknownFis[0x40];          // Unknown Fis: offset 0x60
  UINT8    AhciUnknownFisRsvd[0x60];
} EFI_AHCI_RECEIVED_FIS;

typedef struct {
  UINT8  Madt : 5;
  UINT8  Reserved_5 : 3;
  UINT8  Deto;
  UINT16 Reserved_16;
  UINT32 Reserved_32 : 31;
  UINT32 Supported : 1;
} DEVSLP_TIMING_VARIABLES;

#pragma pack()

typedef struct {
  EFI_AHCI_RECEIVED_FIS     *AhciRFis;
  EFI_AHCI_COMMAND_LIST     *AhciCmdList;
  EFI_AHCI_COMMAND_TABLE    *AhciCommandTable;
  EFI_AHCI_RECEIVED_FIS     *AhciRFisPciAddr;
  EFI_AHCI_COMMAND_LIST     *AhciCmdListPciAddr;
  EFI_AHCI_COMMAND_TABLE    *AhciCommandTablePciAddr;
  UINT64                    MaxCommandListSize;
  UINT64                    MaxCommandTableSize;
  UINT64                    MaxReceiveFisSize;
  VOID                      *MapRFis;
  VOID                      *MapCmdList;
  VOID                      *MapCommandTable;
} EFI_AHCI_REGISTERS;

/**
  This function is used to send out ATAPI commands conforms to the Packet Command
  with PIO Protocol.

  @param PciIo              The PCI IO protocol instance.
  @param AhciRegisters      The pointer to the EFI_AHCI_REGISTERS.
  @param Port               The number of port.
  @param PortMultiplier     The number of port multiplier.
  @param Packet             A pointer to EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET structure.

  @retval EFI_SUCCESS       send out the ATAPI packet command successfully
                            and device sends data successfully.
  @retval EFI_DEVICE_ERROR  the device failed to send data.

**/
EFI_STATUS
EFIAPI
AhciPacketCommandExecute (
  IN  EFI_PCI_IO_PROTOCOL                           *PciIo,
  IN  EFI_AHCI_REGISTERS                            *AhciRegisters,
  IN  UINT8                                         Port,
  IN  UINT8                                         PortMultiplier,
  IN  EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET    *Packet
  );

/**
  Start command for give slot on specific port.

  @param  PciIo              The PCI IO protocol instance.
  @param  Port               The number of port.
  @param  CommandSlot        The number of CommandSlot.
  @param  Timeout            The timeout value of start, uses 100ns as a unit.

  @retval EFI_DEVICE_ERROR   The command start unsuccessfully.
  @retval EFI_TIMEOUT        The operation is time out.
  @retval EFI_SUCCESS        The command start successfully.

**/
EFI_STATUS
EFIAPI
AhciStartCommand (
  IN  EFI_PCI_IO_PROTOCOL       *PciIo,
  IN  UINT8                     Port,
  IN  UINT8                     CommandSlot,
  IN  UINT64                    Timeout
  );

/**
  Stop command running for giving port

  @param  PciIo              The PCI IO protocol instance.
  @param  Port               The number of port.
  @param  Timeout            The timeout value of stop, uses 100ns as a unit.

  @retval EFI_DEVICE_ERROR   The command stop unsuccessfully.
  @retval EFI_TIMEOUT        The operation is time out.
  @retval EFI_SUCCESS        The command stop successfully.

**/
EFI_STATUS
EFIAPI
AhciStopCommand (
  IN  EFI_PCI_IO_PROTOCOL       *PciIo,
  IN  UINT8                     Port,
  IN  UINT64                    Timeout
  );

#endif