18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * bsg endpoint that supports UPIUs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 Western Digital Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include "ufs_bsg.h" 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cistatic int ufs_bsg_get_query_desc_size(struct ufs_hba *hba, int *desc_len, 108c2ecf20Sopenharmony_ci struct utp_upiu_query *qr) 118c2ecf20Sopenharmony_ci{ 128c2ecf20Sopenharmony_ci int desc_size = be16_to_cpu(qr->length); 138c2ecf20Sopenharmony_ci int desc_id = qr->idn; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci if (desc_size <= 0) 168c2ecf20Sopenharmony_ci return -EINVAL; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci ufshcd_map_desc_id_to_length(hba, desc_id, desc_len); 198c2ecf20Sopenharmony_ci if (!*desc_len) 208c2ecf20Sopenharmony_ci return -EINVAL; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci *desc_len = min_t(int, *desc_len, desc_size); 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci return 0; 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int ufs_bsg_verify_query_size(struct ufs_hba *hba, 288c2ecf20Sopenharmony_ci unsigned int request_len, 298c2ecf20Sopenharmony_ci unsigned int reply_len) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int min_req_len = sizeof(struct ufs_bsg_request); 328c2ecf20Sopenharmony_ci int min_rsp_len = sizeof(struct ufs_bsg_reply); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (min_req_len > request_len || min_rsp_len > reply_len) { 358c2ecf20Sopenharmony_ci dev_err(hba->dev, "not enough space assigned\n"); 368c2ecf20Sopenharmony_ci return -EINVAL; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return 0; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int ufs_bsg_alloc_desc_buffer(struct ufs_hba *hba, struct bsg_job *job, 438c2ecf20Sopenharmony_ci uint8_t **desc_buff, int *desc_len, 448c2ecf20Sopenharmony_ci enum query_opcode desc_op) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci struct ufs_bsg_request *bsg_request = job->request; 478c2ecf20Sopenharmony_ci struct utp_upiu_query *qr; 488c2ecf20Sopenharmony_ci u8 *descp; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (desc_op != UPIU_QUERY_OPCODE_WRITE_DESC && 518c2ecf20Sopenharmony_ci desc_op != UPIU_QUERY_OPCODE_READ_DESC) 528c2ecf20Sopenharmony_ci goto out; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci qr = &bsg_request->upiu_req.qr; 558c2ecf20Sopenharmony_ci if (ufs_bsg_get_query_desc_size(hba, desc_len, qr)) { 568c2ecf20Sopenharmony_ci dev_err(hba->dev, "Illegal desc size\n"); 578c2ecf20Sopenharmony_ci return -EINVAL; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (*desc_len > job->request_payload.payload_len) { 618c2ecf20Sopenharmony_ci dev_err(hba->dev, "Illegal desc size\n"); 628c2ecf20Sopenharmony_ci return -EINVAL; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci descp = kzalloc(*desc_len, GFP_KERNEL); 668c2ecf20Sopenharmony_ci if (!descp) 678c2ecf20Sopenharmony_ci return -ENOMEM; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (desc_op == UPIU_QUERY_OPCODE_WRITE_DESC) 708c2ecf20Sopenharmony_ci sg_copy_to_buffer(job->request_payload.sg_list, 718c2ecf20Sopenharmony_ci job->request_payload.sg_cnt, descp, 728c2ecf20Sopenharmony_ci *desc_len); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci *desc_buff = descp; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ciout: 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int ufs_bsg_request(struct bsg_job *job) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct ufs_bsg_request *bsg_request = job->request; 838c2ecf20Sopenharmony_ci struct ufs_bsg_reply *bsg_reply = job->reply; 848c2ecf20Sopenharmony_ci struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent)); 858c2ecf20Sopenharmony_ci unsigned int req_len = job->request_len; 868c2ecf20Sopenharmony_ci unsigned int reply_len = job->reply_len; 878c2ecf20Sopenharmony_ci struct uic_command uc = {}; 888c2ecf20Sopenharmony_ci int msgcode; 898c2ecf20Sopenharmony_ci uint8_t *desc_buff = NULL; 908c2ecf20Sopenharmony_ci int desc_len = 0; 918c2ecf20Sopenharmony_ci enum query_opcode desc_op = UPIU_QUERY_OPCODE_NOP; 928c2ecf20Sopenharmony_ci int ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci ret = ufs_bsg_verify_query_size(hba, req_len, reply_len); 958c2ecf20Sopenharmony_ci if (ret) 968c2ecf20Sopenharmony_ci goto out; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci bsg_reply->reply_payload_rcv_len = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci pm_runtime_get_sync(hba->dev); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci msgcode = bsg_request->msgcode; 1038c2ecf20Sopenharmony_ci switch (msgcode) { 1048c2ecf20Sopenharmony_ci case UPIU_TRANSACTION_QUERY_REQ: 1058c2ecf20Sopenharmony_ci desc_op = bsg_request->upiu_req.qr.opcode; 1068c2ecf20Sopenharmony_ci ret = ufs_bsg_alloc_desc_buffer(hba, job, &desc_buff, 1078c2ecf20Sopenharmony_ci &desc_len, desc_op); 1088c2ecf20Sopenharmony_ci if (ret) { 1098c2ecf20Sopenharmony_ci pm_runtime_put_sync(hba->dev); 1108c2ecf20Sopenharmony_ci goto out; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci fallthrough; 1148c2ecf20Sopenharmony_ci case UPIU_TRANSACTION_NOP_OUT: 1158c2ecf20Sopenharmony_ci case UPIU_TRANSACTION_TASK_REQ: 1168c2ecf20Sopenharmony_ci ret = ufshcd_exec_raw_upiu_cmd(hba, &bsg_request->upiu_req, 1178c2ecf20Sopenharmony_ci &bsg_reply->upiu_rsp, msgcode, 1188c2ecf20Sopenharmony_ci desc_buff, &desc_len, desc_op); 1198c2ecf20Sopenharmony_ci if (ret) 1208c2ecf20Sopenharmony_ci dev_err(hba->dev, 1218c2ecf20Sopenharmony_ci "exe raw upiu: error code %d\n", ret); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case UPIU_TRANSACTION_UIC_CMD: 1258c2ecf20Sopenharmony_ci memcpy(&uc, &bsg_request->upiu_req.uc, UIC_CMD_SIZE); 1268c2ecf20Sopenharmony_ci ret = ufshcd_send_uic_cmd(hba, &uc); 1278c2ecf20Sopenharmony_ci if (ret) 1288c2ecf20Sopenharmony_ci dev_err(hba->dev, 1298c2ecf20Sopenharmony_ci "send uic cmd: error code %d\n", ret); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci memcpy(&bsg_reply->upiu_rsp.uc, &uc, UIC_CMD_SIZE); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci break; 1348c2ecf20Sopenharmony_ci default: 1358c2ecf20Sopenharmony_ci ret = -ENOTSUPP; 1368c2ecf20Sopenharmony_ci dev_err(hba->dev, "unsupported msgcode 0x%x\n", msgcode); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci break; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci pm_runtime_put_sync(hba->dev); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (!desc_buff) 1448c2ecf20Sopenharmony_ci goto out; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (desc_op == UPIU_QUERY_OPCODE_READ_DESC && desc_len) 1478c2ecf20Sopenharmony_ci bsg_reply->reply_payload_rcv_len = 1488c2ecf20Sopenharmony_ci sg_copy_from_buffer(job->request_payload.sg_list, 1498c2ecf20Sopenharmony_ci job->request_payload.sg_cnt, 1508c2ecf20Sopenharmony_ci desc_buff, desc_len); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci kfree(desc_buff); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciout: 1558c2ecf20Sopenharmony_ci bsg_reply->result = ret; 1568c2ecf20Sopenharmony_ci job->reply_len = sizeof(struct ufs_bsg_reply); 1578c2ecf20Sopenharmony_ci /* complete the job here only if no error */ 1588c2ecf20Sopenharmony_ci if (ret == 0) 1598c2ecf20Sopenharmony_ci bsg_job_done(job, ret, bsg_reply->reply_payload_rcv_len); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/** 1658c2ecf20Sopenharmony_ci * ufs_bsg_remove - detach and remove the added ufs-bsg node 1668c2ecf20Sopenharmony_ci * @hba: per adapter object 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * Should be called when unloading the driver. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_civoid ufs_bsg_remove(struct ufs_hba *hba) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct device *bsg_dev = &hba->bsg_dev; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (!hba->bsg_queue) 1758c2ecf20Sopenharmony_ci return; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci bsg_remove_queue(hba->bsg_queue); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci device_del(bsg_dev); 1808c2ecf20Sopenharmony_ci put_device(bsg_dev); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic inline void ufs_bsg_node_release(struct device *dev) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci put_device(dev->parent); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/** 1898c2ecf20Sopenharmony_ci * ufs_bsg_probe - Add ufs bsg device node 1908c2ecf20Sopenharmony_ci * @hba: per adapter object 1918c2ecf20Sopenharmony_ci * 1928c2ecf20Sopenharmony_ci * Called during initial loading of the driver, and before scsi_scan_host. 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ciint ufs_bsg_probe(struct ufs_hba *hba) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct device *bsg_dev = &hba->bsg_dev; 1978c2ecf20Sopenharmony_ci struct Scsi_Host *shost = hba->host; 1988c2ecf20Sopenharmony_ci struct device *parent = &shost->shost_gendev; 1998c2ecf20Sopenharmony_ci struct request_queue *q; 2008c2ecf20Sopenharmony_ci int ret; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci device_initialize(bsg_dev); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci bsg_dev->parent = get_device(parent); 2058c2ecf20Sopenharmony_ci bsg_dev->release = ufs_bsg_node_release; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci dev_set_name(bsg_dev, "ufs-bsg%u", shost->host_no); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci ret = device_add(bsg_dev); 2108c2ecf20Sopenharmony_ci if (ret) 2118c2ecf20Sopenharmony_ci goto out; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci q = bsg_setup_queue(bsg_dev, dev_name(bsg_dev), ufs_bsg_request, NULL, 0); 2148c2ecf20Sopenharmony_ci if (IS_ERR(q)) { 2158c2ecf20Sopenharmony_ci ret = PTR_ERR(q); 2168c2ecf20Sopenharmony_ci goto out; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci hba->bsg_queue = q; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciout: 2248c2ecf20Sopenharmony_ci dev_err(bsg_dev, "fail to initialize a bsg dev %d\n", shost->host_no); 2258c2ecf20Sopenharmony_ci put_device(bsg_dev); 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci} 228