| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #pragma once |
| |
| #include <ddk/device.h> |
| #include <ddk/io-buffer.h> |
| #include <ddk/mmio-buffer.h> |
| #include <ddk/protocol/block.h> |
| #include <ddk/protocol/platform/bus.h> |
| #include <threads.h> |
| #include <zircon/compiler.h> |
| |
| #define UFS_BIT(x) (1L << (x)) |
| #define LOWER_32_BITS(x) ((uint32_t)((x)&0xFFFFFFFFUL)) |
| #define UPPER_32_BITS(x) ((uint32_t)((x) >> 32)) |
| #define clr_bits(v, a) writel(readl(a) & (uint32_t) ~(v), (a)) |
| |
| #define UFS_ERROR(fmt, ...) zxlogf(ERROR, "[%s:%d]" fmt, __func__, \ |
| __LINE__, ##__VA_ARGS__) |
| #define UFS_WARN(fmt, ...) zxlogf(WARN, "[%s:%d]" fmt, __func__, \ |
| __LINE__, ##__VA_ARGS__) |
| #define UFS_INFO(fmt, ...) zxlogf(INFO, "[%s:%d]" fmt, __func__, \ |
| __LINE__, ##__VA_ARGS__) |
| // Uncomment below line for more logs |
| // #define UFS_DEBUG |
| #ifdef UFS_DEBUG |
| #define UFS_DBG(fmt, ...) zxlogf(INFO, "[%s:%d]" fmt, __func__, \ |
| __LINE__, ##__VA_ARGS__) |
| #else |
| #define UFS_DBG(fmt, ...) /*nothing*/ |
| #endif |
| |
| // HCE - Host Controller Enable 34h |
| #define CONTROLLER_ENABLE UFS_BIT(0) |
| #define CONTROLLER_DISABLE 0x0 |
| |
| #define UFS_HCS_DP_BIT UFS_BIT(3) |
| #define UFS_HCS_UCRDY UFS_BIT(3) |
| |
| // HCE Interrupt status control |
| #define UFS_IS_UE_BIT UFS_BIT(2) |
| #define UFS_IS_ULSS_BIT UFS_BIT(8) |
| #define UFS_IS_UCCS_BIT UFS_BIT(10) |
| #define UFS_UTP_RUN_BIT UFS_BIT(0) |
| |
| #define UIC_LINK_STARTUP_CMD 0x16 |
| #define UFS_HCLKDIV_NORMAL_VAL 0xE4 |
| #define UFS_AHT_AH8ITV_MASK 0x3FF |
| #define UFS_AHT_AH8_TIMER 0x1001 |
| |
| #define UFS_SCTRL_CLK_GATE_BYPASS_MASK 0x3F |
| #define UFS_SCTRL_SYSCTRL_BYPASS_MASK (0x3F << 16) |
| #define UFS_SCTRL_CLK_GATE_BYPASS 0x18 |
| #define UFS_SCTRL_SYSCTRL 0x5C |
| |
| // UFS Command codes |
| #define READ_DESC_OPCODE 0x01 |
| #define WRITE_DESC_OPCODE 0x02 |
| #define READ_FLAG_OPCODE 0x05 |
| #define SET_FLAG_OPCODE 0x06 |
| |
| // UFS SCSI command codes |
| #define TEST_UNIT_OPCODE 0x00 |
| #define INQUIRY_OPCODE 0x12 |
| #define READ_CAPA16_OPCODE 0x9E |
| #define READ10_OPCODE 0x28 |
| |
| #define FLAG_ID_FDEVICE_INIT 0x01 |
| |
| // Descriptor Idns |
| #define STANDARD_RD_REQ 0x01 |
| #define STANDARD_WR_REQ 0x81 |
| #define DEVICE_DESC_IDN 0x00 |
| #define DEVICE_DESC_LEN 0x40 |
| #define UPIU_CDB_MAX_LEN 16 |
| #define UFS_MAX_WLUN 0x04 |
| |
| #define ALIGNED_UPIU_SIZE 512 |
| #define PRDT_BUF_SIZE 0x40000 |
| #define DATA_REQ_SIZE 4096 |
| #define UFS_INQUIRY_TFR_LEN 36 |
| #define UFS_INQUIRY_VENDOR_OFF 8 |
| #define UFS_INQUIRY_MODEL_OFF 16 |
| #define UFS_READ_CAPA16_LEN 32 |
| #define UFS_READ_CAPA16_SACT 0x10 |
| #define UFS_DEV_SECT_SIZE 0x1000 |
| |
| // UFSHC UPRO configurations |
| #define UPRO_MPHY_CTRL 0xD0C10000 |
| #define UPRO_MPHY_FSM_TX0 0x00410000 |
| #define UPRO_MPHY_FSM_TX1 0x00410001 |
| #define UPRO_PA_TX_LCC_CTRL 0x155E0000 |
| #define UPRO_MK2_EXTN_SUP 0xD0AB0000 |
| #define UPRO_ERR_PA_IND 0xD0610000 |
| |
| #define MPHY_ATTR_DEMPH_ADDR1 0x1002 |
| #define MPHY_ATTR_DEMPH_ADDR2 0x1102 |
| #define MPHY_ATTR_DEMPH_ADDR3 0x1003 |
| #define MPHY_ATTR_DEMPH_ADDR4 0x1103 |
| #define MPHY_ATTR_DEMPH_VAL1 0xAC78 |
| #define MPHY_ATTR_DEMPH_VAL2 0x2440 |
| |
| #define MPHY_ATTR_DEMPH_ADDR_MSB 0x81170000 |
| #define MPHY_ATTR_DEMPH_ADDR_LSB 0x81160000 |
| #define MPHY_ATTR_DEMPH_VAL_MSB 0x81190000 |
| #define MPHY_ATTR_DEMPH_VAL_LSB 0x81180000 |
| #define MPHY_ATTR_DEMPH_CTRL 0x811C0000 |
| |
| #define BAD_SLOT 0x55 |
| #define NOP_RETRY_COUNT 20 |
| #define MPHY_TX_FSM_RETRY_COUNT 500 |
| #define LINK_STARTUP_UCCS_RETRY_COUNT 200 |
| |
| #define UFS_NUTMRS_SHIFT 16 |
| #define UTP_UFS_STORAGE_CMD (1 << 4) |
| |
| #define UFS_UPIU_REQ_HDR_LEN 12 |
| #define UFS_RESP_LEN_OFF_H 6 |
| #define UFS_RESP_LEN_OFF_L 7 |
| |
| // UFS device descriptor offsets |
| #define UFS_DEV_DESC_NUM_LUNS 0x06 |
| #define UFS_DEV_DESC_MANF_ID_H 0x18 |
| #define UFS_DEV_DESC_MANF_ID_L 0x19 |
| #define UFS_READ_DESC_MIN_LEN 0x02 |
| |
| #define SCSI_CMD_STATUS_GOOD 0x0 |
| #define SCSI_CMD_STATUS_CHK_COND 0x02 |
| |
| // UFS HC Register Offsets |
| enum { |
| REG_CONTROLLER_CAPABILITIES = 0x00, |
| REG_UFS_VERSION = 0x08, |
| REG_CONTROLLER_DEV_ID = 0x10, |
| REG_CONTROLLER_PROD_ID = 0x14, |
| REG_CONTROLLER_AHIT = 0x18, |
| REG_INTERRUPT_STATUS = 0x20, |
| REG_INTERRUPT_ENABLE = 0x24, |
| REG_CONTROLLER_STATUS = 0x30, |
| REG_CONTROLLER_ENABLE = 0x34, |
| REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38, |
| REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C, |
| REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40, |
| REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44, |
| REG_UIC_ERROR_CODE_DME = 0x48, |
| REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C, |
| REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50, |
| REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54, |
| REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58, |
| REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C, |
| REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60, |
| REG_UTP_TASK_REQ_LIST_BASE_L = 0x70, |
| REG_UTP_TASK_REQ_LIST_BASE_H = 0x74, |
| REG_UTP_TASK_REQ_DOOR_BELL = 0x78, |
| REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C, |
| REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80, |
| REG_UIC_COMMAND = 0x90, |
| REG_UIC_COMMAND_ARG_1 = 0x94, |
| REG_UIC_COMMAND_ARG_2 = 0x98, |
| REG_UIC_COMMAND_ARG_3 = 0x9C, |
| REG_UFS_HCLKDIV_OFF = 0xFC, |
| }; |
| |
| // UFS status / Error codes used as return values |
| enum { |
| UFS_LINK_STARTUP_FAIL = -0x01, |
| UFS_UTRD_DOORBELL_TIMEOUT = -0x02, |
| UFS_NOP_RESP_FAIL = -0x03, |
| UFS_NOP_OUT_OCS_FAIL = -0x04, |
| UFS_INVALID_NOP_IN = -0x05, |
| // UPIU response error codes |
| UPIU_RESP_COND_FAIL = -0x06, |
| UPIU_RESP_STAT_FAIL = -0x07, |
| }; |
| |
| enum { |
| MMIO_UFSHC, |
| MMIO_UFS_SCTRL, |
| }; |
| |
| // Rate |
| enum { |
| UFS_RATE_A = 1, |
| UFS_RATE_B, |
| }; |
| |
| // Controller capability masks |
| enum { |
| MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F, |
| MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000, |
| }; |
| |
| enum uic_dme_type { |
| // Configuration |
| DME_GET = 0x01, |
| DME_SET = 0x02, |
| // Control |
| DME_ENABLE = 0x12, |
| }; |
| |
| enum utp_data_tfr_dirn { |
| UTP_NO_DATA_TFR, |
| UTP_HOST_TO_DEVICE = 0x02, |
| UTP_DEVICE_TO_HOST = 0x04, |
| }; |
| |
| enum upiu_cmd_flags { |
| UPIU_CMD_FLAGS_NONE = 0x00, |
| UPIU_CMD_FLAGS_WRITE = 0x20, |
| UPIU_CMD_FLAGS_READ = 0x40, |
| UPIU_CMD_FLAGS_MAX, |
| }; |
| |
| // UFS UPIU transaction type |
| enum upiu_trans_type { |
| UPIU_TYPE_NOP_OUT = 0x00, |
| UPIU_TYPE_CMD = 0x01, |
| UPIU_TYPE_QUERY_REQ = 0x16, |
| UPIU_TYPE_NOP_IN = 0x20, |
| UPIU_TYPE_REJECT = 0x3F, |
| }; |
| |
| enum dma_direction { |
| UFS_DMA_TO_DEVICE = 0x01, |
| UFS_DMA_FROM_DEVICE = 0x02, |
| UFS_DMA_NONE = 0x03, |
| }; |
| |
| enum ufs_link_change_stage { |
| PRE_CHANGE, |
| POST_CHANGE, |
| }; |
| |
| typedef struct { |
| uint64_t log_blk_addr; |
| uint32_t blk_len; |
| uint8_t prot_info; |
| uint8_t log_blk_per_phys_blk_exp; |
| uint16_t low_align_log_blk_addr; |
| uint8_t res[16]; |
| } ufs_readcapa16_data_t; |
| |
| // UFSHCI PRD Entry |
| typedef struct { |
| uint32_t base_addr; |
| uint32_t upper_addr; |
| uint32_t res1; |
| uint32_t size; |
| } ufshcd_prd_t; |
| |
| // NOP OUT UPIU |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t res1; |
| uint8_t task_tag; |
| uint32_t res2; |
| uint8_t tot_ehs_len; |
| uint8_t res3; |
| uint16_t data_seg_len; |
| uint8_t res4[20]; |
| } ufs_nop_req_upiu_t; |
| |
| // NOP IN UPIU |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t res1; |
| uint8_t task_tag; |
| uint8_t res2_0; |
| uint8_t res2_1; |
| uint8_t resp; |
| uint8_t res3; |
| uint8_t tot_ehs_len; |
| uint8_t device_info; |
| uint16_t data_seg_len; |
| uint8_t res4[20]; |
| } ufs_nop_resp_upiu_t; |
| |
| // Query UPIU |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t res1; |
| uint8_t task_tag; |
| uint8_t res2; |
| uint8_t query_func; |
| uint8_t query_resp; |
| uint8_t res3; |
| uint8_t tot_ehs_len; |
| uint8_t res4; |
| uint16_t data_seg_len; |
| uint8_t tsf[16]; |
| uint32_t res5; |
| } ufs_query_req_upiu_t; |
| |
| // UFS Command Descriptor structure |
| typedef struct { |
| uint8_t cmd_upiu[ALIGNED_UPIU_SIZE]; |
| uint8_t resp_upiu[ALIGNED_UPIU_SIZE]; |
| ufshcd_prd_t prd_table[128]; |
| } utp_tfr_cmd_desc_t; |
| |
| // Command UPIU structure |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t lun; |
| uint8_t task_tag; |
| uint8_t cmd_set_type; |
| uint8_t res1_0; |
| uint8_t res1_1; |
| uint8_t res1_2; |
| uint8_t tot_ehs_len; |
| uint8_t res2; |
| uint16_t data_seg_len; |
| uint32_t exp_data_xfer_len; |
| uint8_t cdb[UPIU_CDB_MAX_LEN]; |
| } ufs_utp_cmd_upiu_t; |
| |
| // Response UPIU structure |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t lun; |
| uint8_t task_tag; |
| uint8_t cmd_set_type; |
| uint8_t res1; |
| uint8_t resp; |
| uint8_t status; |
| uint8_t tot_ehs_len; |
| uint8_t device_info; |
| uint16_t data_seg_len; |
| uint32_t resd_xfer_count; |
| uint32_t res2; |
| uint32_t res3; |
| uint32_t res4; |
| uint32_t res5; |
| uint16_t sense_data_len; |
| uint8_t sense_data[18]; |
| } ufs_utp_resp_upiu_t; |
| |
| // UTRD structure |
| typedef struct { |
| uint8_t crypt_cci; |
| uint8_t res1_0; |
| uint8_t crypt_en; |
| uint8_t ct_flags; |
| uint32_t dunl; |
| uint8_t ocs; |
| uint8_t res3_0; |
| uint8_t res3_1; |
| uint8_t res3_2; |
| uint32_t dunu; |
| uint32_t ucdba; |
| uint32_t ucdbau; |
| uint16_t resp_upiu_len; |
| uint16_t resp_upiu_off; |
| uint16_t prd_table_len; |
| uint16_t prd_table_off; |
| } utp_tfr_req_desc_t; |
| |
| // Task Manage request |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t lun; |
| uint8_t res1; |
| uint8_t tm_fn; |
| uint8_t res2_0; |
| uint8_t res2_1; |
| uint8_t tot_ehs_len; |
| uint8_t res3; |
| uint16_t data_seg_len; |
| uint32_t ip_param_1; |
| uint32_t ip_param_2; |
| uint32_t ip_param_3; |
| uint32_t res4; |
| uint32_t res5; |
| } ufs_tm_req_upiu_t; |
| |
| // Task Manage response |
| typedef struct { |
| uint8_t trans_type; |
| uint8_t flags; |
| uint8_t lun; |
| uint8_t task_tag; |
| uint8_t res1_0; |
| uint8_t res1_1; |
| uint8_t resp; |
| uint8_t res2; |
| uint8_t tot_ehs_len; |
| uint8_t res3; |
| uint16_t data_seg_len; |
| uint32_t ip_param_1; |
| uint32_t ip_param_2; |
| uint32_t res4; |
| uint32_t res5; |
| uint32_t res6; |
| } ufs_tm_resp_upiu_t; |
| |
| // UTMRD structure |
| typedef struct { |
| uint8_t res1_0; |
| uint8_t res1_1; |
| uint8_t res1_2; |
| uint8_t intr_flag; |
| uint32_t res2; |
| uint8_t ocs; |
| uint8_t res3_0; |
| uint8_t res3_1; |
| uint8_t res3_2; |
| uint32_t res4; |
| ufs_tm_req_upiu_t tm_req_upiu; |
| ufs_tm_resp_upiu_t tm_resp_upiu; |
| } utp_task_req_desc_t; |
| |
| // Local reference block |
| typedef struct { |
| uint8_t cmd_type; |
| uint8_t data_direction; |
| uint8_t rw_flags; |
| uint8_t ocs; |
| uint8_t xfer_cmd_status; |
| uint32_t tfr_size; |
| uint8_t task_tag; |
| uint32_t lun; |
| utp_tfr_req_desc_t* utrd; |
| ufs_utp_cmd_upiu_t* cmd_upiu; |
| ufs_utp_resp_upiu_t* resp_upiu; |
| ufshcd_prd_t* prdt; |
| } ufs_hcd_lrb_t; |
| |
| typedef struct { |
| const char* name; |
| zx_status_t (*link_startup)(volatile void* regs, uint8_t status); |
| } ufs_hba_variant_ops_t; |
| |
| // UFS Host bus adaptor |
| typedef struct ufs_hba { |
| uint8_t nutmrs; |
| uint8_t nutrs; |
| uint32_t caps; |
| uint32_t ufs_version; |
| uint8_t num_lun; |
| uint8_t active_lun; |
| uint16_t manufacturer_id; |
| |
| // UFS Command descriptor |
| io_buffer_t ucdl_dma_buf; |
| // UTP Transfer request descriptor |
| io_buffer_t utrl_dma_buf; |
| // UTP Task management descriptor |
| io_buffer_t utmrl_dma_buf; |
| // UFS request buffer |
| io_buffer_t req_dma_buf; |
| utp_tfr_cmd_desc_t* cmd_desc; |
| utp_tfr_req_desc_t* tfr_desc; |
| utp_task_req_desc_t* req_desc; |
| ufs_hcd_lrb_t* lrb_buf; |
| void* req_buf; |
| ulong outstanding_xfer_reqs; |
| ulong outstanding_tm_tasks; |
| zx_time_t timeout; |
| ufs_hba_variant_ops_t* vops; |
| } ufs_hba_t; |
| |
| // UFS LUN Block device |
| typedef struct ufs_lun_blk_dev { |
| zx_device_t* zxdev; |
| block_info_t block_info; |
| int lun_id; |
| } ufs_lun_blk_dev_t; |
| |
| // UFS device |
| typedef struct ufshc_dev { |
| pdev_protocol_t pdev; |
| zx_device_t* zxdev; |
| ufs_lun_blk_dev_t lun_blk_devs[UFS_MAX_WLUN]; |
| mmio_buffer_t ufshc_mmio; |
| zx_handle_t bti; |
| ufs_hba_t ufs_hba; |
| thrd_t worker_thread; |
| } ufshc_dev_t; |
| |
| static inline int32_t find_first_zero_bit(ulong* addr, uint8_t bits) { |
| ulong* value = addr; |
| int32_t i; |
| |
| for (i = 0; i < bits; i++) { |
| if (0 == (*value & (1 << i))) |
| return i; |
| } |
| |
| return -1; |
| } |
| |
| #ifdef UFS_DEBUG |
| static inline void dbg_dump_buffer(uint8_t* buf, uint32_t len, const char* name) { |
| zxlogf(INFO, "%s_buffer:\n", name); |
| for (uint32_t index = 0; index < len; index++) { |
| zxlogf(INFO, "buf[%d]=0x%x ", index, buf[index]); |
| if (index && !(index % 10)) |
| zxlogf(INFO, "\n"); |
| } |
| zxlogf(INFO, "\n"); |
| } |
| #else |
| static inline void dbg_dump_buffer(uint8_t* buf, uint32_t len, const char* name) {} |
| #endif |
| |
| zx_status_t ufshc_send_uic_command(volatile void* regs, |
| uint32_t command, |
| uint32_t arg1, |
| uint32_t arg3); |
| uint32_t ufshc_uic_cmd_read(volatile void* regs, uint32_t command, uint32_t arg1); |
| void ufshc_disable_auto_h8(volatile void* regs); |
| void ufshc_check_h8(volatile void* regs); |
| zx_status_t ufshc_init(ufshc_dev_t* dev, ufs_hba_variant_ops_t* ufs_hi3660_vops); |
| zx_status_t ufs_create_worker_thread(ufshc_dev_t* dev); |