|
@ -30,9 +30,138 @@ |
|
|
static ufs_params_t ufs_params; |
|
|
static ufs_params_t ufs_params; |
|
|
static int nutrs; /* Number of UTP Transfer Request Slots */ |
|
|
static int nutrs; /* Number of UTP Transfer Request Slots */ |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ufs_uic_error_handler - UIC error interrupts handler |
|
|
|
|
|
* @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) |
|
|
|
|
|
* |
|
|
|
|
|
* Returns |
|
|
|
|
|
* 0 - ignore error |
|
|
|
|
|
* -EIO - fatal error, needs re-init |
|
|
|
|
|
* -EAGAIN - non-fatal error, retries are sufficient |
|
|
|
|
|
*/ |
|
|
|
|
|
static int ufs_uic_error_handler(bool ignore_linereset) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t data; |
|
|
|
|
|
int result = 0; |
|
|
|
|
|
|
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UECPA); |
|
|
|
|
|
if (data & UFS_UIC_PA_ERROR_MASK) { |
|
|
|
|
|
if (data & PA_LAYER_GEN_ERR) { |
|
|
|
|
|
if (!ignore_linereset) { |
|
|
|
|
|
return -EIO; |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
result = -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UECDL); |
|
|
|
|
|
if (data & UFS_UIC_DL_ERROR_MASK) { |
|
|
|
|
|
if (data & PA_INIT_ERR) { |
|
|
|
|
|
return -EIO; |
|
|
|
|
|
} |
|
|
|
|
|
result = -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* NL/TL/DME error requires retries */ |
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UECN); |
|
|
|
|
|
if (data & UFS_UIC_NL_ERROR_MASK) { |
|
|
|
|
|
result = -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UECT); |
|
|
|
|
|
if (data & UFS_UIC_TL_ERROR_MASK) { |
|
|
|
|
|
result = -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UECDME); |
|
|
|
|
|
if (data & UFS_UIC_DME_ERROR_MASK) { |
|
|
|
|
|
result = -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ufs_error_handler - error interrupts handler |
|
|
|
|
|
* @status: interrupt status |
|
|
|
|
|
* @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) |
|
|
|
|
|
* |
|
|
|
|
|
* Returns |
|
|
|
|
|
* 0 - ignore error |
|
|
|
|
|
* -EIO - fatal error, needs re-init |
|
|
|
|
|
* -EAGAIN - non-fatal error, retries are sufficient |
|
|
|
|
|
*/ |
|
|
|
|
|
static int ufs_error_handler(uint32_t status, bool ignore_linereset) |
|
|
|
|
|
{ |
|
|
|
|
|
int result; |
|
|
|
|
|
|
|
|
|
|
|
if (status & UFS_INT_UE) { |
|
|
|
|
|
result = ufs_uic_error_handler(ignore_linereset); |
|
|
|
|
|
if (result != 0) { |
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Return I/O error on fatal error, it is upto the caller to re-init UFS */ |
|
|
|
|
|
if (status & UFS_INT_FATAL) { |
|
|
|
|
|
return -EIO; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* retry for non-fatal errors */ |
|
|
|
|
|
return -EAGAIN; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* ufs_wait_for_int_status - wait for expected interrupt status |
|
|
|
|
|
* @expected: expected interrupt status bit |
|
|
|
|
|
* @timeout_ms: timeout in milliseconds to poll for |
|
|
|
|
|
* @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error) |
|
|
|
|
|
* |
|
|
|
|
|
* Returns |
|
|
|
|
|
* 0 - received expected interrupt and cleared it |
|
|
|
|
|
* -EIO - fatal error, needs re-init |
|
|
|
|
|
* -EAGAIN - non-fatal error, caller can retry |
|
|
|
|
|
* -ETIMEDOUT - timed out waiting for interrupt status |
|
|
|
|
|
*/ |
|
|
|
|
|
static int ufs_wait_for_int_status(const uint32_t expected_status, |
|
|
|
|
|
unsigned int timeout_ms, |
|
|
|
|
|
bool ignore_linereset) |
|
|
|
|
|
{ |
|
|
|
|
|
uint32_t interrupt_status, interrupts_enabled; |
|
|
|
|
|
int result = 0; |
|
|
|
|
|
|
|
|
|
|
|
interrupts_enabled = mmio_read_32(ufs_params.reg_base + IE); |
|
|
|
|
|
do { |
|
|
|
|
|
interrupt_status = mmio_read_32(ufs_params.reg_base + IS) & interrupts_enabled; |
|
|
|
|
|
if (interrupt_status & UFS_INT_ERR) { |
|
|
|
|
|
mmio_write_32(ufs_params.reg_base + IS, interrupt_status & UFS_INT_ERR); |
|
|
|
|
|
result = ufs_error_handler(interrupt_status, ignore_linereset); |
|
|
|
|
|
if (result != 0) { |
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (interrupt_status & expected_status) { |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
mdelay(1); |
|
|
|
|
|
} while (timeout_ms-- > 0); |
|
|
|
|
|
|
|
|
|
|
|
if (!(interrupt_status & expected_status)) { |
|
|
|
|
|
return -ETIMEDOUT; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
mmio_write_32(ufs_params.reg_base + IS, expected_status); |
|
|
|
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd) |
|
|
int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd) |
|
|
{ |
|
|
{ |
|
|
unsigned int data; |
|
|
unsigned int data; |
|
|
|
|
|
int result; |
|
|
|
|
|
|
|
|
if (base == 0 || cmd == NULL) |
|
|
if (base == 0 || cmd == NULL) |
|
|
return -EINVAL; |
|
|
return -EINVAL; |
|
@ -46,10 +175,12 @@ int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd) |
|
|
mmio_write_32(base + UCMDARG3, cmd->arg3); |
|
|
mmio_write_32(base + UCMDARG3, cmd->arg3); |
|
|
mmio_write_32(base + UICCMD, cmd->op); |
|
|
mmio_write_32(base + UICCMD, cmd->op); |
|
|
|
|
|
|
|
|
do { |
|
|
result = ufs_wait_for_int_status(UFS_INT_UCCS, UIC_CMD_TIMEOUT_MS, |
|
|
data = mmio_read_32(base + IS); |
|
|
cmd->op == DME_SET); |
|
|
} while ((data & UFS_INT_UCCS) == 0); |
|
|
if (result != 0) { |
|
|
mmio_write_32(base + IS, UFS_INT_UCCS); |
|
|
return result; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK; |
|
|
return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -83,9 +214,10 @@ int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val) |
|
|
result = ufshc_send_uic_cmd(base, &cmd); |
|
|
result = ufshc_send_uic_cmd(base, &cmd); |
|
|
if (result == 0) |
|
|
if (result == 0) |
|
|
break; |
|
|
break; |
|
|
data = mmio_read_32(base + IS); |
|
|
/* -EIO requires UFS re-init */ |
|
|
if (data & UFS_INT_UE) |
|
|
if (result == -EIO) { |
|
|
return -EINVAL; |
|
|
return result; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
if (retries >= UFS_UIC_COMMAND_RETRIES) |
|
|
if (retries >= UFS_UIC_COMMAND_RETRIES) |
|
|
return -EIO; |
|
|
return -EIO; |
|
@ -97,7 +229,6 @@ int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val) |
|
|
int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val) |
|
|
int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val) |
|
|
{ |
|
|
{ |
|
|
uintptr_t base; |
|
|
uintptr_t base; |
|
|
unsigned int data; |
|
|
|
|
|
int result, retries; |
|
|
int result, retries; |
|
|
uic_cmd_t cmd; |
|
|
uic_cmd_t cmd; |
|
|
|
|
|
|
|
@ -113,9 +244,10 @@ int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val) |
|
|
result = ufshc_send_uic_cmd(base, &cmd); |
|
|
result = ufshc_send_uic_cmd(base, &cmd); |
|
|
if (result == 0) |
|
|
if (result == 0) |
|
|
break; |
|
|
break; |
|
|
data = mmio_read_32(base + IS); |
|
|
/* -EIO requires UFS re-init */ |
|
|
if (data & UFS_INT_UE) |
|
|
if (result == -EIO) { |
|
|
return -EINVAL; |
|
|
return result; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
if (retries >= UFS_UIC_COMMAND_RETRIES) |
|
|
if (retries >= UFS_UIC_COMMAND_RETRIES) |
|
|
return -EIO; |
|
|
return -EIO; |
|
@ -193,9 +325,10 @@ static int ufshc_reset(uintptr_t base) |
|
|
return -EIO; |
|
|
return -EIO; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* Enable Interrupts */ |
|
|
/* Enable UIC Interrupts alone. We can ignore other interrupts until
|
|
|
data = UFS_INT_UCCS | UFS_INT_ULSS | UFS_INT_UE | UFS_INT_UTPES | |
|
|
* link is up as there might be spurious error interrupts during link-up |
|
|
UFS_INT_DFES | UFS_INT_HCFES | UFS_INT_SBFES; |
|
|
*/ |
|
|
|
|
|
data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS; |
|
|
mmio_write_32(base + IE, data); |
|
|
mmio_write_32(base + IE, data); |
|
|
|
|
|
|
|
|
return 0; |
|
|
return 0; |
|
@ -229,6 +362,13 @@ static int ufshc_link_startup(uintptr_t base) |
|
|
data = mmio_read_32(base + IS); |
|
|
data = mmio_read_32(base + IS); |
|
|
if (data & UFS_INT_ULSS) |
|
|
if (data & UFS_INT_ULSS) |
|
|
mmio_write_32(base + IS, UFS_INT_ULSS); |
|
|
mmio_write_32(base + IS, UFS_INT_ULSS); |
|
|
|
|
|
|
|
|
|
|
|
/* clear UE set due to line-reset */ |
|
|
|
|
|
if (data & UFS_INT_UE) { |
|
|
|
|
|
mmio_write_32(base + IS, UFS_INT_UE); |
|
|
|
|
|
} |
|
|
|
|
|
/* clearing line-reset, UECPA is cleared on read */ |
|
|
|
|
|
mmio_read_32(base + UECPA); |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
return -EIO; |
|
|
return -EIO; |
|
@ -472,21 +612,22 @@ static void ufs_send_request(int task_tag) |
|
|
mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1 << slot); |
|
|
mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1 << slot); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static int ufs_check_resp(utp_utrd_t *utrd, int trans_type) |
|
|
static int ufs_check_resp(utp_utrd_t *utrd, int trans_type, unsigned int timeout_ms) |
|
|
{ |
|
|
{ |
|
|
utrd_header_t *hd; |
|
|
utrd_header_t *hd; |
|
|
resp_upiu_t *resp; |
|
|
resp_upiu_t *resp; |
|
|
sense_data_t *sense; |
|
|
sense_data_t *sense; |
|
|
unsigned int data; |
|
|
unsigned int data; |
|
|
int slot; |
|
|
int slot, result; |
|
|
|
|
|
|
|
|
hd = (utrd_header_t *)utrd->header; |
|
|
hd = (utrd_header_t *)utrd->header; |
|
|
resp = (resp_upiu_t *)utrd->resp_upiu; |
|
|
resp = (resp_upiu_t *)utrd->resp_upiu; |
|
|
do { |
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + IS); |
|
|
result = ufs_wait_for_int_status(UFS_INT_UTRCS, timeout_ms, false); |
|
|
if ((data & ~(UFS_INT_UCCS | UFS_INT_UTRCS)) != 0) |
|
|
if (result != 0) { |
|
|
return -EIO; |
|
|
return result; |
|
|
} while ((data & UFS_INT_UTRCS) == 0); |
|
|
} |
|
|
|
|
|
|
|
|
slot = utrd->task_tag - 1; |
|
|
slot = utrd->task_tag - 1; |
|
|
|
|
|
|
|
|
data = mmio_read_32(ufs_params.reg_base + UTRLDBR); |
|
|
data = mmio_read_32(ufs_params.reg_base + UTRLDBR); |
|
@ -510,6 +651,7 @@ static int ufs_check_resp(utp_utrd_t *utrd, int trans_type) |
|
|
|
|
|
|
|
|
(void)resp; |
|
|
(void)resp; |
|
|
(void)slot; |
|
|
(void)slot; |
|
|
|
|
|
(void)data; |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -523,7 +665,7 @@ static void ufs_send_cmd(utp_utrd_t *utrd, uint8_t cmd_op, uint8_t lun, int lba, |
|
|
result = ufs_prepare_cmd(utrd, cmd_op, lun, lba, buf, length); |
|
|
result = ufs_prepare_cmd(utrd, cmd_op, lun, lba, buf, length); |
|
|
assert(result == 0); |
|
|
assert(result == 0); |
|
|
ufs_send_request(utrd->task_tag); |
|
|
ufs_send_request(utrd->task_tag); |
|
|
result = ufs_check_resp(utrd, RESPONSE_UPIU); |
|
|
result = ufs_check_resp(utrd, RESPONSE_UPIU, CMD_TIMEOUT_MS); |
|
|
if (result == 0 || result == -EIO) { |
|
|
if (result == 0 || result == -EIO) { |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
@ -574,7 +716,7 @@ static void ufs_verify_init(void) |
|
|
get_utrd(&utrd); |
|
|
get_utrd(&utrd); |
|
|
ufs_prepare_nop_out(&utrd); |
|
|
ufs_prepare_nop_out(&utrd); |
|
|
ufs_send_request(utrd.task_tag); |
|
|
ufs_send_request(utrd.task_tag); |
|
|
result = ufs_check_resp(&utrd, NOP_IN_UPIU); |
|
|
result = ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS); |
|
|
assert(result == 0); |
|
|
assert(result == 0); |
|
|
(void)result; |
|
|
(void)result; |
|
|
} |
|
|
} |
|
@ -607,7 +749,7 @@ static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel, |
|
|
get_utrd(&utrd); |
|
|
get_utrd(&utrd); |
|
|
ufs_prepare_query(&utrd, op, idn, index, sel, buf, size); |
|
|
ufs_prepare_query(&utrd, op, idn, index, sel, buf, size); |
|
|
ufs_send_request(utrd.task_tag); |
|
|
ufs_send_request(utrd.task_tag); |
|
|
result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU); |
|
|
result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU, QUERY_REQ_TIMEOUT_MS); |
|
|
assert(result == 0); |
|
|
assert(result == 0); |
|
|
resp = (query_resp_upiu_t *)utrd.resp_upiu; |
|
|
resp = (query_resp_upiu_t *)utrd.resp_upiu; |
|
|
#ifdef UFS_RESP_DEBUG |
|
|
#ifdef UFS_RESP_DEBUG |
|
@ -895,6 +1037,11 @@ int ufs_init(const ufs_ops_t *ops, ufs_params_t *params) |
|
|
result = ufshc_link_startup(ufs_params.reg_base); |
|
|
result = ufshc_link_startup(ufs_params.reg_base); |
|
|
assert(result == 0); |
|
|
assert(result == 0); |
|
|
|
|
|
|
|
|
|
|
|
/* enable all interrupts */ |
|
|
|
|
|
data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS; |
|
|
|
|
|
data |= UFS_INT_UTRCS | UFS_INT_ERR; |
|
|
|
|
|
mmio_write_32(ufs_params.reg_base + IE, data); |
|
|
|
|
|
|
|
|
ufs_enum(); |
|
|
ufs_enum(); |
|
|
|
|
|
|
|
|
ufs_get_device_info(&card); |
|
|
ufs_get_device_info(&card); |
|
|