scsi: ufs-qcom: add sys-fs entries for PM QoS control
Add sys-fs entries to allow user space control PM QoS latency parameters and enable/disable PM QoS voting. The entries are added under: /sys/bus/platform/devices/624000.ufshc/624000.ufshc:ufs_variant/ pm_qos_enable: write 0 to disable PM QoS, 1 to enable. Example: "echo 1 > pm_qos_enable" pm_qos_latency_us: write the desired value for each cpu group, separated by a comma. Example: "echo 10,20 > pm_qos_latency_us" Change-Id: I9797a1e62c4867ab831b4f18cbb1e0ca9834247b Signed-off-by: Gilad Broner <gbroner@codeaurora.org> Signed-off-by: Krishna Konda <kkonda@codeaurora.org> [venkatg@codeaurora.org: resolved trivial merge conflicts] Signed-off-by: Venkat Gopalakrishnan <venkatg@codeaurora.org>
This commit is contained in:
parent
38da06a770
commit
8f1b4b5eb0
3 changed files with 138 additions and 2 deletions
|
@ -58,8 +58,6 @@ static int ufs_qcom_update_sec_cfg(struct ufs_hba *hba, bool restore_sec_cfg);
|
||||||
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
|
static void ufs_qcom_get_default_testbus_cfg(struct ufs_qcom_host *host);
|
||||||
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
static int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||||
u32 clk_cycles);
|
u32 clk_cycles);
|
||||||
static int ufs_qcom_pm_qos_init(struct ufs_qcom_host *host);
|
|
||||||
static void ufs_qcom_pm_qos_remove(struct ufs_qcom_host *host);
|
|
||||||
static void ufs_qcom_pm_qos_suspend(struct ufs_qcom_host *host);
|
static void ufs_qcom_pm_qos_suspend(struct ufs_qcom_host *host);
|
||||||
|
|
||||||
static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len,
|
static void ufs_qcom_dump_regs(struct ufs_hba *hba, int offset, int len,
|
||||||
|
@ -1410,9 +1408,122 @@ static void ufs_qcom_pm_qos_unvote_work(struct work_struct *work)
|
||||||
group->latency_us, UFS_QCOM_PM_QOS_UNVOTE_TIMEOUT_US);
|
group->latency_us, UFS_QCOM_PM_QOS_UNVOTE_TIMEOUT_US);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t ufs_qcom_pm_qos_enable_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||||
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
|
||||||
|
return snprintf(buf, PAGE_SIZE, "%d\n", host->pm_qos.is_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ufs_qcom_pm_qos_enable_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||||
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
unsigned long value;
|
||||||
|
unsigned long flags;
|
||||||
|
bool enable;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (kstrtoul(buf, 0, &value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
enable = !!value;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must take the spinlock and save irqs before changing the enabled
|
||||||
|
* flag in order to keep correctness of PM QoS release.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
|
if (enable == host->pm_qos.is_enabled) {
|
||||||
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
host->pm_qos.is_enabled = enable;
|
||||||
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||||
|
cancel_work_sync(&host->pm_qos.groups[i].vote_work);
|
||||||
|
cancel_work_sync(&host->pm_qos.groups[i].unvote_work);
|
||||||
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
|
host->pm_qos.groups[i].state = PM_QOS_UNVOTED;
|
||||||
|
host->pm_qos.groups[i].active_reqs = 0;
|
||||||
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
|
pm_qos_update_request(&host->pm_qos.groups[i].req,
|
||||||
|
PM_QOS_DEFAULT_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ufs_qcom_pm_qos_latency_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||||
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||||
|
ret = snprintf(&buf[offset], PAGE_SIZE,
|
||||||
|
"cpu group #%d(mask=0x%lx): %d\n", i,
|
||||||
|
host->pm_qos.groups[i].mask.bits[0],
|
||||||
|
host->pm_qos.groups[i].latency_us);
|
||||||
|
if (ret > 0)
|
||||||
|
offset += ret;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t ufs_qcom_pm_qos_latency_store(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct ufs_hba *hba = dev_get_drvdata(dev->parent);
|
||||||
|
struct ufs_qcom_host *host = ufshcd_get_variant(hba);
|
||||||
|
unsigned long value;
|
||||||
|
unsigned long flags;
|
||||||
|
char *strbuf;
|
||||||
|
char *strbuf_copy;
|
||||||
|
char *token;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* reserve one byte for null termination */
|
||||||
|
strbuf = kmalloc(count + 1, GFP_KERNEL);
|
||||||
|
if (!strbuf)
|
||||||
|
return -ENOMEM;
|
||||||
|
strbuf_copy = strbuf;
|
||||||
|
strlcpy(strbuf, buf, count + 1);
|
||||||
|
|
||||||
|
for (i = 0; i < host->pm_qos.num_groups; i++) {
|
||||||
|
token = strsep(&strbuf, ",");
|
||||||
|
if (!token)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = kstrtoul(token, 0, &value);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
spin_lock_irqsave(hba->host->host_lock, flags);
|
||||||
|
host->pm_qos.groups[i].latency_us = value;
|
||||||
|
spin_unlock_irqrestore(hba->host->host_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(strbuf_copy);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static int ufs_qcom_pm_qos_init(struct ufs_qcom_host *host)
|
static int ufs_qcom_pm_qos_init(struct ufs_qcom_host *host)
|
||||||
{
|
{
|
||||||
struct device_node *node = host->hba->dev->of_node;
|
struct device_node *node = host->hba->dev->of_node;
|
||||||
|
struct device_attribute *attr;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int num_groups;
|
int num_groups;
|
||||||
int num_values;
|
int num_values;
|
||||||
|
@ -1510,6 +1621,26 @@ static int ufs_qcom_pm_qos_init(struct ufs_qcom_host *host)
|
||||||
pm_qos_add_request(&host->pm_qos.groups[i].req,
|
pm_qos_add_request(&host->pm_qos.groups[i].req,
|
||||||
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE);
|
||||||
|
|
||||||
|
/* PM QoS latency sys-fs attribute */
|
||||||
|
attr = &host->pm_qos.latency_attr;
|
||||||
|
attr->show = ufs_qcom_pm_qos_latency_show;
|
||||||
|
attr->store = ufs_qcom_pm_qos_latency_store;
|
||||||
|
sysfs_attr_init(&attr->attr);
|
||||||
|
attr->attr.name = "pm_qos_latency_us";
|
||||||
|
attr->attr.mode = S_IRUGO | S_IWUSR;
|
||||||
|
if (device_create_file(host->hba->var->dev, attr))
|
||||||
|
dev_dbg(host->hba->dev, "Failed to create sysfs for pm_qos_latency_us\n");
|
||||||
|
|
||||||
|
/* PM QoS enable sys-fs attribute */
|
||||||
|
attr = &host->pm_qos.enable_attr;
|
||||||
|
attr->show = ufs_qcom_pm_qos_enable_show;
|
||||||
|
attr->store = ufs_qcom_pm_qos_enable_store;
|
||||||
|
sysfs_attr_init(&attr->attr);
|
||||||
|
attr->attr.name = "pm_qos_enable";
|
||||||
|
attr->attr.mode = S_IRUGO | S_IWUSR;
|
||||||
|
if (device_create_file(host->hba->var->dev, attr))
|
||||||
|
dev_dbg(host->hba->dev, "Failed to create sysfs for pm_qos enable\n");
|
||||||
|
|
||||||
host->pm_qos.is_enabled = true;
|
host->pm_qos.is_enabled = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -283,6 +283,8 @@ struct ufs_qcom_pm_qos_cpu_group {
|
||||||
/**
|
/**
|
||||||
* struct ufs_qcom_pm_qos - data related to PM QoS voting logic
|
* struct ufs_qcom_pm_qos - data related to PM QoS voting logic
|
||||||
* @groups: PM QoS cpu group state array
|
* @groups: PM QoS cpu group state array
|
||||||
|
* @enable_attr: sysfs attribute to enable/disable PM QoS voting logic
|
||||||
|
* @latency_attr: sysfs attribute to set latency value
|
||||||
* @workq: single threaded workqueue to run PM QoS voting/unvoting
|
* @workq: single threaded workqueue to run PM QoS voting/unvoting
|
||||||
* @num_clusters: number of clusters defined
|
* @num_clusters: number of clusters defined
|
||||||
* @default_cpu: cpu to use for voting for request not specifying a cpu
|
* @default_cpu: cpu to use for voting for request not specifying a cpu
|
||||||
|
@ -290,6 +292,8 @@ struct ufs_qcom_pm_qos_cpu_group {
|
||||||
*/
|
*/
|
||||||
struct ufs_qcom_pm_qos {
|
struct ufs_qcom_pm_qos {
|
||||||
struct ufs_qcom_pm_qos_cpu_group *groups;
|
struct ufs_qcom_pm_qos_cpu_group *groups;
|
||||||
|
struct device_attribute enable_attr;
|
||||||
|
struct device_attribute latency_attr;
|
||||||
struct workqueue_struct *workq;
|
struct workqueue_struct *workq;
|
||||||
int num_groups;
|
int num_groups;
|
||||||
int default_cpu;
|
int default_cpu;
|
||||||
|
|
|
@ -369,6 +369,7 @@ struct ufs_hba_pm_qos_variant_ops {
|
||||||
* @name: variant name
|
* @name: variant name
|
||||||
*/
|
*/
|
||||||
struct ufs_hba_variant {
|
struct ufs_hba_variant {
|
||||||
|
struct device *dev;
|
||||||
const char *name;
|
const char *name;
|
||||||
struct ufs_hba_variant_ops *vops;
|
struct ufs_hba_variant_ops *vops;
|
||||||
struct ufs_hba_crypto_variant_ops *crypto_vops;
|
struct ufs_hba_crypto_variant_ops *crypto_vops;
|
||||||
|
|
Loading…
Add table
Reference in a new issue