blob: 2d990a06194b0f4ed790b90da3fc7cb0326b63f5 [file] [log] [blame]
// 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/platform/bus.h>
#include <zircon/compiler.h>
#ifndef __BYTE_ORDER__
#error Compiler does not provide __BYTE_ORDER__
#endif
static inline uint32_t SWAP_32(uint32_t x) {
return __builtin_bswap32(x);
}
static inline uint16_t SWAP_16(uint16_t x) {
return __builtin_bswap16(x);
}
// standard swap macros
#if BYTE_ORDER == BIG_ENDIAN
#define LE64(val) SWAP_64(val)
#define LE32(val) SWAP_32(val)
#define LE16(val) SWAP_16(val)
#define BE64(val) (val)
#define BE32(val) (val)
#define BE16(val) (val)
#else
#define LE64(val) (val)
#define LE32(val) (val)
#define LE16(val) (val)
#define BE64(val) SWAP_64(val)
#define BE32(val) SWAP_32(val)
#define BE16(val) SWAP_16(val)
#endif
#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
#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 ALIGNED_UPIU_SIZE 512
// 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
// 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,
};
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,
UPIU_CMD_FLAGS_MAX,
};
// UFS UPIU transaction type
enum upiu_trans_type {
UPIU_TYPE_NOP_OUT = 0x00,
UPIU_TYPE_QUERY_REQ = 0x16,
UPIU_TYPE_NOP_IN = 0x20,
UPIU_TYPE_REJECT = 0x3F,
};
enum ufs_link_change_stage {
PRE_CHANGE,
POST_CHANGE,
};
// 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[16];
} 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;
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;
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;
ulong outstanding_xfer_reqs;
ulong outstanding_tm_tasks;
zx_time_t timeout;
ufs_hba_variant_ops_t* vops;
} ufs_hba_t;
// UFS device
typedef struct {
pdev_protocol_t pdev;
zx_device_t* zxdev;
mmio_buffer_t ufshc_mmio;
zx_handle_t bti;
ufs_hba_t ufs_hba;
} 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;
}
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);