Merge "PM / devfreq: memlat: Add a core to memory frequency mapping table"

This commit is contained in:
Linux Build Service Account 2016-08-18 12:04:42 -07:00 committed by Gerrit - the friendly Code Review server
commit 339e4ce018
3 changed files with 112 additions and 48 deletions

View file

@ -7,10 +7,16 @@ Required properties:
- compatible: Must be "qcom,arm-memlat-mon" - compatible: Must be "qcom,arm-memlat-mon"
- qcom,cpulist: List of CPU phandles to be monitored in a cluster - qcom,cpulist: List of CPU phandles to be monitored in a cluster
- qcom,target-dev: The DT device that corresponds to this master port - qcom,target-dev: The DT device that corresponds to this master port
- qcom,core-dev-table: A mapping table of core frequency to a required bandwidth vote at the
given core frequency.
Example: Example:
qcom,arm-memlat-mon { qcom,arm-memlat-mon {
compatible = "qcom,arm-memlat-mon"; compatible = "qcom,arm-memlat-mon";
qcom,cpulist = <&CPU0 &CPU1>; qcom,cpulist = <&CPU0 &CPU1>;
qcom,target-dev = <&memlat0>; qcom,target-dev = <&memlat0>;
qcom,core-dev-table =
< 300000 1525>,
< 499200 3143>,
< 1881600 5859>;
}; };

View file

@ -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 * 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 * it under the terms of the GNU General Public License version 2 and
@ -35,8 +35,6 @@
struct memlat_node { struct memlat_node {
unsigned int ratio_ceil; unsigned int ratio_ceil;
unsigned int freq_thresh_mhz;
unsigned int mult_factor;
bool mon_started; bool mon_started;
struct list_head list; struct list_head list;
void *orig_data; void *orig_data;
@ -83,45 +81,25 @@ show_attr(__attr) \
store_attr(__attr, min, max) \ store_attr(__attr, min, max) \
static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr) 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; struct memlat_hwmon *hw = node->hw;
unsigned long max_freq = 0; struct core_dev_map *map = hw->freq_map;
unsigned int ratio; unsigned long freq = 0;
hw->get_cnt(hw); if (!map)
goto out;
for (i = 0; i < hw->num_cores; i++) { while (map->core_mhz && map->core_mhz < coref)
ratio = hw->core_stats[i].inst_count; map++;
if (!map->core_mhz)
map--;
freq = map->target_freq;
if (hw->core_stats[i].mem_count) out:
ratio /= hw->core_stats[i].mem_count; pr_debug("freq: %lu -> dev: %lu\n", coref, freq);
return freq;
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 (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);
return max_freq;
} }
static struct memlat_node *find_memlat_node(struct devfreq *df) 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, unsigned long *freq,
u32 *flag) u32 *flag)
{ {
unsigned long mhz; int i, lat_dev;
struct memlat_node *node = df->data; struct memlat_node *node = df->data;
struct memlat_hwmon *hw = node->hw;
unsigned long max_freq = 0;
unsigned int ratio;
mhz = compute_dev_vote(df); hw->get_cnt(hw);
*freq = mhz ? (mhz * node->mult_factor) : 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 > 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; return 0;
} }
gov_attr(ratio_ceil, 1U, 1000U); gov_attr(ratio_ceil, 1U, 10000U);
gov_attr(freq_thresh_mhz, 300U, 5000U);
gov_attr(mult_factor, 1U, 10U);
static struct attribute *dev_attr[] = { static struct attribute *dev_attr[] = {
&dev_attr_ratio_ceil.attr, &dev_attr_ratio_ceil.attr,
&dev_attr_freq_thresh_mhz.attr,
&dev_attr_mult_factor.attr,
NULL, NULL,
}; };
@ -295,6 +301,48 @@ static struct devfreq_governor devfreq_gov_memlat = {
.event_handler = devfreq_memlat_ev_handler, .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 register_memlat(struct device *dev, struct memlat_hwmon *hw)
{ {
int ret = 0; int ret = 0;
@ -311,10 +359,14 @@ int register_memlat(struct device *dev, struct memlat_hwmon *hw)
node->attr_grp = &dev_attr_group; node->attr_grp = &dev_attr_group;
node->ratio_ceil = 10; node->ratio_ceil = 10;
node->freq_thresh_mhz = 900;
node->mult_factor = 8;
node->hw = hw; 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); mutex_lock(&list_lock);
list_add_tail(&node->list, &memlat_list); list_add_tail(&node->list, &memlat_list);
mutex_unlock(&list_lock); mutex_unlock(&list_lock);

View file

@ -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 * 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 * it under the terms of the GNU General Public License version 2 and
@ -31,6 +31,11 @@ struct dev_stats {
unsigned long freq; unsigned long freq;
}; };
struct core_dev_map {
unsigned int core_mhz;
unsigned int target_freq;
};
/** /**
* struct memlat_hwmon - Memory Latency HW monitor info * struct memlat_hwmon - Memory Latency HW monitor info
* @start_hwmon: Start the HW monitoring * @start_hwmon: Start the HW monitoring
@ -63,6 +68,7 @@ struct memlat_hwmon {
struct dev_stats *core_stats; struct dev_stats *core_stats;
struct devfreq *df; struct devfreq *df;
struct core_dev_map *freq_map;
}; };
#ifdef CONFIG_DEVFREQ_GOV_MEMLAT #ifdef CONFIG_DEVFREQ_GOV_MEMLAT