Merge "PM / devfreq: memlat: Add a core to memory frequency mapping table"
This commit is contained in:
commit
339e4ce018
3 changed files with 112 additions and 48 deletions
|
@ -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>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue