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

Commit 6540cafe authored by Amir Vajid's avatar Amir Vajid
Browse files

PM / devfreq: icc: add support for L3 voting



Add support for L3 voting in the devfreq icc driver.
This includes populating the device's OPP table with L3
frequencies from HW and voting in this same unit of Hz
as accepted by the L3 interconnect provider.

Change-Id: I877f41324b55b333f4e5ef9f34232563357712c8
Signed-off-by: default avatarAmir Vajid <avajid@codeaurora.org>
parent ed7a74e8
Loading
Loading
Loading
Loading
+108 −5
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013-2014, 2018, 2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2013-2014, 2018-2019, The Linux Foundation. All rights reserved.
 */

#define pr_fmt(fmt) "devfreq-icc: " fmt
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_address.h>
#include <trace/events/power.h>
#include <linux/platform_device.h>
#include <linux/interconnect.h>
@@ -33,22 +34,33 @@ struct dev_data {
	u32				cur_ab;
	u32				cur_ib;
	unsigned long			gov_ab;
	bool				is_l3;
	struct devfreq			*df;
	struct devfreq_dev_profile	dp;
};

#define MAX_L3_ENTRIES	40U
static unsigned long	l3_freqs[MAX_L3_ENTRIES];
static			DEFINE_MUTEX(l3_freqs_lock);
static bool		use_cached_l3_freqs;

static int set_bw(struct device *dev, u32 new_ib, u32 new_ab)
{
	struct dev_data *d = dev_get_drvdata(dev);
	int ret;
	u32 icc_ib = new_ib, icc_ab = new_ab;

	if (d->cur_ib == new_ib && d->cur_ab == new_ab)
		return 0;

	dev_dbg(dev, "BW MBps: AB: %d IB: %d\n", new_ab, new_ib);
	if (!d->is_l3) {
		icc_ib = Bps_to_icc(new_ib * MBYTE);
		icc_ab = Bps_to_icc(new_ab * MBYTE);
	}

	dev_dbg(dev, "ICC BW: AB: %u IB: %u\n", icc_ab, icc_ib);

	ret = icc_set_bw(d->icc_path, Bps_to_icc(new_ab * MBYTE),
				Bps_to_icc(new_ib * MBYTE));
	ret = icc_set_bw(d->icc_path, icc_ab, icc_ib);
	if (ret < 0) {
		dev_err(dev, "icc set bandwidth request failed (%d)\n", ret);
	} else {
@@ -80,6 +92,83 @@ static int icc_get_dev_status(struct device *dev,
	return 0;
}

#define INIT_HZ			300000000UL
#define XO_HZ			19200000UL
#define FTBL_ROW_SIZE		4
#define SRC_MASK		GENMASK(31, 30)
#define SRC_SHIFT		30
#define MULT_MASK		GENMASK(7, 0)

static int populate_l3_opp_table(struct device *dev)
{
	int idx, ret;
	u32 data, src, mult, i;
	unsigned long freq, prev_freq = 0;
	struct resource res;
	void __iomem *ftbl_base;
	unsigned int ftbl_row_size = FTBL_ROW_SIZE;

	idx = of_property_match_string(dev->of_node, "reg-names", "ftbl-base");
	if (idx < 0) {
		dev_err(dev, "Unable to find ftbl-base: %d\n", idx);
		return -EINVAL;
	}

	ret = of_address_to_resource(dev->of_node, idx, &res);
	if (ret < 0) {
		dev_err(dev, "Unable to get resource from address: %d\n", ret);
		return -EINVAL;
	}

	ftbl_base = devm_ioremap(dev, res.start, resource_size(&res));
	if (!ftbl_base) {
		dev_err(dev, "Unable to map ftbl-base!\n");
		return -ENOMEM;
	}

	of_property_read_u32(dev->of_node, "qcom,ftbl-row-size",
						&ftbl_row_size);

	for (i = 0; i < MAX_L3_ENTRIES; i++) {
		data = readl_relaxed(ftbl_base + i * ftbl_row_size);
		src = ((data & SRC_MASK) >> SRC_SHIFT);
		mult = (data & MULT_MASK);
		freq = src ? XO_HZ * mult : INIT_HZ;

		/* Two of the same frequencies means end of table */
		if (i > 0 && prev_freq == freq)
			break;

		dev_pm_opp_add(dev, freq, 0);
		l3_freqs[i] = freq;
		prev_freq = freq;
	}

	devm_iounmap(dev, ftbl_base);
	use_cached_l3_freqs = true;

	return 0;
}

static int copy_l3_opp_table(struct device *dev)
{
	int idx;

	for (idx = 0; idx < MAX_L3_ENTRIES; idx++) {
		if (l3_freqs[idx])
			dev_pm_opp_add(dev, l3_freqs[idx], 0);
		else
			break;
	}

	if (!idx) {
		dev_err(dev, "No L3 frequencies copied for device!\n");
		return -EINVAL;
	}

	return 0;
}

#define PROP_ACTIVE	"qcom,active-only"
#define ACTIVE_ONLY_TAG	0x3

@@ -111,7 +200,20 @@ int devfreq_add_icc(struct device *dev)
		}
	}

	if (of_device_is_compatible(dev->of_node, "qcom,devfreq-icc-l3")) {
		mutex_lock(&l3_freqs_lock);
		if (use_cached_l3_freqs) {
			mutex_unlock(&l3_freqs_lock);
			ret = copy_l3_opp_table(dev);
		} else {
			ret = populate_l3_opp_table(dev);
			mutex_unlock(&l3_freqs_lock);
		}
		d->is_l3 = true;
	} else {
		ret = dev_pm_opp_of_add_table(dev);
		d->is_l3 = false;
	}
	if (ret < 0)
		dev_err(dev, "Couldn't parse OPP table:%d\n", ret);

@@ -172,6 +274,7 @@ static int devfreq_icc_remove(struct platform_device *pdev)
}

static const struct of_device_id devfreq_icc_match_table[] = {
	{ .compatible = "qcom,devfreq-icc-l3" },
	{ .compatible = "qcom,devfreq-icc-llcc" },
	{ .compatible = "qcom,devfreq-icc-ddr" },
	{ .compatible = "qcom,devfreq-icc" },