summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRohit Gupta <rohgup@codeaurora.org>2016-04-13 16:55:04 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-08-17 16:37:23 -0700
commit3087c5f20db68ae27013593f332de06a9ac114aa (patch)
tree2aaf1d917ff2ba6ba40d06f93ff952d239764dfd /drivers
parent573979dee2a76e4d7f61cb867c12afbaed7e1eb5 (diff)
PM / devfreq: memlat: Add a core to memory frequency mapping table
Add a core to memory frequency mapping table, which establishes a relationship between the core frequency and its corresponding bandwidth vote. The governor expects a "qcom,core-dev-table" table as part of a given memlat hardware monitor's device tree node. This table is read upon registration of the memlat governor. The table is then used to determine the memory bandwidth vote corresponding to the maximum of the core frequencies. CRs-Fixed: 1054146 Change-Id: I9df118da1433125b02c937bf1799a0944b110fac Signed-off-by: Rohit Gupta <rohgup@codeaurora.org> Signed-off-by: David Keitel <dkeitel@codeaurora.org> Suggested-by: Saravana Kannan <skannan@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/devfreq/governor_memlat.c146
-rw-r--r--drivers/devfreq/governor_memlat.h8
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