@@ -17,6 +17,7 @@ Contents
3.4 SCSI Error handling
4. UFSHCD User Interface
4.1 UFS Query IOCTL
+ 4.2 UFS Auto-Hibern8 IOCTL
1. Overview
@@ -174,6 +175,52 @@ host and device using IOCTL interface.
This enables user to control some of the UFS specific features inaccessible
in other way.
+4.2 UFS Auto-Hibern8 IOCTL
+
+ This interface enables user to get/set information about UFS Host
+ Auto-Hibern8 feature for capable hosts. You can use following snippet
+ to comunicate with this interface:
+
+ #include <sys/ioctl.h>
+ #include <scsi/ufs/ioctl.h>
+ #include <scsi/ufs/ufshci.h>
+
+ static int handleWrite(int fd, uint8_t scale, uint16_t timer_val)
+ {
+ ufs_ioctl_auto_hibern8_data hibern8_data;
+
+ /* State, that we want to write the data */
+ hibern8_data.write = true;
+
+ /* Timer scale (check <scsi/ufs/ufshci.h>) */
+ hibern8_data.scale = *scale;
+
+ /* Timer value (max. 0x3fff) */
+ hibern8_data.timer_val = *timer_val;
+
+ /* [fd] used here shall be opened UFS device */
+ return ioctl(fd, UFS_IOCTL_AUTO_HIBERN8, &hibern8_data);
+ }
+
+ static int handleRead(int fd, uint8_t *scale, uint16_t *timer_val)
+ {
+ ufs_ioctl_auto_hibern8_data hibern8_data;
+ int error;
+
+ /* State, that we want to read data */
+ hibern8_data.write = false;
+
+ /* [fd] used here shall be opened UFS device */
+ error = ioctl(fd, UFS_IOCTL_AUTO_HIBERN8, &hibern8_data);
+
+ if (!error) {
+ *scale = hibern8_data.scale;
+ *timer_val = hibern8_data.timer_val;
+ }
+
+ return error;
+ }
+
UFS Specifications can be found at,
UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf
@@ -273,6 +273,62 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
return err;
}
+static int ufshcd_auto_hibern8_ioctl(struct ufs_hba *hba, void __user *buffer)
+{
+ struct ufs_ioctl_auto_hibern8_data *ioctl_data;
+ int err = 0;
+ u32 status = 0;
+
+ if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT))
+ return -ENOTSUPP;
+
+ if (!buffer)
+ return -EINVAL;
+
+ ioctl_data = kzalloc(sizeof(struct ufs_ioctl_auto_hibern8_data),
+ GFP_KERNEL);
+ if (!ioctl_data) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* extract params from user buffer */
+ if (copy_from_user(ioctl_data, buffer, sizeof(*ioctl_data))) {
+ err = -EFAULT;
+ goto out_release_mem;
+ }
+
+ if (ioctl_data->write) {
+ if (ioctl_data->timer_val > UFSHCD_AHIBERN8_TIMER_MASK ||
+ (ioctl_data->scale >= UFSHCD_AHIBERN8_SCALE_MAX)) {
+ err = -EINVAL;
+ goto out_release_mem;
+ }
+
+ /* Write valid state to host */
+ ufshcd_setup_auto_hibern8(hba, ioctl_data->scale,
+ ioctl_data->timer_val);
+ } else {
+ status = ufshcd_read_auto_hibern8_state(hba);
+ ioctl_data->scale =
+ (status & UFSHCD_AHIBERN8_SCALE_MASK) >> 10;
+ ioctl_data->timer_val =
+ (status & UFSHCD_AHIBERN8_TIMER_MASK);
+
+ /* Copy state to user */
+ err = copy_to_user(buffer, ioctl_data, sizeof(*ioctl_data));
+ }
+
+out_release_mem:
+ kfree(ioctl_data);
+out:
+ if (err)
+ dev_err(hba->dev, "Auto-Hibern8 request failed (error: %d)",
+ err);
+
+ return err;
+}
+
/**
* ufshcd_ioctl - ufs ioctl callback registered in scsi_host
* @dev: scsi device required for per LUN queries
@@ -281,6 +337,7 @@ static int ufshcd_query_ioctl(struct ufs_hba *hba, u8 lun, void __user *buffer)
*
* Supported commands:
* UFS_IOCTL_QUERY
+ * UFS_IOCTL_AUTO_HIBERN8
*/
int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
{
@@ -297,6 +354,11 @@ int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
buffer);
pm_runtime_put_sync(hba->dev);
break;
+ case UFS_IOCTL_AUTO_HIBERN8:
+ pm_runtime_get_sync(hba->dev);
+ err = ufshcd_auto_hibern8_ioctl(hba, buffer);
+ pm_runtime_put_sync(hba->dev);
+ break;
default:
err = -EOPNOTSUPP;
break;
@@ -933,6 +933,30 @@ static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up)
}
/**
+ * ufshcd_read_auto_hibern8_state - Reads hosts auto-hibern8 feature state
+ * @hba: per adapter instance
+ */
+u32 ufshcd_read_auto_hibern8_state(struct ufs_hba *hba)
+{
+ return ufshcd_readl(hba, REG_AUTO_HIBERNATE_IDLE_TIMER);
+}
+
+/**
+ * ufshcd_setup_auto_hibern8 - Sets up hosts auto-hibern8 feature
+ * @hba: per adapter instance
+ * @scale: timer scale (1/10/100us/1/10/100ms)
+ * @timer_val: value to be multipled with scale (idle timeout)
+ */
+void ufshcd_setup_auto_hibern8(struct ufs_hba *hba, u8 scale, u16 timer_val)
+{
+ u32 val = (scale << 10) & UFSHCD_AHIBERN8_SCALE_MASK;
+
+ val |= timer_val & UFSHCD_AHIBERN8_TIMER_MASK;
+
+ ufshcd_writel(hba, val, REG_AUTO_HIBERNATE_IDLE_TIMER);
+}
+
+/**
* ufshcd_is_devfreq_scaling_required - check if scaling is required or not
* @hba: per adapter instance
* @scale_up: True if scaling up and false if scaling down
@@ -5338,6 +5362,7 @@ static void ufshcd_tmc_handler(struct ufs_hba *hba)
static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
{
hba->errors = UFSHCD_ERROR_MASK & intr_status;
+
if (hba->errors)
ufshcd_check_errors(hba);
@@ -860,6 +860,10 @@ int ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id,
u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun);
u16 ufshcd_upiu_wlun_to_scsi_wlun(u8 upiu_wlun_id);
+/* Expose Auto-Hibern8 API */
+void ufshcd_setup_auto_hibern8(struct ufs_hba *hba, u8 scale, u16 timer_val);
+u32 ufshcd_read_auto_hibern8_state(struct ufs_hba *hba);
+
/* Wrapper functions for safely calling variant operations */
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
{
@@ -36,6 +36,8 @@
#ifndef _UFSHCI_H
#define _UFSHCI_H
+#include <scsi/ufs/ufshci.h>
+
enum {
TASK_REQ_UPIU_SIZE_DWORDS = 8,
TASK_RSP_UPIU_SIZE_DWORDS = 8,
@@ -86,6 +88,7 @@ enum {
enum {
MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
+ MASK_AUTO_HIBERN8_SUPPORT = 0x00800000,
MASK_64_ADDRESSING_SUPPORT = 0x01000000,
MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000,
@@ -136,9 +139,9 @@ enum {
#define CONTROLLER_FATAL_ERROR UFS_BIT(16)
#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
-#define UFSHCD_UIC_PWR_MASK (UIC_HIBERNATE_ENTER |\
- UIC_HIBERNATE_EXIT |\
- UIC_POWER_MODE)
+#define UFSHCD_UHS_MASK (UIC_HIBERNATE_EXIT | UIC_HIBERNATE_ENTER)
+
+#define UFSHCD_UIC_PWR_MASK (UFSHCD_UHS_MASK | UIC_POWER_MODE)
#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL | UFSHCD_UIC_PWR_MASK)
@@ -255,7 +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
+ * include/uapi/scsi/ufs/ioctl.h defines 0x53A0 - 0x53A1
*/
/* Used to obtain PUN and LUN info. Conflicts with CDROMAUDIOBUFSIZ */
@@ -1,3 +1,4 @@
# UAPI Header export list
header-y += ioctl.h
header-y += ufs.h
+header-y += ufshci.h
@@ -8,6 +8,7 @@
* SCSI_IOCTL_GET_PCI
*/
#define UFS_IOCTL_QUERY 0x53A0
+#define UFS_IOCTL_AUTO_HIBERN8 0x53A1
/**
* struct ufs_ioctl_query_data - used to transfer data to and from user via
@@ -58,4 +59,34 @@ struct ufs_ioctl_query_data {
__u8 *buffer;
};
+/**
+ * struct ufs_ioctl_auto_hibern8_data - used to hold Auto-Hibern8 feature
+ * configuration
+ *
+ * @write: flag indicating whether config should be written or read
+ * @scale: scale of the timer (length of one tick)
+ * @timer_val: value of the timer to be multipled by scale (0x0000-0x3FFF)
+ *
+ * Received/Submitted: scale, timer_val
+ */
+struct ufs_ioctl_auto_hibern8_data {
+ /*
+ * This flag indicates whether configuration wirtten in this structure
+ * should be written, or overwritten by reading currently written
+ */
+ bool write;
+
+ /*
+ * Scale of the timer. Prease refer to <uapi/scsi/ufs/ufshci.h> for
+ * correct values and their meaning.
+ */
+ __u8 scale;
+
+ /*
+ * Actual timer value, which will be multipled by the scale.
+ * Maximal value: 1023. 0 will disable the feature.
+ */
+ __u16 timer_val;
+};
+
#endif /* UAPI_UFS_IOCTL_H_ */
new file mode 100644
@@ -0,0 +1,17 @@
+#ifndef _UAPI_UFSHCI_H
+#define _UAPI_UFSHCI_H
+
+enum {
+ UFSHCD_AHIBERN8_SCALE_1US = 0,
+ UFSHCD_AHIBERN8_SCALE_10US = 1,
+ UFSHCD_AHIBERN8_SCALE_100US = 2,
+ UFSHCD_AHIBERN8_SCALE_1MS = 3,
+ UFSHCD_AHIBERN8_SCALE_10MS = 4,
+ UFSHCD_AHIBERN8_SCALE_100MS = 5,
+ UFSHCD_AHIBERN8_SCALE_MAX,
+};
+
+#define UFSHCD_AHIBERN8_TIMER_MASK 0x03ff
+#define UFSHCD_AHIBERN8_SCALE_MASK 0x1C00
+
+#endif