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"
- 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,core-dev-table: A mapping table of core frequency to a required bandwidth vote at the
given core frequency.
Example:
qcom,arm-memlat-mon {
compatible = "qcom,arm-memlat-mon";
qcom,cpulist = <&CPU0 &CPU1>;
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
* 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;
struct core_dev_map *map = hw->freq_map;
unsigned long freq = 0;
hw->get_cnt(hw);
if (!map)
goto out;
for (i = 0; i < hw->num_cores; i++) {
ratio = hw->core_stats[i].inst_count;
while (map->core_mhz && map->core_mhz < coref)
map++;
if (!map->core_mhz)
map--;
freq = map->target_freq;
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 (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;
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;
mhz = compute_dev_vote(df);
*freq = mhz ? (mhz * node->mult_factor) : 0;
hw->get_cnt(hw);
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;
}
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);

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
* 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