mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-13 00:00:00 -04:00
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: Amir Vajid <avajid@codeaurora.org>
This commit is contained in:
parent
ed7a74e813
commit
6540cafe57
1 changed files with 108 additions and 5 deletions
|
|
@ -1,6 +1,6 @@
|
|||
// 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);
|
||||
}
|
||||
|
||||
ret = icc_set_bw(d->icc_path, Bps_to_icc(new_ab * MBYTE),
|
||||
Bps_to_icc(new_ib * MBYTE));
|
||||
dev_dbg(dev, "ICC BW: AB: %u IB: %u\n", icc_ab, icc_ib);
|
||||
|
||||
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)
|
|||
}
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_of_add_table(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" },
|
||||
|
|
|
|||
Loading…
Reference in a new issue