Loading Documentation/devicetree/bindings/devfreq/devbw.txt +33 −14 Original line number Diff line number Diff line Loading @@ -9,11 +9,11 @@ Required properties: - compatible: Must be "qcom,devbw" - qcom,src-dst-ports: A list of tuples where each tuple consists of a bus master port number and a bus slave port number. - qcom,bw-tbl: A list of meaningful instantaneous bandwidth values (in MB/s) that can be requested from the device master port to the slave port. The list of values depend on the supported bus/slave frequencies and the bus width. - operating-points-v2: A phandle to the OPP v2 table that holds meaningful instantaneous bandwidth values (in MB/s) that can be requested from the device master port to the slave port. The list of values depend on the supported bus/slave frequencies and the bus width. Optional properties: - qcom,active-only: Indicates that the bandwidth votes need to be Loading @@ -23,17 +23,36 @@ Optional properties: Example: bw_opp_table: bw-opp-table { compatible = "operating-points-v2"; opp-75 { opp-hz = /bits/ 64 < 572 >; /* 75 MHz */ }; opp-150 { opp-hz = /bits/ 64 < 1144 >; /* 150 MHz */ }; opp-200 { opp-hz = /bits/ 64 < 1525 >; /* 200 MHz */ }; opp-307 { opp-hz = /bits/ 64 < 2342 >; /* 307 MHz */ }; opp-460 { opp-hz = /bits/ 64 < 3509 >; /* 460 MHz */ }; opp-614 { opp-hz = /bits/ 64 < 4684 >; /* 614 MHz */ }; opp-800 { opp-hz = /bits/ 64 < 6103 >; /* 800 MHz */ }; opp-931 { opp-hz = /bits/ 64 < 7102 >; /* 931 MHz */ }; }; qcom,cpubw { compatible = "qcom,devbw"; qcom,src-dst-ports = <1 512>, <2 512>; qcom,active-only; qcom,bw-tbl = < 572 /* 75 MHz */ >, < 1144 /* 150 MHz */ >, < 1525 /* 200 MHz */ >, < 2342 /* 307 MHz */ >, < 3509 /* 460 MHz */ >, < 4684 /* 614 MHz */ >, < 6103 /* 800 MHz */ >, < 7102 /* 931 MHz */ >; operating-points-v2 = <&bw_opp_table>; }; drivers/devfreq/devfreq.c +13 −1 Original line number Diff line number Diff line Loading @@ -542,6 +542,7 @@ static void devfreq_dev_release(struct device *dev) devfreq->profile->exit(devfreq->dev.parent); mutex_destroy(&devfreq->lock); mutex_destroy(&devfreq->sysfs_lock); kfree(devfreq); } Loading Loading @@ -584,6 +585,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } mutex_init(&devfreq->lock); mutex_init(&devfreq->sysfs_lock); mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; Loading Loading @@ -1003,12 +1005,13 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, goto out; } mutex_lock(&df->sysfs_lock); if (df->governor) { ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); if (ret) { dev_warn(dev, "%s: Governor %s not stopped(%d)\n", __func__, df->governor->name, ret); goto out; goto gov_stop_out; } } prev_gov = df->governor; Loading @@ -1026,6 +1029,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, NULL); } } gov_stop_out: mutex_unlock(&df->sysfs_lock); out: mutex_unlock(&devfreq_list_lock); Loading Loading @@ -1120,8 +1126,10 @@ static ssize_t polling_interval_store(struct device *dev, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; mutex_unlock(&df->sysfs_lock); return ret; } Loading @@ -1139,6 +1147,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); max = df->max_freq; if (value && max && value > max) { Loading @@ -1151,6 +1160,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); mutex_unlock(&df->sysfs_lock); return ret; } Loading @@ -1174,6 +1184,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); min = df->min_freq; if (value && min && value < min) { Loading @@ -1186,6 +1197,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); mutex_unlock(&df->sysfs_lock); return ret; } static DEVICE_ATTR_RW(min_freq); Loading drivers/devfreq/devfreq_devbw.c +10 −47 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2014, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2014, 2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "devbw: " fmt Loading Loading @@ -70,33 +70,15 @@ static int set_bw(struct device *dev, int new_ib, int new_ab) return ret; } static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, u32 flags) { int i; unsigned long atmost, atleast, f; atmost = p->freq_table[0]; atleast = p->freq_table[p->max_state-1]; for (i = 0; i < p->max_state; i++) { f = p->freq_table[i]; if (f <= *freq) atmost = max(f, atmost); if (f >= *freq) atleast = min(f, atleast); } if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) *freq = atmost; else *freq = atleast; } static int devbw_target(struct device *dev, unsigned long *freq, u32 flags) { struct dev_data *d = dev_get_drvdata(dev); struct dev_pm_opp *opp; opp = devfreq_recommended_opp(dev, freq, flags); if (!IS_ERR(opp)) dev_pm_opp_put(opp); find_freq(&d->dp, freq, flags); return set_bw(dev, *freq, d->gov_ab); } Loading @@ -110,14 +92,13 @@ static int devbw_get_dev_status(struct device *dev, } #define PROP_PORTS "qcom,src-dst-ports" #define PROP_TBL "qcom,bw-tbl" #define PROP_ACTIVE "qcom,active-only" int devfreq_add_devbw(struct device *dev) { struct dev_data *d; struct devfreq_dev_profile *p; u32 *data, ports[MAX_PATHS * 2]; u32 ports[MAX_PATHS * 2]; const char *gov_name; int ret, len, i, num_paths; Loading Loading @@ -166,27 +147,9 @@ int devfreq_add_devbw(struct device *dev) p->target = devbw_target; p->get_dev_status = devbw_get_dev_status; if (of_find_property(dev->of_node, PROP_TBL, &len)) { len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) return -ENOMEM; ret = of_property_read_u32_array(dev->of_node, PROP_TBL, data, len); ret = dev_pm_opp_of_add_table(dev); if (ret) return ret; for (i = 0; i < len; i++) p->freq_table[i] = data[i]; p->max_state = len; } dev_err(dev, "Couldn't parse OPP table:%d\n", ret); d->bus_client = msm_bus_scale_register_client(&d->bw_data); if (!d->bus_client) { Loading drivers/devfreq/governor_memlat.c +71 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "mem_lat: " fmt Loading Loading @@ -35,6 +35,7 @@ struct memlat_node { struct memlat_hwmon *hw; struct devfreq_governor *gov; struct attribute_group *attr_grp; unsigned long resume_freq; }; static LIST_HEAD(memlat_list); Loading Loading @@ -204,6 +205,39 @@ static int gov_start(struct devfreq *df) return ret; } static int gov_suspend(struct devfreq *df) { struct memlat_node *node = df->data; unsigned long prev_freq = df->previous_freq; node->mon_started = false; devfreq_monitor_suspend(df); mutex_lock(&df->lock); update_devfreq(df); mutex_unlock(&df->lock); node->resume_freq = max(prev_freq, 1UL); return 0; } static int gov_resume(struct devfreq *df) { struct memlat_node *node = df->data; mutex_lock(&df->lock); update_devfreq(df); mutex_unlock(&df->lock); node->resume_freq = 0; devfreq_monitor_resume(df); node->mon_started = true; return 0; } static void gov_stop(struct devfreq *df) { struct memlat_node *node = df->data; Loading @@ -225,6 +259,18 @@ static int devfreq_memlat_get_freq(struct devfreq *df, unsigned long max_freq = 0; unsigned int ratio; /* * node->resume_freq is set to 0 at the end of resume (after the update) * and is set to df->prev_freq at the end of suspend (after the update). * This function will be called as part of the update_devfreq call in * both scenarios. As a result, this block will cause a 0 vote during * suspend and a vote for df->prev_freq during resume. */ if (!node->mon_started) { *freq = node->resume_freq; return 0; } hw->get_cnt(hw); for (i = 0; i < hw->num_cores; i++) { Loading Loading @@ -323,6 +369,30 @@ static int devfreq_memlat_ev_handler(struct devfreq *df, "Disabled Memory Latency governor\n"); break; case DEVFREQ_GOV_SUSPEND: ret = gov_suspend(df); if (ret) { dev_err(df->dev.parent, "Unable to suspend memlat governor (%d)\n", ret); return ret; } dev_dbg(df->dev.parent, "Suspended memlat governor\n"); break; case DEVFREQ_GOV_RESUME: ret = gov_resume(df); if (ret) { dev_err(df->dev.parent, "Unable to resume memlat governor (%d)\n", ret); return ret; } dev_dbg(df->dev.parent, "Resumed memlat governor\n"); break; case DEVFREQ_GOV_INTERVAL: sample_ms = *(unsigned int *)data; sample_ms = max(MIN_MS, sample_ms); Loading include/linux/devfreq.h +1 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,7 @@ struct devfreq { struct list_head node; struct mutex lock; struct mutex sysfs_lock; struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; Loading Loading
Documentation/devicetree/bindings/devfreq/devbw.txt +33 −14 Original line number Diff line number Diff line Loading @@ -9,11 +9,11 @@ Required properties: - compatible: Must be "qcom,devbw" - qcom,src-dst-ports: A list of tuples where each tuple consists of a bus master port number and a bus slave port number. - qcom,bw-tbl: A list of meaningful instantaneous bandwidth values (in MB/s) that can be requested from the device master port to the slave port. The list of values depend on the supported bus/slave frequencies and the bus width. - operating-points-v2: A phandle to the OPP v2 table that holds meaningful instantaneous bandwidth values (in MB/s) that can be requested from the device master port to the slave port. The list of values depend on the supported bus/slave frequencies and the bus width. Optional properties: - qcom,active-only: Indicates that the bandwidth votes need to be Loading @@ -23,17 +23,36 @@ Optional properties: Example: bw_opp_table: bw-opp-table { compatible = "operating-points-v2"; opp-75 { opp-hz = /bits/ 64 < 572 >; /* 75 MHz */ }; opp-150 { opp-hz = /bits/ 64 < 1144 >; /* 150 MHz */ }; opp-200 { opp-hz = /bits/ 64 < 1525 >; /* 200 MHz */ }; opp-307 { opp-hz = /bits/ 64 < 2342 >; /* 307 MHz */ }; opp-460 { opp-hz = /bits/ 64 < 3509 >; /* 460 MHz */ }; opp-614 { opp-hz = /bits/ 64 < 4684 >; /* 614 MHz */ }; opp-800 { opp-hz = /bits/ 64 < 6103 >; /* 800 MHz */ }; opp-931 { opp-hz = /bits/ 64 < 7102 >; /* 931 MHz */ }; }; qcom,cpubw { compatible = "qcom,devbw"; qcom,src-dst-ports = <1 512>, <2 512>; qcom,active-only; qcom,bw-tbl = < 572 /* 75 MHz */ >, < 1144 /* 150 MHz */ >, < 1525 /* 200 MHz */ >, < 2342 /* 307 MHz */ >, < 3509 /* 460 MHz */ >, < 4684 /* 614 MHz */ >, < 6103 /* 800 MHz */ >, < 7102 /* 931 MHz */ >; operating-points-v2 = <&bw_opp_table>; };
drivers/devfreq/devfreq.c +13 −1 Original line number Diff line number Diff line Loading @@ -542,6 +542,7 @@ static void devfreq_dev_release(struct device *dev) devfreq->profile->exit(devfreq->dev.parent); mutex_destroy(&devfreq->lock); mutex_destroy(&devfreq->sysfs_lock); kfree(devfreq); } Loading Loading @@ -584,6 +585,7 @@ struct devfreq *devfreq_add_device(struct device *dev, } mutex_init(&devfreq->lock); mutex_init(&devfreq->sysfs_lock); mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; Loading Loading @@ -1003,12 +1005,13 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, goto out; } mutex_lock(&df->sysfs_lock); if (df->governor) { ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL); if (ret) { dev_warn(dev, "%s: Governor %s not stopped(%d)\n", __func__, df->governor->name, ret); goto out; goto gov_stop_out; } } prev_gov = df->governor; Loading @@ -1026,6 +1029,9 @@ static ssize_t governor_store(struct device *dev, struct device_attribute *attr, NULL); } } gov_stop_out: mutex_unlock(&df->sysfs_lock); out: mutex_unlock(&devfreq_list_lock); Loading Loading @@ -1120,8 +1126,10 @@ static ssize_t polling_interval_store(struct device *dev, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value); ret = count; mutex_unlock(&df->sysfs_lock); return ret; } Loading @@ -1139,6 +1147,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); max = df->max_freq; if (value && max && value > max) { Loading @@ -1151,6 +1160,7 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); mutex_unlock(&df->sysfs_lock); return ret; } Loading @@ -1174,6 +1184,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; mutex_lock(&df->sysfs_lock); mutex_lock(&df->lock); min = df->min_freq; if (value && min && value < min) { Loading @@ -1186,6 +1197,7 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, ret = count; unlock: mutex_unlock(&df->lock); mutex_unlock(&df->sysfs_lock); return ret; } static DEVICE_ATTR_RW(min_freq); Loading
drivers/devfreq/devfreq_devbw.c +10 −47 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013-2014, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2013-2014, 2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "devbw: " fmt Loading Loading @@ -70,33 +70,15 @@ static int set_bw(struct device *dev, int new_ib, int new_ab) return ret; } static void find_freq(struct devfreq_dev_profile *p, unsigned long *freq, u32 flags) { int i; unsigned long atmost, atleast, f; atmost = p->freq_table[0]; atleast = p->freq_table[p->max_state-1]; for (i = 0; i < p->max_state; i++) { f = p->freq_table[i]; if (f <= *freq) atmost = max(f, atmost); if (f >= *freq) atleast = min(f, atleast); } if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) *freq = atmost; else *freq = atleast; } static int devbw_target(struct device *dev, unsigned long *freq, u32 flags) { struct dev_data *d = dev_get_drvdata(dev); struct dev_pm_opp *opp; opp = devfreq_recommended_opp(dev, freq, flags); if (!IS_ERR(opp)) dev_pm_opp_put(opp); find_freq(&d->dp, freq, flags); return set_bw(dev, *freq, d->gov_ab); } Loading @@ -110,14 +92,13 @@ static int devbw_get_dev_status(struct device *dev, } #define PROP_PORTS "qcom,src-dst-ports" #define PROP_TBL "qcom,bw-tbl" #define PROP_ACTIVE "qcom,active-only" int devfreq_add_devbw(struct device *dev) { struct dev_data *d; struct devfreq_dev_profile *p; u32 *data, ports[MAX_PATHS * 2]; u32 ports[MAX_PATHS * 2]; const char *gov_name; int ret, len, i, num_paths; Loading Loading @@ -166,27 +147,9 @@ int devfreq_add_devbw(struct device *dev) p->target = devbw_target; p->get_dev_status = devbw_get_dev_status; if (of_find_property(dev->of_node, PROP_TBL, &len)) { len /= sizeof(*data); data = devm_kzalloc(dev, len * sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; p->freq_table = devm_kzalloc(dev, len * sizeof(*p->freq_table), GFP_KERNEL); if (!p->freq_table) return -ENOMEM; ret = of_property_read_u32_array(dev->of_node, PROP_TBL, data, len); ret = dev_pm_opp_of_add_table(dev); if (ret) return ret; for (i = 0; i < len; i++) p->freq_table[i] = data[i]; p->max_state = len; } dev_err(dev, "Couldn't parse OPP table:%d\n", ret); d->bus_client = msm_bus_scale_register_client(&d->bw_data); if (!d->bus_client) { Loading
drivers/devfreq/governor_memlat.c +71 −1 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2015-2018, 2019, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "mem_lat: " fmt Loading Loading @@ -35,6 +35,7 @@ struct memlat_node { struct memlat_hwmon *hw; struct devfreq_governor *gov; struct attribute_group *attr_grp; unsigned long resume_freq; }; static LIST_HEAD(memlat_list); Loading Loading @@ -204,6 +205,39 @@ static int gov_start(struct devfreq *df) return ret; } static int gov_suspend(struct devfreq *df) { struct memlat_node *node = df->data; unsigned long prev_freq = df->previous_freq; node->mon_started = false; devfreq_monitor_suspend(df); mutex_lock(&df->lock); update_devfreq(df); mutex_unlock(&df->lock); node->resume_freq = max(prev_freq, 1UL); return 0; } static int gov_resume(struct devfreq *df) { struct memlat_node *node = df->data; mutex_lock(&df->lock); update_devfreq(df); mutex_unlock(&df->lock); node->resume_freq = 0; devfreq_monitor_resume(df); node->mon_started = true; return 0; } static void gov_stop(struct devfreq *df) { struct memlat_node *node = df->data; Loading @@ -225,6 +259,18 @@ static int devfreq_memlat_get_freq(struct devfreq *df, unsigned long max_freq = 0; unsigned int ratio; /* * node->resume_freq is set to 0 at the end of resume (after the update) * and is set to df->prev_freq at the end of suspend (after the update). * This function will be called as part of the update_devfreq call in * both scenarios. As a result, this block will cause a 0 vote during * suspend and a vote for df->prev_freq during resume. */ if (!node->mon_started) { *freq = node->resume_freq; return 0; } hw->get_cnt(hw); for (i = 0; i < hw->num_cores; i++) { Loading Loading @@ -323,6 +369,30 @@ static int devfreq_memlat_ev_handler(struct devfreq *df, "Disabled Memory Latency governor\n"); break; case DEVFREQ_GOV_SUSPEND: ret = gov_suspend(df); if (ret) { dev_err(df->dev.parent, "Unable to suspend memlat governor (%d)\n", ret); return ret; } dev_dbg(df->dev.parent, "Suspended memlat governor\n"); break; case DEVFREQ_GOV_RESUME: ret = gov_resume(df); if (ret) { dev_err(df->dev.parent, "Unable to resume memlat governor (%d)\n", ret); return ret; } dev_dbg(df->dev.parent, "Resumed memlat governor\n"); break; case DEVFREQ_GOV_INTERVAL: sample_ms = *(unsigned int *)data; sample_ms = max(MIN_MS, sample_ms); Loading
include/linux/devfreq.h +1 −0 Original line number Diff line number Diff line Loading @@ -149,6 +149,7 @@ struct devfreq { struct list_head node; struct mutex lock; struct mutex sysfs_lock; struct device dev; struct devfreq_dev_profile *profile; const struct devfreq_governor *governor; Loading