Add support to those uic commands, that are currently supported by ufshcd api: the variants of dme_{peer}_{set_get}. At this point better not to add any new api, as careless uic command may turn the device into a brick. Signed-off-by: Avri Altman <avri.altman@xxxxxxx> --- drivers/scsi/ufs/ufs_bsg.c | 54 +++++++++++++++++++++++++++++++++++++++++++++- drivers/scsi/ufs/ufs_bsg.h | 2 ++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/ufs/ufs_bsg.c b/drivers/scsi/ufs/ufs_bsg.c index c6c2ac7..65144ae 100644 --- a/drivers/scsi/ufs/ufs_bsg.c +++ b/drivers/scsi/ufs/ufs_bsg.c @@ -64,6 +64,53 @@ static int ufs_bsg_verify_query_size(unsigned int request_len, return 0; } +static int ufs_bsg_exec_uic_cmd(struct ufs_hba *hba, struct uic_command *uc) +{ + u32 attr_sel = uc->argument1; + u8 attr_set = (uc->argument2 >> 16) & 0xff; + u32 mib_val = uc->argument3; + int cmd = uc->command; + int ret = 0; + + switch (cmd) { + case UIC_CMD_DME_GET: + ret = ufshcd_dme_get_attr(hba, attr_sel, &mib_val, DME_LOCAL); + break; + case UIC_CMD_DME_SET: + ret = ufshcd_dme_set_attr(hba, attr_sel, attr_set, mib_val, + DME_LOCAL); + break; + case UIC_CMD_DME_PEER_GET: + ret = ufshcd_dme_get_attr(hba, attr_sel, &mib_val, DME_PEER); + break; + case UIC_CMD_DME_PEER_SET: + ret = ufshcd_dme_set_attr(hba, attr_sel, attr_set, mib_val, + DME_PEER); + break; + case UIC_CMD_DME_POWERON: + case UIC_CMD_DME_POWEROFF: + case UIC_CMD_DME_ENABLE: + case UIC_CMD_DME_RESET: + case UIC_CMD_DME_END_PT_RST: + case UIC_CMD_DME_LINK_STARTUP: + case UIC_CMD_DME_HIBER_ENTER: + case UIC_CMD_DME_HIBER_EXIT: + case UIC_CMD_DME_TEST_MODE: + ret = -ENOTSUPP; + pr_err("%s unsupported command 0x%x\n", __func__, cmd); + break; + default: + ret = -EINVAL; + } + + if (ret) + pr_err("%s error in command 0x%x\n", __func__, cmd); + + uc->argument3 = mib_val; + + return ret; +} + static int ufs_bsg_request(struct bsg_job *job) { struct ufs_bsg_request *bsg_request = job->request; @@ -72,6 +119,7 @@ static int ufs_bsg_request(struct bsg_job *job) unsigned int request_len = job->request_len; unsigned int reply_len = job->reply_len; struct utp_upiu_query *qr; + struct uic_command uc = {}; struct utp_upiu_req *req_upiu = NULL; struct utp_upiu_req *rsp_upiu = NULL; int msgcode; @@ -126,7 +174,11 @@ static int ufs_bsg_request(struct bsg_job *job) break; case UPIU_TRANSACTION_UIC_CMD: - /* later */ + memcpy(&uc, &bsg_request->tsf.uc, UIC_CMD_SIZE); + ret = ufs_bsg_exec_uic_cmd(hba, &uc); + memcpy(&bsg_reply->tsf.uc, &uc, UIC_CMD_SIZE); + + break; case UPIU_TRANSACTION_COMMAND: case UPIU_TRANSACTION_DATA_OUT: not_supported: diff --git a/drivers/scsi/ufs/ufs_bsg.h b/drivers/scsi/ufs/ufs_bsg.h index 4da1837..3373a25 100644 --- a/drivers/scsi/ufs/ufs_bsg.h +++ b/drivers/scsi/ufs/ufs_bsg.h @@ -14,6 +14,8 @@ #define UFS_BSG_NOP (-1) #define UPIU_TRANSACTION_UIC_CMD 0x1F +/* uic commands are 4DW long, per UFSHCI V2.1 paragraph 5.6.1 */ +#define UIC_CMD_SIZE (sizeof(u32) * 4) enum { REQ_UPIU_SIZE_DWORDS = 8, -- 1.9.1