From: Michał Potomski <michalx.potomski@xxxxxxxxx> Based on unmerged patch: https://patchwork.kernel.org/patch/6435721/ This patch introduces ioctl() API using SCSI ioctl() API for user-space in preparation to wider usage. We came to a conclusion, that ioctl() interface is more convenient and readable for features, that we've planned to add to the driver in near future, namely UFS 2.1, e.g. Crypto Engine configuration. This level of simplicity and flexibility is simply not achievable with SysFS interface as its attributes shall contain only simple values without mixing types. This patch introduces new config SCSI_UFSHCD_IOCTL This config flag enables/disables this API by designing whether it should be compiled into UFS driver on which it lays. Inherited from base: - ioctl() API for UFS Changes from base: - Support for Query Write operations - UFS ioctl() is now a separate and optional Signed-off-by: Michał Potomski <michalx.potomski@xxxxxxxxx> Signed-off-by: Szymon Mielczarek <szymonx.mielczarek@xxxxxxxxx> --- Documentation/scsi/ufs.txt | 49 ++++++- drivers/scsi/ufs/Kconfig | 12 ++ drivers/scsi/ufs/Makefile | 5 +- drivers/scsi/ufs/ufs.h | 53 ++----- drivers/scsi/ufs/ufshcd-ioctl.c | 310 ++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd-ioctl.h | 32 +++++ drivers/scsi/ufs/ufshcd.c | 45 ++++-- drivers/scsi/ufs/ufshcd.h | 11 ++ include/scsi/scsi.h | 1 + include/uapi/scsi/Kbuild | 1 + include/uapi/scsi/ufs/Kbuild | 3 + include/uapi/scsi/ufs/ioctl.h | 61 ++++++++ include/uapi/scsi/ufs/ufs.h | 69 +++++++++ 13 files changed, 596 insertions(+), 56 deletions(-) create mode 100644 drivers/scsi/ufs/ufshcd-ioctl.c create mode 100644 drivers/scsi/ufs/ufshcd-ioctl.h create mode 100644 include/uapi/scsi/ufs/Kbuild create mode 100644 include/uapi/scsi/ufs/ioctl.h create mode 100644 include/uapi/scsi/ufs/ufs.h diff --git a/Documentation/scsi/ufs.txt b/Documentation/scsi/ufs.txt index 41a6164..7326974 100644 --- a/Documentation/scsi/ufs.txt +++ b/Documentation/scsi/ufs.txt @@ -15,6 +15,8 @@ Contents 3.2 UTP Transfer requests 3.3 UFS error handling 3.4 SCSI Error handling +4. UFSHCD User Interface + 4.1 UFS Query IOCTL 1. Overview @@ -125,9 +127,54 @@ The current UFSHCD implementation supports following functionality, SCSI Midlayer through .eh_abort_handler, .eh_device_reset_handler and .eh_host_reset_handler. -In this version of UFSHCD Query requests and power management +In this version of UFSHCD power management functionality are not implemented. + +4. UFSHCD User Interface +------------------ + +Current impementation of UFSHCD enables user to communicate with +host and device using IOCTL interface. + +4.1 UFS Query IOCTL + + This interface enables user to get information about UFS Device + and set some of its configurable properties. You can use following + snippet to communicate with this interface: + + #include <sys/ioctl.h> + #include <scsi/ufs/ioctl.h> + #include <scsi/ufs/ufs.h> + + static int handleQuery(int fd, uint8_t opcode, uint8_t idn, + uint8_t buf_size, uint8_t *buffer) + { + ufs_ioctl_query_data query_data; + + /* Operation code (check <scsi/ufs/ufs.h>) */ + query_data.opcode = opcode; + + /* Query ID (check <scsi/ufs/ufs.h>) */ + query_data.idn = idn; + + /* + * Pointer to allocated memory. Filled with proper data if + * [opcode] determines write operation. + */ + query_data.buffer = buffer; + + /* Size of allocated memory */ + query_data.buf_size = buf_size; + + /* [fd] used here shall be opened UFS device */ + return ioctl(fd, UFS_IOCTL_QUERY, &query_data); + } + + This enables user to control some of the UFS specific features inaccessible + in other way. + + UFS Specifications can be found at, UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index e27b4d4..7c8618c 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig @@ -61,6 +61,18 @@ config SCSI_UFSHCD_PCI If unsure, say N. +config SCSI_UFSHCD_IOCTL + bool "Enable ioctl() Configuration Interface for this module" + depends on SCSI_UFSHCD + ---help--- + This enables UFS Configuration Interface for user-space. + Interface enables you to configure and read state of some + of the UFS features, which are inavailable in other way. + + If you intend to configure UFS Host/Device through it, say Y here. + + If unsure, say N. + config SCSI_UFS_DWC_TC_PCI tristate "DesignWare pci support using a G210 Test Chip" depends on SCSI_UFSHCD_PCI diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile index 6e77cb0..85bc418 100644 --- a/drivers/scsi/ufs/Makefile +++ b/drivers/scsi/ufs/Makefile @@ -1,7 +1,10 @@ # UFSHCD makefile +ufshcd-core-y := ufshcd.o +ufshcd-core-$(CONFIG_SCSI_UFSHCD_IOCTL) += ufshcd-ioctl.o + obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o -obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o +obj-$(CONFIG_SCSI_UFSHCD) += ufshcd-core.o obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h index 54deeb7..06da302 100644 --- a/drivers/scsi/ufs/ufs.h +++ b/drivers/scsi/ufs/ufs.h @@ -38,6 +38,7 @@ #include <linux/mutex.h> #include <linux/types.h> +#include <scsi/ufs/ufs.h> #define MAX_CDB_SIZE 16 #define GENERAL_UPIU_REQUEST_SIZE 32 @@ -73,6 +74,16 @@ enum { UFS_UPIU_RPMB_WLUN = 0xC4, }; +/** + * ufs_is_valid_unit_desc_lun - checks if the given LUN has a unit descriptor + * @lun: LU number to check + * @return: true if the lun has a matching unit descriptor, false otherwise + */ +static inline bool ufs_is_valid_unit_desc_lun(u8 lun) +{ + return (lun == UFS_UPIU_RPMB_WLUN || (lun < UFS_UPIU_MAX_GENERAL_LUN)); +} + /* * UFS Protocol Information Unit related definitions */ @@ -128,35 +139,6 @@ enum { UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, }; -/* Flag idn for Query Requests*/ -enum flag_idn { - QUERY_FLAG_IDN_FDEVICEINIT = 0x01, - QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, - QUERY_FLAG_IDN_BKOPS_EN = 0x04, -}; - -/* Attribute idn for Query requests */ -enum attr_idn { - QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, - QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, - QUERY_ATTR_IDN_EE_CONTROL = 0x0D, - QUERY_ATTR_IDN_EE_STATUS = 0x0E, -}; - -/* Descriptor idn for Query requests */ -enum desc_idn { - QUERY_DESC_IDN_DEVICE = 0x0, - QUERY_DESC_IDN_CONFIGURATION = 0x1, - QUERY_DESC_IDN_UNIT = 0x2, - QUERY_DESC_IDN_RFU_0 = 0x3, - QUERY_DESC_IDN_INTERCONNECT = 0x4, - QUERY_DESC_IDN_STRING = 0x5, - QUERY_DESC_IDN_RFU_1 = 0x6, - QUERY_DESC_IDN_GEOMETRY = 0x7, - QUERY_DESC_IDN_POWER = 0x8, - QUERY_DESC_IDN_MAX, -}; - enum desc_header_offset { QUERY_DESC_LENGTH_OFFSET = 0x00, QUERY_DESC_DESC_TYPE_OFFSET = 0x01, @@ -274,19 +256,6 @@ enum bkops_status { BKOPS_STATUS_MAX = BKOPS_STATUS_CRITICAL, }; -/* UTP QUERY Transaction Specific Fields OpCode */ -enum query_opcode { - UPIU_QUERY_OPCODE_NOP = 0x0, - UPIU_QUERY_OPCODE_READ_DESC = 0x1, - UPIU_QUERY_OPCODE_WRITE_DESC = 0x2, - UPIU_QUERY_OPCODE_READ_ATTR = 0x3, - UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4, - UPIU_QUERY_OPCODE_READ_FLAG = 0x5, - UPIU_QUERY_OPCODE_SET_FLAG = 0x6, - UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, - UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, -}; - /* Query response result code */ enum { QUERY_RESULT_SUCCESS = 0x00, diff --git a/drivers/scsi/ufs/ufshcd-ioctl.c b/drivers/scsi/ufs/ufshcd-ioctl.c new file mode 100644 index 0000000..ae70bf5 --- /dev/null +++ b/drivers/scsi/ufs/ufshcd-ioctl.c @@ -0,0 +1,310 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * UFS ioctl - this architecture can be used to configure driver and + * device/host parameters, that are otherwise unavailable for such operation + */ + +#include <scsi/ufs/ioctl.h> + +#include "ufshcd-ioctl.h" + +static int ufshcd_ioctl_query_desc(struct ufs_hba *hba, + struct ufs_ioctl_query_data *ioctl_data, + void *data_ptr, u8 lun, int length, bool write) +{ + int err = 0; + u8 index; + + /* LUN indexed descriptors */ + if (ioctl_data->idn == QUERY_DESC_IDN_UNIT) { + if (ufs_is_valid_unit_desc_lun(lun)) { + index = lun; + } else { + err = -EINVAL; + goto out; + } + /* LUN independent descriptors */ + } else if (ioctl_data->idn < QUERY_DESC_IDN_MAX) { + index = 0; + /* Invalid descriptors */ + } else { + err = -EINVAL; + goto out; + } + + err = ufshcd_query_descriptor(hba, ioctl_data->opcode, + ioctl_data->idn, index, 0, data_ptr, &length); + + if (!err && !write) + ioctl_data->buf_size = + min_t(int, ioctl_data->buf_size, length); + else + ioctl_data->buf_size = 0; + +out: + if (err) + dev_err(hba->dev, "Query Descriptor failed (error: %d)", err); + + return err; +} + +static int ufshcd_ioctl_query_attr(struct ufs_hba *hba, + struct ufs_ioctl_query_data *ioctl_data, + void *data_ptr, u8 lun, bool write) +{ + int err = 0; + u8 index; + + /* LUN indexed attributes */ + if (ioctl_data->idn == QUERY_ATTR_IDN_DYN_CAP_NEEDED || + ioctl_data->idn == QUERY_ATTR_IDN_PRG_BLK_NUM) { + if (ufs_is_valid_unit_desc_lun(lun)) { + index = lun; + } else { + err = -EINVAL; + goto out; + } + /* LUN independent attributes */ + } else if (ioctl_data->idn < QUERY_ATTR_IDN_MAX) { + index = 0; + /* Invalid attribiutes */ + } else { + err = -EINVAL; + goto out; + } + + err = ufshcd_query_attr(hba, ioctl_data->opcode, + ioctl_data->idn, index, 0, data_ptr); + + if (!err && !write) + ioctl_data->buf_size = + min_t(int, ioctl_data->buf_size, sizeof(u32)); + else + ioctl_data->buf_size = 0; + +out: + if (err) + dev_err(hba->dev, "Query Attribute failed (error: %d)", err); + + return err; +} + +static int ufshcd_ioctl_query_flag(struct ufs_hba *hba, + struct ufs_ioctl_query_data *ioctl_data, void *data_ptr, + bool write) +{ + int err = 0; + + /* + * Some flags are added to reserved space between flags in + * more or less recent UFS Specs. If it's reserved for current + * device, we will get an R/W error during operation and return it. + */ + if (ioctl_data->idn >= QUERY_FLAG_IDN_MAX) { + err = -EINVAL; + goto out; + } + + err = ufshcd_query_flag(hba, ioctl_data->opcode, + ioctl_data->idn, data_ptr); + + if (!err && !write) + ioctl_data->buf_size = + min_t(int, ioctl_data->buf_size, sizeof(bool)); + else + ioctl_data->buf_size = 0; + +out: + if (err) + dev_err(hba->dev, "Query Flag failed (error: %d)", err); + + return err; +} + +/** + * ufshcd_query_ioctl - perform user read queries + * @hba: per-adapter instance + * @lun: used for lun specific queries + * @buffer: user space buffer for reading and submitting query data and params + * + * Returns 0 for success or negative error code otherwise + * + * Expected/Submitted buffer structure is struct ufs_ioctl_query_data. + * It will read the opcode, idn and buf_length parameters, and put the + * response in the buffer field while updating the used size in buf_length. + */ +static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer) +{ + /* + * ioctl_data->buffer is untouchable - it's IO user data ptr. + * + * We might and will copy reply to it or request from it, but it's + * still pointer to user-space data, so treat it as __user argument. + */ + struct ufs_ioctl_query_data *ioctl_data; + int err = 0; + void *data_ptr = NULL; + int length = 0; + size_t data_size = sizeof(*ioctl_data); + bool write = false; + + if (!buffer) + return -EINVAL; + + /* Prepare space in kernel for users request */ + ioctl_data = kzalloc(data_size, GFP_KERNEL); + + if (!ioctl_data) { + err = -ENOMEM; + goto out; + } + + /* extract params from user buffer */ + if (copy_from_user(ioctl_data, buffer, data_size)) { + err = -EFAULT; + goto out_release_mem; + } + + if (!ioctl_data->buf_size) { + /* Noting to do */ + err = 0; + goto out_release_mem; + } + + switch (ioctl_data->opcode) { + case UPIU_QUERY_OPCODE_WRITE_DESC: + write = true; + case UPIU_QUERY_OPCODE_READ_DESC: + ufshcd_map_desc_id_to_length(hba, ioctl_data->idn, &length); + break; + case UPIU_QUERY_OPCODE_WRITE_ATTR: + write = true; + case UPIU_QUERY_OPCODE_READ_ATTR: + length = sizeof(u32); + break; + case UPIU_QUERY_OPCODE_SET_FLAG: + case UPIU_QUERY_OPCODE_CLEAR_FLAG: + case UPIU_QUERY_OPCODE_TOGGLE_FLAG: + write = true; + case UPIU_QUERY_OPCODE_READ_FLAG: + length = sizeof(bool); + break; + default: + length = 0; + break; + } + + if (!length) { + err = -EINVAL; + goto out_release_mem; + } + + /* We have to allocate memory at this level */ + data_ptr = kzalloc(length, GFP_KERNEL); + + if (!data_ptr) { + err = -ENOMEM; + goto out_release_mem; + } + + if (write) { + length = min_t(int, length, ioctl_data->buf_size); + err = copy_from_user(data_ptr, ioctl_data->buffer, length); + + if (err) + goto out_release_mem; + } + + /* Verify legal parameters & send query */ + switch (ioctl_data->opcode) { + case UPIU_QUERY_OPCODE_WRITE_DESC: + case UPIU_QUERY_OPCODE_READ_DESC: + err = ufshcd_ioctl_query_desc(hba, ioctl_data, data_ptr, + lun, length, write); + break; + case UPIU_QUERY_OPCODE_WRITE_ATTR: + case UPIU_QUERY_OPCODE_READ_ATTR: + err = ufshcd_ioctl_query_attr(hba, ioctl_data, data_ptr, + lun, write); + break; + case UPIU_QUERY_OPCODE_SET_FLAG: + case UPIU_QUERY_OPCODE_CLEAR_FLAG: + case UPIU_QUERY_OPCODE_TOGGLE_FLAG: + case UPIU_QUERY_OPCODE_READ_FLAG: + err = ufshcd_ioctl_query_flag(hba, ioctl_data, + data_ptr, write); + break; + default: + err = -EINVAL; + goto out_release_mem; + } + + if (err) + goto out_release_mem; + + /* Copy basic data to user */ + err = copy_to_user(buffer, ioctl_data, data_size); + + /* + * We copy result of query to ptr copied + * from user-space in the beginning + * if there is anything to be copied to user. + */ + if (!err && ioctl_data->buf_size) + err = copy_to_user(ioctl_data->buffer, data_ptr, + ioctl_data->buf_size); + +out_release_mem: + kfree(ioctl_data); + kfree(data_ptr); +out: + if (err) + dev_err(hba->dev, "User Query failed (error: %d)", err); + + return err; +} + +/** + * ufshcd_ioctl - ufs ioctl callback registered in scsi_host + * @dev: scsi device required for per LUN queries + * @cmd: command opcode + * @buffer: user space buffer for transferring data + * + * Supported commands: + * UFS_IOCTL_QUERY + */ +int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer) +{ + struct ufs_hba *hba = shost_priv(dev->host); + int err = 0; + + if (WARN_ON(!hba)) + return -ENODEV; + + switch (cmd) { + case UFS_IOCTL_QUERY: + pm_runtime_get_sync(hba->dev); + err = ufshcd_query_ioctl(hba, ufshcd_scsi_to_upiu_lun(dev->lun), + buffer); + pm_runtime_put_sync(hba->dev); + break; + default: + err = -EOPNOTSUPP; + break; + } + + if (err) + dev_err(hba->dev, "UFS ioctl() failed (cmd=%04x error: %d)", + cmd, err); + + return err; +} diff --git a/drivers/scsi/ufs/ufshcd-ioctl.h b/drivers/scsi/ufs/ufshcd-ioctl.h new file mode 100644 index 0000000..c86257d --- /dev/null +++ b/drivers/scsi/ufs/ufshcd-ioctl.h @@ -0,0 +1,32 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * UFS ioctl - this architecture can be used to configure driver and + * device/host parameters, that are otherwise unavailable for such operation + */ + +#ifndef _UFSHCD_IOCTL_H +#define _UFSHCD_IOCTL_H + +#include <linux/errno.h> + +#include "ufshcd.h" + +#ifdef CONFIG_SCSI_UFSHCD_IOCTL +int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer); +#else +static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer) +{ + return -ENOIOCTLCMD; +} +#endif + +#endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index abc7e87..9f3a39d 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -41,9 +41,11 @@ #include <linux/devfreq.h> #include <linux/nls.h> #include <linux/of.h> + #include "ufshcd.h" #include "ufs_quirks.h" #include "unipro.h" +#include "ufshcd-ioctl.h" #define CREATE_TRACE_POINTS #include <trace/events/ufs.h> @@ -2244,7 +2246,7 @@ static int ufshcd_comp_scsi_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) * * Returns UPIU LUN id */ -static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) +u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) { if (scsi_is_wlun(scsi_lun)) return (scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID) @@ -2259,7 +2261,7 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) * * Returns SCSI W-LUN id */ -static inline u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id) +u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id) { return (upiu_wlun_id & ~UFS_UPIU_WLUN_ID) | SCSI_W_LUN_BASE; } @@ -2720,7 +2722,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, * * Returns 0 for success, non-zero in case of failure */ -static int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector, u32 *attr_val) { struct ufs_query_req *request = NULL; @@ -2810,7 +2812,22 @@ static int ufshcd_query_attr_retry(struct ufs_hba *hba, return ret; } -static int __ufshcd_query_descriptor(struct ufs_hba *hba, +/** + * ufshcd_query_descriptor - API function for sending descriptor + * requests + * hba: per-adapter instance + * opcode: attribute opcode + * idn: attribute idn to access + * index: index field + * selector: selector field + * desc_buf: the buffer that contains the descriptor + * buf_len: length parameter passed to the device + * + * Returns 0 for success, non-zero in case of failure. + * The buf_len parameter will contain, on return, the length parameter + * received on the response. + */ +int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode, enum desc_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len) { @@ -2875,8 +2892,8 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba, } /** - * ufshcd_query_descriptor_retry - API function for sending descriptor - * requests + * ufshcd_query_descriptor_retry - function for sending descriptor + * requests with retry mechanism * hba: per-adapter instance * opcode: attribute opcode * idn: attribute idn to access @@ -2890,16 +2907,16 @@ static int __ufshcd_query_descriptor(struct ufs_hba *hba, * received on the response. */ static int ufshcd_query_descriptor_retry(struct ufs_hba *hba, - enum query_opcode opcode, - enum desc_idn idn, u8 index, - u8 selector, - u8 *desc_buf, int *buf_len) + enum query_opcode opcode, + enum desc_idn idn, u8 index, + u8 selector, + u8 *desc_buf, int *buf_len) { int err; int retries; for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) { - err = __ufshcd_query_descriptor(hba, opcode, idn, index, + err = ufshcd_query_descriptor(hba, opcode, idn, index, selector, desc_buf, buf_len); if (!err || err == -EINVAL) break; @@ -3184,7 +3201,7 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba, * Unit descriptors are only available for general purpose LUs (LUN id * from 0 to 7) and RPMB Well known LU. */ - if (lun != UFS_UPIU_RPMB_WLUN && (lun >= UFS_UPIU_MAX_GENERAL_LUN)) + if (!ufs_is_valid_unit_desc_lun(lun)) return -EOPNOTSUPP; return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun, @@ -6502,6 +6519,10 @@ static enum blk_eh_timer_return ufshcd_eh_timed_out(struct scsi_cmnd *scmd) .eh_device_reset_handler = ufshcd_eh_device_reset_handler, .eh_host_reset_handler = ufshcd_eh_host_reset_handler, .eh_timed_out = ufshcd_eh_timed_out, + .ioctl = ufshcd_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ufshcd_ioctl, +#endif .this_id = -1, .sg_tablesize = SG_ALL, .cmd_per_lun = UFSHCD_CMD_PER_LUN, diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index cdc8bd0..bf8a670 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -841,6 +841,13 @@ static inline bool ufshcd_is_hs_mode(struct ufs_pa_layer_attr *pwr_info) /* Expose Query-Request API */ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode, enum flag_idn idn, bool *flag_res); + +int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode, + enum attr_idn idn, u8 index, u8 selector, u32 *attr_val); + +int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode, + enum desc_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len); + int ufshcd_hold(struct ufs_hba *hba, bool async); void ufshcd_release(struct ufs_hba *hba); @@ -849,6 +856,10 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba); +/* Expose API for obtaining UPIU/SCSI LUN */ +u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun); +u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id); + /* Wrapper functions for safely calling variant operations */ static inline const char *ufshcd_get_var_name(struct ufs_hba *hba) { diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index a1e1930..f128e9e 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -255,6 +255,7 @@ static inline int scsi_is_wlun(u64 lun) * Here are some scsi specific ioctl commands which are sometimes useful. * * Note that include/linux/cdrom.h also defines IOCTL 0x5300 - 0x5395 + * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 */ /* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */ diff --git a/include/uapi/scsi/Kbuild b/include/uapi/scsi/Kbuild index d791e0a..fad00e0 100644 --- a/include/uapi/scsi/Kbuild +++ b/include/uapi/scsi/Kbuild @@ -1,5 +1,6 @@ # UAPI Header export list header-y += fc/ +header-y += ufs/ header-y += scsi_bsg_fc.h header-y += scsi_netlink.h header-y += scsi_netlink_fc.h diff --git a/include/uapi/scsi/ufs/Kbuild b/include/uapi/scsi/ufs/Kbuild new file mode 100644 index 0000000..cc3ef20 --- /dev/null +++ b/include/uapi/scsi/ufs/Kbuild @@ -0,0 +1,3 @@ +# UAPI Header export list +header-y += ioctl.h +header-y += ufs.h diff --git a/include/uapi/scsi/ufs/ioctl.h b/include/uapi/scsi/ufs/ioctl.h new file mode 100644 index 0000000..1c65505 --- /dev/null +++ b/include/uapi/scsi/ufs/ioctl.h @@ -0,0 +1,61 @@ +#ifndef UAPI_UFS_IOCTL_H_ +#define UAPI_UFS_IOCTL_H_ + +#include <linux/types.h> + +/* + * IOCTL opcode for ufs queries has the following opcode after + * SCSI_IOCTL_GET_PCI + */ +#define UFS_IOCTL_QUERY 0x53A0 + +/** + * struct ufs_ioctl_query_data - used to transfer data to and from user via + * ioctl + * + * @opcode: type of data to query (descriptor/attribute/flag) + * @idn: id of the data structure + * @buf_size: number of allocated bytes/data size on return + * @buffer: data location + * + * Received: buffer and buf_size (available space for transferred data) + * Submitted: opcode, idn, length, buf_size + * Optionally submitted: buffer, buf_size (in Write operations) + */ +struct ufs_ioctl_query_data { + /* + * User should select one of the opcode defined in "enum query_opcode". + * Please check include/uapi/scsi/ufs/ufs.h for the definition of it. + * Note that only UPIU_QUERY_OPCODE_READ_DESC, + * UPIU_QUERY_OPCODE_READ_ATTR & UPIU_QUERY_OPCODE_READ_FLAG are + * supported as of now. All other query_opcode would be considered + * invalid. + * As of now only read query operations are supported. + */ + __u32 opcode; + /* + * User should select one of the idn from "enum flag_idn" or "enum + * attr_idn" or "enum desc_idn" based on whether opcode above is + * attribute, flag or descriptor. + * Please check include/uapi/scsi/ufs/ufs.h for the definition of it. + */ + __u8 idn; + /* + * User should specify the size of the buffer (buffer[0] below) where + * it wants to read the query data (attribute/flag/descriptor). + * As we might end up reading less data then what is specified in + * buf_size. So we are updating buf_size to what exactly we have read. + */ + __u16 buf_size; + /* + * Pointer to the the data buffer where kernel will copy + * the query data (attribute/flag/descriptor) read from the UFS device + * Note: + * For Read Descriptor you will have to allocate at the most 255 bytes + * For Read Attribute you will have to allocate 4 bytes + * For Read Flag you will have to allocate 1 byte + */ + __u8 *buffer; +}; + +#endif /* UAPI_UFS_IOCTL_H_ */ diff --git a/include/uapi/scsi/ufs/ufs.h b/include/uapi/scsi/ufs/ufs.h new file mode 100644 index 0000000..096ebea --- /dev/null +++ b/include/uapi/scsi/ufs/ufs.h @@ -0,0 +1,69 @@ +#ifndef UAPI_UFS_H_ +#define UAPI_UFS_H_ + +/* Flag idn for Query Requests*/ +enum flag_idn { + QUERY_FLAG_IDN_FDEVICEINIT = 0x01, + QUERY_FLAG_IDN_PERM_WP_EN = 0x02, + QUERY_FLAG_IDN_PWR_ON_WPE = 0x03, + QUERY_FLAG_IDN_BKOPS_EN = 0x04, + QUERY_FLAG_IDN_DEV_LIFESPAN_MOD_EN = 0x05, + QUERY_FLAG_IDN_PURGE_EN = 0x06, + QUERY_FLAG_IDN_PHY_RES_RM = 0x08, + QUERY_FLAG_IDN_BUSY_RTC = 0x09, + QUERY_FLAG_IDN_PERM_DIS_FW_UPD = 0x0B, + QUERY_FLAG_IDN_MAX, +}; + +/* Attribute idn for Query requests */ +enum attr_idn { + QUERY_ATTR_IDN_BOOT_LUN_EN = 0x00, + QUERY_ATTR_IDN_RESERVED = 0x01, + QUERY_ATTR_IDN_CUR_PWR_MODE = 0x02, + QUERY_ATTR_IDN_ACTIVE_ICC_LVL = 0x03, + QUERY_ATTR_IDN_OUT_OF_ORD_DATA = 0x04, + QUERY_ATTR_IDN_BKOPS_STATUS = 0x05, + QUERY_ATTR_IDN_PURGE_STATUS = 0x06, + QUERY_ATTR_IDN_MAX_DIN_SIZE = 0x07, + QUERY_ATTR_IDN_MAX_DOUT_SIZE = 0x08, + QUERY_ATTR_IDN_DYN_CAP_NEEDED = 0x09, + QUERY_ATTR_IDN_REF_CLK_FREQ = 0x0A, + QUERY_ATTR_IDN_CONF_DESC_LOCK = 0x0B, + QUERY_ATTR_IDN_MAX_NUM_RTT = 0x0C, + QUERY_ATTR_IDN_EE_CONTROL = 0x0D, + QUERY_ATTR_IDN_EE_STATUS = 0x0E, + QUERY_ATTR_IDN_SEC_PASSED = 0x0F, + QUERY_ATTR_IDN_CTX_CONF = 0x10, + QUERY_ATTR_IDN_PRG_BLK_NUM = 0x11, + QUERY_ATTR_IDN_MAX, +}; + +/* Descriptor idn for Query requests */ +enum desc_idn { + QUERY_DESC_IDN_DEVICE = 0x0, + QUERY_DESC_IDN_CONFIGURATION = 0x1, + QUERY_DESC_IDN_UNIT = 0x2, + QUERY_DESC_IDN_RFU_0 = 0x3, + QUERY_DESC_IDN_INTERCONNECT = 0x4, + QUERY_DESC_IDN_STRING = 0x5, + QUERY_DESC_IDN_RFU_1 = 0x6, + QUERY_DESC_IDN_GEOMETRY = 0x7, + QUERY_DESC_IDN_POWER = 0x8, + QUERY_DESC_IDN_DEV_HEALTH = 0x9, + QUERY_DESC_IDN_MAX, +}; + +/* UTP QUERY Transaction Specific Fields OpCode */ +enum query_opcode { + UPIU_QUERY_OPCODE_NOP = 0x0, + UPIU_QUERY_OPCODE_READ_DESC = 0x1, + UPIU_QUERY_OPCODE_WRITE_DESC = 0x2, + UPIU_QUERY_OPCODE_READ_ATTR = 0x3, + UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4, + UPIU_QUERY_OPCODE_READ_FLAG = 0x5, + UPIU_QUERY_OPCODE_SET_FLAG = 0x6, + UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7, + UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8, +}; + +#endif /* UAPI_UFS_H_ */ -- 1.9.1 -------------------------------------------------------------------- Intel Technology Poland sp. z o.o. ul. Slowackiego 173 | 80-298 Gdansk | Sad Rejonowy Gdansk Polnoc | VII Wydzial Gospodarczy Krajowego Rejestru Sadowego - KRS 101882 | NIP 957-07-52-316 | Kapital zakladowy 200.000 PLN. Ta wiadomosc wraz z zalacznikami jest przeznaczona dla okreslonego adresata i moze zawierac informacje poufne. W razie przypadkowego otrzymania tej wiadomosci, prosimy o powiadomienie nadawcy oraz trwale jej usuniecie; jakiekolwiek przegladanie lub rozpowszechnianie jest zabronione. This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). If you are not the intended recipient, please contact the sender and delete all copies; any review or distribution by others is strictly prohibited.