Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 870b180a authored by Rohit Gupta's avatar Rohit Gupta
Browse files

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: default avatarRohit Gupta <rohgup@codeaurora.org>
Signed-off-by: default avatarDavid Keitel <dkeitel@codeaurora.org>
Suggested-by: default avatarSaravana Kannan <skannan@codeaurora.org>
parent 3536bd39
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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>;
	};
+99 −47
Original line number Diff line number Diff line
/*
 * 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)
@@ -223,23 +201,51 @@ static void gov_stop(struct devfreq *df)
static int devfreq_memlat_get_freq(struct devfreq *df,
					unsigned long *freq)
{
	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,
};

@@ -294,6 +300,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;
@@ -310,10 +358,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);
+7 −1
Original line number Diff line number Diff line
/*
 * 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