diff options
| author | Chanwoo Choi <cw00.choi@samsung.com> | 2020-10-08 12:39:59 +0900 |
|---|---|---|
| committer | Chanwoo Choi <cw00.choi@samsung.com> | 2021-05-17 00:33:08 +0900 |
| commit | f3ff8a27ab2f41bffcc6d620e6f475a7a8822ccf (patch) | |
| tree | 1fac1d034048309afefc2e22aa97efd128efba0e | |
| parent | 6efb943b8616ec53a5e444193dccf1af9ad627b5 (diff) | |
| download | linux-f3ff8a27ab2f41bffcc6d620e6f475a7a8822ccf.tar.gz | |
PM / devfreq: Add new up_threshold and down_differential sysfs attrs
The up_threshold and down_differential variables were used to determine
the next frequency on simple_ondemand governor. It was possible to tune
two values by devfreq device driver. But on user-space, could not change
the them at the runtime for the tunning.
In order to support the tuning the up_threshold and down_differential
on user-space, add new following new sysfs attributes:
: /sys/class/devfreq/[devfreq dev name]/up_threshold (range: [0, 100])
: /sys/class/devfreq/[devfreq dev name]/down_differential (range: [0,100])
[Definition for governor attribute]
- DEVFREQ_GOV_ATTR_UP_THRESHOLD
: /sys/class/devfreq/[devfreq dev name]/up_threshold
- DEVFREQ_GOV_ATTR_DOWN_DIFF
: /sys/class/devfreq/[devfreq dev name]/down_differential
These attributes are used for only simple_ondemand governor until now
as following:
------------------------------------------------------------------------------
| simple | perfor | power | user | passive | tegra30
| ondemand | mance | save | space| |
------------------------------------------------------------------------------
(skip of other attrs) |
up_threshold | O | X | X | X | X | X
down_differential | O | X | X | X | X | X
------------------------------------------------------------------------------
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-devfreq | 31 | ||||
| -rw-r--r-- | drivers/devfreq/devfreq.c | 141 | ||||
| -rw-r--r-- | drivers/devfreq/exynos-bus.c | 12 | ||||
| -rw-r--r-- | drivers/devfreq/governor.h | 10 | ||||
| -rw-r--r-- | drivers/devfreq/governor_simpleondemand.c | 23 | ||||
| -rw-r--r-- | drivers/devfreq/rk3399_dmc.c | 7 | ||||
| -rw-r--r-- | drivers/memory/samsung/exynos5422-dmc.c | 12 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufs-qcom.c | 4 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 9 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 1 | ||||
| -rw-r--r-- | include/linux/devfreq.h | 34 |
11 files changed, 220 insertions, 64 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq index 5e6b74f304062..e71595c84f22c 100644 --- a/Documentation/ABI/testing/sysfs-class-devfreq +++ b/Documentation/ABI/testing/sysfs-class-devfreq @@ -129,3 +129,34 @@ Description: A list of governors that support the node: - simple_ondemand + +What: /sys/class/devfreq/.../up_threshold +Date: September 2020 +Contact: Chanwoo Choi <cw00.choi@samsung.com> +Description: + This ABI shows and stores the up_threshold by users. + If the load is over this value, the frequency jumps. + and if user stores less than down_differential, + the value is replaced with down_differential. + + The default value is 90 and valid value is [0,100]. + up_threshold > donw_differental must hold. + + A list of governors that support the node: + - simple_ondemand + +What: /sys/class/devfreq/.../down_differential +Date: September 2020 +Contact: Chanwoo Choi <cw00.choi@samsung.com> +Description: + This ABI shows and stores the down_differential by users. + If the load is under upthreshold - downdifferential, + the governor may consider slowing the frequency down + and if user stores larget than up_threshold + the value is replaced with up_threshold. + + The default value is 5 and valid value is [0,100]. + downdifferential < upthreshold must hold. + + A list of governors that support the node: + - simple_ondemand diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index fe08c46642f7c..c0d58d052755d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1843,6 +1843,109 @@ out: } static DEVICE_ATTR_RW(timer); +static ssize_t up_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = to_devfreq(dev); + + if (!df->profile) + return -EINVAL; + + return sprintf(buf, "%d\n", df->profile->up_threshold + ? df->profile->up_threshold + : DEVFREQ_UP_THRESHOLD); +} + +static ssize_t up_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + unsigned int value, down_differential; + int ret = 0; + + if (df->stop_polling) + return 0; + + if (!df->profile) + return -EINVAL; + + ret = sscanf(buf, "%u", &value); + if (ret != 1) + return -EINVAL; + + if (value > 100) + value = 100; + + down_differential = df->profile->down_differential + ? df->profile->down_differential + : DEVFREQ_DOWN_DIFFERENCTIAL; + if (value < down_differential) + value = down_differential; + + mutex_lock(&df->lock); + df->profile->up_threshold = value; + ret = update_devfreq(df); + mutex_unlock(&df->lock); + + if (ret) + dev_err(df->dev.parent, + "failed to update frequency from PM QoS (%d)\n", ret); + ret = count; + return ret; +} +static DEVICE_ATTR_RW(up_threshold); + +static ssize_t down_differential_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = to_devfreq(dev); + + if (!df->profile) + return -EINVAL; + + return sprintf(buf, "%d\n", df->profile->down_differential + ? df->profile->down_differential + : DEVFREQ_DOWN_DIFFERENCTIAL); +} + +static ssize_t down_differential_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + unsigned int value, up_threshold; + int ret = 0; + + if (df->stop_polling) + return 0; + + if (!df->profile) + return -EINVAL; + + ret = sscanf(buf, "%u", &value); + if (ret != 1) + return -EINVAL; + + up_threshold = df->profile->up_threshold + ? df->profile->up_threshold + : DEVFREQ_UP_THRESHOLD; + if (value > up_threshold) + value = up_threshold; + + mutex_lock(&df->lock); + df->profile->down_differential = value; + ret = update_devfreq(df); + mutex_unlock(&df->lock); + + if (ret) + dev_err(df->dev.parent, + "failed to update frequency from PM QoS (%d)\n", ret); + ret = count; + return ret; +} +static DEVICE_ATTR_RW(down_differential); + #define CREATE_SYSFS_FILE(df, name) \ { \ int ret; \ @@ -1861,6 +1964,10 @@ static void create_sysfs_files(struct devfreq *devfreq, CREATE_SYSFS_FILE(devfreq, polling_interval); if (IS_SUPPORTED_ATTR(gov->attrs, TIMER)) CREATE_SYSFS_FILE(devfreq, timer); + if (IS_SUPPORTED_ATTR(gov->attrs, UP_THRESHOLD)) + CREATE_SYSFS_FILE(devfreq, up_threshold); + if (IS_SUPPORTED_ATTR(gov->attrs, DOWN_DIFF)) + CREATE_SYSFS_FILE(devfreq, down_differential); } /* Remove the specific sysfs files which depend on each governor. */ @@ -1872,6 +1979,12 @@ static void remove_sysfs_files(struct devfreq *devfreq, &dev_attr_polling_interval.attr); if (IS_SUPPORTED_ATTR(gov->attrs, TIMER)) sysfs_remove_file(&devfreq->dev.kobj, &dev_attr_timer.attr); + if (IS_SUPPORTED_ATTR(gov->attrs, UP_THRESHOLD)) + sysfs_remove_file(&devfreq->dev.kobj, + &dev_attr_up_threshold.attr); + if (IS_SUPPORTED_ATTR(gov->attrs, DOWN_DIFF)) + sysfs_remove_file(&devfreq->dev.kobj, + &dev_attr_down_differential.attr); } /** @@ -1891,22 +2004,28 @@ static int devfreq_summary_show(struct seq_file *s, void *data) unsigned long cur_freq, min_freq, max_freq; unsigned int polling_ms; unsigned int timer; + unsigned int up_threshold; + unsigned int down_differential; - seq_printf(s, "%-30s %-30s %-15s %-10s %10s %12s %12s %12s\n", + seq_printf(s, "%-30s %-30s %-15s %-10s %10s %8s %9s %12s %12s %12s\n", "dev", "parent_dev", "governor", "timer", "polling_ms", + "up_thres", + "down_diff", "cur_freq_Hz", "min_freq_Hz", "max_freq_Hz"); - seq_printf(s, "%30s %30s %15s %10s %10s %12s %12s %12s\n", + seq_printf(s, "%30s %30s %15s %10s %10s %8s %9s %12s %12s %12s\n", "------------------------------", "------------------------------", "---------------", "----------", "----------", + "--------", + "---------", "------------", "------------", "------------"); @@ -1935,15 +2054,31 @@ static int devfreq_summary_show(struct seq_file *s, void *data) polling_ms = devfreq->profile->polling_ms; else polling_ms = 0; + + if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, UP_THRESHOLD)) + up_threshold = devfreq->profile->up_threshold + ? devfreq->profile->up_threshold + : DEVFREQ_UP_THRESHOLD; + else + up_threshold = 0; + + if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, DOWN_DIFF)) + down_differential = devfreq->profile->down_differential + ? devfreq->profile->down_differential + : DEVFREQ_DOWN_DIFFERENCTIAL; + else + down_differential = 0; mutex_unlock(&devfreq->lock); seq_printf(s, - "%-30s %-30s %-15s %-10s %10d %12ld %12ld %12ld\n", + "%-30s %-30s %-15s %-10s %10d %8d %9d %12ld %12ld %12ld\n", dev_name(&devfreq->dev), p_devfreq ? dev_name(&p_devfreq->dev) : "null", devfreq->governor->name, polling_ms ? timer_name[timer] : "null", polling_ms, + up_threshold, + down_differential, cur_freq, min_freq, max_freq); diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index e689101abc930..77d66f75f41b1 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -295,26 +295,20 @@ static int exynos_bus_profile_init(struct exynos_bus *bus, struct devfreq_dev_profile *profile) { struct device *dev = bus->dev; - struct devfreq_simple_ondemand_data *ondemand_data; int ret; /* Initialize the struct profile and governor data for parent device */ profile->polling_ms = 50; + profile->up_threshold = 40; + profile->down_differential = 5; profile->target = exynos_bus_target; profile->get_dev_status = exynos_bus_get_dev_status; profile->exit = exynos_bus_exit; - ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL); - if (!ondemand_data) - return -ENOMEM; - - ondemand_data->upthreshold = 40; - ondemand_data->downdifferential = 5; - /* Add devfreq device to monitor and handle the exynos bus */ bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, - ondemand_data); + NULL); if (IS_ERR(bus->devfreq)) { dev_err(dev, "failed to add devfreq device\n"); return PTR_ERR(bus->devfreq); diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h index 2d69a0ce6291b..86c9867b83ca1 100644 --- a/drivers/devfreq/governor.h +++ b/drivers/devfreq/governor.h @@ -43,9 +43,19 @@ * : Indicate polling_interval sysfs attribute * - DEVFREQ_GOV_ATTR_TIMER * : Indicate timer sysfs attribute + * - DEVFREQ_GOV_ATTR_UP_THRESHOLD + * : Indicate up_threshold sysfs attribute + * - DEVFREQ_GOV_ATTR_DOWN_DIFF + * : Indicate down_differential sysfs attribute */ #define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0) #define DEVFREQ_GOV_ATTR_TIMER BIT(1) +#define DEVFREQ_GOV_ATTR_UP_THRESHOLD BIT(2) +#define DEVFREQ_GOV_ATTR_DOWN_DIFF BIT(3) + +/* Default constants for DevFreq-Simple-Ondemand (DFSO) */ +#define DEVFREQ_UP_THRESHOLD (90) +#define DEVFREQ_DOWN_DIFFERENCTIAL (5) /** * struct devfreq_governor - Devfreq policy governor diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index d57b82a2b5707..93f6061667d99 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -12,18 +12,14 @@ #include <linux/math64.h> #include "governor.h" -/* Default constants for DevFreq-Simple-Ondemand (DFSO) */ -#define DFSO_UPTHRESHOLD (90) -#define DFSO_DOWNDIFFERENCTIAL (5) static int devfreq_simple_ondemand_func(struct devfreq *df, unsigned long *freq) { int err; struct devfreq_dev_status *stat; unsigned long long a, b; - unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; - unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; - struct devfreq_simple_ondemand_data *data = df->data; + unsigned int dfso_upthreshold = DEVFREQ_UP_THRESHOLD; + unsigned int dfso_downdifferential = DEVFREQ_DOWN_DIFFERENCTIAL; err = devfreq_update_stats(df); if (err) @@ -31,12 +27,11 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, stat = &df->last_status; - if (data) { - if (data->upthreshold) - dfso_upthreshold = data->upthreshold; - if (data->downdifferential) - dfso_downdifferential = data->downdifferential; - } + if (df->profile->up_threshold) + dfso_upthreshold = df->profile->up_threshold; + if (df->profile->down_differential) + dfso_downdifferential = df->profile->down_differential; + if (dfso_upthreshold > 100 || dfso_upthreshold < dfso_downdifferential) return -EINVAL; @@ -118,7 +113,9 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, static struct devfreq_governor devfreq_simple_ondemand = { .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL - | DEVFREQ_GOV_ATTR_TIMER, + | DEVFREQ_GOV_ATTR_TIMER + | DEVFREQ_GOV_ATTR_UP_THRESHOLD + | DEVFREQ_GOV_ATTR_DOWN_DIFF, .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, }; diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 293857ebfd75d..bf7492178f8c2 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -58,7 +58,6 @@ struct dram_timing { struct rk3399_dmcfreq { struct device *dev; struct devfreq *devfreq; - struct devfreq_simple_ondemand_data ondemand_data; struct clk *dmc_clk; struct devfreq_event_dev *edev; struct mutex lock; @@ -431,9 +430,9 @@ no_pmu: } of_property_read_u32(np, "upthreshold", - &data->ondemand_data.upthreshold); + &rk3399_devfreq_dmc_profile.up_threshold); of_property_read_u32(np, "downdifferential", - &data->ondemand_data.downdifferential); + &rk3399_devfreq_dmc_profile.down_differential); data->rate = clk_get_rate(data->dmc_clk); @@ -452,7 +451,7 @@ no_pmu: data->devfreq = devm_devfreq_add_device(dev, &rk3399_devfreq_dmc_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, - &data->ondemand_data); + NULL); if (IS_ERR(data->devfreq)) { ret = PTR_ERR(data->devfreq); goto err_free_opp; diff --git a/drivers/memory/samsung/exynos5422-dmc.c b/drivers/memory/samsung/exynos5422-dmc.c index 9c8318923ed0b..33336b12b5088 100644 --- a/drivers/memory/samsung/exynos5422-dmc.c +++ b/drivers/memory/samsung/exynos5422-dmc.c @@ -152,7 +152,6 @@ struct dmc_opp_table { struct exynos5_dmc { struct device *dev; struct devfreq *df; - struct devfreq_simple_ondemand_data gov_data; void __iomem *base_drexi0; void __iomem *base_drexi1; struct regmap *clk_regmap; @@ -1493,8 +1492,8 @@ static int exynos5_dmc_probe(struct platform_device *pdev) * Setup default thresholds for the devfreq governor. * The values are chosen based on experiments. */ - dmc->gov_data.upthreshold = 55; - dmc->gov_data.downdifferential = 5; + exynos5_dmc_df_profile.up_threshold = 55; + exynos5_dmc_df_profile.down_differential = 5; exynos5_dmc_enable_perf_events(dmc); @@ -1510,15 +1509,14 @@ static int exynos5_dmc_probe(struct platform_device *pdev) * Setup default thresholds for the devfreq governor. * The values are chosen based on experiments. */ - dmc->gov_data.upthreshold = 10; - dmc->gov_data.downdifferential = 5; - + exynos5_dmc_df_profile.up_threshold = 10; + exynos5_dmc_df_profile.down_differential = 5; exynos5_dmc_df_profile.polling_ms = 100; } dmc->df = devm_devfreq_add_device(dev, &exynos5_dmc_df_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, - &dmc->gov_data); + NULL); if (IS_ERR(dmc->df)) { ret = PTR_ERR(dmc->df); diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 2a3dd21da6a60..b9631861f7fbe 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -1461,8 +1461,8 @@ static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, d = (struct devfreq_simple_ondemand_data *)data; p->polling_ms = 60; - d->upthreshold = 70; - d->downdifferential = 5; + p->up_threshold = 70; + p->down_differential = 5; } #else static void ufs_qcom_config_scaling_param(struct ufs_hba *hba, diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3eb54937f1d8a..43592112520cb 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -1442,12 +1442,11 @@ static int ufshcd_devfreq_init(struct ufs_hba *hba) dev_pm_opp_add(hba->dev, clki->min_freq, 0); dev_pm_opp_add(hba->dev, clki->max_freq, 0); - ufshcd_vops_config_scaling_param(hba, &hba->vps->devfreq_profile, - &hba->vps->ondemand_data); + ufshcd_vops_config_scaling_param(hba, &hba->vps->devfreq_profile, NULL); devfreq = devfreq_add_device(hba->dev, &hba->vps->devfreq_profile, DEVFREQ_GOV_SIMPLE_ONDEMAND, - &hba->vps->ondemand_data); + NULL); if (IS_ERR(devfreq)) { ret = PTR_ERR(devfreq); dev_err(hba->dev, "Unable to register with devfreq %d\n", ret); @@ -7975,8 +7974,8 @@ static struct ufs_hba_variant_params ufs_hba_vps = { .devfreq_profile.polling_ms = 100, .devfreq_profile.target = ufshcd_devfreq_target, .devfreq_profile.get_dev_status = ufshcd_devfreq_get_dev_status, - .ondemand_data.upthreshold = 70, - .ondemand_data.downdifferential = 5, + .devfreq_profile.up_threshold = 70, + .devfreq_profile.down_differential = 5, }; static struct scsi_host_template ufshcd_driver_template = { diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 5eb66a8debc71..a9a309073af74 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -640,7 +640,6 @@ enum ufshcd_caps { struct ufs_hba_variant_params { struct devfreq_dev_profile devfreq_profile; - struct devfreq_simple_ondemand_data ondemand_data; u16 hba_enable_delay_us; u32 wb_flush_threshold; }; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 142474b4af963..12c5933d39864 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -102,12 +102,26 @@ struct devfreq_dev_status { * * @is_cooling_device: A self-explanatory boolean giving the device a * cooling effect property. + * @up_threshold: If the load is over this value, the frequency jumps. + * Specify 0 to use the default. Valid value = 0 to 100. + * @down_differential: If the load is under upthreshold - downdifferential, + * the governor may consider slowing the frequency down. + * Specify 0 to use the default. Valid value = 0 to 100. + * downdifferential < upthreshold must hold. */ struct devfreq_dev_profile { unsigned long initial_freq; unsigned int polling_ms; enum devfreq_timer timer; bool is_cooling_device; + * @up_threshold: If the load is over this value, the frequency jumps. + * Specify 0 to use the default. Valid value = 0 to 100. + * @down_differential: If the load is under upthreshold - downdifferential, + * the governor may consider slowing the frequency down. + * Specify 0 to use the default. Valid value = 0 to 100. + * downdifferential < upthreshold must hold. + unsigned int up_threshold; + unsigned int down_differential; int (*target)(struct device *dev, unsigned long *freq, u32 flags); int (*get_dev_status)(struct device *dev, @@ -267,26 +281,6 @@ struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node); struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, const char *phandle_name, int index); -#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) -/** - * struct devfreq_simple_ondemand_data - ``void *data`` fed to struct devfreq - * and devfreq_add_device - * @upthreshold: If the load is over this value, the frequency jumps. - * Specify 0 to use the default. Valid value = 0 to 100. - * @downdifferential: If the load is under upthreshold - downdifferential, - * the governor may consider slowing the frequency down. - * Specify 0 to use the default. Valid value = 0 to 100. - * downdifferential < upthreshold must hold. - * - * If the fed devfreq_simple_ondemand_data pointer is NULL to the governor, - * the governor uses the default values. - */ -struct devfreq_simple_ondemand_data { - unsigned int upthreshold; - unsigned int downdifferential; -}; -#endif - #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) /** * struct devfreq_passive_data - ``void *data`` fed to struct devfreq |
