/** @file Copyright (c) 2016 Linaro Ltd. Copyright (c) 2016 Hisilicon Limited. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define READ_REG32(Base, Offset) MmioRead32 ((Base) + (Offset)) #define WRITE_REG32(Base, Offset, Val) MmioWrite32 ((Base) + (Offset), (Val)) #define PHY_READ_REG32(Base, Offset, phy) MmioRead32 ((Base) + (Offset) + 0x400 * (phy)) #define PHY_WRITE_REG32(Base, Offset, phy, Val) MmioWrite32 ((Base) + (Offset) + 0x400 * (phy), (Val)) #define DLVRY_QUEUE_ENABLE 0x0 #define IOST_BASE_ADDR_LO 0x8 #define IOST_BASE_ADDR_HI 0xc #define ITCT_BASE_ADDR_LO 0x10 #define ITCT_BASE_ADDR_HI 0x14 #define BROKEN_MSG_ADDR_LO 0x18 #define BROKEN_MSG_ADDR_HI 0x1c #define PHY_CONTEXT 0x20 #define PHY_PORT_NUM_MA 0x28 #define HGC_TRANS_TASK_CNT_LIMIT 0x38 #define AXI_AHB_CLK_CFG 0x3c #define HGC_SAS_TXFAIL_RETRY_CTRL 0x84 #define HGC_GET_ITV_TIME 0x90 #define DEVICE_MSG_WORK_MODE 0x94 #define I_T_NEXUS_LOSS_TIME 0xa0 #define BUS_INACTIVE_LIMIT_TIME 0xa8 #define REJECT_TO_OPEN_LIMIT_TIME 0xac #define CFG_AGING_TIME 0xbc #define HGC_DFX_CFG2 0xc0 #define FIS_LIST_BADDR_L 0xc4 #define CFG_1US_TIMER_TRSH 0xcc #define CFG_SAS_CONFIG 0xd4 #define INT_COAL_EN 0x1bc #define OQ_INT_COAL_TIME 0x1c0 #define OQ_INT_COAL_CNT 0x1c4 #define ENT_INT_COAL_TIME 0x1c8 #define ENT_INT_COAL_CNT 0x1cc #define OQ_INT_SRC 0x1d0 #define OQ_INT_SRC_MSK 0x1d4 #define ENT_INT_SRC1 0x1d8 #define ENT_INT_SRC2 0x1dc #define ENT_INT_SRC_MSK1 0x1e0 #define ENT_INT_SRC_MSK2 0x1e4 #define SAS_ECC_INTR_MSK 0x1ec #define HGC_ERR_STAT_EN 0x238 #define DLVRY_Q_0_BASE_ADDR_LO 0x260 #define DLVRY_Q_0_BASE_ADDR_HI 0x264 #define DLVRY_Q_0_DEPTH 0x268 #define DLVRY_Q_0_WR_PTR 0x26c #define DLVRY_Q_0_RD_PTR 0x270 #define COMPL_Q_0_BASE_ADDR_LO 0x4e0 #define COMPL_Q_0_BASE_ADDR_HI 0x4e4 #define COMPL_Q_0_DEPTH 0x4e8 #define COMPL_Q_0_WR_PTR 0x4ec #define COMPL_Q_0_RD_PTR 0x4f0 #define AXI_CFG 0x5100 #define PORT_BASE 0x800 #define PHY_CFG (PORT_BASE + 0x0) #define PHY_CFG_ENA_OFF 0 #define PHY_CFG_ENA_MSK (0x1 << PHY_CFG_ENA_OFF) #define PHY_CFG_DC_OPT_OFF 2 #define PHY_CFG_DC_OPT_MSK (0x1 << PHY_CFG_DC_OPT_OFF) #define PROG_PHY_LINK_RATE (PORT_BASE + 0xc) #define PHY_CTRL (PORT_BASE + 0x14) #define PHY_CTRL_RESET BIT0 #define PHY_RATE_NEGO (PORT_BASE + 0x30) #define PHY_PCN (PORT_BASE + 0x44) #define SL_TOUT_CFG (PORT_BASE + 0x8c) #define SL_CONTROL (PORT_BASE + 0x94) #define SL_CONTROL_NOTIFY_EN BIT0 #define TX_ID_DWORD0 (PORT_BASE + 0x9c) #define TX_ID_DWORD1 (PORT_BASE + 0xa0) #define TX_ID_DWORD2 (PORT_BASE + 0xa4) #define TX_ID_DWORD3 (PORT_BASE + 0xa8) #define TX_ID_DWORD4 (PORT_BASE + 0xaC) #define TX_ID_DWORD5 (PORT_BASE + 0xb0) #define TX_ID_DWORD6 (PORT_BASE + 0xb4) #define RX_IDAF_DWORD3 (PORT_BASE + 0xd0) #define RX_IDAF_DWORD4 (PORT_BASE + 0xd4) #define RXOP_CHECK_CFG_H (PORT_BASE + 0xfc) #define DONE_RECEIVED_TIME (PORT_BASE + 0x12c) #define CON_CFG_DRIVER (PORT_BASE + 0x130) #define PHY_CONFIG2 (PORT_BASE + 0x1a8) #define PHY_CONFIG2_FORCE_TXDEEMPH_OFF 3 #define PHY_CONFIG2_FORCE_TXDEEMPH_MSK (0x1 << PHY_CONFIG2_FORCE_TXDEEMPH_OFF) #define CHL_INT_COAL_EN (PORT_BASE + 0x1d0) #define CHL_INT0 (PORT_BASE + 0x1b0) #define CHL_INT0_PHYCTRL_NOTRDY BIT0 #define CHL_INT1 (PORT_BASE + 0x1b4) #define CHL_INT2 (PORT_BASE + 0x1b8) #define CHL_INT2_SL_PHY_ENA BIT6 #define CHL_INT0_MSK (PORT_BASE + 0x1bc) #define CHL_INT0_MSK_PHYCTRL_NOTRDY BIT0 #define CHL_INT1_MSK (PORT_BASE + 0x1c0) #define CHL_INT2_MSK (PORT_BASE + 0x1c4) #define DMA_TX_STATUS (PORT_BASE + 0x2d0) #define DMA_TX_STATUS_BUSY BIT0 #define DMA_RX_STATUS (PORT_BASE + 0x2e8) #define DMA_RX_STATUS_BUSY BIT0 #define QUEUE_CNT 32 #define QUEUE_SLOTS 256 #define SLOT_ENTRIES 8192 #define PHY_CNT 8 #define MAX_ITCT_ENTRIES 1 // Completion header #define CMPLT_HDR_IPTT_OFF 0 #define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF) #define BIT(x) (1 << x) // HW dma structures // Delivery queue header // dw0 #define CMD_HDR_RESP_REPORT_OFF 5 #define CMD_HDR_RESP_REPORT_MSK 0x20 #define CMD_HDR_TLR_CTRL_OFF 6 #define CMD_HDR_TLR_CTRL_MSK 0xc0 #define CMD_HDR_PORT_OFF 17 #define CMD_HDR_PORT_MSK 0xe0000 #define CMD_HDR_PRIORITY_OFF 27 #define CMD_HDR_PRIORITY_MSK 0x8000000 #define CMD_HDR_MODE_OFF 28 #define CMD_HDR_MODE_MSK 0x10000000 #define CMD_HDR_CMD_OFF 29 #define CMD_HDR_CMD_MSK 0xe0000000 // dw1 #define CMD_HDR_VERIFY_DTL_OFF 10 #define CMD_HDR_VERIFY_DTL_MSK 0x400 #define CMD_HDR_SSP_FRAME_TYPE_OFF 13 #define CMD_HDR_SSP_FRAME_TYPE_MSK 0xe000 #define CMD_HDR_DEVICE_ID_OFF 16 #define CMD_HDR_DEVICE_ID_MSK 0xffff0000 // dw2 #define CMD_HDR_CFL_OFF 0 #define CMD_HDR_CFL_MSK 0x1ff #define CMD_HDR_MRFL_OFF 15 #define CMD_HDR_MRFL_MSK 0xff8000 #define CMD_HDR_FIRST_BURST_OFF 25 #define CMD_HDR_FIRST_BURST_MSK 0x2000000 // dw3 #define CMD_HDR_IPTT_OFF 0 #define CMD_HDR_IPTT_MSK 0xffff // dw6 #define CMD_HDR_DATA_SGL_LEN_OFF 16 #define CMD_HDR_DATA_SGL_LEN_MSK 0xffff0000 // Completion header #define CMPLT_HDR_IPTT_OFF 0 #define CMPLT_HDR_IPTT_MSK (0xffff << CMPLT_HDR_IPTT_OFF) #define CMPLT_HDR_CMD_CMPLT_MSK BIT17 #define CMPLT_HDR_ERR_RCRD_XFRD_MSK BIT18 #define CMPLT_HDR_RSPNS_XFRD_MSK BIT19 #define CMPLT_HDR_IO_CFG_ERR_MSK BIT27 #define SENSE_DATA_PRES 26 #define SGE_LIMIT 0x10000 #define upper_32_bits(n) ((UINT32)(((n) >> 16) >> 16)) #define lower_32_bits(n) ((UINT32)(n)) #define MAX_TARGET_ID 4 // Generic HW DMA host memory structures struct hisi_sas_cmd_hdr { UINT32 dw0; UINT32 dw1; UINT32 dw2; UINT32 transfer_tags; UINT32 data_transfer_len; UINT32 first_burst_num; UINT32 sg_len; UINT32 dw7; UINT64 cmd_table_addr; UINT64 sts_buffer_addr; UINT64 prd_table_addr; UINT64 dif_prd_table_addr; }; struct hisi_sas_complete_hdr { UINT32 data; }; struct hisi_sas_iost { UINT64 qw0; UINT64 qw1; UINT64 qw2; UINT64 qw3; }; struct hisi_sas_itct { UINT64 qw0; UINT64 sas_addr; UINT64 qw2; UINT64 qw3; UINT64 qw4; UINT64 qw_sata_ncq0_3; UINT64 qw_sata_ncq7_4; UINT64 qw_sata_ncq11_8; UINT64 qw_sata_ncq15_12; UINT64 qw_sata_ncq19_16; UINT64 qw_sata_ncq23_20; UINT64 qw_sata_ncq27_24; UINT64 qw_sata_ncq31_28; UINT64 qw_non_ncq_iptt; UINT64 qw_rsvd0; UINT64 qw_rsvd1; }; struct hisi_sas_breakpoint { UINT8 data[128]; }; struct hisi_sas_sge { UINT64 addr; UINT32 page_ctrl_0; UINT32 page_ctrl_1; UINT32 data_len; UINT32 data_off; }; struct hisi_sas_sge_page { struct hisi_sas_sge sg[512]; }; struct hisi_sas_cmd { UINT8 cmd[128]; }; struct hisi_sas_sts { UINT32 status[260]; }; struct hisi_sas_slot { BOOLEAN used; }; struct hisi_hba { struct hisi_sas_cmd_hdr *cmd_hdr[QUEUE_CNT]; struct hisi_sas_complete_hdr *complete_hdr[QUEUE_CNT]; struct hisi_sas_sge_page *sge[QUEUE_CNT]; struct hisi_sas_sts *status_buf[QUEUE_CNT]; struct hisi_sas_cmd *command_table[QUEUE_CNT]; struct hisi_sas_iost *iost; struct hisi_sas_itct *itct; struct hisi_sas_breakpoint *breakpoint; struct hisi_sas_slot *slots; UINT32 base; int queue; int port_id; UINT32 LatestTargetId; UINT64 LatestLun; }; #pragma pack (1) typedef struct { VENDOR_DEVICE_PATH Vendor; UINT64 PhysBase; EFI_DEVICE_PATH_PROTOCOL End; } SAS_V1_TRANSPORT_DEVICE_PATH; #pragma pack () typedef struct { UINT32 Signature; EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode; EFI_EXT_SCSI_PASS_THRU_PROTOCOL ExtScsiPassThru; SAS_V1_TRANSPORT_DEVICE_PATH *DevicePath; struct hisi_hba *hba; EFI_EVENT TimerEvent; } SAS_V1_INFO; #define SAS_DEVICE_SIGNATURE SIGNATURE_32 ('S','A','S','0') #define SAS_FROM_PASS_THRU(a) CR (a, SAS_V1_INFO, ExtScsiPassThru, SAS_DEVICE_SIGNATURE) STATIC EFI_STATUS prepare_cmd ( struct hisi_hba *hba, EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet ) { struct hisi_sas_slot *slot; struct hisi_sas_cmd_hdr *hdr; struct hisi_sas_sge_page *sge; struct hisi_sas_sts *sts; struct hisi_sas_cmd *cmd; EFI_SCSI_SENSE_DATA *SensePtr = Packet->SenseData; VOID *Buffer = NULL; UINTN BufferSize = 0; int queue = hba->queue; UINT32 r, w = 0, slot_idx = 0; UINT32 base = hba->base; UINT8 *p; EFI_PHYSICAL_ADDRESS BufferAddress; EFI_STATUS Status = EFI_SUCCESS; VOID *BufferMap = NULL; DMA_MAP_OPERATION DmaOperation = MapOperationBusMasterCommonBuffer; while (1) { w = READ_REG32(base, DLVRY_Q_0_WR_PTR + (queue * 0x14)); r = READ_REG32(base, DLVRY_Q_0_RD_PTR + (queue * 0x14)); slot_idx = queue * QUEUE_SLOTS + w; slot = &hba->slots[slot_idx]; if (slot->used || (r == (w+1) % QUEUE_SLOTS)) { queue = (queue + 1) % QUEUE_CNT; if (queue == hba->queue) { DEBUG ((EFI_D_ERROR, "could not find free slot\n")); return EFI_NOT_READY; } continue; } break; } hdr = &hba->cmd_hdr[queue][w]; cmd = &hba->command_table[queue][w]; sts = &hba->status_buf[queue][w]; sge = &hba->sge[queue][w]; ZeroMem (cmd, sizeof (struct hisi_sas_cmd)); ZeroMem (sts, sizeof (struct hisi_sas_sts)); if (SensePtr) ZeroMem (SensePtr, sizeof (EFI_SCSI_SENSE_DATA)); slot->used = TRUE; hba->queue = (queue + 1) % QUEUE_CNT; // Only consider ssp hdr->dw0 = (1 << CMD_HDR_RESP_REPORT_OFF) | (0x2 << CMD_HDR_TLR_CTRL_OFF) | (hba->port_id << CMD_HDR_PORT_OFF) | (1 << CMD_HDR_MODE_OFF) | (1 << CMD_HDR_CMD_OFF); hdr->dw1 = 1 << CMD_HDR_VERIFY_DTL_OFF; hdr->dw1 |= 0 << CMD_HDR_DEVICE_ID_OFF; hdr->dw2 = 0x83000d; hdr->transfer_tags = slot_idx << CMD_HDR_IPTT_OFF; if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_READ) { Buffer = Packet->InDataBuffer; BufferSize = Packet->InTransferLength; if (Buffer) { hdr->dw1 |= 1 << CMD_HDR_SSP_FRAME_TYPE_OFF; DmaOperation = MapOperationBusMasterWrite; } } else if (Packet->DataDirection == EFI_EXT_SCSI_DATA_DIRECTION_WRITE) { Buffer = Packet->OutDataBuffer; BufferSize = Packet->OutTransferLength; if (Buffer) { DmaOperation = MapOperationBusMasterRead; hdr->dw1 |= 2 << CMD_HDR_SSP_FRAME_TYPE_OFF; } } else { hdr->dw1 |= 0 << CMD_HDR_SSP_FRAME_TYPE_OFF; } hdr->data_transfer_len = BufferSize; hdr->cmd_table_addr = (UINT64)cmd; hdr->sts_buffer_addr = (UINT64)sts; CopyMem (&cmd->cmd[36], Packet->Cdb, Packet->CdbLength); if (Buffer != NULL) { struct hisi_sas_sge *sg; UINT32 remain, len, pos = 0, i = 0; Status = DmaMap (DmaOperation, Buffer, &BufferSize, &BufferAddress, &BufferMap); if (EFI_ERROR (Status)) { return Status; } remain = len = BufferSize; while (remain) { if (len > SGE_LIMIT) len = SGE_LIMIT; sg = &sge->sg[i]; sg->addr = (UINT64)(BufferAddress + pos); sg->page_ctrl_0 = sg->page_ctrl_1 = 0; sg->data_len = len; sg->data_off = 0; remain -= len; pos += len; len = remain; i++; } hdr->prd_table_addr = (UINT64)sge; hdr->sg_len = i << CMD_HDR_DATA_SGL_LEN_OFF; } // Ensure descriptor effective before start dma MemoryFence(); // Start dma WRITE_REG32(base, DLVRY_Q_0_WR_PTR + queue * 0x14, ++w % QUEUE_SLOTS); // Wait for dma complete while (slot->used) { if (READ_REG32(base, OQ_INT_SRC) & BIT(queue)) { struct hisi_sas_complete_hdr *complete_hdr; UINT32 data, rd; rd = READ_REG32(base, COMPL_Q_0_RD_PTR + (0x14 * queue)); complete_hdr = &hba->complete_hdr[queue][rd]; data = complete_hdr->data; // Check whether dma transfer error if ((data & CMPLT_HDR_ERR_RCRD_XFRD_MSK) && !(data & CMPLT_HDR_RSPNS_XFRD_MSK)) { DEBUG ((EFI_D_VERBOSE, "sas retry data=0x%x\n", data)); DEBUG ((EFI_D_VERBOSE, "sts[0]=0x%x\n", sts->status[0])); DEBUG ((EFI_D_VERBOSE, "sts[1]=0x%x\n", sts->status[1])); DEBUG ((EFI_D_VERBOSE, "sts[2]=0x%x\n", sts->status[2])); Status = EFI_NOT_READY; // wait 1 second and retry, some disk need long time to be ready // and ScsiDisk treat retry over 3 times as error MicroSecondDelay(1000000); } // Update read point WRITE_REG32(base, COMPL_Q_0_RD_PTR + (0x14 * queue), w); // Clear int WRITE_REG32(base, OQ_INT_SRC, BIT(queue)); slot->used = FALSE; break; } // Wait for status change in polling NanoSecondDelay (100); } if (BufferMap) DmaUnmap (BufferMap); p = (UINT8 *)&sts->status[0]; if (p[SENSE_DATA_PRES]) { // Disk not ready normal return for ScsiDiskTestUnitReady do next try SensePtr->Sense_Key = EFI_SCSI_SK_NOT_READY; SensePtr->Addnl_Sense_Code = EFI_SCSI_ASC_NOT_READY; SensePtr->Addnl_Sense_Code_Qualifier = EFI_SCSI_ASCQ_IN_PROGRESS; // wait 1 second for disk spin up, refer drivers/scsi/sd.c MicroSecondDelay(1000000); } return Status; } STATIC VOID hisi_sas_v1_init(struct hisi_hba *hba, PLATFORM_SAS_PROTOCOL *plat) { int i, j; UINT32 val, base = hba->base; // Reset for (i = 0; i < PHY_CNT; i++) { UINT32 phy_ctrl = PHY_READ_REG32(base, PHY_CTRL, i); phy_ctrl |= PHY_CTRL_RESET; PHY_WRITE_REG32(base, PHY_CTRL, i, phy_ctrl); } // spec says safe to wait 50us after reset MicroSecondDelay(50); // Ensure DMA tx & rx idle for (i = 0; i < PHY_CNT; i++) { UINT32 dma_tx_status, dma_rx_status; for (j = 0; j < 100; j++) { dma_tx_status = PHY_READ_REG32(base, DMA_TX_STATUS, i); dma_rx_status = PHY_READ_REG32(base, DMA_RX_STATUS, i); if (!(dma_tx_status & DMA_TX_STATUS_BUSY) && !(dma_rx_status & DMA_RX_STATUS_BUSY)) break; // Wait for status change in polling NanoSecondDelay (100); } } // Ensure axi bus idle for (j = 0; j < 100; j++) { UINT32 axi_status = READ_REG32(base, AXI_CFG); if (axi_status == 0) break; // Wait for status change in polling NanoSecondDelay (100); } plat->Init(plat); WRITE_REG32(base, DLVRY_QUEUE_ENABLE, 0xffffffff); WRITE_REG32(base, HGC_TRANS_TASK_CNT_LIMIT, 0x11); WRITE_REG32(base, DEVICE_MSG_WORK_MODE, 0x1); WRITE_REG32(base, HGC_SAS_TXFAIL_RETRY_CTRL, 0x1ff); WRITE_REG32(base, HGC_ERR_STAT_EN, 0x401); WRITE_REG32(base, CFG_1US_TIMER_TRSH, 0x64); WRITE_REG32(base, HGC_GET_ITV_TIME, 0x1); WRITE_REG32(base, I_T_NEXUS_LOSS_TIME, 0x64); WRITE_REG32(base, BUS_INACTIVE_LIMIT_TIME, 0x2710); WRITE_REG32(base, REJECT_TO_OPEN_LIMIT_TIME, 0x1); WRITE_REG32(base, CFG_AGING_TIME, 0x7a12); WRITE_REG32(base, HGC_DFX_CFG2, 0x9c40); WRITE_REG32(base, FIS_LIST_BADDR_L, 0x2); WRITE_REG32(base, INT_COAL_EN, 0xc); WRITE_REG32(base, OQ_INT_COAL_TIME, 0x186a0); WRITE_REG32(base, OQ_INT_COAL_CNT, 1); WRITE_REG32(base, ENT_INT_COAL_TIME, 0x1); WRITE_REG32(base, ENT_INT_COAL_CNT, 0x1); WRITE_REG32(base, OQ_INT_SRC, 0xffffffff); WRITE_REG32(base, ENT_INT_SRC1, 0xffffffff); WRITE_REG32(base, ENT_INT_SRC_MSK1, 0); WRITE_REG32(base, ENT_INT_SRC2, 0xffffffff); WRITE_REG32(base, ENT_INT_SRC_MSK2, 0); WRITE_REG32(base, SAS_ECC_INTR_MSK, 0); WRITE_REG32(base, AXI_AHB_CLK_CFG, 0x2); WRITE_REG32(base, CFG_SAS_CONFIG, 0x22000000); for (i = 0; i < PHY_CNT; i++) { PHY_WRITE_REG32(base, PROG_PHY_LINK_RATE, i, 0x88a); PHY_WRITE_REG32(base, PHY_CONFIG2, i, 0x7c080); PHY_WRITE_REG32(base, PHY_RATE_NEGO, i, 0x415ee00); PHY_WRITE_REG32(base, PHY_PCN, i, 0x80a80000); PHY_WRITE_REG32(base, SL_TOUT_CFG, i, 0x7d7d7d7d); PHY_WRITE_REG32(base, DONE_RECEIVED_TIME, i, 0x0); PHY_WRITE_REG32(base, RXOP_CHECK_CFG_H, i, 0x1000); PHY_WRITE_REG32(base, DONE_RECEIVED_TIME, i, 0); PHY_WRITE_REG32(base, CON_CFG_DRIVER, i, 0x13f0a); PHY_WRITE_REG32(base, CHL_INT_COAL_EN, i, 3); PHY_WRITE_REG32(base, DONE_RECEIVED_TIME, i, 8); } for (i = 0; i < QUEUE_CNT; i++) { WRITE_REG32(base, DLVRY_Q_0_BASE_ADDR_HI + (i * 0x14), upper_32_bits((UINT64)(hba->cmd_hdr[i]))); WRITE_REG32(base, DLVRY_Q_0_BASE_ADDR_LO + (i * 0x14), lower_32_bits((UINT64)(hba->cmd_hdr[i]))); WRITE_REG32(base, DLVRY_Q_0_DEPTH + (i * 0x14), QUEUE_SLOTS); WRITE_REG32(base, COMPL_Q_0_BASE_ADDR_HI + (i * 0x14), upper_32_bits((UINT64)(hba->complete_hdr[i]))); WRITE_REG32(base, COMPL_Q_0_BASE_ADDR_LO + (i * 0x14), lower_32_bits((UINT64)(hba->complete_hdr[i]))); WRITE_REG32(base, COMPL_Q_0_DEPTH + (i * 0x14), QUEUE_SLOTS); } WRITE_REG32(base, ITCT_BASE_ADDR_LO, lower_32_bits((UINT64)(hba->itct))); WRITE_REG32(base, ITCT_BASE_ADDR_HI, upper_32_bits((UINT64)(hba->itct))); WRITE_REG32(base, IOST_BASE_ADDR_LO, lower_32_bits((UINT64)(hba->iost))); WRITE_REG32(base, IOST_BASE_ADDR_HI, upper_32_bits((UINT64)(hba->iost))); WRITE_REG32(base, BROKEN_MSG_ADDR_LO, lower_32_bits((UINT64)(hba->breakpoint))); WRITE_REG32(base, BROKEN_MSG_ADDR_HI, upper_32_bits((UINT64)(hba->breakpoint))); for (i = 0; i < PHY_CNT; i++) { // Clear interrupt status val = PHY_READ_REG32(base, CHL_INT0, i); PHY_WRITE_REG32(base, CHL_INT0, i, val); val = PHY_READ_REG32(base, CHL_INT1, i); PHY_WRITE_REG32(base, CHL_INT1, i, val); val = PHY_READ_REG32(base, CHL_INT2, i); PHY_WRITE_REG32(base, CHL_INT2, i, val); // Bypass chip bug mask abnormal intr PHY_WRITE_REG32(base, CHL_INT0_MSK, i, 0x3fffff & ~CHL_INT0_MSK_PHYCTRL_NOTRDY); } // Init phy for (i = 0; i < PHY_CNT; i++) { PHY_WRITE_REG32(base, TX_ID_DWORD0, i, 0x10010e00); PHY_WRITE_REG32(base, TX_ID_DWORD1, i, 0x16); PHY_WRITE_REG32(base, TX_ID_DWORD2, i, 0x20880150); PHY_WRITE_REG32(base, TX_ID_DWORD3, i, 0x16); PHY_WRITE_REG32(base, TX_ID_DWORD4, i, 0x20880150); PHY_WRITE_REG32(base, TX_ID_DWORD5, i, 0x0); val = PHY_READ_REG32(base, PHY_CFG, i); val &= ~PHY_CFG_DC_OPT_MSK; val |= 1 << PHY_CFG_DC_OPT_OFF; PHY_WRITE_REG32(base, PHY_CFG, i, val); val = PHY_READ_REG32(base, PHY_CONFIG2, i); val &= ~PHY_CONFIG2_FORCE_TXDEEMPH_MSK; PHY_WRITE_REG32(base, PHY_CONFIG2, i, val); val = PHY_READ_REG32(base, PHY_CFG, i); val |= PHY_CFG_ENA_MSK; PHY_WRITE_REG32(base, PHY_CFG, i, val); } } STATIC VOID sas_init(SAS_V1_INFO *SasV1Info, PLATFORM_SAS_PROTOCOL *plat) { struct hisi_hba *hba = SasV1Info->hba; int i, s; for (i = 0; i < QUEUE_CNT; i++) { s = sizeof(struct hisi_sas_cmd_hdr) * QUEUE_SLOTS; DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->cmd_hdr[i]); ASSERT (hba->cmd_hdr[i] != NULL); ZeroMem (hba->cmd_hdr[i], s); s = sizeof(struct hisi_sas_complete_hdr) * QUEUE_SLOTS; DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->complete_hdr[i]); ASSERT (hba->complete_hdr[i] != NULL); ZeroMem (hba->complete_hdr[i], s); s = sizeof(struct hisi_sas_sts) * QUEUE_SLOTS; DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->status_buf[i]); ASSERT (hba->status_buf[i] != NULL); ZeroMem (hba->status_buf[i], s); s = sizeof(struct hisi_sas_cmd) * QUEUE_SLOTS; DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->command_table[i]); ASSERT (hba->command_table[i] != NULL); ZeroMem (hba->command_table[i], s); s = sizeof(struct hisi_sas_sge_page) * QUEUE_SLOTS; DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->sge[i]); ASSERT (hba->sge[i] != NULL); ZeroMem (hba->sge[i], s); } s = SLOT_ENTRIES * sizeof(struct hisi_sas_iost); DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->iost); ASSERT (hba->iost != NULL); ZeroMem (hba->iost, s); s = SLOT_ENTRIES * sizeof(struct hisi_sas_breakpoint); DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->breakpoint); ASSERT (hba->breakpoint != NULL); ZeroMem (hba->breakpoint, s); s = MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); DmaAllocateBuffer (EfiBootServicesData, EFI_SIZE_TO_PAGES (s), (VOID *)&hba->itct); ASSERT (hba->itct != NULL); ZeroMem (hba->itct, s); hba->slots = AllocateZeroPool (SLOT_ENTRIES * sizeof(struct hisi_sas_slot)); ASSERT (hba->slots != NULL); hisi_sas_v1_init(hba, plat); } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruFunction ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN UINT8 *Target, IN UINT64 Lun, IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet, IN EFI_EVENT Event OPTIONAL ) { SAS_V1_INFO *SasV1Info = SAS_FROM_PASS_THRU(This); struct hisi_hba *hba = SasV1Info->hba; return prepare_cmd(hba, Packet); } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruGetNextTargetLun ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN OUT UINT8 **Target, IN OUT UINT64 *Lun ) { SAS_V1_INFO *SasV1Info = SAS_FROM_PASS_THRU(This); struct hisi_hba *hba = SasV1Info->hba; UINT8 ScsiId[TARGET_MAX_BYTES]; UINT8 TargetId; if (*Target == NULL || Lun == NULL) { return EFI_INVALID_PARAMETER; } SetMem (ScsiId, TARGET_MAX_BYTES, 0xFF); TargetId = (*Target)[0]; if (TargetId == MAX_TARGET_ID) { return EFI_NOT_FOUND; } if (CompareMem(*Target, ScsiId, TARGET_MAX_BYTES) == 0) { SetMem (*Target, TARGET_MAX_BYTES,0); } else { (*Target)[0] = (UINT8) (hba->LatestTargetId + 1); } *Lun = 0; // // Update the LatestTargetId. // hba->LatestTargetId = (*Target)[0]; hba->LatestLun = *Lun; return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruBuildDevicePath ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN UINT8 *Target, IN UINT64 Lun, IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath ) { SAS_V1_INFO *SasV1Info = SAS_FROM_PASS_THRU(This); *DevicePath = DuplicateDevicePath ((EFI_DEVICE_PATH_PROTOCOL *)(SasV1Info->DevicePath)); return EFI_SUCCESS; } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruGetTargetLun ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, OUT UINT8 **Target, OUT UINT64 *Lun ) { return EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruResetChannel ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This ) { return EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruResetTarget ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN UINT8 *Target, IN UINT64 Lun ) { return EFI_UNSUPPORTED; } STATIC EFI_STATUS EFIAPI SasV1ExtScsiPassThruGetNextTarget ( IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This, IN OUT UINT8 **Target ) { return EFI_UNSUPPORTED; } STATIC EFI_EXT_SCSI_PASS_THRU_PROTOCOL SasV1ExtScsiPassThruProtocolTemplate = { NULL, SasV1ExtScsiPassThruFunction, SasV1ExtScsiPassThruGetNextTargetLun, SasV1ExtScsiPassThruBuildDevicePath, SasV1ExtScsiPassThruGetTargetLun, SasV1ExtScsiPassThruResetChannel, SasV1ExtScsiPassThruResetTarget, SasV1ExtScsiPassThruGetNextTarget }; EFI_STATUS EFIAPI SasDriverBindingSupported ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { PLATFORM_SAS_PROTOCOL *plat; EFI_STATUS Status; Status = gBS->OpenProtocol ( Controller, &gPlatformSasProtocolGuid, (VOID **) &plat, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } if (EFI_ERROR (Status)) { return Status; } // // Close the Sas Host used to perform the supported test // gBS->CloseProtocol ( Controller, &gPlatformSasProtocolGuid, This->DriverBindingHandle, Controller ); return EFI_SUCCESS; } EFI_STATUS EFIAPI SasDriverBindingStart ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath ) { EFI_STATUS Status; PLATFORM_SAS_PROTOCOL *plat; SAS_V1_INFO *SasV1Info = NULL; SAS_V1_TRANSPORT_DEVICE_PATH *DevicePath; UINT32 val, base; int i, phy_id = 0; struct hisi_sas_itct *itct; struct hisi_hba *hba; Status = gBS->OpenProtocol ( Controller, &gPlatformSasProtocolGuid, (VOID **) &plat, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_BY_DRIVER ); if (EFI_ERROR (Status)) { if (Status == EFI_ALREADY_STARTED) { return EFI_SUCCESS; } return Status; } SasV1Info = AllocateZeroPool (sizeof (SAS_V1_INFO)); ASSERT (SasV1Info); SasV1Info->Signature = SAS_DEVICE_SIGNATURE; SasV1Info->hba = AllocateZeroPool (sizeof(struct hisi_hba)); ASSERT (SasV1Info->hba); hba = SasV1Info->hba; base = hba->base = plat->BaseAddr; sas_init(SasV1Info, plat); // Wait for sas controller phyup happen MicroSecondDelay(100000); for (i = 0; i < PHY_CNT; i++) { val = PHY_READ_REG32(base, CHL_INT2, i); if (val & CHL_INT2_SL_PHY_ENA) { phy_id = i; } } itct = &hba->itct[0]; //device_id = 0 hba->port_id = (READ_REG32(base, PHY_PORT_NUM_MA) >> (4 * phy_id)) & 0xf; // Setup itct itct->qw0 = 0x355; itct->sas_addr = PHY_READ_REG32(base, RX_IDAF_DWORD3, phy_id); itct->sas_addr = itct->sas_addr << 32 | PHY_READ_REG32(base, RX_IDAF_DWORD4, phy_id); itct->qw2 = 0; // Clear phyup PHY_WRITE_REG32(base, CHL_INT2, phy_id, CHL_INT2_SL_PHY_ENA); val = PHY_READ_REG32(base, CHL_INT0, phy_id); val &= ~CHL_INT0_PHYCTRL_NOTRDY; PHY_WRITE_REG32(base, CHL_INT0, phy_id, val); PHY_WRITE_REG32(base, CHL_INT0_MSK, phy_id, 0x3ce3ee); // Need notify val = PHY_READ_REG32(base, SL_CONTROL, phy_id); val |= SL_CONTROL_NOTIFY_EN; PHY_WRITE_REG32(base, SL_CONTROL, phy_id, val); // wait 100ms required for notify takes effect, refer drivers/scsi/hisi_sas/hisi_sas_v1_hw.c MicroSecondDelay(100000); val = PHY_READ_REG32(base, SL_CONTROL, phy_id); val &= ~SL_CONTROL_NOTIFY_EN; PHY_WRITE_REG32(base, SL_CONTROL, phy_id, val); CopyMem (&SasV1Info->ExtScsiPassThru, &SasV1ExtScsiPassThruProtocolTemplate, sizeof (EFI_EXT_SCSI_PASS_THRU_PROTOCOL)); SasV1Info->ExtScsiPassThruMode.AdapterId = 2; SasV1Info->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL; SasV1Info->ExtScsiPassThruMode.IoAlign = 64; //cache line align SasV1Info->ExtScsiPassThru.Mode = &SasV1Info->ExtScsiPassThruMode; DevicePath = (SAS_V1_TRANSPORT_DEVICE_PATH *)CreateDeviceNode ( HARDWARE_DEVICE_PATH, HW_VENDOR_DP, sizeof (SAS_V1_TRANSPORT_DEVICE_PATH)); ASSERT (DevicePath != NULL); SasV1Info->DevicePath = DevicePath; CopyMem (&DevicePath->Vendor.Guid, &gPlatformSasProtocolGuid, sizeof (EFI_GUID)); DevicePath->PhysBase = base; SetDevicePathNodeLength (&DevicePath->Vendor, sizeof (*DevicePath) - sizeof (DevicePath->End)); SetDevicePathEndNode (&DevicePath->End); Status = gBS->InstallMultipleProtocolInterfaces ( &Controller, &gEfiDevicePathProtocolGuid, DevicePath, &gEfiExtScsiPassThruProtocolGuid, &SasV1Info->ExtScsiPassThru, NULL); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; } EFI_STATUS EFIAPI SasDriverBindingStop ( IN EFI_DRIVER_BINDING_PROTOCOL *This, IN EFI_HANDLE Controller, IN UINTN NumberOfChildren, IN EFI_HANDLE *ChildHandleBuffer ) { SAS_V1_INFO *SasV1Info; EFI_STATUS Status; EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsi; int i, s; Status = gBS->OpenProtocol ( Controller, &gEfiExtScsiPassThruProtocolGuid, (VOID **) &ExtScsi, This->DriverBindingHandle, Controller, EFI_OPEN_PROTOCOL_GET_PROTOCOL ); if (EFI_ERROR (Status)) { return Status; } SasV1Info = SAS_FROM_PASS_THRU(ExtScsi); Status = gBS->UninstallMultipleProtocolInterfaces ( Controller, &gEfiDevicePathProtocolGuid, SasV1Info->DevicePath, &gEfiExtScsiPassThruProtocolGuid, &SasV1Info->ExtScsiPassThru, NULL); if (!EFI_ERROR (Status)) { gBS->CloseProtocol ( Controller, &gPlatformSasProtocolGuid, This->DriverBindingHandle, Controller ); gBS->CloseEvent (SasV1Info->TimerEvent); for (i = 0; i < QUEUE_CNT; i++) { s = sizeof(struct hisi_sas_cmd_hdr) * QUEUE_SLOTS; DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->cmd_hdr[i]); s = sizeof(struct hisi_sas_complete_hdr) * QUEUE_SLOTS; DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->complete_hdr[i]); s = sizeof(struct hisi_sas_sts) * QUEUE_SLOTS; DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->status_buf[i]); s = sizeof(struct hisi_sas_cmd) * QUEUE_SLOTS; DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->command_table[i]); s = sizeof(struct hisi_sas_sge_page) * QUEUE_SLOTS; DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->sge[i]); } s = SLOT_ENTRIES * sizeof(struct hisi_sas_iost); DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->iost); s = SLOT_ENTRIES * sizeof(struct hisi_sas_breakpoint); DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->breakpoint); s = MAX_ITCT_ENTRIES * sizeof(struct hisi_sas_itct); DmaFreeBuffer(EFI_SIZE_TO_PAGES (s), (VOID *)SasV1Info->hba->itct); FreePool (SasV1Info->hba->slots); FreePool (SasV1Info->hba); FreePool (SasV1Info); return EFI_SUCCESS; } return Status; } EFI_DRIVER_BINDING_PROTOCOL gSasDriverBinding = { SasDriverBindingSupported, SasDriverBindingStart, SasDriverBindingStop, 0xa, NULL, NULL }; EFI_STATUS SasV1Initialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { return EfiLibInstallDriverBindingComponentName2 ( ImageHandle, SystemTable, &gSasDriverBinding, ImageHandle, NULL, NULL ); }