From patchwork Tue Jul 25 09:39:55 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Potomski, MichalX" X-Patchwork-Id: 9861547 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 9B8496038C for ; Tue, 25 Jul 2017 09:38:55 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8E25A26224 for ; Tue, 25 Jul 2017 09:38:55 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8134D28609; Tue, 25 Jul 2017 09:38:55 +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 8058326224 for ; Tue, 25 Jul 2017 09:38:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751031AbdGYJis (ORCPT ); Tue, 25 Jul 2017 05:38:48 -0400 Received: from mga11.intel.com ([192.55.52.93]:9996 "EHLO mga11.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751000AbdGYJis (ORCPT ); Tue, 25 Jul 2017 05:38:48 -0400 Received: from orsmga005.jf.intel.com ([10.7.209.41]) by fmsmga102.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 25 Jul 2017 02:38:47 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.40,411,1496127600"; d="scan'208";a="129167405" Received: from mpotom-tieto.ir.intel.com ([10.103.116.137]) by orsmga005.jf.intel.com with ESMTP; 25 Jul 2017 02:38:45 -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, szymonx.mielczarek@intel.com Subject: [PATCH] scsi: ufs: Implement Auto-Hibern8 setup Date: Tue, 25 Jul 2017 11:39:55 +0200 Message-Id: <20170725093955.18389-1-michalx.potomski@intel.com> X-Mailer: git-send-email 2.13.0.67.g10c78a1 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 SysFS. 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 acheived. 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 --- drivers/scsi/ufs/ufshcd.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.h | 2 ++ drivers/scsi/ufs/ufshci.h | 23 +++++++++--- 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 5bc9dc14e075..a401ab61b5a2 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -931,6 +931,31 @@ 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_HIBERN8_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 << UFSHCI_AHIBERN8_SCALE_OFFSET) + & UFSHCI_AHIBERN8_SCALE_MASK; + + val |= timer_val & UFSHCI_AHIBERN8_TIMER_MASK; + + ufshcd_writel(hba, val, REG_AUTO_HIBERN8_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 @@ -5715,6 +5740,55 @@ static int ufshcd_abort(struct scsi_cmnd *cmd) return err; } +#define UFS_AHIBERN8_SCALE_STEP_MAGNITUDE 10 + +static ssize_t ufshcd_auto_hibern8_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + u32 val; + unsigned long timer; + u8 scale; + char *unit; + + val = ufshcd_read_auto_hibern8_state(hba); + timer = val & UFSHCI_AHIBERN8_TIMER_MASK; + scale = (val & UFSHCI_AHIBERN8_SCALE_MASK) + >> UFSHCI_AHIBERN8_SCALE_OFFSET; + + unit = scale >= UFSHCI_AHIBERN8_SCALE_1MS ? "ms" : "us"; + + for (scale %= UFSHCI_AHIBERN8_SCALE_1MS; scale > 0; --scale) + timer *= UFS_AHIBERN8_SCALE_STEP_MAGNITUDE; + + return snprintf(buf, PAGE_SIZE, "%ld %s\n", timer, unit); +} + +static ssize_t ufshcd_auto_hibern8_store(struct device *dev, + struct device_attribute *atr, const char *buf, size_t count) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + unsigned long timer; + u8 scale = UFSHCI_AHIBERN8_SCALE_1US; + + if (kstrtoul(buf, 0, &timer)) { + return -EINVAL; + } + + while (timer > UFSHCI_AHIBERN8_TIMER_MASK && + scale < UFSHCI_AHIBERN8_SCALE_MAX) { + timer /= UFS_AHIBERN8_SCALE_STEP_MAGNITUDE; + ++scale; + } + + if (scale >= UFSHCI_AHIBERN8_SCALE_MAX) + return -EINVAL; + + ufshcd_setup_auto_hibern8(hba, scale, (u16) timer); + + return count; +} + /** * ufshcd_host_reset_and_restore - reset and restore host controller * @hba: per-adapter instance @@ -7693,16 +7767,34 @@ static void ufshcd_add_spm_lvl_sysfs_nodes(struct ufs_hba *hba) dev_err(hba->dev, "Failed to create sysfs for spm_lvl\n"); } +static void ufshcd_add_ahibern8_sysfs_node(struct ufs_hba *hba) +{ + if (!(hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT)) + return; + + hba->ahibern8_attr.show = ufshcd_auto_hibern8_show; + hba->ahibern8_attr.store = ufshcd_auto_hibern8_store; + sysfs_attr_init(&hba->ahibern8_attr.attr); + hba->ahibern8_attr.attr.name = "ufs_auto_hibern8"; + hba->ahibern8_attr.attr.mode = 0600; + if (device_create_file(hba->dev, &hba->ahibern8_attr)) + dev_err(hba->dev, "Failed to create sysfs for ufs_auto_hibern8\n"); +} + static inline void ufshcd_add_sysfs_nodes(struct ufs_hba *hba) { ufshcd_add_rpm_lvl_sysfs_nodes(hba); ufshcd_add_spm_lvl_sysfs_nodes(hba); + ufshcd_add_ahibern8_sysfs_node(hba); } static inline void ufshcd_remove_sysfs_nodes(struct ufs_hba *hba) { device_remove_file(hba->dev, &hba->rpm_lvl_attr); device_remove_file(hba->dev, &hba->spm_lvl_attr); + + if (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) + device_remove_file(hba->dev, &hba->ahibern8_attr); } /** diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index cdc8bd05f7df..5ae470bad75e 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -677,6 +677,8 @@ struct ufs_hba { struct rw_semaphore clk_scaling_lock; struct ufs_desc_size desc_size; + + struct device_attribute ahibern8_attr; }; /* Returns true if clocks can be gated. Otherwise false */ diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index f60145d4a66e..3bf8826318b0 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -48,7 +48,7 @@ enum { REG_UFS_VERSION = 0x08, REG_CONTROLLER_DEV_ID = 0x10, REG_CONTROLLER_PROD_ID = 0x14, - REG_AUTO_HIBERNATE_IDLE_TIMER = 0x18, + REG_AUTO_HIBERN8_IDLE_TIMER = 0x18, REG_INTERRUPT_STATUS = 0x20, REG_INTERRUPT_ENABLE = 0x24, REG_CONTROLLER_STATUS = 0x30, @@ -86,6 +86,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 +137,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) @@ -428,4 +429,18 @@ struct utp_task_req_desc { __le32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS]; }; +enum { + UFSHCI_AHIBERN8_SCALE_1US = 0, + UFSHCI_AHIBERN8_SCALE_10US = 1, + UFSHCI_AHIBERN8_SCALE_100US = 2, + UFSHCI_AHIBERN8_SCALE_1MS = 3, + UFSHCI_AHIBERN8_SCALE_10MS = 4, + UFSHCI_AHIBERN8_SCALE_100MS = 5, + UFSHCI_AHIBERN8_SCALE_MAX, +}; + +#define UFSHCI_AHIBERN8_TIMER_MASK 0x03ff +#define UFSHCI_AHIBERN8_SCALE_MASK 0x1C00 +#define UFSHCI_AHIBERN8_SCALE_OFFSET 10 + #endif /* End of Header */