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 int ufs_qcom_set_dme_vs_core_clk_ctrl_clear_div(struct ufs_hba *hba,
|
||||
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_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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct device_node *node = host->hba->dev->of_node;
|
||||
struct device_attribute *attr;
|
||||
int ret = 0;
|
||||
int num_groups;
|
||||
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_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;
|
||||
|
||||
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
|
||||
* @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
|
||||
* @num_clusters: number of clusters defined
|
||||
* @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_cpu_group *groups;
|
||||
struct device_attribute enable_attr;
|
||||
struct device_attribute latency_attr;
|
||||
struct workqueue_struct *workq;
|
||||
int num_groups;
|
||||
int default_cpu;
|
||||
|
|
|
@ -369,6 +369,7 @@ struct ufs_hba_pm_qos_variant_ops {
|
|||
* @name: variant name
|
||||
*/
|
||||
struct ufs_hba_variant {
|
||||
struct device *dev;
|
||||
const char *name;
|
||||
struct ufs_hba_variant_ops *vops;
|
||||
struct ufs_hba_crypto_variant_ops *crypto_vops;
|
||||
|
|
Loading…
Add table
Reference in a new issue