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

Commit 5365c43f authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "PM / devfreq: cache_hwmon: Use array for reporting monitor stats"

parents 94bfbcf6 60896a51
Loading
Loading
Loading
Loading
+48 −13
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spinlock.h>
#include "governor_bw_hwmon.h"

@@ -31,11 +32,17 @@
#define MON_MASK(m)		((m)->base + 0x298)
#define MON_MATCH(m)		((m)->base + 0x29C)

struct bwmon_spec {
	bool wrap_on_thres;
	bool overflow;
};

struct bwmon {
	void __iomem *base;
	void __iomem *global_base;
	unsigned int mport;
	unsigned int irq;
	const struct bwmon_spec *spec;
	struct device *dev;
	struct bw_hwmon hw;
};
@@ -94,7 +101,7 @@ static void mon_irq_disable(struct bwmon *m)
	writel_relaxed(val, MON_INT_EN(m));
}

static int mon_irq_status(struct bwmon *m)
static unsigned int mon_irq_status(struct bwmon *m)
{
	u32 mval;

@@ -103,12 +110,12 @@ static int mon_irq_status(struct bwmon *m)
	dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval,
			readl_relaxed(GLB_INT_STATUS(m)));

	return mval & 0x1;
	return mval;
}

static void mon_irq_clear(struct bwmon *m)
{
	writel_relaxed(0x1, MON_INT_CLR(m));
	writel_relaxed(0x3, MON_INT_CLR(m));
	/* Ensure the monitor IRQ is clear before clearing GLB IRQ */
	mb();
	writel_relaxed(1 << m->mport, GLB_INT_CLR(m));
@@ -127,14 +134,22 @@ static u32 mon_get_limit(struct bwmon *m)
	return readl_relaxed(MON_THRES(m));
}

#define THRES_HIT(status)	(status & BIT(0))
#define OVERFLOW(status)	(status & BIT(1))
static unsigned long mon_get_count(struct bwmon *m)
{
	unsigned long count;
	unsigned long count, status;

	count = readl_relaxed(MON_CNT(m));
	status = mon_irq_status(m);

	dev_dbg(m->dev, "Counter: %08lx\n", count);
	if (mon_irq_status(m))

	if (OVERFLOW(status) && m->spec->overflow)
		count += 0xFFFFFFFF;
	if (THRES_HIT(status) && m->spec->wrap_on_thres)
		count += mon_get_limit(m);

	dev_dbg(m->dev, "Actual Count: %08lx\n", count);

	return count;
@@ -173,11 +188,17 @@ static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw,

	mbps = mon_get_count(m);
	mbps = bytes_to_mbps(mbps, us);

	/*
	 * The fudging of mbps when calculating limit is to workaround a HW
	 * design issue. Needs further tuning.
	 * If the counter wraps on thres, don't set the thres too low.
	 * Setting it too low runs the risk of the counter wrapping around
	 * multiple times before the IRQ is processed.
	 */
	if (likely(!m->spec->wrap_on_thres))
		limit = mbps_to_bytes(mbps, sample_ms, tol);
	else
		limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol);

	mon_set_limit(m, limit);

	mon_clear(m);
@@ -273,11 +294,23 @@ static int resume_bw_hwmon(struct bw_hwmon *hw)

/*************************************************************************/

static const struct bwmon_spec spec[] = {
	{ .wrap_on_thres = true, .overflow = false },
	{ .wrap_on_thres = false, .overflow = true },
};

static const struct of_device_id bimc_bwmon_match_table[] = {
	{ .compatible = "qcom,bimc-bwmon", .data = &spec[0] },
	{ .compatible = "qcom,bimc-bwmon2", .data = &spec[1] },
	{}
};

static int bimc_bwmon_driver_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct resource *res;
	struct bwmon *m;
	const struct of_device_id *id;
	int ret;
	u32 data;

@@ -293,6 +326,13 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
	}
	m->mport = data;

	id = of_match_device(bimc_bwmon_match_table, dev);
	if (!id) {
		dev_err(dev, "Unknown device type!\n");
		return -ENODEV;
	}
	m->spec = id->data;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base");
	if (!res) {
		dev_err(dev, "base not found!\n");
@@ -339,11 +379,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev)
	return 0;
}

static const struct of_device_id bimc_bwmon_match_table[] = {
	{ .compatible = "qcom,bimc-bwmon" },
	{}
};

static struct platform_driver bimc_bwmon_driver = {
	.probe = bimc_bwmon_driver_probe,
	.driver = {
+170 −79
Original line number Diff line number Diff line
@@ -20,14 +20,41 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/devfreq.h>
#include <trace/events/power.h>
#include "governor.h"
#include "governor_cache_hwmon.h"

struct cache_hwmon_node {
	unsigned int cycles_per_low_req;
	unsigned int cycles_per_med_req;
	unsigned int cycles_per_high_req;
	unsigned int min_busy;
	unsigned int max_busy;
	unsigned int tolerance_mrps;
	unsigned int guard_band_mhz;
	unsigned int decay_rate;
	unsigned long prev_mhz;
	ktime_t prev_ts;
	bool mon_started;
	struct list_head list;
	void *orig_data;
	struct cache_hwmon *hw;
	struct attribute_group *attr_grp;
};

static LIST_HEAD(cache_hwmon_list);
static DEFINE_MUTEX(list_lock);

static int use_cnt;
static DEFINE_MUTEX(state_lock);

#define show_attr(name) \
static ssize_t show_##name(struct device *dev,				\
			struct device_attribute *attr, char *buf)	\
{									\
	return scnprintf(buf, PAGE_SIZE, "%u\n", name);			\
	struct devfreq *df = to_devfreq(dev);				\
	struct cache_hwmon_node *hw = df->data;				\
	return scnprintf(buf, PAGE_SIZE, "%u\n", hw->name);		\
}

#define store_attr(name, _min, _max) \
@@ -37,12 +64,14 @@ static ssize_t store_##name(struct device *dev, \
{									\
	int ret;							\
	unsigned int val;						\
	struct devfreq *df = to_devfreq(dev);				\
	struct cache_hwmon_node *hw = df->data;				\
	ret = kstrtoint(buf, 10, &val);					\
	if (ret)							\
		return ret;						\
	val = max(val, _min);						\
	val = min(val, _max);						\
	name = val;							\
	hw->name = val;							\
	return count;							\
}

@@ -51,28 +80,31 @@ show_attr(__attr) \
store_attr(__attr, (min), (max))	\
static DEVICE_ATTR(__attr, 0644, show_##__attr, store_##__attr)


static struct cache_hwmon *hw;
static unsigned int cycles_per_low_req;
static unsigned int cycles_per_med_req = 20;
static unsigned int cycles_per_high_req = 35;
static unsigned int min_busy = 100;
static unsigned int max_busy = 100;
static unsigned int tolerance_mrps = 5;
static unsigned int guard_band_mhz = 100;
static unsigned int decay_rate = 90;

#define MIN_MS	10U
#define MAX_MS	500U
static unsigned int sample_ms = 50;
static unsigned long prev_mhz;
static ktime_t prev_ts;

static unsigned long measure_mrps_and_set_irq(struct devfreq *df,
static struct cache_hwmon_node *find_hwmon_node(struct devfreq *df)
{
	struct cache_hwmon_node *node, *found = NULL;

	mutex_lock(&list_lock);
	list_for_each_entry(node, &cache_hwmon_list, list)
		if (node->hw->dev == df->dev.parent ||
		    node->hw->of_node == df->dev.parent->of_node) {
			found = node;
			break;
		}
	mutex_unlock(&list_lock);

	return found;
}

static unsigned long measure_mrps_and_set_irq(struct cache_hwmon_node *node,
			struct mrps_stats *stat)
{
	ktime_t ts;
	unsigned int us;
	struct cache_hwmon *hw = node->hw;

	/*
	 * Since we are stopping the counters, we don't want this short work
@@ -84,59 +116,70 @@ static unsigned long measure_mrps_and_set_irq(struct devfreq *df,
	preempt_disable();

	ts = ktime_get();
	us = ktime_to_us(ktime_sub(ts, prev_ts));
	us = ktime_to_us(ktime_sub(ts, node->prev_ts));
	if (!us)
		us = 1;

	hw->meas_mrps_and_set_irq(df, tolerance_mrps, us, stat);
	prev_ts = ts;
	hw->meas_mrps_and_set_irq(hw, node->tolerance_mrps, us, stat);
	node->prev_ts = ts;

	preempt_enable();

	pr_debug("stat H=%3lu, M=%3lu, T=%3lu, b=%3u, f=%4lu, us=%d\n",
		 stat->high, stat->med, stat->high + stat->med,
		 stat->busy_percent, df->previous_freq / 1000, us);

	trace_cache_hwmon_meas(dev_name(hw->df->dev.parent), stat->mrps[HIGH],
			       stat->mrps[MED], stat->mrps[LOW],
			       stat->busy_percent, us);
	return 0;
}

static void compute_cache_freq(struct mrps_stats *mrps, unsigned long *freq)
static void compute_cache_freq(struct cache_hwmon_node *node,
		struct mrps_stats *mrps, unsigned long *freq)
{
	unsigned long new_mhz;
	unsigned int busy;

	new_mhz = mrps->high * cycles_per_high_req
		+ mrps->med * cycles_per_med_req
		+ mrps->low * cycles_per_low_req;
	new_mhz = mrps->mrps[HIGH] * node->cycles_per_high_req
		+ mrps->mrps[MED] * node->cycles_per_med_req
		+ mrps->mrps[LOW] * node->cycles_per_low_req;

	busy = max(min_busy, mrps->busy_percent);
	busy = min(max_busy, busy);
	busy = max(node->min_busy, mrps->busy_percent);
	busy = min(node->max_busy, busy);

	new_mhz *= 100;
	new_mhz /= busy;

	if (new_mhz < prev_mhz) {
		new_mhz = new_mhz * decay_rate + prev_mhz * (100 - decay_rate);
	if (new_mhz < node->prev_mhz) {
		new_mhz = new_mhz * node->decay_rate + node->prev_mhz
				* (100 - node->decay_rate);
		new_mhz /= 100;
	}
	prev_mhz = new_mhz;
	node->prev_mhz = new_mhz;

	new_mhz += guard_band_mhz;
	new_mhz += node->guard_band_mhz;
	*freq = new_mhz * 1000;
	trace_cache_hwmon_update(dev_name(node->hw->df->dev.parent), *freq);
}

#define TOO_SOON_US	(1 * USEC_PER_MSEC)
static irqreturn_t mon_intr_handler(int irq, void *dev)
int update_cache_hwmon(struct cache_hwmon *hwmon)
{
	struct devfreq *df = dev;
	struct cache_hwmon_node *node;
	struct devfreq *df;
	ktime_t ts;
	unsigned int us;
	int ret;

	if (!hw->is_valid_irq(df))
		return IRQ_NONE;

	pr_debug("Got interrupt\n");
	if (!hwmon)
		return -EINVAL;
	df = hwmon->df;
	if (!df)
		return -ENODEV;
	node = df->data;
	if (!node)
		return -ENODEV;
	if (!node->mon_started)
		return -EBUSY;

	dev_dbg(df->dev.parent, "Got update request\n");
	devfreq_monitor_stop(df);

	/*
@@ -152,27 +195,30 @@ static irqreturn_t mon_intr_handler(int irq, void *dev)
	 *    readjusted.
	 */
	ts = ktime_get();
	us = ktime_to_us(ktime_sub(ts, prev_ts));
	us = ktime_to_us(ktime_sub(ts, node->prev_ts));
	if (us > TOO_SOON_US) {
		mutex_lock(&df->lock);
		ret = update_devfreq(df);
		if (ret)
			pr_err("Unable to update freq on IRQ!\n");
			dev_err(df->dev.parent,
				"Unable to update freq on request!\n");
		mutex_unlock(&df->lock);
	}

	devfreq_monitor_start(df);

	return IRQ_HANDLED;
	return 0;
}

static int devfreq_cache_hwmon_get_freq(struct devfreq *df,
					unsigned long *freq)
{
	struct mrps_stats stat;
	struct cache_hwmon_node *node = df->data;

	measure_mrps_and_set_irq(df, &stat);
	compute_cache_freq(&stat, freq);
	memset(&stat, 0, sizeof(stat));
	measure_mrps_and_set_irq(node, &stat);
	compute_cache_freq(node, &stat, freq);

	return 0;
}
@@ -207,58 +253,73 @@ static int start_monitoring(struct devfreq *df)
{
	int ret;
	struct mrps_stats mrps;

	prev_ts = ktime_get();
	prev_mhz = 0;
	mrps.high = (df->previous_freq / 1000) - guard_band_mhz;
	mrps.high /= cycles_per_high_req;

	ret = hw->start_hwmon(df, &mrps);
	struct device *dev = df->dev.parent;
	struct cache_hwmon_node *node;
	struct cache_hwmon *hw;

	node = find_hwmon_node(df);
	if (!node) {
		dev_err(dev, "Unable to find HW monitor!\n");
		return -ENODEV;
	}
	hw = node->hw;
	hw->df = df;
	node->orig_data = df->data;
	df->data = node;

	node->prev_ts = ktime_get();
	node->prev_mhz = 0;
	mrps.mrps[HIGH] = (df->previous_freq / 1000) - node->guard_band_mhz;
	mrps.mrps[HIGH] /= node->cycles_per_high_req;
	mrps.mrps[MED] = mrps.mrps[LOW] = 0;

	ret = hw->start_hwmon(hw, &mrps);
	if (ret) {
		pr_err("Unable to start HW monitor!\n");
		return ret;
		dev_err(dev, "Unable to start HW monitor!\n");
		goto err_start;
	}

	devfreq_monitor_start(df);

	ret = request_threaded_irq(hw->irq, NULL, mon_intr_handler,
			  IRQF_ONESHOT | IRQF_SHARED,
			  "cache_hwmon", df);
	if (ret) {
		pr_err("Unable to register interrupt handler!\n");
		goto req_irq_fail;
	}
	node->mon_started = true;

	ret = sysfs_create_group(&df->dev.kobj, &dev_attr_group);
	if (ret) {
		pr_err("Error creating sys entries!\n");
		dev_err(dev, "Error creating sys entries!\n");
		goto sysfs_fail;
	}

	return 0;

sysfs_fail:
	disable_irq(hw->irq);
	free_irq(hw->irq, df);
req_irq_fail:
	node->mon_started = false;
	devfreq_monitor_stop(df);
	hw->stop_hwmon(df);
	hw->stop_hwmon(hw);
err_start:
	df->data = node->orig_data;
	node->orig_data = NULL;
	hw->df = NULL;
	return ret;
}

static void stop_monitoring(struct devfreq *df)
{
	struct cache_hwmon_node *node = df->data;
	struct cache_hwmon *hw = node->hw;

	sysfs_remove_group(&df->dev.kobj, &dev_attr_group);
	disable_irq(hw->irq);
	free_irq(hw->irq, df);
	node->mon_started = false;
	devfreq_monitor_stop(df);
	hw->stop_hwmon(df);
	hw->stop_hwmon(hw);
	df->data = node->orig_data;
	node->orig_data = NULL;
	hw->df = NULL;
}

static int devfreq_cache_hwmon_ev_handler(struct devfreq *df,
					unsigned int event, void *data)
{
	int ret;
	unsigned int sample_ms;

	switch (event) {
	case DEVFREQ_GOV_START:
@@ -271,12 +332,12 @@ static int devfreq_cache_hwmon_ev_handler(struct devfreq *df,
		if (ret)
			return ret;

		pr_debug("Enabled Cache HW monitor governor\n");
		dev_dbg(df->dev.parent, "Enabled Cache HW monitor governor\n");
		break;

	case DEVFREQ_GOV_STOP:
		stop_monitoring(df);
		pr_debug("Disabled Cache HW monitor governor\n");
		dev_dbg(df->dev.parent, "Disabled Cache HW monitor governor\n");
		break;

	case DEVFREQ_GOV_INTERVAL:
@@ -296,18 +357,48 @@ static struct devfreq_governor devfreq_cache_hwmon = {
	.event_handler = devfreq_cache_hwmon_ev_handler,
};

int register_cache_hwmon(struct cache_hwmon *hwmon)
int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon)
{
	int ret;

	hw = hwmon;
	int ret = 0;
	struct cache_hwmon_node *node;

	if (!hwmon->dev && !hwmon->of_node)
		return -EINVAL;

	node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
	if (!node)
		return -ENOMEM;

	node->cycles_per_med_req = 20;
	node->cycles_per_high_req = 35;
	node->min_busy = 100;
	node->max_busy = 100;
	node->tolerance_mrps = 5;
	node->guard_band_mhz = 100;
	node->decay_rate = 90;
	node->hw = hwmon;
	node->attr_grp = &dev_attr_group;

	mutex_lock(&state_lock);
	if (!use_cnt) {
		ret = devfreq_add_governor(&devfreq_cache_hwmon);
	if (ret) {
		pr_err("devfreq governor registration failed\n");
		if (!ret)
			use_cnt++;
	}
	mutex_unlock(&state_lock);

	if (!ret) {
		dev_info(dev, "Cache HWmon governor registered.\n");
	} else {
		dev_err(dev, "Failed to add Cache HWmon governor\n");
		return ret;
	}

	return 0;
	mutex_lock(&list_lock);
	list_add_tail(&node->list, &cache_hwmon_list);
	mutex_unlock(&list_lock);

	return ret;
}

MODULE_DESCRIPTION("HW monitor based cache freq driver");
+35 −10
Original line number Diff line number Diff line
@@ -9,27 +9,52 @@
#include <linux/kernel.h>
#include <linux/devfreq.h>

enum request_group {
	HIGH,
	MED,
	LOW,
	MAX_NUM_GROUPS,
};

struct mrps_stats {
	unsigned long high;
	unsigned long med;
	unsigned long low;
	unsigned long mrps[MAX_NUM_GROUPS];
	unsigned int busy_percent;
};

/**
 * struct cache_hwmon - devfreq Cache HW monitor info
 * @start_hwmon:	Start the HW monitoring
 * @stop_hwmon:		Stop the HW monitoring
 * @meas_mrps_and_set_irq:	Return the measured count and set up the
 *				IRQ to fire if usage exceeds current
 *				measurement by @tol percent.
 * @dev:		device that this HW monitor can monitor.
 * @of_node:		OF node of device that this HW monitor can monitor.
 * @df:			Devfreq node that this HW montior is being used
 *			for. NULL when not actively in use, and non-NULL
 *			when in use.
 */
struct cache_hwmon {
	int (*start_hwmon)(struct devfreq *df, struct mrps_stats *mrps);
	void (*stop_hwmon)(struct devfreq *df);
	bool (*is_valid_irq)(struct devfreq *df);
	unsigned long (*meas_mrps_and_set_irq)(struct devfreq *df,
	int (*start_hwmon)(struct cache_hwmon *hw, struct mrps_stats *mrps);
	void (*stop_hwmon)(struct cache_hwmon *hw);
	unsigned long (*meas_mrps_and_set_irq)(struct cache_hwmon *hw,
					unsigned int tol, unsigned int us,
					struct mrps_stats *mrps);
	int irq;
	struct device *dev;
	struct device_node *of_node;
	struct devfreq *df;
};

#ifdef CONFIG_DEVFREQ_GOV_QCOM_CACHE_HWMON
int register_cache_hwmon(struct cache_hwmon *hwmon);
int register_cache_hwmon(struct device *dev, struct cache_hwmon *hwmon);
int update_cache_hwmon(struct cache_hwmon *hwmon);
#else
static inline int register_cache_hwmon(struct cache_hwmon *hwmon)
static inline int register_cache_hwmon(struct device *dev,
				       struct cache_hwmon *hwmon)
{
	return 0;
}
int update_cache_hwmon(struct cache_hwmon *hwmon)
{
	return 0;
}
+43 −0
Original line number Diff line number Diff line
@@ -684,6 +684,49 @@ TRACE_EVENT(bw_hwmon_update,
		__entry->down_thres)
);

TRACE_EVENT(cache_hwmon_meas,
	TP_PROTO(const char *name, unsigned long high_mrps,
		 unsigned long med_mrps, unsigned long low_mrps,
		 unsigned int busy_percent, unsigned int us),
	TP_ARGS(name, high_mrps, med_mrps, low_mrps, busy_percent, us),
	TP_STRUCT__entry(
		__string(name, name)
		__field(unsigned long, high_mrps)
		__field(unsigned long, med_mrps)
		__field(unsigned long, low_mrps)
		__field(unsigned long, total_mrps)
		__field(unsigned int, busy_percent)
		__field(unsigned int, us)
	),
	TP_fast_assign(
		__assign_str(name, name);
		__entry->high_mrps = high_mrps;
		__entry->med_mrps = med_mrps;
		__entry->low_mrps = low_mrps;
		__entry->total_mrps = high_mrps + med_mrps + low_mrps;
		__entry->busy_percent = busy_percent;
		__entry->us = us;
	),
	TP_printk("dev=%s H=%lu M=%lu L=%lu T=%lu busy_pct=%u period=%u",
		  __get_str(name), __entry->high_mrps, __entry->med_mrps,
		  __entry->low_mrps, __entry->total_mrps,
		  __entry->busy_percent, __entry->us)
);

TRACE_EVENT(cache_hwmon_update,
	TP_PROTO(const char *name, unsigned long freq_mhz),
	TP_ARGS(name, freq_mhz),
	TP_STRUCT__entry(
		__string(name, name)
		__field(unsigned long, freq)
	),
	TP_fast_assign(
		__assign_str(name, name);
		__entry->freq = freq_mhz;
	),
	TP_printk("dev=%s freq=%lu", __get_str(name), __entry->freq)
);

#endif /* _TRACE_POWER_H */

/* This part must be outside protection */