diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/devfreq/governor_memlat.c | 146 | ||||
-rw-r--r-- | drivers/devfreq/governor_memlat.h | 8 |
2 files changed, 106 insertions, 48 deletions
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c index 2851e9935fab..010f9defe33e 100644 --- a/drivers/devfreq/governor_memlat.c +++ b/drivers/devfreq/governor_memlat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2016, 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 and @@ -35,8 +35,6 @@ struct memlat_node { unsigned int ratio_ceil; - unsigned int freq_thresh_mhz; - unsigned int mult_factor; bool mon_started; struct list_head list; void *orig_data; @@ -83,45 +81,25 @@ show_attr(__attr) \ store_attr(__attr, min, max) \ static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) -static unsigned long compute_dev_vote(struct devfreq *df) +static unsigned long core_to_dev_freq(struct memlat_node *node, + unsigned long coref) { - int i, lat_dev; - struct memlat_node *node = df->data; struct memlat_hwmon *hw = node->hw; - unsigned long max_freq = 0; - unsigned int ratio; - - hw->get_cnt(hw); + struct core_dev_map *map = hw->freq_map; + unsigned long freq = 0; - for (i = 0; i < hw->num_cores; i++) { - ratio = hw->core_stats[i].inst_count; - - if (hw->core_stats[i].mem_count) - ratio /= hw->core_stats[i].mem_count; - - trace_memlat_dev_meas(dev_name(df->dev.parent), - hw->core_stats[i].id, - hw->core_stats[i].inst_count, - hw->core_stats[i].mem_count, - hw->core_stats[i].freq, ratio); - - if (ratio && ratio <= node->ratio_ceil - && hw->core_stats[i].freq >= node->freq_thresh_mhz - && hw->core_stats[i].freq > max_freq) { - lat_dev = i; - max_freq = hw->core_stats[i].freq; - } - } + if (!map) + goto out; - if (max_freq) - trace_memlat_dev_update(dev_name(df->dev.parent), - hw->core_stats[lat_dev].id, - hw->core_stats[lat_dev].inst_count, - hw->core_stats[lat_dev].mem_count, - hw->core_stats[lat_dev].freq, - max_freq * node->mult_factor); + while (map->core_mhz && map->core_mhz < coref) + map++; + if (!map->core_mhz) + map--; + freq = map->target_freq; - return max_freq; +out: + pr_debug("freq: %lu -> dev: %lu\n", coref, freq); + return freq; } static struct memlat_node *find_memlat_node(struct devfreq *df) @@ -224,23 +202,51 @@ static int devfreq_memlat_get_freq(struct devfreq *df, unsigned long *freq, u32 *flag) { - unsigned long mhz; + int i, lat_dev; struct memlat_node *node = df->data; + struct memlat_hwmon *hw = node->hw; + unsigned long max_freq = 0; + unsigned int ratio; + + hw->get_cnt(hw); + + for (i = 0; i < hw->num_cores; i++) { + ratio = hw->core_stats[i].inst_count; - mhz = compute_dev_vote(df); - *freq = mhz ? (mhz * node->mult_factor) : 0; + if (hw->core_stats[i].mem_count) + ratio /= hw->core_stats[i].mem_count; + + trace_memlat_dev_meas(dev_name(df->dev.parent), + hw->core_stats[i].id, + hw->core_stats[i].inst_count, + hw->core_stats[i].mem_count, + hw->core_stats[i].freq, ratio); + + if (ratio && ratio <= node->ratio_ceil + && hw->core_stats[i].freq > max_freq) { + lat_dev = i; + max_freq = hw->core_stats[i].freq; + } + } + + if (max_freq) { + max_freq = core_to_dev_freq(node, max_freq); + trace_memlat_dev_update(dev_name(df->dev.parent), + hw->core_stats[lat_dev].id, + hw->core_stats[lat_dev].inst_count, + hw->core_stats[lat_dev].mem_count, + hw->core_stats[lat_dev].freq, + max_freq); + } + *freq = max_freq; return 0; } -gov_attr(ratio_ceil, 1U, 1000U); -gov_attr(freq_thresh_mhz, 300U, 5000U); -gov_attr(mult_factor, 1U, 10U); +gov_attr(ratio_ceil, 1U, 10000U); static struct attribute *dev_attr[] = { &dev_attr_ratio_ceil.attr, - &dev_attr_freq_thresh_mhz.attr, - &dev_attr_mult_factor.attr, NULL, }; @@ -295,6 +301,48 @@ static struct devfreq_governor devfreq_gov_memlat = { .event_handler = devfreq_memlat_ev_handler, }; +#define NUM_COLS 2 +static struct core_dev_map *init_core_dev_map(struct device *dev, + char *prop_name) +{ + int len, nf, i, j; + u32 data; + struct core_dev_map *tbl; + int ret; + + if (!of_find_property(dev->of_node, prop_name, &len)) + return NULL; + len /= sizeof(data); + + if (len % NUM_COLS || len == 0) + return NULL; + nf = len / NUM_COLS; + + tbl = devm_kzalloc(dev, (nf + 1) * sizeof(struct core_dev_map), + GFP_KERNEL); + if (!tbl) + return NULL; + + for (i = 0, j = 0; i < nf; i++, j += 2) { + ret = of_property_read_u32_index(dev->of_node, prop_name, j, + &data); + if (ret) + return NULL; + tbl[i].core_mhz = data / 1000; + + ret = of_property_read_u32_index(dev->of_node, prop_name, j + 1, + &data); + if (ret) + return NULL; + tbl[i].target_freq = data; + pr_debug("Entry%d CPU:%u, Dev:%u\n", i, tbl[i].core_mhz, + tbl[i].target_freq); + } + tbl[i].core_mhz = 0; + + return tbl; +} + int register_memlat(struct device *dev, struct memlat_hwmon *hw) { int ret = 0; @@ -311,10 +359,14 @@ int register_memlat(struct device *dev, struct memlat_hwmon *hw) node->attr_grp = &dev_attr_group; node->ratio_ceil = 10; - node->freq_thresh_mhz = 900; - node->mult_factor = 8; node->hw = hw; + hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table"); + if (!hw->freq_map) { + dev_err(dev, "Couldn't find the core-dev freq table!\n"); + return -EINVAL; + } + mutex_lock(&list_lock); list_add_tail(&node->list, &memlat_list); mutex_unlock(&list_lock); diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h index 19a35009a4a0..a0e52a0997ad 100644 --- a/drivers/devfreq/governor_memlat.h +++ b/drivers/devfreq/governor_memlat.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2016, 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 and @@ -31,6 +31,11 @@ struct dev_stats { unsigned long freq; }; +struct core_dev_map { + unsigned int core_mhz; + unsigned int target_freq; +}; + /** * struct memlat_hwmon - Memory Latency HW monitor info * @start_hwmon: Start the HW monitoring @@ -63,6 +68,7 @@ struct memlat_hwmon { struct dev_stats *core_stats; struct devfreq *df; + struct core_dev_map *freq_map; }; #ifdef CONFIG_DEVFREQ_GOV_MEMLAT |