ANDROID: scsi: ufs: add manual_gc in sysfs with UFS 3.1

This patch adds manual_gc control via sysfs entries.

> cat /sys/devices/platform/soc/1d84000.ufshc/manual_gc
on
> echo 1 > /sys/devices/platform/soc/1d84000.ufshc/manual_gc

Mot-CRs-fixed: (CR)
Link: https://android-review.googlesource.com/c/kernel/common/+/1426015

Change-Id: I265093c126d71f2e594ed305ad7653a9315b4d63
Reviewed-on: https://gerrit.mot.com/1987790
SME-Granted: SME Approvals Granted
SLTApproved: Slta Waiver
Tested-by: Jira Key
Reviewed-by: Huosheng Liao <liaohs@motorola.com>
Submit-Approved: Jira Key
This commit is contained in:
Jaegeuk Kim 2020-03-02 19:03:09 -08:00 • committed by Xiaojun Ji
commit 7a4f190e62
5 changed files with 263 additions and 5 deletions

View file

@ -249,6 +249,12 @@ config SCSI_SKHPB
help
Activate or deactive SKHPB driver
config SCSI_SKHID
bool "Activate HPB Host-aware Performance Booster"
depends on (SCSI_UFSHCD || SCSI_UFSHCD_QTI)
help
Activate or deactive SKHPB driver
config MICRON_HPB
bool "Activate micron HPB Host-aware Performance Booster"
depends on SCSI_UFSHCD && UFSFEATURE && UFSHPB

View file

@ -7,6 +7,9 @@
#include <asm/unaligned.h>
#include "ufs.h"
#if defined(CONFIG_SCSI_SKHID)
#include "ufshcd.h"
#endif
#include "ufs-sysfs.h"
static const char *ufschd_uic_link_state_to_string(
@ -182,6 +185,129 @@ static ssize_t auto_hibern8_store(struct device *dev,
return count;
}
#if defined(CONFIG_SCSI_SKHID)
static ssize_t manual_gc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
u32 status = MANUAL_GC_OFF;
if (hba->manual_gc.state == MANUAL_GC_DISABLE)
return scnprintf(buf, PAGE_SIZE, "%s", "disabled\n");
pm_runtime_get_sync(hba->dev);
//down_read(&hba->query_lock);
if (hba->manual_gc.hagc_support) {
int err = ufshcd_query_attr_retry(hba,
UPIU_QUERY_OPCODE_READ_ATTR,
QUERY_ATTR_IDN_MANUAL_GC_STATUS, 0, 0, &status);
hba->manual_gc.hagc_support = err ? false: true;
}
//up_read(&hba->query_lock);
pm_runtime_mark_last_busy(hba->dev);
pm_runtime_put_noidle(hba->dev);
if (!hba->manual_gc.hagc_support)
return scnprintf(buf, PAGE_SIZE, "%s", "bkops\n");
return scnprintf(buf, PAGE_SIZE, "%s",
status == MANUAL_GC_OFF ? "off\n" : "on\n");
}
static int manual_gc_enable(struct ufs_hba *hba, u32 *value)
{
return ufshcd_query_attr_retry(hba,
UPIU_QUERY_OPCODE_WRITE_ATTR,
QUERY_ATTR_IDN_MANUAL_GC_CONT, 0, 0,
value);
}
static ssize_t manual_gc_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
u32 value;
int err = 0;
if (kstrtou32(buf, 0, &value))
return -EINVAL;
if (value >= MANUAL_GC_MAX)
return -EINVAL;
if (value == MANUAL_GC_DISABLE || value == MANUAL_GC_ENABLE) {
hba->manual_gc.state = value;
return count;
}
if (hba->manual_gc.state == MANUAL_GC_DISABLE)
return count;
pm_runtime_get_sync(hba->dev);
if (hba->manual_gc.hagc_support)
hba->manual_gc.hagc_support =
manual_gc_enable(hba, &value) ? false : true;
if (!hba->manual_gc.hagc_support) {
enum query_opcode opcode = (value == MANUAL_GC_ON) ?
UPIU_QUERY_OPCODE_SET_FLAG:
UPIU_QUERY_OPCODE_CLEAR_FLAG;
err = ufshcd_bkops_ctrl(hba, (value == MANUAL_GC_ON) ?
BKOPS_STATUS_NON_CRITICAL:
BKOPS_STATUS_CRITICAL);
if (!hba->auto_bkops_enabled)
err = -EAGAIN;
/* flush wb buffer */
if (hba->dev_info.wspecversion >= 0x0310) {
u8 index = ufshcd_wb_get_query_index(hba);
ufshcd_query_flag_retry(hba, opcode,
QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8,
index, NULL);
ufshcd_query_flag_retry(hba, opcode,
QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN, index, NULL);
}
}
if (err || hrtimer_active(&hba->manual_gc.hrtimer)) {
pm_runtime_mark_last_busy(hba->dev);
pm_runtime_put_noidle(hba->dev);
return count;
} else {
/* pm_runtime_put_sync in delay_ms */
hrtimer_start(&hba->manual_gc.hrtimer,
ms_to_ktime(hba->manual_gc.delay_ms),
HRTIMER_MODE_REL);
}
return count;
}
static ssize_t manual_gc_hold_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
return snprintf(buf, PAGE_SIZE, "%lu\n", hba->manual_gc.delay_ms);
}
static ssize_t manual_gc_hold_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct ufs_hba *hba = dev_get_drvdata(dev);
unsigned long value;
if (kstrtoul(buf, 0, &value))
return -EINVAL;
hba->manual_gc.delay_ms = value;
return count;
}
#endif
static DEVICE_ATTR_RW(rpm_lvl);
static DEVICE_ATTR_RO(rpm_target_dev_state);
static DEVICE_ATTR_RO(rpm_target_link_state);
@ -189,6 +315,10 @@ static DEVICE_ATTR_RW(spm_lvl);
static DEVICE_ATTR_RO(spm_target_dev_state);
static DEVICE_ATTR_RO(spm_target_link_state);
static DEVICE_ATTR_RW(auto_hibern8);
#if defined(CONFIG_SCSI_SKHID)
static DEVICE_ATTR_RW(manual_gc);
static DEVICE_ATTR_RW(manual_gc_hold);
#endif
static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
&dev_attr_rpm_lvl.attr,
@ -198,6 +328,10 @@ static struct attribute *ufs_sysfs_ufshcd_attrs[] = {
&dev_attr_spm_target_dev_state.attr,
&dev_attr_spm_target_link_state.attr,
&dev_attr_auto_hibern8.attr,
#if defined(CONFIG_SCSI_SKHID)
&dev_attr_manual_gc.attr,
&dev_attr_manual_gc_hold.attr,
#endif
NULL
};

View file

@ -198,6 +198,11 @@ enum attr_idn {
QUERY_ATTR_IDN_TW_BUF_LIFETIME_EST = 0x1E,
QUERY_ATTR_IDN_TW_CURR_BUF_SIZE = 0x1F,
#endif
#if defined(CONFIG_SCSI_SKHID)
/* use one reserved bit */
QUERY_ATTR_IDN_MANUAL_GC_CONT = 0x12,
QUERY_ATTR_IDN_MANUAL_GC_STATUS = 0x13,
#endif
#if defined(CONFIG_UFSFEATURE)
QUERY_ATTR_IDN_SUP_VENDOR_OPTIONS = 0xFF,
#endif
@ -385,6 +390,16 @@ enum geometry_desc_param {
#endif
};
#if defined(CONFIG_SCSI_SKHID)
enum {
MANUAL_GC_OFF = 0,
MANUAL_GC_ON,
MANUAL_GC_DISABLE,
MANUAL_GC_ENABLE,
MANUAL_GC_MAX,
};
#endif
/* Health descriptor parameters offsets in bytes*/
enum health_desc_param {
HEALTH_DESC_PARAM_LEN = 0x0,

View file

@ -140,6 +140,11 @@ unsigned int storage_mfrid;
/* Polling time to wait for fDeviceInit */
#define FDEVICEINIT_COMPL_TIMEOUT 5000 /* millisecs */
#if defined(CONFIG_SCSI_SKHID)
/* for manual gc */
#define UFSHCD_MANUAL_GC_HOLD_HIBERN8 2000 /* 2 seconds */
#endif
#define ufshcd_toggle_vreg(_dev, _vreg, _on) \
({ \
int _ret; \
@ -1698,6 +1703,51 @@ static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n");
}
#if defined(CONFIG_SCSI_SKHID)
static enum hrtimer_restart ufshcd_mgc_hrtimer_handler(struct hrtimer *timer)
{
struct ufs_hba *hba = container_of(timer, struct ufs_hba,
manual_gc.hrtimer);
queue_work(hba->manual_gc.mgc_workq, &hba->manual_gc.hibern8_work);
return HRTIMER_NORESTART;
}
static void ufshcd_mgc_hibern8_work(struct work_struct *work)
{
struct ufs_hba *hba = container_of(work, struct ufs_hba,
manual_gc.hibern8_work);
pm_runtime_mark_last_busy(hba->dev);
pm_runtime_put_noidle(hba->dev);
/* bkops will be disabled when power down */
}
static void ufshcd_init_manual_gc(struct ufs_hba *hba)
{
struct ufs_manual_gc *mgc = &hba->manual_gc;
char wq_name[sizeof("ufs_mgc_hibern8_work")];
mgc->state = MANUAL_GC_ENABLE;
mgc->hagc_support = true;
mgc->delay_ms = UFSHCD_MANUAL_GC_HOLD_HIBERN8;
hrtimer_init(&mgc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
mgc->hrtimer.function = ufshcd_mgc_hrtimer_handler;
INIT_WORK(&mgc->hibern8_work, ufshcd_mgc_hibern8_work);
snprintf(wq_name, ARRAY_SIZE(wq_name), "ufs_mgc_hibern8_work_%d",
hba->host->host_no);
hba->manual_gc.mgc_workq = create_singlethread_workqueue(wq_name);
}
static void ufshcd_exit_manual_gc(struct ufs_hba *hba)
{
hrtimer_cancel(&hba->manual_gc.hrtimer);
cancel_work_sync(&hba->manual_gc.hibern8_work);
destroy_workqueue(hba->manual_gc.mgc_workq);
}
#endif
static void ufshcd_ungate_work(struct work_struct *work)
{
int ret;
@ -3080,7 +3130,7 @@ static inline void ufshcd_init_query(struct ufs_hba *hba,
(*request)->upiu_req.selector = selector;
}
#if defined(CONFIG_SCSI_SKHPB)
#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_SCSI_SKHID)
int ufshcd_query_flag_retry(struct ufs_hba *hba,
enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res)
#else
@ -3091,6 +3141,11 @@ static int ufshcd_query_flag_retry(struct ufs_hba *hba,
int ret;
int retries;
#if defined(CONFIG_SCSI_SKHID)
if (ufshcd_eh_in_progress(hba))
return -EBUSY;
#endif
for (retries = 0; retries < QUERY_REQ_RETRIES; retries++) {
ret = ufshcd_query_flag(hba, opcode, idn, index, flag_res);
if (ret)
@ -3255,14 +3310,25 @@ EXPORT_SYMBOL_GPL(ufshcd_query_attr);
*
* Returns 0 for success, non-zero in case of failure
*/
#if defined(CONFIG_SCSI_SKHID)
int ufshcd_query_attr_retry(struct ufs_hba *hba,
enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector,
u32 *attr_val)
#else
static int ufshcd_query_attr_retry(struct ufs_hba *hba,
enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector,
u32 *attr_val)
#endif
{
int ret = 0;
u32 retries;
for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
#if defined(CONFIG_SCSI_SKHID)
if (ufshcd_eh_in_progress(hba))
return -EBUSY;
#endif
for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
ret = ufshcd_query_attr(hba, opcode, idn, index,
selector, attr_val);
if (ret)
@ -5591,12 +5657,21 @@ static inline int ufshcd_get_bkops_status(struct ufs_hba *hba, u32 *status)
* to know whether auto bkops is enabled or disabled after this function
* returns control to it.
*/
#if defined(CONFIG_SCSI_SKHID)
int ufshcd_bkops_ctrl(struct ufs_hba *hba, enum bkops_status status)
#else
static int ufshcd_bkops_ctrl(struct ufs_hba *hba,
enum bkops_status status)
#endif
{
int err;
u32 curr_status = 0;
#if defined(CONFIG_SCSI_SKHID)
if (ufshcd_eh_in_progress(hba))
return -EBUSY;
#endif
err = ufshcd_get_bkops_status(hba, &curr_status);
if (err) {
dev_err(hba->dev, "%s: failed to get BKOPS status %d\n",
@ -9557,7 +9632,9 @@ void ufshcd_remove(struct ufs_hba *hba)
/* disable interrupts */
ufshcd_disable_intr(hba, hba->intr_mask);
ufshcd_hba_stop(hba, true);
#if defined(CONFIG_SCSI_SKHID)
ufshcd_exit_manual_gc(hba);
#endif
ufshcd_exit_clk_scaling(hba);
ufshcd_exit_clk_gating(hba);
if (ufshcd_is_clkscaling_supported(hba))
@ -9742,7 +9819,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
ufshcd_init_clk_gating(hba);
ufshcd_init_clk_scaling(hba);
#if defined(CONFIG_SCSI_SKHID)
ufshcd_init_manual_gc(hba);
#endif
/*
* In order to avoid any spurious interrupt immediately after
* registering UFS controller interrupt handler, clear any pending UFS
@ -9847,6 +9926,9 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
out_remove_scsi_host:
scsi_remove_host(hba->host);
exit_gating:
#if defined(CONFIG_SCSI_SKHID)
ufshcd_exit_manual_gc(hba);
#endif
ufshcd_exit_clk_scaling(hba);
ufshcd_exit_clk_gating(hba);
destroy_workqueue(hba->eh_wq);

View file

@ -484,6 +484,18 @@ struct ufs_clk_gating {
struct workqueue_struct *clk_gating_workq;
};
#if defined(CONFIG_SCSI_SKHID)
/* for manual gc */
struct ufs_manual_gc {
int state;
bool hagc_support;
struct hrtimer hrtimer;
unsigned long delay_ms;
struct work_struct hibern8_work;
struct workqueue_struct *mgc_workq;
};
#endif
struct ufs_saved_pwr_info {
struct ufs_pa_layer_attr info;
bool is_valid;
@ -936,6 +948,9 @@ struct ufs_hba {
/* Keeps information of the UFS device connected to this host */
struct ufs_dev_info dev_info;
bool auto_bkops_enabled;
#if defined(CONFIG_SCSI_SKHID)
struct ufs_manual_gc manual_gc;
#endif
struct ufs_vreg_info vreg_info;
struct list_head clk_list_head;
@ -1252,6 +1267,12 @@ extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
u32 *mib_val, u8 peer);
extern int ufshcd_config_pwr_mode(struct ufs_hba *hba,
struct ufs_pa_layer_attr *desired_pwr_mode);
#if defined(CONFIG_SCSI_SKHID)
extern int ufshcd_query_attr_retry(struct ufs_hba *hba,
enum query_opcode opcode, enum attr_idn idn, u8 index, u8 selector,
u32 *attr_val);
extern int ufshcd_bkops_ctrl(struct ufs_hba *hba, enum bkops_status status);
#endif
/* UIC command interfaces for DME primitives */
#define DME_LOCAL 0
#define DME_PEER 1
@ -1541,7 +1562,7 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun)
return scsi_lun & UFS_UPIU_MAX_UNIT_NUM_ID;
}
#if defined(CONFIG_SCSI_SKHPB)
#if defined(CONFIG_SCSI_SKHPB) || defined(CONFIG_SCSI_SKHID)
int ufshcd_query_flag_retry(struct ufs_hba *hba,
enum query_opcode opcode, enum flag_idn idn, u8 index, bool *flag_res);
#endif