@@ -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
@@ -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
@@ -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
@@ -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,
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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 */
@@ -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,
@@ -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)
{
@@ -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 */
@@ -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
new file mode 100644
@@ -0,0 +1,3 @@
+# UAPI Header export list
+header-y += ioctl.h
+header-y += ufs.h
new file mode 100644
@@ -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_ */
new file mode 100644
@@ -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_ */