mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-13 16:16:15 -04:00
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:
parent
52a2cf3351
commit
7a4f190e62
5 changed files with 263 additions and 5 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue