mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-11-07 04:09:21 +00:00
mmc: core: Add sysfs entries for dynamic control of clock scaling
Add sysfs attributes to allow dynamic control of clock scaling parameters. These attributes are used to enable/disable clock scaling at runtime and change the up_threshold, down_threshold, and polling_interval values. Complete documentation for these sysfs entries are provided at "Documentation/mmc/mmc-dev-attrs.txt". Change-Id: I4753c75c3b3eeea91e93bceba7af2535b793612e Signed-off-by: Sujit Reddy Thumma <sthumma@codeaurora.org>
This commit is contained in:
parent
4080abfa57
commit
eb6ad18edd
2 changed files with 203 additions and 2 deletions
|
@ -39,7 +39,7 @@ SD and MMC Device Attributes
|
|||
|
||||
All attributes are read-only.
|
||||
|
||||
cid Card Identifaction Register
|
||||
cid Card Identification Register
|
||||
csd Card Specific Data Register
|
||||
scr SD Card Configuration Register (SD only)
|
||||
date Manufacturing Date (from CID Register)
|
||||
|
@ -108,3 +108,41 @@ This attribute appears only if CONFIG_MMC_CLKGATE is enabled.
|
|||
clkgate_delay Tune the clock gating delay with desired value in milliseconds.
|
||||
|
||||
echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
|
||||
|
||||
SD/MMC/SDIO Clock Scaling Attributes
|
||||
====================================
|
||||
|
||||
Read and write accesses are provided to following attributes.
|
||||
|
||||
polling_interval Measured in milliseconds, this attribute
|
||||
defines how often we need to check the card
|
||||
usage and make decisions on frequency scaling.
|
||||
|
||||
up_threshold This attribute defines what should be the
|
||||
average card usage between the polling
|
||||
interval for the mmc core to make a decision
|
||||
on whether it should increase the frequency.
|
||||
For example when it is set to '35' it means
|
||||
that between the checking intervals the card
|
||||
needs to be on average more than 35% in use to
|
||||
scale up the frequency. The value should be
|
||||
between 0 - 100 so that it can be compared
|
||||
against load percentage.
|
||||
|
||||
down_threshold Similar to up_threshold, but on lowering the
|
||||
frequency. For example, when it is set to '2'
|
||||
it means that between the checking intervals
|
||||
the card needs to be on average less than 2%
|
||||
in use to scale down the clocks to minimum
|
||||
frequency. The value should be between 0 - 100
|
||||
so that it can be compared against load
|
||||
percentage.
|
||||
|
||||
enable Enable clock scaling for hosts (and cards)
|
||||
that support ultrahigh speed modes
|
||||
(SDR104, DDR50, HS200).
|
||||
|
||||
echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/polling_interval
|
||||
echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/up_threshold
|
||||
echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/down_threshold
|
||||
echo <desired value> > /sys/class/mmc_host/mmcX/clk_scaling/enable
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2003 Russell King, All Rights Reserved.
|
||||
* Copyright (C) 2007-2008 Pierre Ossman
|
||||
* Copyright (C) 2010 Linus Walleij
|
||||
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -491,6 +492,163 @@ free:
|
|||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_alloc_host);
|
||||
|
||||
static ssize_t show_enable(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", mmc_can_scale_clk(host));
|
||||
}
|
||||
|
||||
static ssize_t store_enable(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value, freq;
|
||||
int retval = -EINVAL;
|
||||
|
||||
if (!host || !host->card || kstrtoul(buf, 0, &value))
|
||||
goto err;
|
||||
|
||||
if (value && !mmc_can_scale_clk(host)) {
|
||||
if (mmc_card_ddr_mode(host->card) ||
|
||||
mmc_card_hs200(host->card) ||
|
||||
mmc_card_uhs(host->card)) {
|
||||
host->caps2 |= MMC_CAP2_CLK_SCALE;
|
||||
mmc_init_clk_scaling(host);
|
||||
}
|
||||
|
||||
if (!mmc_can_scale_clk(host)) {
|
||||
host->caps2 &= ~MMC_CAP2_CLK_SCALE;
|
||||
goto err;
|
||||
}
|
||||
} else if (!value && mmc_can_scale_clk(host)) {
|
||||
host->caps2 &= ~MMC_CAP2_CLK_SCALE;
|
||||
mmc_disable_clk_scaling(host);
|
||||
|
||||
/* Set to max. frequency, since we are disabling */
|
||||
if (host->bus_ops && host->bus_ops->change_bus_speed) {
|
||||
freq = mmc_get_max_frequency(host);
|
||||
if (host->bus_ops->change_bus_speed(host, &freq))
|
||||
goto err;
|
||||
}
|
||||
host->clk_scaling.initialized = false;
|
||||
}
|
||||
return count;
|
||||
err:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static ssize_t show_up_threshold(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", host->clk_scaling.up_threshold);
|
||||
}
|
||||
|
||||
#define MAX_PERCENTAGE 100
|
||||
static ssize_t store_up_threshold(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.up_threshold = value;
|
||||
|
||||
pr_debug("%s: clkscale_up_thresh set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_down_threshold(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
host->clk_scaling.down_threshold);
|
||||
}
|
||||
|
||||
static ssize_t store_down_threshold(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value) || (value > MAX_PERCENTAGE))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.down_threshold = value;
|
||||
|
||||
pr_debug("%s: clkscale_down_thresh set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_polling(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%lu milliseconds\n",
|
||||
host->clk_scaling.polling_delay_ms);
|
||||
}
|
||||
|
||||
static ssize_t store_polling(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long value;
|
||||
|
||||
if (!host || kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
host->clk_scaling.polling_delay_ms = value;
|
||||
|
||||
pr_debug("%s: clkscale_polling_delay_ms set to %lu\n",
|
||||
mmc_hostname(host), value);
|
||||
return count;
|
||||
}
|
||||
|
||||
DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
|
||||
show_enable, store_enable);
|
||||
DEVICE_ATTR(polling_interval, S_IRUGO | S_IWUSR,
|
||||
show_polling, store_polling);
|
||||
DEVICE_ATTR(up_threshold, S_IRUGO | S_IWUSR,
|
||||
show_up_threshold, store_up_threshold);
|
||||
DEVICE_ATTR(down_threshold, S_IRUGO | S_IWUSR,
|
||||
show_down_threshold, store_down_threshold);
|
||||
|
||||
static struct attribute *clk_scaling_attrs[] = {
|
||||
&dev_attr_enable.attr,
|
||||
&dev_attr_up_threshold.attr,
|
||||
&dev_attr_down_threshold.attr,
|
||||
&dev_attr_polling_interval.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group clk_scaling_attr_grp = {
|
||||
.name = "clk_scaling",
|
||||
.attrs = clk_scaling_attrs,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_PERF_PROFILING
|
||||
static ssize_t
|
||||
show_perf(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
|
@ -582,6 +740,11 @@ int mmc_add_host(struct mmc_host *host)
|
|||
host->clk_scaling.down_threshold = 5;
|
||||
host->clk_scaling.polling_delay_ms = 100;
|
||||
|
||||
err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
|
||||
if (err)
|
||||
pr_err("%s: failed to create clk scale sysfs group with err %d\n",
|
||||
__func__, err);
|
||||
|
||||
err = sysfs_create_group(&host->parent->kobj, &dev_attr_grp);
|
||||
if (err)
|
||||
pr_err("%s: failed to create sysfs group with err %d\n",
|
||||
|
@ -615,7 +778,7 @@ void mmc_remove_host(struct mmc_host *host)
|
|||
mmc_remove_host_debugfs(host);
|
||||
#endif
|
||||
sysfs_remove_group(&host->parent->kobj, &dev_attr_grp);
|
||||
|
||||
sysfs_remove_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
|
||||
|
||||
device_del(&host->class_dev);
|
||||
|
||||
|
|
Loading…
Reference in a new issue