From patchwork Thu May 11 14:01:47 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Potomski, MichalX" X-Patchwork-Id: 9722435 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 19DD06031B for ; Thu, 11 May 2017 16:25:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0E9A4286AD for ; Thu, 11 May 2017 16:25:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0346E286B8; Thu, 11 May 2017 16:25:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 1BE3B286AD for ; Thu, 11 May 2017 16:25:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S933499AbdEKOCx (ORCPT ); Thu, 11 May 2017 10:02:53 -0400 Received: from mga14.intel.com ([192.55.52.115]:44524 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933458AbdEKOBi (ORCPT ); Thu, 11 May 2017 10:01:38 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga103.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 11 May 2017 07:01:37 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.38,324,1491289200"; d="scan'208";a="1146633358" Received: from mpotom-tieto.ir.intel.com ([10.103.116.163]) by fmsmga001.fm.intel.com with ESMTP; 11 May 2017 07:01:35 -0700 From: Michal Potomski To: linux-scsi@vger.kernel.org Cc: vinholikatti@gmail.com, martin.petersen@oracle.com, jejb@linux.vnet.ibm.com, subhashj@codeaurora.org, =?UTF-8?q?Micha=C5=82=20Potomski?= Subject: [PATCH v1 2/4] scsi: ufs: Implement Auto-Hibern8 setup Date: Thu, 11 May 2017 16:01:47 +0200 Message-Id: <1494511309-32256-3-git-send-email-michalx.potomski@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1494511309-32256-1-git-send-email-michalx.potomski@intel.com> References: <1494511309-32256-1-git-send-email-michalx.potomski@intel.com> MIME-Version: 1.0 Sender: linux-scsi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-scsi@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Michał Potomski Since Auto-Hibern8 feature has to be enabled by the user proper API has been given via ioctl() by adding it to already existing implementation of UFS ioctl(). We expose this API to user-space, since we don't know in driver, what kind of additional Power Management rules user wants to use. Due to that we want to expose API, to let user or high level S/W decide, whether it wants to use Auto-Hibern8 feature for Power Saving and give him "slider" to decide between performance and power efficiency. This is important because different platforms using the same UFS host and/or device might want different options on this one, e.g. High-End Desktop PC might want to have this feature disabled, while Mobile or Server platforms might want to have this feature enabled, but levels will vary depending on what's to be achieved. As this feature is meant to be transparent for driver, we'd like to let user decide whether he wants this enabled or not. Signed-off-by: Michał Potomski --- Documentation/scsi/ufs.txt | 47 +++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd-ioctl.c | 62 +++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.c | 25 +++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 4 +++ drivers/scsi/ufs/ufshci.h | 9 ++++-- include/scsi/scsi.h | 2 +- include/uapi/scsi/ufs/Kbuild | 1 + include/uapi/scsi/ufs/ioctl.h | 31 +++++++++++++++++++++ include/uapi/scsi/ufs/ufshci.h | 17 +++++++++++ 9 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 include/uapi/scsi/ufs/ufshci.h diff --git a/Documentation/scsi/ufs.txt b/Documentation/scsi/ufs.txt index 7326974..6c0e995 100644 --- a/Documentation/scsi/ufs.txt +++ b/Documentation/scsi/ufs.txt @@ -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 + #include + #include + + 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 ) */ + 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 diff --git a/drivers/scsi/ufs/ufshcd-ioctl.c b/drivers/scsi/ufs/ufshcd-ioctl.c index ae70bf5..71f4026 100644 --- a/drivers/scsi/ufs/ufshcd-ioctl.c +++ b/drivers/scsi/ufs/ufshcd-ioctl.c @@ -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; diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9f3a39d..6fcbeb5 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -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); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index bf8a670..392f702 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -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) { diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index f60145d..789bbd8 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -36,6 +36,8 @@ #ifndef _UFSHCI_H #define _UFSHCI_H +#include + 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) diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index f128e9e..43f87ad 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -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 */ diff --git a/include/uapi/scsi/ufs/Kbuild b/include/uapi/scsi/ufs/Kbuild index cc3ef20..07a2ce3 100644 --- a/include/uapi/scsi/ufs/Kbuild +++ b/include/uapi/scsi/ufs/Kbuild @@ -1,3 +1,4 @@ # UAPI Header export list header-y += ioctl.h header-y += ufs.h +header-y += ufshci.h diff --git a/include/uapi/scsi/ufs/ioctl.h b/include/uapi/scsi/ufs/ioctl.h index 1c65505..f3583de 100644 --- a/include/uapi/scsi/ufs/ioctl.h +++ b/include/uapi/scsi/ufs/ioctl.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 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_ */ diff --git a/include/uapi/scsi/ufs/ufshci.h b/include/uapi/scsi/ufs/ufshci.h new file mode 100644 index 0000000..c50065b --- /dev/null +++ b/include/uapi/scsi/ufs/ufshci.h @@ -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