From ed7a74e813565c75dd706e5e4eb947269ed690b1 Mon Sep 17 00:00:00 2001 From: Amir Vajid Date: Mon, 16 Dec 2019 12:27:33 -0800 Subject: [PATCH 1/2] PM / devfreq: memlat: add support for shared core-dev tables A core-dev table is explicity defined in each memlat device today. However, some devices may want to share the same core-dev table so add support for parsing a shared core-dev table that can be used across multiple memlat devices. Change-Id: I5ee0d439c199a6414d2adaeed8328f3dc99b2a84 Signed-off-by: Amir Vajid --- drivers/devfreq/governor_memlat.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c index 980d74f6fc15..780fc4aec4bd 100644 --- a/drivers/devfreq/governor_memlat.c +++ b/drivers/devfreq/governor_memlat.c @@ -489,6 +489,9 @@ static struct memlat_node *register_common(struct device *dev, if (hw->get_child_of_node) of_node = hw->get_child_of_node(dev); + if (of_parse_phandle(of_node, "qcom,core-dev-table", 0)) + of_node = of_parse_phandle(of_node, "qcom,core-dev-table", 0); + hw->freq_map = init_core_dev_map(dev, of_node, "qcom,core-dev-table"); if (!hw->freq_map) { From 6540cafe57e2a3de483885407642427e2766db28 Mon Sep 17 00:00:00 2001 From: Amir Vajid Date: Tue, 26 Nov 2019 15:57:36 -0800 Subject: [PATCH 2/2] 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 --- drivers/devfreq/devfreq_icc.c | 113 ++++++++++++++++++++++++++++++++-- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/drivers/devfreq/devfreq_icc.c b/drivers/devfreq/devfreq_icc.c index a9888faedacc..9f893237b232 100644 --- a/drivers/devfreq/devfreq_icc.c +++ b/drivers/devfreq/devfreq_icc.c @@ -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 #include #include +#include #include #include #include @@ -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" },