This series brings initial support for memory interconnect to Tegra20,
Tegra30 and Tegra124 SoCs.
For the starter only display controllers and devfreq devices are getting
interconnect API support, others could be supported later on. The display
controllers have the biggest demand for interconnect API right now because
dynamic memory frequency scaling can't be done safely without taking into
account bandwidth requirement from the displays. In particular this series
fixes distorted display output on T30 Ouya and T124 TK1 devices.
Changelog:
v10 - In a longer run it will be much nicer if we could support EMC
hardware versioning on Tegra20 and it's not late to support it now.
Hence I added these new patches:
dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
memory: tegra20: Support hardware versioning and clean up OPP table initialization
- Removed error message from tegra30-devfreq driver about missing OPP
properties in a device-tree because EMC driver already prints that
message and it uses OPP API error code instead of checking DT directly,
which is a more correct way of doing that.
v9: - Squashed "memory: tegra30-emc: Factor out clk initialization" into
patch "tegra30: Support interconnect framework".
Suggested by Krzysztof Kozlowski.
- Improved Kconfig in the patch "memory: tegra124-emc: Make driver modular"
by adding CONFIG_TEGRA124_CLK_EMC entry, which makes clk-driver changes
to look a bit more cleaner. Suggested by Krzysztof Kozlowski.
- Dropped voltage regulator support from ICC and DT patches for now
because there is a new discussion about using a power domain abstraction
for controlling the regulator, which is likely to happen.
- Replaced direct "operating-points-v2" property checking in EMC drivers
with checking of a returned error code from dev_pm_opp_of_add_table().
Note that I haven't touched T20 EMC driver because it's very likely
that we'll replace that code with a common helper soon anyways.
Suggested by Viresh Kumar.
- The T30 DT patches now include EMC OPP changes for Ouya board, which
is available now in linux-next.
Dmitry Osipenko (19):
dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
memory: tegra20: Support hardware versioning and clean up OPP table
initialization
memory: tegra30: Support interconnect framework
memory: tegra124-emc: Make driver modular
memory: tegra124-emc: Continue probing if timings are missing in
device-tree
memory: tegra124: Support interconnect framework
drm/tegra: dc: Support memory bandwidth management
drm/tegra: dc: Extend debug stats with total number of events
PM / devfreq: tegra30: Support interconnect and OPPs from device-tree
PM / devfreq: tegra30: Separate configurations per-SoC generation
PM / devfreq: tegra20: Deprecate in a favor of emc-stat based driver
ARM: tegra: Correct EMC registers size in Tegra20 device-tree
ARM: tegra: Add interconnect properties to Tegra20 device-tree
ARM: tegra: Add interconnect properties to Tegra30 device-tree
ARM: tegra: Add interconnect properties to Tegra124 device-tree
ARM: tegra: Add nvidia,memory-controller phandle to Tegra20 EMC
device-tree
ARM: tegra: Add EMC OPP properties to Tegra20 device-trees
ARM: tegra: Add EMC OPP and ICC properties to Tegra30 EMC and ACTMON
device-tree nodes
ARM: tegra: Add EMC OPP and ICC properties to Tegra124 EMC and ACTMON
device-tree nodes
.../memory-controllers/nvidia,tegra20-emc.txt | 6 +
MAINTAINERS | 1 -
arch/arm/boot/dts/tegra124-apalis-emc.dtsi | 8 +
.../arm/boot/dts/tegra124-jetson-tk1-emc.dtsi | 8 +
arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi | 10 +
.../arm/boot/dts/tegra124-nyan-blaze-emc.dtsi | 10 +
.../boot/dts/tegra124-peripherals-opp.dtsi | 419 ++++++++++++++++++
arch/arm/boot/dts/tegra124.dtsi | 31 ++
.../boot/dts/tegra20-acer-a500-picasso.dts | 5 +
arch/arm/boot/dts/tegra20-colibri.dtsi | 4 +
arch/arm/boot/dts/tegra20-paz00.dts | 4 +
.../arm/boot/dts/tegra20-peripherals-opp.dtsi | 109 +++++
arch/arm/boot/dts/tegra20.dtsi | 33 +-
...30-asus-nexus7-grouper-memory-timings.dtsi | 12 +
arch/arm/boot/dts/tegra30-ouya.dts | 8 +
.../arm/boot/dts/tegra30-peripherals-opp.dtsi | 383 ++++++++++++++++
arch/arm/boot/dts/tegra30.dtsi | 33 +-
drivers/clk/tegra/Kconfig | 3 +
drivers/clk/tegra/Makefile | 2 +-
drivers/clk/tegra/clk-tegra124-emc.c | 41 +-
drivers/clk/tegra/clk-tegra124.c | 26 +-
drivers/clk/tegra/clk.h | 18 +-
drivers/devfreq/Kconfig | 10 -
drivers/devfreq/Makefile | 1 -
drivers/devfreq/tegra20-devfreq.c | 210 ---------
drivers/devfreq/tegra30-devfreq.c | 147 +++---
drivers/gpu/drm/tegra/Kconfig | 1 +
drivers/gpu/drm/tegra/dc.c | 359 +++++++++++++++
drivers/gpu/drm/tegra/dc.h | 19 +
drivers/gpu/drm/tegra/drm.c | 14 +
drivers/gpu/drm/tegra/hub.c | 3 +
drivers/gpu/drm/tegra/plane.c | 121 +++++
drivers/gpu/drm/tegra/plane.h | 15 +
drivers/memory/tegra/Kconfig | 5 +-
drivers/memory/tegra/tegra124-emc.c | 377 ++++++++++++++--
drivers/memory/tegra/tegra124.c | 82 +++-
drivers/memory/tegra/tegra20-emc.c | 48 +-
drivers/memory/tegra/tegra30-emc.c | 344 +++++++++++++-
drivers/memory/tegra/tegra30.c | 173 +++++++-
include/linux/clk/tegra.h | 8 +
include/soc/tegra/emc.h | 16 -
41 files changed, 2725 insertions(+), 402 deletions(-)
create mode 100644 arch/arm/boot/dts/tegra124-peripherals-opp.dtsi
create mode 100644 arch/arm/boot/dts/tegra20-peripherals-opp.dtsi
create mode 100644 arch/arm/boot/dts/tegra30-peripherals-opp.dtsi
delete mode 100644 drivers/devfreq/tegra20-devfreq.c
delete mode 100644 include/soc/tegra/emc.h
--
2.29.2
Now Internal and External memory controllers are memory interconnection
providers. This allows us to use interconnect API for tuning of memory
configuration. EMC driver now supports OPPs and DVFS. MC driver now
supports tuning of memory arbitration latency, which needs to be done
for ISO memory clients, like a Display client for example.
Tested-by: Peter Geis <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/memory/tegra/Kconfig | 1 +
drivers/memory/tegra/tegra30-emc.c | 344 +++++++++++++++++++++++++++--
drivers/memory/tegra/tegra30.c | 173 ++++++++++++++-
3 files changed, 496 insertions(+), 22 deletions(-)
diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
index 2a4a16bcf91c..ca7077a06f4c 100644
--- a/drivers/memory/tegra/Kconfig
+++ b/drivers/memory/tegra/Kconfig
@@ -24,6 +24,7 @@ config TEGRA30_EMC
tristate "NVIDIA Tegra30 External Memory Controller driver"
default y
depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
+ select PM_OPP
help
This driver is for the External Memory Controller (EMC) found on
Tegra30 chips. The EMC controls the external DRAM on the board.
diff --git a/drivers/memory/tegra/tegra30-emc.c b/drivers/memory/tegra/tegra30-emc.c
index 3488786da03b..969105a41f81 100644
--- a/drivers/memory/tegra/tegra30-emc.c
+++ b/drivers/memory/tegra/tegra30-emc.c
@@ -14,16 +14,21 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/err.h>
+#include <linux/interconnect-provider.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/types.h>
+#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>
#include "mc.h"
@@ -323,9 +328,21 @@ struct emc_timing {
bool emc_cfg_dyn_self_ref;
};
+enum emc_rate_request_type {
+ EMC_RATE_DEBUG,
+ EMC_RATE_ICC,
+ EMC_RATE_TYPE_MAX,
+};
+
+struct emc_rate_request {
+ unsigned long min_rate;
+ unsigned long max_rate;
+};
+
struct tegra_emc {
struct device *dev;
struct tegra_mc *mc;
+ struct icc_provider provider;
struct notifier_block clk_nb;
struct clk *clk;
void __iomem *regs;
@@ -352,6 +369,15 @@ struct tegra_emc {
unsigned long min_rate;
unsigned long max_rate;
} debugfs;
+
+ /*
+ * There are multiple sources in the EMC driver which could request
+ * a min/max clock rate, these rates are contained in this array.
+ */
+ struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
+
+ /* protect shared rate-change code path */
+ struct mutex rate_lock;
};
static int emc_seq_update_timing(struct tegra_emc *emc)
@@ -1094,6 +1120,83 @@ static long emc_round_rate(unsigned long rate,
return timing->rate;
}
+static void tegra_emc_rate_requests_init(struct tegra_emc *emc)
+{
+ unsigned int i;
+
+ for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
+ emc->requested_rate[i].min_rate = 0;
+ emc->requested_rate[i].max_rate = ULONG_MAX;
+ }
+}
+
+static int emc_request_rate(struct tegra_emc *emc,
+ unsigned long new_min_rate,
+ unsigned long new_max_rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = emc->requested_rate;
+ unsigned long min_rate = 0, max_rate = ULONG_MAX;
+ unsigned int i;
+ int err;
+
+ /* select minimum and maximum rates among the requested rates */
+ for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
+ if (i == type) {
+ min_rate = max(new_min_rate, min_rate);
+ max_rate = min(new_max_rate, max_rate);
+ } else {
+ min_rate = max(req->min_rate, min_rate);
+ max_rate = min(req->max_rate, max_rate);
+ }
+ }
+
+ if (min_rate > max_rate) {
+ dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
+ __func__, type, min_rate, max_rate);
+ return -ERANGE;
+ }
+
+ /*
+ * EMC rate-changes should go via OPP API because it manages voltage
+ * changes.
+ */
+ err = dev_pm_opp_set_rate(emc->dev, min_rate);
+ if (err)
+ return err;
+
+ emc->requested_rate[type].min_rate = new_min_rate;
+ emc->requested_rate[type].max_rate = new_max_rate;
+
+ return 0;
+}
+
+static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = &emc->requested_rate[type];
+ int ret;
+
+ mutex_lock(&emc->rate_lock);
+ ret = emc_request_rate(emc, rate, req->max_rate, type);
+ mutex_unlock(&emc->rate_lock);
+
+ return ret;
+}
+
+static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = &emc->requested_rate[type];
+ int ret;
+
+ mutex_lock(&emc->rate_lock);
+ ret = emc_request_rate(emc, req->min_rate, rate, type);
+ mutex_unlock(&emc->rate_lock);
+
+ return ret;
+}
+
/*
* debugfs interface
*
@@ -1177,7 +1280,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
- err = clk_set_min_rate(emc->clk, rate);
+ err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0)
return err;
@@ -1207,7 +1310,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
- err = clk_set_max_rate(emc->clk, rate);
+ err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0)
return err;
@@ -1264,6 +1367,214 @@ static void tegra_emc_debugfs_init(struct tegra_emc *emc)
emc, &tegra_emc_debug_max_rate_fops);
}
+static inline struct tegra_emc *
+to_tegra_emc_provider(struct icc_provider *provider)
+{
+ return container_of(provider, struct tegra_emc, provider);
+}
+
+static struct icc_node_data *
+emc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
+{
+ struct icc_provider *provider = data;
+ struct icc_node_data *ndata;
+ struct icc_node *node;
+
+ /* External Memory is the only possible ICC route */
+ list_for_each_entry(node, &provider->nodes, node_list) {
+ if (node->id != TEGRA_ICC_EMEM)
+ continue;
+
+ ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
+ if (!ndata)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * SRC and DST nodes should have matching TAG in order to have
+ * it set by default for a requested path.
+ */
+ ndata->tag = TEGRA_MC_ICC_TAG_ISO;
+ ndata->node = node;
+
+ return ndata;
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct tegra_emc *emc = to_tegra_emc_provider(dst->provider);
+ unsigned long long peak_bw = icc_units_to_bps(dst->peak_bw);
+ unsigned long long avg_bw = icc_units_to_bps(dst->avg_bw);
+ unsigned long long rate = max(avg_bw, peak_bw);
+ const unsigned int dram_data_bus_width_bytes = 4;
+ const unsigned int ddr = 2;
+ int err;
+
+ /*
+ * Tegra30 EMC runs on a clock rate of SDRAM bus. This means that
+ * EMC clock rate is twice smaller than the peak data rate because
+ * data is sampled on both EMC clock edges.
+ */
+ do_div(rate, ddr * dram_data_bus_width_bytes);
+ rate = min_t(u64, rate, U32_MAX);
+
+ err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int tegra_emc_interconnect_init(struct tegra_emc *emc)
+{
+ const struct tegra_mc_soc *soc = emc->mc->soc;
+ struct icc_node *node;
+ int err;
+
+ emc->provider.dev = emc->dev;
+ emc->provider.set = emc_icc_set;
+ emc->provider.data = &emc->provider;
+ emc->provider.aggregate = soc->icc_ops->aggregate;
+ emc->provider.xlate_extended = emc_of_icc_xlate_extended;
+
+ err = icc_provider_add(&emc->provider);
+ if (err)
+ goto err_msg;
+
+ /* create External Memory Controller node */
+ node = icc_node_create(TEGRA_ICC_EMC);
+ if (IS_ERR(node)) {
+ err = PTR_ERR(node);
+ goto del_provider;
+ }
+
+ node->name = "External Memory Controller";
+ icc_node_add(node, &emc->provider);
+
+ /* link External Memory Controller to External Memory (DRAM) */
+ err = icc_link_create(node, TEGRA_ICC_EMEM);
+ if (err)
+ goto remove_nodes;
+
+ /* create External Memory node */
+ node = icc_node_create(TEGRA_ICC_EMEM);
+ if (IS_ERR(node)) {
+ err = PTR_ERR(node);
+ goto remove_nodes;
+ }
+
+ node->name = "External Memory (DRAM)";
+ icc_node_add(node, &emc->provider);
+
+ return 0;
+
+remove_nodes:
+ icc_nodes_remove(&emc->provider);
+del_provider:
+ icc_provider_del(&emc->provider);
+err_msg:
+ dev_err(emc->dev, "failed to initialize ICC: %d\n", err);
+
+ return err;
+}
+
+static int tegra_emc_opp_table_init(struct tegra_emc *emc)
+{
+ u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
+ struct opp_table *clk_opp_table, *hw_opp_table;
+ int err;
+
+ clk_opp_table = dev_pm_opp_set_clkname(emc->dev, NULL);
+ err = PTR_ERR_OR_ZERO(clk_opp_table);
+ if (err) {
+ dev_err(emc->dev, "failed to set OPP clk: %d\n", err);
+ return err;
+ }
+
+ hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
+ err = PTR_ERR_OR_ZERO(hw_opp_table);
+ if (err) {
+ dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
+ goto put_clk_table;
+ }
+
+ err = dev_pm_opp_of_add_table(emc->dev);
+ if (err) {
+ if (err == -ENODEV)
+ dev_err(emc->dev, "OPP table not found, please update your device tree\n");
+ else
+ dev_err(emc->dev, "failed to add OPP table: %d\n", err);
+
+ goto put_hw_table;
+ }
+
+ dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
+ hw_version, clk_get_rate(emc->clk) / 1000000);
+
+ /* first dummy rate-set initializes voltage state */
+ err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
+ if (err) {
+ dev_err(emc->dev, "failed to initialize OPP clock: %d\n", err);
+ goto remove_table;
+ }
+
+ return 0;
+
+remove_table:
+ dev_pm_opp_of_remove_table(emc->dev);
+put_hw_table:
+ dev_pm_opp_put_supported_hw(hw_opp_table);
+put_clk_table:
+ dev_pm_opp_put_clkname(clk_opp_table);
+
+ return err;
+}
+
+static void devm_tegra_emc_unset_callback(void *data)
+{
+ tegra20_clk_set_emc_round_callback(NULL, NULL);
+}
+
+static void devm_tegra_emc_unreg_clk_notifier(void *data)
+{
+ struct tegra_emc *emc = data;
+
+ clk_notifier_unregister(emc->clk, &emc->clk_nb);
+}
+
+static int tegra_emc_init_clk(struct tegra_emc *emc)
+{
+ int err;
+
+ tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
+
+ err = devm_add_action_or_reset(emc->dev, devm_tegra_emc_unset_callback,
+ NULL);
+ if (err)
+ return err;
+
+ emc->clk = devm_clk_get(emc->dev, NULL);
+ if (IS_ERR(emc->clk)) {
+ dev_err(emc->dev, "failed to get EMC clock: %pe\n", emc->clk);
+ return PTR_ERR(emc->clk);
+ }
+
+ err = clk_notifier_register(emc->clk, &emc->clk_nb);
+ if (err) {
+ dev_err(emc->dev, "failed to register clk notifier: %d\n", err);
+ return err;
+ }
+
+ err = devm_add_action_or_reset(emc->dev,
+ devm_tegra_emc_unreg_clk_notifier, emc);
+ if (err)
+ return err;
+
+ return 0;
+}
+
static int tegra_emc_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -1280,6 +1591,7 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (IS_ERR(emc->mc))
return PTR_ERR(emc->mc);
+ mutex_init(&emc->rate_lock);
emc->clk_nb.notifier_call = emc_clk_change_notify;
emc->dev = &pdev->dev;
@@ -1312,24 +1624,18 @@ static int tegra_emc_probe(struct platform_device *pdev)
return err;
}
- tegra20_clk_set_emc_round_callback(emc_round_rate, emc);
-
- emc->clk = devm_clk_get(&pdev->dev, "emc");
- if (IS_ERR(emc->clk)) {
- err = PTR_ERR(emc->clk);
- dev_err(&pdev->dev, "failed to get emc clock: %d\n", err);
- goto unset_cb;
- }
+ err = tegra_emc_init_clk(emc);
+ if (err)
+ return err;
- err = clk_notifier_register(emc->clk, &emc->clk_nb);
- if (err) {
- dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
- err);
- goto unset_cb;
- }
+ err = tegra_emc_opp_table_init(emc);
+ if (err)
+ return err;
platform_set_drvdata(pdev, emc);
+ tegra_emc_rate_requests_init(emc);
tegra_emc_debugfs_init(emc);
+ tegra_emc_interconnect_init(emc);
/*
* Don't allow the kernel module to be unloaded. Unloading adds some
@@ -1339,11 +1645,6 @@ static int tegra_emc_probe(struct platform_device *pdev)
try_module_get(THIS_MODULE);
return 0;
-
-unset_cb:
- tegra20_clk_set_emc_round_callback(NULL, NULL);
-
- return err;
}
static int tegra_emc_suspend(struct device *dev)
@@ -1397,6 +1698,7 @@ static struct platform_driver tegra_emc_driver = {
.of_match_table = tegra_emc_of_match,
.pm = &tegra_emc_pm_ops,
.suppress_bind_attrs = true,
+ .sync_state = icc_sync_state,
},
};
module_platform_driver(tegra_emc_driver);
diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c
index d0314f29608d..ea849003014b 100644
--- a/drivers/memory/tegra/tegra30.c
+++ b/drivers/memory/tegra/tegra30.c
@@ -4,7 +4,8 @@
*/
#include <linux/of.h>
-#include <linux/mm.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
#include <dt-bindings/memory/tegra30-mc.h>
@@ -1083,6 +1084,175 @@ static const struct tegra_mc_reset tegra30_mc_resets[] = {
TEGRA30_MC_RESET(VI, 0x200, 0x204, 17),
};
+static void tegra30_mc_tune_client_latency(struct tegra_mc *mc,
+ const struct tegra_mc_client *client,
+ unsigned int bandwidth_mbytes_sec)
+{
+ u32 arb_tolerance_compensation_nsec, arb_tolerance_compensation_div;
+ const struct tegra_mc_la *la = &client->la;
+ unsigned int fifo_size = client->fifo_size;
+ u32 arb_nsec, la_ticks, value;
+
+ /* see 18.4.1 Client Configuration in Tegra3 TRM v03p */
+ if (bandwidth_mbytes_sec)
+ arb_nsec = fifo_size * NSEC_PER_USEC / bandwidth_mbytes_sec;
+ else
+ arb_nsec = U32_MAX;
+
+ /*
+ * Latency allowness should be set with consideration for the module's
+ * latency tolerance and internal buffering capabilities.
+ *
+ * Display memory clients use isochronous transfers and have very low
+ * tolerance to a belated transfers. Hence we need to compensate the
+ * memory arbitration imperfection for them in order to prevent FIFO
+ * underflow condition when memory bus is busy.
+ *
+ * VI clients also need a stronger compensation.
+ */
+ switch (client->swgroup) {
+ case TEGRA_SWGROUP_MPCORE:
+ case TEGRA_SWGROUP_PTC:
+ /*
+ * We always want lower latency for these clients, hence
+ * don't touch them.
+ */
+ return;
+
+ case TEGRA_SWGROUP_DC:
+ case TEGRA_SWGROUP_DCB:
+ arb_tolerance_compensation_nsec = 1050;
+ arb_tolerance_compensation_div = 2;
+ break;
+
+ case TEGRA_SWGROUP_VI:
+ arb_tolerance_compensation_nsec = 1050;
+ arb_tolerance_compensation_div = 1;
+ break;
+
+ default:
+ arb_tolerance_compensation_nsec = 150;
+ arb_tolerance_compensation_div = 1;
+ break;
+ }
+
+ if (arb_nsec > arb_tolerance_compensation_nsec)
+ arb_nsec -= arb_tolerance_compensation_nsec;
+ else
+ arb_nsec = 0;
+
+ arb_nsec /= arb_tolerance_compensation_div;
+
+ /*
+ * Latency allowance is a number of ticks a request from a particular
+ * client may wait in the EMEM arbiter before it becomes a high-priority
+ * request.
+ */
+ la_ticks = arb_nsec / mc->tick;
+ la_ticks = min(la_ticks, la->mask);
+
+ value = mc_readl(mc, la->reg);
+ value &= ~(la->mask << la->shift);
+ value |= la_ticks << la->shift;
+ mc_writel(mc, value, la->reg);
+}
+
+static int tegra30_mc_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct tegra_mc *mc = icc_provider_to_tegra_mc(src->provider);
+ const struct tegra_mc_client *client = &mc->soc->clients[src->id];
+ u64 peak_bandwidth = icc_units_to_bps(src->peak_bw);
+
+ /*
+ * Skip pre-initialization that is done by icc_node_add(), which sets
+ * bandwidth to maximum for all clients before drivers are loaded.
+ *
+ * This doesn't make sense for us because we don't have drivers for all
+ * clients and it's okay to keep configuration left from bootloader
+ * during boot, at least for today.
+ */
+ if (src == dst)
+ return 0;
+
+ /* convert bytes/sec to megabytes/sec */
+ do_div(peak_bandwidth, 1000000);
+
+ tegra30_mc_tune_client_latency(mc, client, peak_bandwidth);
+
+ return 0;
+}
+
+static int tegra30_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ /*
+ * ISO clients need to reserve extra bandwidth up-front because
+ * there could be high bandwidth pressure during initial filling
+ * of the client's FIFO buffers. Secondly, we need to take into
+ * account impurities of the memory subsystem.
+ */
+ if (tag & TEGRA_MC_ICC_TAG_ISO)
+ peak_bw = tegra_mc_scale_percents(peak_bw, 400);
+
+ *agg_avg += avg_bw;
+ *agg_peak = max(*agg_peak, peak_bw);
+
+ return 0;
+}
+
+static struct icc_node_data *
+tegra30_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
+{
+ struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
+ const struct tegra_mc_client *client;
+ unsigned int i, idx = spec->args[0];
+ struct icc_node_data *ndata;
+ struct icc_node *node;
+
+ list_for_each_entry(node, &mc->provider.nodes, node_list) {
+ if (node->id != idx)
+ continue;
+
+ ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
+ if (!ndata)
+ return ERR_PTR(-ENOMEM);
+
+ client = &mc->soc->clients[idx];
+ ndata->node = node;
+
+ switch (client->swgroup) {
+ case TEGRA_SWGROUP_DC:
+ case TEGRA_SWGROUP_DCB:
+ case TEGRA_SWGROUP_PTC:
+ case TEGRA_SWGROUP_VI:
+ /* these clients are isochronous by default */
+ ndata->tag = TEGRA_MC_ICC_TAG_ISO;
+ break;
+
+ default:
+ ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
+ break;
+ }
+
+ return ndata;
+ }
+
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ if (mc->soc->clients[i].id == idx)
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static const struct tegra_mc_icc_ops tegra30_mc_icc_ops = {
+ .xlate_extended = tegra30_mc_of_icc_xlate_extended,
+ .aggregate = tegra30_mc_icc_aggreate,
+ .set = tegra30_mc_icc_set,
+};
+
const struct tegra_mc_soc tegra30_mc_soc = {
.clients = tegra30_mc_clients,
.num_clients = ARRAY_SIZE(tegra30_mc_clients),
@@ -1097,4 +1267,5 @@ const struct tegra_mc_soc tegra30_mc_soc = {
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra30_mc_resets,
.num_resets = ARRAY_SIZE(tegra30_mc_resets),
+ .icc_ops = &tegra30_mc_icc_ops,
};
--
2.29.2
Add interconnect properties to the Memory Controller, External Memory
Controller and the Display Controller nodes in order to describe hardware
interconnection.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
arch/arm/boot/dts/tegra20.dtsi | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 9347f7789245..2e1304493f7d 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -111,6 +111,17 @@ dc@54200000 {
nvidia,head = <0>;
+ interconnects = <&mc TEGRA20_MC_DISPLAY0A &emc>,
+ <&mc TEGRA20_MC_DISPLAY0B &emc>,
+ <&mc TEGRA20_MC_DISPLAY1B &emc>,
+ <&mc TEGRA20_MC_DISPLAY0C &emc>,
+ <&mc TEGRA20_MC_DISPLAYHC &emc>;
+ interconnect-names = "wina",
+ "winb",
+ "winb-vfilter",
+ "winc",
+ "cursor";
+
rgb {
status = "disabled";
};
@@ -128,6 +139,17 @@ dc@54240000 {
nvidia,head = <1>;
+ interconnects = <&mc TEGRA20_MC_DISPLAY0AB &emc>,
+ <&mc TEGRA20_MC_DISPLAY0BB &emc>,
+ <&mc TEGRA20_MC_DISPLAY1BB &emc>,
+ <&mc TEGRA20_MC_DISPLAY0CB &emc>,
+ <&mc TEGRA20_MC_DISPLAYHCB &emc>;
+ interconnect-names = "wina",
+ "winb",
+ "winb-vfilter",
+ "winc",
+ "cursor";
+
rgb {
status = "disabled";
};
@@ -630,15 +652,17 @@ mc: memory-controller@7000f000 {
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
#iommu-cells = <0>;
+ #interconnect-cells = <1>;
};
- memory-controller@7000f400 {
+ emc: memory-controller@7000f400 {
compatible = "nvidia,tegra20-emc";
reg = <0x7000f400 0x400>;
interrupts = <GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&tegra_car TEGRA20_CLK_EMC>;
#address-cells = <1>;
#size-cells = <0>;
+ #interconnect-cells = <0>;
};
fuse@7000f800 {
--
2.29.2
It's useful to know the total number of underflow events and currently
the debug stats are getting reset each time CRTC is being disabled. Let's
account the overall number of events that doesn't get a reset.
Tested-by: Peter Geis <[email protected]>
Tested-by: Nicolas Chauvet <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/gpu/drm/tegra/dc.c | 10 ++++++++++
drivers/gpu/drm/tegra/dc.h | 5 +++++
2 files changed, 15 insertions(+)
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 5c587cfd1bb2..b6676f1fe358 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -1539,6 +1539,11 @@ static int tegra_dc_show_stats(struct seq_file *s, void *data)
seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
+ seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
+ seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
+ seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
+ seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
+
return 0;
}
@@ -2310,6 +2315,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
/*
dev_dbg(dc->dev, "%s(): frame end\n", __func__);
*/
+ dc->stats.frames_total++;
dc->stats.frames++;
}
@@ -2318,6 +2324,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
*/
drm_crtc_handle_vblank(&dc->base);
+ dc->stats.vblank_total++;
dc->stats.vblank++;
}
@@ -2325,6 +2332,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
/*
dev_dbg(dc->dev, "%s(): underflow\n", __func__);
*/
+ dc->stats.underflow_total++;
dc->stats.underflow++;
}
@@ -2332,11 +2340,13 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
/*
dev_dbg(dc->dev, "%s(): overflow\n", __func__);
*/
+ dc->stats.overflow_total++;
dc->stats.overflow++;
}
if (status & HEAD_UF_INT) {
dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
+ dc->stats.underflow_total++;
dc->stats.underflow++;
}
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 0d7bdf66a1ec..ba4ed35139fb 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -48,6 +48,11 @@ struct tegra_dc_stats {
unsigned long vblank;
unsigned long underflow;
unsigned long overflow;
+
+ unsigned long frames_total;
+ unsigned long vblank_total;
+ unsigned long underflow_total;
+ unsigned long overflow_total;
};
struct tegra_windowgroup_soc {
--
2.29.2
This patch moves ACTMON driver away from generating OPP table by itself,
transitioning it to use the table which comes from device-tree. This
change breaks compatibility with older device-trees and brings support
for the interconnect framework to the driver. This is a mandatory change
which needs to be done in order to implement interconnect-based memory
DVFS, i.e. device-trees need to be updated. Now ACTMON issues a memory
bandwidth requests using dev_pm_opp_set_bw() instead of driving EMC clock
rate directly.
Tested-by: Peter Geis <[email protected]>
Tested-by: Nicolas Chauvet <[email protected]>
Acked-by: Chanwoo Choi <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/devfreq/tegra30-devfreq.c | 79 +++++++++++++++----------------
1 file changed, 37 insertions(+), 42 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 38cc0d014738..145ef91ae092 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -19,6 +19,8 @@
#include <linux/reset.h>
#include <linux/workqueue.h>
+#include <soc/tegra/fuse.h>
+
#include "governor.h"
#define ACTMON_GLB_STATUS 0x0
@@ -155,6 +157,7 @@ struct tegra_devfreq_device {
struct tegra_devfreq {
struct devfreq *devfreq;
+ struct opp_table *opp_table;
struct reset_control *reset;
struct clk *clock;
@@ -612,34 +615,19 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
u32 flags)
{
- struct tegra_devfreq *tegra = dev_get_drvdata(dev);
- struct devfreq *devfreq = tegra->devfreq;
struct dev_pm_opp *opp;
- unsigned long rate;
- int err;
+ int ret;
opp = devfreq_recommended_opp(dev, freq, flags);
if (IS_ERR(opp)) {
dev_err(dev, "Failed to find opp for %lu Hz\n", *freq);
return PTR_ERR(opp);
}
- rate = dev_pm_opp_get_freq(opp);
- dev_pm_opp_put(opp);
-
- err = clk_set_min_rate(tegra->emc_clock, rate * KHZ);
- if (err)
- return err;
-
- err = clk_set_rate(tegra->emc_clock, 0);
- if (err)
- goto restore_min_rate;
- return 0;
-
-restore_min_rate:
- clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
+ ret = dev_pm_opp_set_bw(dev, opp);
+ dev_pm_opp_put(opp);
- return err;
+ return ret;
}
static int tegra_devfreq_get_dev_status(struct device *dev,
@@ -655,7 +643,7 @@ static int tegra_devfreq_get_dev_status(struct device *dev,
stat->private_data = tegra;
/* The below are to be used by the other governors */
- stat->current_frequency = cur_freq;
+ stat->current_frequency = cur_freq * KHZ;
actmon_dev = &tegra->devices[MCALL];
@@ -705,7 +693,12 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
target_freq = max(target_freq, dev->target_freq);
}
- *freq = target_freq;
+ /*
+ * tegra-devfreq driver operates with KHz units, while OPP table
+ * entries use Hz units. Hence we need to convert the units for the
+ * devfreq core.
+ */
+ *freq = target_freq * KHZ;
return 0;
}
@@ -774,6 +767,7 @@ static struct devfreq_governor tegra_devfreq_governor = {
static int tegra_devfreq_probe(struct platform_device *pdev)
{
+ u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
@@ -822,11 +816,25 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return err;
}
+ tegra->opp_table = dev_pm_opp_set_supported_hw(&pdev->dev,
+ &hw_version, 1);
+ err = PTR_ERR_OR_ZERO(tegra->opp_table);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to set supported HW: %d\n", err);
+ return err;
+ }
+
+ err = dev_pm_opp_of_add_table(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add OPP table: %d\n", err);
+ goto put_hw;
+ }
+
err = clk_prepare_enable(tegra->clock);
if (err) {
dev_err(&pdev->dev,
"Failed to prepare and enable ACTMON clock\n");
- return err;
+ goto remove_table;
}
err = reset_control_reset(tegra->reset);
@@ -850,23 +858,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
dev->regs = tegra->regs + dev->config->offset;
}
- for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
- rate = clk_round_rate(tegra->emc_clock, rate);
-
- if (rate < 0) {
- dev_err(&pdev->dev,
- "Failed to round clock rate: %ld\n", rate);
- err = rate;
- goto remove_opps;
- }
-
- err = dev_pm_opp_add(&pdev->dev, rate / KHZ, 0);
- if (err) {
- dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
- goto remove_opps;
- }
- }
-
platform_set_drvdata(pdev, tegra);
tegra->clk_rate_change_nb.notifier_call = tegra_actmon_clk_notify_cb;
@@ -882,7 +873,6 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
- tegra_devfreq_profile.initial_freq /= KHZ;
devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
"tegra_actmon", NULL);
@@ -902,6 +892,10 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
reset_control_reset(tegra->reset);
disable_clk:
clk_disable_unprepare(tegra->clock);
+remove_table:
+ dev_pm_opp_of_remove_table(&pdev->dev);
+put_hw:
+ dev_pm_opp_put_supported_hw(tegra->opp_table);
return err;
}
@@ -913,11 +907,12 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
devfreq_remove_device(tegra->devfreq);
devfreq_remove_governor(&tegra_devfreq_governor);
- dev_pm_opp_remove_all_dynamic(&pdev->dev);
-
reset_control_reset(tegra->reset);
clk_disable_unprepare(tegra->clock);
+ dev_pm_opp_of_remove_table(&pdev->dev);
+ dev_pm_opp_put_supported_hw(tegra->opp_table);
+
return 0;
}
--
2.29.2
Document opp-supported-hw property, which is not strictly necessary to
have on Tegra20, but it's very convenient to have because all other SoC
core devices will use hardware versioning, and thus, it's good to maintain
the consistency.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
.../bindings/memory-controllers/nvidia,tegra20-emc.txt | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
index 67ac8d1297da..fe99ce1013bd 100644
--- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
@@ -16,6 +16,12 @@ Properties:
- #interconnect-cells : Should be 0.
- operating-points-v2: See ../bindings/opp/opp.txt for details.
+For each opp entry in 'operating-points-v2' table:
+- opp-supported-hw: One bitfield indicating SoC process ID mask
+
+ A bitwise AND is performed against this value and if any bit
+ matches, the OPP gets enabled.
+
Optional properties:
- core-supply: Phandle of voltage regulator of the SoC "core" power domain.
--
2.29.2
Now Internal and External memory controllers are memory interconnection
providers. This allows us to use interconnect API for tuning of memory
configuration. EMC driver now supports OPPs and DVFS.
Tested-by: Nicolas Chauvet <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/memory/tegra/Kconfig | 1 +
drivers/memory/tegra/tegra124-emc.c | 320 +++++++++++++++++++++++++++-
drivers/memory/tegra/tegra124.c | 82 ++++++-
3 files changed, 391 insertions(+), 12 deletions(-)
diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig
index f5b451403c58..a70967a56e52 100644
--- a/drivers/memory/tegra/Kconfig
+++ b/drivers/memory/tegra/Kconfig
@@ -36,6 +36,7 @@ config TEGRA124_EMC
default y
depends on TEGRA_MC && ARCH_TEGRA_124_SOC
select TEGRA124_CLK_EMC
+ select PM_OPP
help
This driver is for the External Memory Controller (EMC) found on
Tegra124 chips. The EMC controls the external DRAM on the board.
diff --git a/drivers/memory/tegra/tegra124-emc.c b/drivers/memory/tegra/tegra124-emc.c
index 8fb8c1af25c9..9706550bdd5d 100644
--- a/drivers/memory/tegra/tegra124-emc.c
+++ b/drivers/memory/tegra/tegra124-emc.c
@@ -12,20 +12,26 @@
#include <linux/clk/tegra.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
+#include <linux/interconnect-provider.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
#include <linux/sort.h>
#include <linux/string.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/mc.h>
+#include "mc.h"
+
#define EMC_FBIO_CFG5 0x104
#define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3
#define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT 0
+#define EMC_FBIO_CFG5_DRAM_WIDTH_X64 BIT(4)
#define EMC_INTSTATUS 0x0
#define EMC_INTSTATUS_CLKCHANGE_COMPLETE BIT(4)
@@ -461,6 +467,17 @@ struct emc_timing {
u32 emc_zcal_interval;
};
+enum emc_rate_request_type {
+ EMC_RATE_DEBUG,
+ EMC_RATE_ICC,
+ EMC_RATE_TYPE_MAX,
+};
+
+struct emc_rate_request {
+ unsigned long min_rate;
+ unsigned long max_rate;
+};
+
struct tegra_emc {
struct device *dev;
@@ -471,6 +488,7 @@ struct tegra_emc {
struct clk *clk;
enum emc_dram_type dram_type;
+ unsigned int dram_bus_width;
unsigned int dram_num;
struct emc_timing last_timing;
@@ -482,6 +500,17 @@ struct tegra_emc {
unsigned long min_rate;
unsigned long max_rate;
} debugfs;
+
+ struct icc_provider provider;
+
+ /*
+ * There are multiple sources in the EMC driver which could request
+ * a min/max clock rate, these rates are contained in this array.
+ */
+ struct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];
+
+ /* protect shared rate-change code path */
+ struct mutex rate_lock;
};
/* Timing change sequence functions */
@@ -870,6 +899,14 @@ static void emc_read_current_timing(struct tegra_emc *emc,
static int emc_init(struct tegra_emc *emc)
{
emc->dram_type = readl(emc->regs + EMC_FBIO_CFG5);
+
+ if (emc->dram_type & EMC_FBIO_CFG5_DRAM_WIDTH_X64)
+ emc->dram_bus_width = 64;
+ else
+ emc->dram_bus_width = 32;
+
+ dev_info(emc->dev, "%ubit DRAM bus\n", emc->dram_bus_width);
+
emc->dram_type &= EMC_FBIO_CFG5_DRAM_TYPE_MASK;
emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
@@ -1009,6 +1046,83 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
return NULL;
}
+static void tegra_emc_rate_requests_init(struct tegra_emc *emc)
+{
+ unsigned int i;
+
+ for (i = 0; i < EMC_RATE_TYPE_MAX; i++) {
+ emc->requested_rate[i].min_rate = 0;
+ emc->requested_rate[i].max_rate = ULONG_MAX;
+ }
+}
+
+static int emc_request_rate(struct tegra_emc *emc,
+ unsigned long new_min_rate,
+ unsigned long new_max_rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = emc->requested_rate;
+ unsigned long min_rate = 0, max_rate = ULONG_MAX;
+ unsigned int i;
+ int err;
+
+ /* select minimum and maximum rates among the requested rates */
+ for (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {
+ if (i == type) {
+ min_rate = max(new_min_rate, min_rate);
+ max_rate = min(new_max_rate, max_rate);
+ } else {
+ min_rate = max(req->min_rate, min_rate);
+ max_rate = min(req->max_rate, max_rate);
+ }
+ }
+
+ if (min_rate > max_rate) {
+ dev_err_ratelimited(emc->dev, "%s: type %u: out of range: %lu %lu\n",
+ __func__, type, min_rate, max_rate);
+ return -ERANGE;
+ }
+
+ /*
+ * EMC rate-changes should go via OPP API because it manages voltage
+ * changes.
+ */
+ err = dev_pm_opp_set_rate(emc->dev, min_rate);
+ if (err)
+ return err;
+
+ emc->requested_rate[type].min_rate = new_min_rate;
+ emc->requested_rate[type].max_rate = new_max_rate;
+
+ return 0;
+}
+
+static int emc_set_min_rate(struct tegra_emc *emc, unsigned long rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = &emc->requested_rate[type];
+ int ret;
+
+ mutex_lock(&emc->rate_lock);
+ ret = emc_request_rate(emc, rate, req->max_rate, type);
+ mutex_unlock(&emc->rate_lock);
+
+ return ret;
+}
+
+static int emc_set_max_rate(struct tegra_emc *emc, unsigned long rate,
+ enum emc_rate_request_type type)
+{
+ struct emc_rate_request *req = &emc->requested_rate[type];
+ int ret;
+
+ mutex_lock(&emc->rate_lock);
+ ret = emc_request_rate(emc, req->min_rate, rate, type);
+ mutex_unlock(&emc->rate_lock);
+
+ return ret;
+}
+
/*
* debugfs interface
*
@@ -1081,7 +1195,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
- err = clk_set_min_rate(emc->clk, rate);
+ err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0)
return err;
@@ -1111,7 +1225,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL;
- err = clk_set_max_rate(emc->clk, rate);
+ err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0)
return err;
@@ -1129,15 +1243,6 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
unsigned int i;
int err;
- emc->clk = devm_clk_get(dev, "emc");
- if (IS_ERR(emc->clk)) {
- if (PTR_ERR(emc->clk) != -ENODEV) {
- dev_err(dev, "failed to get EMC clock: %ld\n",
- PTR_ERR(emc->clk));
- return;
- }
- }
-
emc->debugfs.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0;
@@ -1177,6 +1282,177 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
emc, &tegra_emc_debug_max_rate_fops);
}
+static inline struct tegra_emc *
+to_tegra_emc_provider(struct icc_provider *provider)
+{
+ return container_of(provider, struct tegra_emc, provider);
+}
+
+static struct icc_node_data *
+emc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
+{
+ struct icc_provider *provider = data;
+ struct icc_node_data *ndata;
+ struct icc_node *node;
+
+ /* External Memory is the only possible ICC route */
+ list_for_each_entry(node, &provider->nodes, node_list) {
+ if (node->id != TEGRA_ICC_EMEM)
+ continue;
+
+ ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
+ if (!ndata)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * SRC and DST nodes should have matching TAG in order to have
+ * it set by default for a requested path.
+ */
+ ndata->tag = TEGRA_MC_ICC_TAG_ISO;
+ ndata->node = node;
+
+ return ndata;
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+static int emc_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct tegra_emc *emc = to_tegra_emc_provider(dst->provider);
+ unsigned long long peak_bw = icc_units_to_bps(dst->peak_bw);
+ unsigned long long avg_bw = icc_units_to_bps(dst->avg_bw);
+ unsigned long long rate = max(avg_bw, peak_bw);
+ unsigned int dram_data_bus_width_bytes;
+ const unsigned int ddr = 2;
+ int err;
+
+ /*
+ * Tegra124 EMC runs on a clock rate of SDRAM bus. This means that
+ * EMC clock rate is twice smaller than the peak data rate because
+ * data is sampled on both EMC clock edges.
+ */
+ dram_data_bus_width_bytes = emc->dram_bus_width / 8;
+ do_div(rate, ddr * dram_data_bus_width_bytes);
+ rate = min_t(u64, rate, U32_MAX);
+
+ err = emc_set_min_rate(emc, rate, EMC_RATE_ICC);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int tegra_emc_interconnect_init(struct tegra_emc *emc)
+{
+ const struct tegra_mc_soc *soc = emc->mc->soc;
+ struct icc_node *node;
+ int err;
+
+ emc->provider.dev = emc->dev;
+ emc->provider.set = emc_icc_set;
+ emc->provider.data = &emc->provider;
+ emc->provider.aggregate = soc->icc_ops->aggregate;
+ emc->provider.xlate_extended = emc_of_icc_xlate_extended;
+
+ err = icc_provider_add(&emc->provider);
+ if (err)
+ goto err_msg;
+
+ /* create External Memory Controller node */
+ node = icc_node_create(TEGRA_ICC_EMC);
+ if (IS_ERR(node)) {
+ err = PTR_ERR(node);
+ goto del_provider;
+ }
+
+ node->name = "External Memory Controller";
+ icc_node_add(node, &emc->provider);
+
+ /* link External Memory Controller to External Memory (DRAM) */
+ err = icc_link_create(node, TEGRA_ICC_EMEM);
+ if (err)
+ goto remove_nodes;
+
+ /* create External Memory node */
+ node = icc_node_create(TEGRA_ICC_EMEM);
+ if (IS_ERR(node)) {
+ err = PTR_ERR(node);
+ goto remove_nodes;
+ }
+
+ node->name = "External Memory (DRAM)";
+ icc_node_add(node, &emc->provider);
+
+ return 0;
+
+remove_nodes:
+ icc_nodes_remove(&emc->provider);
+del_provider:
+ icc_provider_del(&emc->provider);
+err_msg:
+ dev_err(emc->dev, "failed to initialize ICC: %d\n", err);
+
+ return err;
+}
+
+static int tegra_emc_opp_table_init(struct tegra_emc *emc)
+{
+ u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
+ struct opp_table *clk_opp_table, *hw_opp_table;
+ int err;
+
+ clk_opp_table = dev_pm_opp_set_clkname(emc->dev, NULL);
+ err = PTR_ERR_OR_ZERO(clk_opp_table);
+ if (err) {
+ dev_err(emc->dev, "failed to set OPP clk: %d\n", err);
+ return err;
+ }
+
+ hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
+ err = PTR_ERR_OR_ZERO(hw_opp_table);
+ if (err) {
+ dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
+ goto put_clk_table;
+ }
+
+ err = dev_pm_opp_of_add_table(emc->dev);
+ if (err) {
+ if (err == -ENODEV)
+ dev_err(emc->dev, "OPP table not found, please update your device tree\n");
+ else
+ dev_err(emc->dev, "failed to add OPP table: %d\n", err);
+
+ goto put_hw_table;
+ }
+
+ dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
+ hw_version, clk_get_rate(emc->clk) / 1000000);
+
+ /* first dummy rate-set initializes voltage state */
+ err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
+ if (err) {
+ dev_err(emc->dev, "failed to initialize OPP clock: %d\n", err);
+ goto remove_table;
+ }
+
+ return 0;
+
+remove_table:
+ dev_pm_opp_of_remove_table(emc->dev);
+put_hw_table:
+ dev_pm_opp_put_supported_hw(hw_opp_table);
+put_clk_table:
+ dev_pm_opp_put_clkname(clk_opp_table);
+
+ return err;
+}
+
+static void devm_tegra_emc_unset_callback(void *data)
+{
+ tegra124_clk_set_emc_callbacks(NULL, NULL);
+}
+
static int tegra_emc_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -1188,6 +1464,7 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (!emc)
return -ENOMEM;
+ mutex_init(&emc->rate_lock);
emc->dev = &pdev->dev;
emc->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1223,9 +1500,29 @@ static int tegra_emc_probe(struct platform_device *pdev)
tegra124_clk_set_emc_callbacks(tegra_emc_prepare_timing_change,
tegra_emc_complete_timing_change);
+ err = devm_add_action_or_reset(&pdev->dev, devm_tegra_emc_unset_callback,
+ NULL);
+ if (err)
+ return err;
+
+ emc->clk = devm_clk_get(&pdev->dev, "emc");
+ if (IS_ERR(emc->clk)) {
+ err = PTR_ERR(emc->clk);
+ dev_err(&pdev->dev, "failed to get EMC clock: %d\n", err);
+ return err;
+ }
+
+ err = tegra_emc_opp_table_init(emc);
+ if (err)
+ return err;
+
+ tegra_emc_rate_requests_init(emc);
+
if (IS_ENABLED(CONFIG_DEBUG_FS))
emc_debugfs_init(&pdev->dev, emc);
+ tegra_emc_interconnect_init(emc);
+
/*
* Don't allow the kernel module to be unloaded. Unloading adds some
* extra complexity which doesn't really worth the effort in a case of
@@ -1242,6 +1539,7 @@ static struct platform_driver tegra_emc_driver = {
.name = "tegra-emc",
.of_match_table = tegra_emc_of_match,
.suppress_bind_attrs = true,
+ .sync_state = icc_sync_state,
},
};
module_platform_driver(tegra_emc_driver);
diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c
index e2389573d3c0..459211f50c08 100644
--- a/drivers/memory/tegra/tegra124.c
+++ b/drivers/memory/tegra/tegra124.c
@@ -4,7 +4,8 @@
*/
#include <linux/of.h>
-#include <linux/mm.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
#include <dt-bindings/memory/tegra124-mc.h>
@@ -1010,6 +1011,83 @@ static const struct tegra_mc_reset tegra124_mc_resets[] = {
TEGRA124_MC_RESET(GPU, 0x970, 0x974, 2),
};
+static int tegra124_mc_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ /* TODO: program PTSA */
+ return 0;
+}
+
+static int tegra124_mc_icc_aggreate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ /*
+ * ISO clients need to reserve extra bandwidth up-front because
+ * there could be high bandwidth pressure during initial filling
+ * of the client's FIFO buffers. Secondly, we need to take into
+ * account impurities of the memory subsystem.
+ */
+ if (tag & TEGRA_MC_ICC_TAG_ISO)
+ peak_bw = tegra_mc_scale_percents(peak_bw, 400);
+
+ *agg_avg += avg_bw;
+ *agg_peak = max(*agg_peak, peak_bw);
+
+ return 0;
+}
+
+static struct icc_node_data *
+tegra124_mc_of_icc_xlate_extended(struct of_phandle_args *spec, void *data)
+{
+ struct tegra_mc *mc = icc_provider_to_tegra_mc(data);
+ const struct tegra_mc_client *client;
+ unsigned int i, idx = spec->args[0];
+ struct icc_node_data *ndata;
+ struct icc_node *node;
+
+ list_for_each_entry(node, &mc->provider.nodes, node_list) {
+ if (node->id != idx)
+ continue;
+
+ ndata = kzalloc(sizeof(*ndata), GFP_KERNEL);
+ if (!ndata)
+ return ERR_PTR(-ENOMEM);
+
+ client = &mc->soc->clients[idx];
+ ndata->node = node;
+
+ switch (client->swgroup) {
+ case TEGRA_SWGROUP_DC:
+ case TEGRA_SWGROUP_DCB:
+ case TEGRA_SWGROUP_PTC:
+ case TEGRA_SWGROUP_VI:
+ /* these clients are isochronous by default */
+ ndata->tag = TEGRA_MC_ICC_TAG_ISO;
+ break;
+
+ default:
+ ndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;
+ break;
+ }
+
+ return ndata;
+ }
+
+ for (i = 0; i < mc->soc->num_clients; i++) {
+ if (mc->soc->clients[i].id == idx)
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ dev_err(mc->dev, "invalid ICC client ID %u\n", idx);
+
+ return ERR_PTR(-EINVAL);
+}
+
+static const struct tegra_mc_icc_ops tegra124_mc_icc_ops = {
+ .xlate_extended = tegra124_mc_of_icc_xlate_extended,
+ .aggregate = tegra124_mc_icc_aggreate,
+ .set = tegra124_mc_icc_set,
+};
+
#ifdef CONFIG_ARCH_TEGRA_124_SOC
static const unsigned long tegra124_mc_emem_regs[] = {
MC_EMEM_ARB_CFG,
@@ -1061,6 +1139,7 @@ const struct tegra_mc_soc tegra124_mc_soc = {
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets),
+ .icc_ops = &tegra124_mc_icc_ops,
};
#endif /* CONFIG_ARCH_TEGRA_124_SOC */
@@ -1091,5 +1170,6 @@ const struct tegra_mc_soc tegra132_mc_soc = {
.reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets),
+ .icc_ops = &tegra124_mc_icc_ops,
};
#endif /* CONFIG_ARCH_TEGRA_132_SOC */
--
2.29.2
Add interconnect properties to the Memory Controller, External Memory
Controller and the Display Controller nodes in order to describe hardware
interconnection.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
arch/arm/boot/dts/tegra124.dtsi | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 64f488ba1e72..1801e30b1d3a 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -113,6 +113,19 @@ dc@54200000 {
iommus = <&mc TEGRA_SWGROUP_DC>;
nvidia,head = <0>;
+
+ interconnects = <&mc TEGRA124_MC_DISPLAY0A &emc>,
+ <&mc TEGRA124_MC_DISPLAY0B &emc>,
+ <&mc TEGRA124_MC_DISPLAY0C &emc>,
+ <&mc TEGRA124_MC_DISPLAYHC &emc>,
+ <&mc TEGRA124_MC_DISPLAYD &emc>,
+ <&mc TEGRA124_MC_DISPLAYT &emc>;
+ interconnect-names = "wina",
+ "winb",
+ "winc",
+ "cursor",
+ "wind",
+ "wint";
};
dc@54240000 {
@@ -127,6 +140,15 @@ dc@54240000 {
iommus = <&mc TEGRA_SWGROUP_DCB>;
nvidia,head = <1>;
+
+ interconnects = <&mc TEGRA124_MC_DISPLAY0AB &emc>,
+ <&mc TEGRA124_MC_DISPLAY0BB &emc>,
+ <&mc TEGRA124_MC_DISPLAY0CB &emc>,
+ <&mc TEGRA124_MC_DISPLAYHCB &emc>;
+ interconnect-names = "wina",
+ "winb",
+ "winc",
+ "cursor";
};
hdmi: hdmi@54280000 {
@@ -628,6 +650,7 @@ mc: memory-controller@70019000 {
#iommu-cells = <1>;
#reset-cells = <1>;
+ #interconnect-cells = <1>;
};
emc: external-memory-controller@7001b000 {
@@ -637,6 +660,8 @@ emc: external-memory-controller@7001b000 {
clock-names = "emc";
nvidia,memory-controller = <&mc>;
+
+ #interconnect-cells = <0>;
};
sata@70020000 {
--
2.29.2
Previously we were using count-weight of the T124 for T30 in order to
get EMC clock rate that was reasonable for T30. In fact the count-weight
should be x2 times smaller on T30, but then devfreq was producing a bit
too low EMC clock rate for ISO memory clients, like display controller
for example.
Now both Tegra ACTMON and Tegra DRM display drivers support interconnect
framework and display driver tells to ICC what a minimum memory bandwidth
is needed, preventing FIFO underflows. Thus, now we can use a proper
count-weight value for Tegra30 and MC_ALL device config needs a bit more
aggressive boosting.
Add a separate ACTMON driver configuration that is specific to Tegra30.
Tested-by: Peter Geis <[email protected]>
Tested-by: Nicolas Chauvet <[email protected]>
Acked-by: Chanwoo Choi <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/devfreq/tegra30-devfreq.c | 68 ++++++++++++++++++++++++-------
1 file changed, 54 insertions(+), 14 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 145ef91ae092..117cad7968ab 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -57,13 +57,6 @@
#define ACTMON_BELOW_WMARK_WINDOW 3
#define ACTMON_BOOST_FREQ_STEP 16000
-/*
- * Activity counter is incremented every 256 memory transactions, and each
- * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
- * 4 * 256 = 1024.
- */
-#define ACTMON_COUNT_WEIGHT 0x400
-
/*
* ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
* translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
@@ -111,7 +104,7 @@ enum tegra_actmon_device {
MCCPU,
};
-static const struct tegra_devfreq_device_config actmon_device_configs[] = {
+static const struct tegra_devfreq_device_config tegra124_device_configs[] = {
{
/* MCALL: All memory accesses (including from the CPUs) */
.offset = 0x1c0,
@@ -133,6 +126,28 @@ static const struct tegra_devfreq_device_config actmon_device_configs[] = {
},
};
+static const struct tegra_devfreq_device_config tegra30_device_configs[] = {
+ {
+ /* MCALL: All memory accesses (including from the CPUs) */
+ .offset = 0x1c0,
+ .irq_mask = 1 << 26,
+ .boost_up_coeff = 200,
+ .boost_down_coeff = 50,
+ .boost_up_threshold = 20,
+ .boost_down_threshold = 10,
+ },
+ {
+ /* MCCPU: memory accesses from the CPUs */
+ .offset = 0x200,
+ .irq_mask = 1 << 25,
+ .boost_up_coeff = 800,
+ .boost_down_coeff = 40,
+ .boost_up_threshold = 27,
+ .boost_down_threshold = 10,
+ .avg_dependency_threshold = 16000, /* 16MHz in kHz units */
+ },
+};
+
/**
* struct tegra_devfreq_device - state specific to an ACTMON device
*
@@ -155,6 +170,12 @@ struct tegra_devfreq_device {
unsigned long target_freq;
};
+struct tegra_devfreq_soc_data {
+ const struct tegra_devfreq_device_config *configs;
+ /* Weight value for count measurements */
+ unsigned int count_weight;
+};
+
struct tegra_devfreq {
struct devfreq *devfreq;
struct opp_table *opp_table;
@@ -171,11 +192,13 @@ struct tegra_devfreq {
struct delayed_work cpufreq_update_work;
struct notifier_block cpu_rate_change_nb;
- struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
+ struct tegra_devfreq_device devices[2];
unsigned int irq;
bool started;
+
+ const struct tegra_devfreq_soc_data *soc;
};
struct tegra_actmon_emc_ratio {
@@ -488,7 +511,7 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
tegra_devfreq_update_avg_wmark(tegra, dev);
tegra_devfreq_update_wmark(tegra, dev);
- device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
+ device_writel(dev, tegra->soc->count_weight, ACTMON_DEV_COUNT_WEIGHT);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
@@ -779,6 +802,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
if (!tegra)
return -ENOMEM;
+ tegra->soc = of_device_get_match_data(&pdev->dev);
+
tegra->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(tegra->regs))
return PTR_ERR(tegra->regs);
@@ -852,9 +877,9 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
tegra->max_freq = rate / KHZ;
- for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
+ for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
dev = tegra->devices + i;
- dev->config = actmon_device_configs + i;
+ dev->config = tegra->soc->configs + i;
dev->regs = tegra->regs + dev->config->offset;
}
@@ -916,9 +941,24 @@ static int tegra_devfreq_remove(struct platform_device *pdev)
return 0;
}
+static const struct tegra_devfreq_soc_data tegra124_soc = {
+ .configs = tegra124_device_configs,
+
+ /*
+ * Activity counter is incremented every 256 memory transactions,
+ * and each transaction takes 4 EMC clocks.
+ */
+ .count_weight = 4 * 256,
+};
+
+static const struct tegra_devfreq_soc_data tegra30_soc = {
+ .configs = tegra30_device_configs,
+ .count_weight = 2 * 256,
+};
+
static const struct of_device_id tegra_devfreq_of_match[] = {
- { .compatible = "nvidia,tegra30-actmon" },
- { .compatible = "nvidia,tegra124-actmon" },
+ { .compatible = "nvidia,tegra30-actmon", .data = &tegra30_soc, },
+ { .compatible = "nvidia,tegra124-actmon", .data = &tegra124_soc, },
{ },
};
--
2.29.2
Add nvidia,memory-controller to the Tegra20 External Memory Controller
node. This allows to perform a direct lookup of the Memory Controller
instead of walking up the whole tree. This puts Tegra20 device-tree on
par with Tegra30+.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
arch/arm/boot/dts/tegra20.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi
index 2e1304493f7d..8f8ad81916e7 100644
--- a/arch/arm/boot/dts/tegra20.dtsi
+++ b/arch/arm/boot/dts/tegra20.dtsi
@@ -663,6 +663,8 @@ emc: memory-controller@7000f400 {
#address-cells = <1>;
#size-cells = <0>;
#interconnect-cells = <0>;
+
+ nvidia,memory-controller = <&mc>;
};
fuse@7000f800 {
--
2.29.2
Remove tegra20-devfreq in order to replace it with a EMC_STAT based
devfreq driver. Previously we were going to use MC_STAT based
tegra20-devfreq driver because EMC_STAT wasn't working properly, but
now that problem is resolved. This resolves complications imposed by
the removed driver since it was depending on both EMC and MC drivers
simultaneously.
Acked-by: Chanwoo Choi <[email protected]>
Signed-off-by: Dmitry Osipenko <[email protected]>
---
MAINTAINERS | 1 -
drivers/devfreq/Kconfig | 10 --
drivers/devfreq/Makefile | 1 -
drivers/devfreq/tegra20-devfreq.c | 210 ------------------------------
4 files changed, 222 deletions(-)
delete mode 100644 drivers/devfreq/tegra20-devfreq.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f10105cac6f..56c560320f00 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11370,7 +11370,6 @@ L: [email protected]
L: [email protected]
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
S: Maintained
-F: drivers/devfreq/tegra20-devfreq.c
F: drivers/devfreq/tegra30-devfreq.c
MEMORY MANAGEMENT
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 0ee36ae2fa79..00704efe6398 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
It reads ACTMON counters of memory controllers and adjusts the
operating frequencies and voltages with OPP support.
-config ARM_TEGRA20_DEVFREQ
- tristate "NVIDIA Tegra20 DEVFREQ Driver"
- depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
- depends on COMMON_CLK
- select DEVFREQ_GOV_SIMPLE_ONDEMAND
- help
- This adds the DEVFREQ driver for the Tegra20 family of SoCs.
- It reads Memory Controller counters and adjusts the operating
- frequencies and voltages with OPP support.
-
config ARM_RK3399_DMC_DEVFREQ
tristate "ARM RK3399 DMC DEVFREQ Driver"
depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 3ca1ad0ecb97..a16333ea7034 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -13,7 +13,6 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
-obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
# DEVFREQ Event Drivers
obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
diff --git a/drivers/devfreq/tegra20-devfreq.c b/drivers/devfreq/tegra20-devfreq.c
deleted file mode 100644
index fd801534771d..000000000000
--- a/drivers/devfreq/tegra20-devfreq.c
+++ /dev/null
@@ -1,210 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * NVIDIA Tegra20 devfreq driver
- *
- * Copyright (C) 2019 GRATE-DRIVER project
- */
-
-#include <linux/clk.h>
-#include <linux/devfreq.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
-#include <linux/slab.h>
-
-#include <soc/tegra/mc.h>
-
-#include "governor.h"
-
-#define MC_STAT_CONTROL 0x90
-#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
-#define MC_STAT_EMC_CLOCKS 0xa4
-#define MC_STAT_EMC_CONTROL 0xa8
-#define MC_STAT_EMC_COUNT 0xb8
-
-#define EMC_GATHER_CLEAR (1 << 8)
-#define EMC_GATHER_ENABLE (3 << 8)
-
-struct tegra_devfreq {
- struct devfreq *devfreq;
- struct clk *emc_clock;
- void __iomem *regs;
-};
-
-static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
- u32 flags)
-{
- struct tegra_devfreq *tegra = dev_get_drvdata(dev);
- struct devfreq *devfreq = tegra->devfreq;
- struct dev_pm_opp *opp;
- unsigned long rate;
- int err;
-
- opp = devfreq_recommended_opp(dev, freq, flags);
- if (IS_ERR(opp))
- return PTR_ERR(opp);
-
- rate = dev_pm_opp_get_freq(opp);
- dev_pm_opp_put(opp);
-
- err = clk_set_min_rate(tegra->emc_clock, rate);
- if (err)
- return err;
-
- err = clk_set_rate(tegra->emc_clock, 0);
- if (err)
- goto restore_min_rate;
-
- return 0;
-
-restore_min_rate:
- clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
-
- return err;
-}
-
-static int tegra_devfreq_get_dev_status(struct device *dev,
- struct devfreq_dev_status *stat)
-{
- struct tegra_devfreq *tegra = dev_get_drvdata(dev);
-
- /*
- * EMC_COUNT returns number of memory events, that number is lower
- * than the number of clocks. Conversion ratio of 1/8 results in a
- * bit higher bandwidth than actually needed, it is good enough for
- * the time being because drivers don't support requesting minimum
- * needed memory bandwidth yet.
- *
- * TODO: adjust the ratio value once relevant drivers will support
- * memory bandwidth management.
- */
- stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT);
- stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8;
- stat->current_frequency = clk_get_rate(tegra->emc_clock);
-
- writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL);
- writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL);
-
- return 0;
-}
-
-static struct devfreq_dev_profile tegra_devfreq_profile = {
- .polling_ms = 500,
- .target = tegra_devfreq_target,
- .get_dev_status = tegra_devfreq_get_dev_status,
-};
-
-static struct tegra_mc *tegra_get_memory_controller(void)
-{
- struct platform_device *pdev;
- struct device_node *np;
- struct tegra_mc *mc;
-
- np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart");
- if (!np)
- return ERR_PTR(-ENOENT);
-
- pdev = of_find_device_by_node(np);
- of_node_put(np);
- if (!pdev)
- return ERR_PTR(-ENODEV);
-
- mc = platform_get_drvdata(pdev);
- if (!mc)
- return ERR_PTR(-EPROBE_DEFER);
-
- return mc;
-}
-
-static int tegra_devfreq_probe(struct platform_device *pdev)
-{
- struct tegra_devfreq *tegra;
- struct tegra_mc *mc;
- unsigned long max_rate;
- unsigned long rate;
- int err;
-
- mc = tegra_get_memory_controller();
- if (IS_ERR(mc)) {
- err = PTR_ERR(mc);
- dev_err(&pdev->dev, "failed to get memory controller: %d\n",
- err);
- return err;
- }
-
- tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
- if (!tegra)
- return -ENOMEM;
-
- /* EMC is a system-critical clock that is always enabled */
- tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
- if (IS_ERR(tegra->emc_clock))
- return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
- "failed to get emc clock\n");
-
- tegra->regs = mc->regs;
-
- max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
-
- for (rate = 0; rate <= max_rate; rate++) {
- rate = clk_round_rate(tegra->emc_clock, rate);
-
- err = dev_pm_opp_add(&pdev->dev, rate, 0);
- if (err) {
- dev_err(&pdev->dev, "failed to add opp: %d\n", err);
- goto remove_opps;
- }
- }
-
- /*
- * Reset statistic gathers state, select global bandwidth for the
- * statistics collection mode and set clocks counter saturation
- * limit to maximum.
- */
- writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL);
- writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL);
- writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT);
-
- platform_set_drvdata(pdev, tegra);
-
- tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
- DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
- if (IS_ERR(tegra->devfreq)) {
- err = PTR_ERR(tegra->devfreq);
- goto remove_opps;
- }
-
- return 0;
-
-remove_opps:
- dev_pm_opp_remove_all_dynamic(&pdev->dev);
-
- return err;
-}
-
-static int tegra_devfreq_remove(struct platform_device *pdev)
-{
- struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
-
- devfreq_remove_device(tegra->devfreq);
- dev_pm_opp_remove_all_dynamic(&pdev->dev);
-
- return 0;
-}
-
-static struct platform_driver tegra_devfreq_driver = {
- .probe = tegra_devfreq_probe,
- .remove = tegra_devfreq_remove,
- .driver = {
- .name = "tegra20-devfreq",
- },
-};
-module_platform_driver(tegra_devfreq_driver);
-
-MODULE_ALIAS("platform:tegra20-devfreq");
-MODULE_AUTHOR("Dmitry Osipenko <[email protected]>");
-MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
-MODULE_LICENSE("GPL v2");
--
2.29.2
Add EMC OPP DVFS/DFS tables and interconnect paths that will be used for
dynamic memory bandwidth scaling based on memory utilization statistics.
Update board device-trees by removing unsupported EMC OPPs.
Note that ACTMON watches all memory interconnect paths, but we use a
single CPU-READ interconnect path for driving memory bandwidth, for
simplicity.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
arch/arm/boot/dts/tegra124-apalis-emc.dtsi | 8 +
.../arm/boot/dts/tegra124-jetson-tk1-emc.dtsi | 8 +
arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi | 10 +
.../arm/boot/dts/tegra124-nyan-blaze-emc.dtsi | 10 +
.../boot/dts/tegra124-peripherals-opp.dtsi | 419 ++++++++++++++++++
arch/arm/boot/dts/tegra124.dtsi | 6 +
6 files changed, 461 insertions(+)
create mode 100644 arch/arm/boot/dts/tegra124-peripherals-opp.dtsi
diff --git a/arch/arm/boot/dts/tegra124-apalis-emc.dtsi b/arch/arm/boot/dts/tegra124-apalis-emc.dtsi
index 32401457ae71..a7ac805eeed5 100644
--- a/arch/arm/boot/dts/tegra124-apalis-emc.dtsi
+++ b/arch/arm/boot/dts/tegra124-apalis-emc.dtsi
@@ -1465,3 +1465,11 @@ timing-924000000 {
};
};
};
+
+&emc_icc_dvfs_opp_table {
+ /delete-node/ opp@1200000000,1100;
+};
+
+&emc_bw_dfs_opp_table {
+ /delete-node/ opp@1200000000;
+};
diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1-emc.dtsi b/arch/arm/boot/dts/tegra124-jetson-tk1-emc.dtsi
index 861d3f22116b..df4e463afbd1 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1-emc.dtsi
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1-emc.dtsi
@@ -2420,3 +2420,11 @@ timing-924000000 {
};
};
};
+
+&emc_icc_dvfs_opp_table {
+ /delete-node/ opp@1200000000,1100;
+};
+
+&emc_bw_dfs_opp_table {
+ /delete-node/ opp@1200000000;
+};
diff --git a/arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi b/arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi
index c91647d13a50..a0f56cc9da5c 100644
--- a/arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi
+++ b/arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi
@@ -6649,3 +6649,13 @@ timing-792000000 {
};
};
};
+
+&emc_icc_dvfs_opp_table {
+ /delete-node/ opp@924000000,1100;
+ /delete-node/ opp@1200000000,1100;
+};
+
+&emc_bw_dfs_opp_table {
+ /delete-node/ opp@924000000;
+ /delete-node/ opp@1200000000;
+};
diff --git a/arch/arm/boot/dts/tegra124-nyan-blaze-emc.dtsi b/arch/arm/boot/dts/tegra124-nyan-blaze-emc.dtsi
index d2beea0bd15f..35c98734d35f 100644
--- a/arch/arm/boot/dts/tegra124-nyan-blaze-emc.dtsi
+++ b/arch/arm/boot/dts/tegra124-nyan-blaze-emc.dtsi
@@ -2048,3 +2048,13 @@ timing-792000000 {
};
};
};
+
+&emc_icc_dvfs_opp_table {
+ /delete-node/ opp@924000000,1100;
+ /delete-node/ opp@1200000000,1100;
+};
+
+&emc_bw_dfs_opp_table {
+ /delete-node/ opp@924000000;
+ /delete-node/ opp@1200000000;
+};
diff --git a/arch/arm/boot/dts/tegra124-peripherals-opp.dtsi b/arch/arm/boot/dts/tegra124-peripherals-opp.dtsi
new file mode 100644
index 000000000000..49d9420a3289
--- /dev/null
+++ b/arch/arm/boot/dts/tegra124-peripherals-opp.dtsi
@@ -0,0 +1,419 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/ {
+ emc_icc_dvfs_opp_table: emc-dvfs-opp-table {
+ compatible = "operating-points-v2";
+
+ opp@12750000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <12750000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@12750000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <12750000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@12750000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <12750000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@12750000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <12750000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@20400000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <20400000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@20400000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <20400000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@20400000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <20400000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@20400000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <20400000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@40800000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <40800000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@40800000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <40800000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@40800000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <40800000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@40800000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <40800000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@68000000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <68000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@68000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <68000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@68000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <68000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@68000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <68000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@102000000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <102000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@102000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <102000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@102000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <102000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@102000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <102000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@204000000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <204000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@204000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <204000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@204000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <204000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@204000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <204000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@264000000,800 {
+ opp-microvolt = <800000 800000 1150000>;
+ opp-hz = /bits/ 64 <264000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@264000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <264000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@264000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <264000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@264000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <264000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@300000000,850 {
+ opp-microvolt = <850000 850000 1150000>;
+ opp-hz = /bits/ 64 <300000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@300000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <300000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@300000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <300000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@300000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <300000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@348000000,850 {
+ opp-microvolt = <850000 850000 1150000>;
+ opp-hz = /bits/ 64 <348000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@348000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <348000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@348000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <348000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@348000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <348000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@396000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <396000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@396000000,1000 {
+ opp-microvolt = <1000000 1000000 1150000>;
+ opp-hz = /bits/ 64 <396000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@396000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <396000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@396000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <396000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@528000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <528000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@528000000,1000 {
+ opp-microvolt = <1000000 1000000 1150000>;
+ opp-hz = /bits/ 64 <528000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@528000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <528000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@528000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <528000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@600000000,950 {
+ opp-microvolt = <950000 950000 1150000>;
+ opp-hz = /bits/ 64 <600000000>;
+ opp-supported-hw = <0x0008>;
+ };
+
+ opp@600000000,1000 {
+ opp-microvolt = <1000000 1000000 1150000>;
+ opp-hz = /bits/ 64 <600000000>;
+ opp-supported-hw = <0x0003>;
+ };
+
+ opp@600000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <600000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@600000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <600000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@792000000,1000 {
+ opp-microvolt = <1000000 1000000 1150000>;
+ opp-hz = /bits/ 64 <792000000>;
+ opp-supported-hw = <0x000B>;
+ };
+
+ opp@792000000,1050 {
+ opp-microvolt = <1050000 1050000 1150000>;
+ opp-hz = /bits/ 64 <792000000>;
+ opp-supported-hw = <0x0010>;
+ };
+
+ opp@792000000,1110 {
+ opp-microvolt = <1110000 1110000 1150000>;
+ opp-hz = /bits/ 64 <792000000>;
+ opp-supported-hw = <0x0004>;
+ };
+
+ opp@924000000,1100 {
+ opp-microvolt = <1100000 1100000 1150000>;
+ opp-hz = /bits/ 64 <924000000>;
+ opp-supported-hw = <0x0013>;
+ };
+
+ opp@1200000000,1100 {
+ opp-microvolt = <1100000 1100000 1150000>;
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-supported-hw = <0x0003>;
+ };
+ };
+
+ emc_bw_dfs_opp_table: emc-bandwidth-opp-table {
+ compatible = "operating-points-v2";
+
+ opp@12750000 {
+ opp-hz = /bits/ 64 <12750000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <204000>;
+ };
+
+ opp@20400000 {
+ opp-hz = /bits/ 64 <20400000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <326400>;
+ };
+
+ opp@40800000 {
+ opp-hz = /bits/ 64 <40800000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <652800>;
+ };
+
+ opp@68000000 {
+ opp-hz = /bits/ 64 <68000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <1088000>;
+ };
+
+ opp@102000000 {
+ opp-hz = /bits/ 64 <102000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <1632000>;
+ };
+
+ opp@204000000 {
+ opp-hz = /bits/ 64 <204000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <3264000>;
+ };
+
+ opp@264000000 {
+ opp-hz = /bits/ 64 <264000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <4224000>;
+ };
+
+ opp@300000000 {
+ opp-hz = /bits/ 64 <300000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <4800000>;
+ };
+
+ opp@348000000 {
+ opp-hz = /bits/ 64 <348000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <5568000>;
+ };
+
+ opp@396000000 {
+ opp-hz = /bits/ 64 <396000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <6336000>;
+ };
+
+ opp@528000000 {
+ opp-hz = /bits/ 64 <528000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <8448000>;
+ };
+
+ opp@600000000 {
+ opp-hz = /bits/ 64 <600000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <9600000>;
+ };
+
+ opp@792000000 {
+ opp-hz = /bits/ 64 <792000000>;
+ opp-supported-hw = <0x001F>;
+ opp-peak-kBps = <12672000>;
+ };
+
+ opp@924000000 {
+ opp-hz = /bits/ 64 <924000000>;
+ opp-supported-hw = <0x0013>;
+ opp-peak-kBps = <14784000>;
+ };
+
+ opp@1200000000 {
+ opp-hz = /bits/ 64 <1200000000>;
+ opp-supported-hw = <0x0003>;
+ opp-peak-kBps = <19200000>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 1801e30b1d3a..46441d10a3fc 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -8,6 +8,8 @@
#include <dt-bindings/thermal/tegra124-soctherm.h>
#include <dt-bindings/soc/tegra-pmc.h>
+#include "tegra124-peripherals-opp.dtsi"
+
/ {
compatible = "nvidia,tegra124";
interrupt-parent = <&lic>;
@@ -290,6 +292,9 @@ actmon@6000c800 {
clock-names = "actmon", "emc";
resets = <&tegra_car 119>;
reset-names = "actmon";
+ operating-points-v2 = <&emc_bw_dfs_opp_table>;
+ interconnects = <&mc TEGRA124_MC_MPCORER &emc>;
+ interconnect-names = "cpu-read";
};
gpio: gpio@6000d000 {
@@ -660,6 +665,7 @@ emc: external-memory-controller@7001b000 {
clock-names = "emc";
nvidia,memory-controller = <&mc>;
+ operating-points-v2 = <&emc_icc_dvfs_opp_table>;
#interconnect-cells = <0>;
};
--
2.29.2
Support hardware versioning, which is now required for Tegra20 EMC OPP.
Clean up OPP table initialization by using a error code returned by OPP
API for judging about the OPP table presence in a device-tree and remove
OPP regulator initialization because we're now going to use power domain
instead of a raw regulator. This puts Tegra20 EMC OPP preparation on par
with the Tegra30/124 EMC drivers.
Signed-off-by: Dmitry Osipenko <[email protected]>
---
drivers/memory/tegra/tegra20-emc.c | 48 +++++++++++++-----------------
1 file changed, 20 insertions(+), 28 deletions(-)
diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index 0320d9df4a20..686aaf477d8a 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -910,43 +910,36 @@ static int tegra_emc_interconnect_init(struct tegra_emc *emc)
static int tegra_emc_opp_table_init(struct tegra_emc *emc)
{
- struct opp_table *reg_opp_table = NULL, *clk_opp_table;
- const char *rname = "core";
+ u32 hw_version = BIT(tegra_sku_info.soc_process_id);
+ struct opp_table *clk_opp_table, *hw_opp_table;
int err;
- /*
- * Legacy device-trees don't have OPP table and EMC driver isn't
- * useful in this case.
- */
- if (!device_property_present(emc->dev, "operating-points-v2")) {
- dev_err(emc->dev,
- "OPP table not found, please update your device tree\n");
- return -ENODEV;
- }
-
- /* voltage scaling is optional */
- if (device_property_present(emc->dev, "core-supply")) {
- reg_opp_table = dev_pm_opp_set_regulators(emc->dev, &rname, 1);
- if (IS_ERR(reg_opp_table))
- return dev_err_probe(emc->dev, PTR_ERR(reg_opp_table),
- "failed to set OPP regulator\n");
- }
-
clk_opp_table = dev_pm_opp_set_clkname(emc->dev, NULL);
err = PTR_ERR_OR_ZERO(clk_opp_table);
if (err) {
dev_err(emc->dev, "failed to set OPP clk: %d\n", err);
- goto put_reg_table;
+ return err;
}
- err = dev_pm_opp_of_add_table(emc->dev);
+ hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
+ err = PTR_ERR_OR_ZERO(hw_opp_table);
if (err) {
- dev_err(emc->dev, "failed to add OPP table: %d\n", err);
+ dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
goto put_clk_table;
}
- dev_info(emc->dev, "current clock rate %lu MHz\n",
- clk_get_rate(emc->clk) / 1000000);
+ err = dev_pm_opp_of_add_table(emc->dev);
+ if (err) {
+ if (err == -ENODEV)
+ dev_err(emc->dev, "OPP table not found, please update your device tree\n");
+ else
+ dev_err(emc->dev, "failed to add OPP table: %d\n", err);
+
+ goto put_hw_table;
+ }
+
+ dev_info(emc->dev, "OPP HW ver. 0x%x, current clock rate %lu MHz\n",
+ hw_version, clk_get_rate(emc->clk) / 1000000);
/* first dummy rate-set initializes voltage state */
err = dev_pm_opp_set_rate(emc->dev, clk_get_rate(emc->clk));
@@ -959,11 +952,10 @@ static int tegra_emc_opp_table_init(struct tegra_emc *emc)
remove_table:
dev_pm_opp_of_remove_table(emc->dev);
+put_hw_table:
+ dev_pm_opp_put_supported_hw(hw_opp_table);
put_clk_table:
dev_pm_opp_put_clkname(clk_opp_table);
-put_reg_table:
- if (reg_opp_table)
- dev_pm_opp_put_regulators(reg_opp_table);
return err;
}
--
2.29.2
On 23-11-20, 03:27, Dmitry Osipenko wrote:
> This series brings initial support for memory interconnect to Tegra20,
> Tegra30 and Tegra124 SoCs.
>
> For the starter only display controllers and devfreq devices are getting
> interconnect API support, others could be supported later on. The display
> controllers have the biggest demand for interconnect API right now because
> dynamic memory frequency scaling can't be done safely without taking into
> account bandwidth requirement from the displays. In particular this series
> fixes distorted display output on T30 Ouya and T124 TK1 devices.
>
> Changelog:
>
> v10 - In a longer run it will be much nicer if we could support EMC
> hardware versioning on Tegra20 and it's not late to support it now.
> Hence I added these new patches:
>
> dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
> memory: tegra20: Support hardware versioning and clean up OPP table initialization
>
> - Removed error message from tegra30-devfreq driver about missing OPP
> properties in a device-tree because EMC driver already prints that
> message and it uses OPP API error code instead of checking DT directly,
> which is a more correct way of doing that.
Looks good to me (from OPP APIs usage perspective). Thanks for
continuing with this and fixing all the issues Dmitry.
--
viresh
Hi Dmitry,
On 23.11.20 2:27, Dmitry Osipenko wrote:
> Now Internal and External memory controllers are memory interconnection
> providers. This allows us to use interconnect API for tuning of memory
> configuration. EMC driver now supports OPPs and DVFS. MC driver now
> supports tuning of memory arbitration latency, which needs to be done
> for ISO memory clients, like a Display client for example.
>
> Tested-by: Peter Geis <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
Acked-by: Georgi Djakov <[email protected]>
Thank you for the continuous work on this patchset!
BR,
Georgi
> ---
> drivers/memory/tegra/Kconfig | 1 +
> drivers/memory/tegra/tegra30-emc.c | 344 +++++++++++++++++++++++++++--
> drivers/memory/tegra/tegra30.c | 173 ++++++++++++++-
> 3 files changed, 496 insertions(+), 22 deletions(-)
On 23.11.20 2:27, Dmitry Osipenko wrote:
> Now Internal and External memory controllers are memory interconnection
> providers. This allows us to use interconnect API for tuning of memory
> configuration. EMC driver now supports OPPs and DVFS.
>
> Tested-by: Nicolas Chauvet <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
Acked-by: Georgi Djakov <[email protected]>
Thanks,
Georgi
> ---
> drivers/memory/tegra/Kconfig | 1 +
> drivers/memory/tegra/tegra124-emc.c | 320 +++++++++++++++++++++++++++-
> drivers/memory/tegra/tegra124.c | 82 ++++++-
> 3 files changed, 391 insertions(+), 12 deletions(-)
Hi Dmitry,
On 11/23/20 9:27 AM, Dmitry Osipenko wrote:
> This patch moves ACTMON driver away from generating OPP table by itself,
> transitioning it to use the table which comes from device-tree. This
> change breaks compatibility with older device-trees and brings support
> for the interconnect framework to the driver. This is a mandatory change
> which needs to be done in order to implement interconnect-based memory
> DVFS, i.e. device-trees need to be updated. Now ACTMON issues a memory
> bandwidth requests using dev_pm_opp_set_bw() instead of driving EMC clock
> rate directly.
>
> Tested-by: Peter Geis <[email protected]>
> Tested-by: Nicolas Chauvet <[email protected]>
> Acked-by: Chanwoo Choi <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> drivers/devfreq/tegra30-devfreq.c | 79 +++++++++++++++----------------
> 1 file changed, 37 insertions(+), 42 deletions(-)
>
(snip)
When the patches related to icc are merged, I'll merge patch9/10.
Thanks for your work.
--
Best Regards,
Chanwoo Choi
Samsung Electronics
On 11/23/20 9:27 AM, Dmitry Osipenko wrote:
> Remove tegra20-devfreq in order to replace it with a EMC_STAT based
> devfreq driver. Previously we were going to use MC_STAT based
> tegra20-devfreq driver because EMC_STAT wasn't working properly, but
> now that problem is resolved. This resolves complications imposed by
> the removed driver since it was depending on both EMC and MC drivers
> simultaneously.
>
> Acked-by: Chanwoo Choi <[email protected]>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> MAINTAINERS | 1 -
> drivers/devfreq/Kconfig | 10 --
> drivers/devfreq/Makefile | 1 -
> drivers/devfreq/tegra20-devfreq.c | 210 ------------------------------
> 4 files changed, 222 deletions(-)
> delete mode 100644 drivers/devfreq/tegra20-devfreq.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5f10105cac6f..56c560320f00 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11370,7 +11370,6 @@ L: [email protected]
> L: [email protected]
> T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git
> S: Maintained
> -F: drivers/devfreq/tegra20-devfreq.c
> F: drivers/devfreq/tegra30-devfreq.c
>
> MEMORY MANAGEMENT
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index 0ee36ae2fa79..00704efe6398 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -121,16 +121,6 @@ config ARM_TEGRA_DEVFREQ
> It reads ACTMON counters of memory controllers and adjusts the
> operating frequencies and voltages with OPP support.
>
> -config ARM_TEGRA20_DEVFREQ
> - tristate "NVIDIA Tegra20 DEVFREQ Driver"
> - depends on ARCH_TEGRA_2x_SOC || COMPILE_TEST
> - depends on COMMON_CLK
> - select DEVFREQ_GOV_SIMPLE_ONDEMAND
> - help
> - This adds the DEVFREQ driver for the Tegra20 family of SoCs.
> - It reads Memory Controller counters and adjusts the operating
> - frequencies and voltages with OPP support.
> -
> config ARM_RK3399_DMC_DEVFREQ
> tristate "ARM RK3399 DMC DEVFREQ Driver"
> depends on (ARCH_ROCKCHIP && HAVE_ARM_SMCCC) || \
> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
> index 3ca1ad0ecb97..a16333ea7034 100644
> --- a/drivers/devfreq/Makefile
> +++ b/drivers/devfreq/Makefile
> @@ -13,7 +13,6 @@ obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o
> obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o
> obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
> obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
> -obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o
>
> # DEVFREQ Event Drivers
> obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
> diff --git a/drivers/devfreq/tegra20-devfreq.c b/drivers/devfreq/tegra20-devfreq.c
> deleted file mode 100644
> index fd801534771d..000000000000
> --- a/drivers/devfreq/tegra20-devfreq.c
> +++ /dev/null
> @@ -1,210 +0,0 @@
> -// SPDX-License-Identifier: GPL-2.0
> -/*
> - * NVIDIA Tegra20 devfreq driver
> - *
> - * Copyright (C) 2019 GRATE-DRIVER project
> - */
> -
> -#include <linux/clk.h>
> -#include <linux/devfreq.h>
> -#include <linux/io.h>
> -#include <linux/kernel.h>
> -#include <linux/module.h>
> -#include <linux/of_device.h>
> -#include <linux/platform_device.h>
> -#include <linux/pm_opp.h>
> -#include <linux/slab.h>
> -
> -#include <soc/tegra/mc.h>
> -
> -#include "governor.h"
> -
> -#define MC_STAT_CONTROL 0x90
> -#define MC_STAT_EMC_CLOCK_LIMIT 0xa0
> -#define MC_STAT_EMC_CLOCKS 0xa4
> -#define MC_STAT_EMC_CONTROL 0xa8
> -#define MC_STAT_EMC_COUNT 0xb8
> -
> -#define EMC_GATHER_CLEAR (1 << 8)
> -#define EMC_GATHER_ENABLE (3 << 8)
> -
> -struct tegra_devfreq {
> - struct devfreq *devfreq;
> - struct clk *emc_clock;
> - void __iomem *regs;
> -};
> -
> -static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
> - u32 flags)
> -{
> - struct tegra_devfreq *tegra = dev_get_drvdata(dev);
> - struct devfreq *devfreq = tegra->devfreq;
> - struct dev_pm_opp *opp;
> - unsigned long rate;
> - int err;
> -
> - opp = devfreq_recommended_opp(dev, freq, flags);
> - if (IS_ERR(opp))
> - return PTR_ERR(opp);
> -
> - rate = dev_pm_opp_get_freq(opp);
> - dev_pm_opp_put(opp);
> -
> - err = clk_set_min_rate(tegra->emc_clock, rate);
> - if (err)
> - return err;
> -
> - err = clk_set_rate(tegra->emc_clock, 0);
> - if (err)
> - goto restore_min_rate;
> -
> - return 0;
> -
> -restore_min_rate:
> - clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
> -
> - return err;
> -}
> -
> -static int tegra_devfreq_get_dev_status(struct device *dev,
> - struct devfreq_dev_status *stat)
> -{
> - struct tegra_devfreq *tegra = dev_get_drvdata(dev);
> -
> - /*
> - * EMC_COUNT returns number of memory events, that number is lower
> - * than the number of clocks. Conversion ratio of 1/8 results in a
> - * bit higher bandwidth than actually needed, it is good enough for
> - * the time being because drivers don't support requesting minimum
> - * needed memory bandwidth yet.
> - *
> - * TODO: adjust the ratio value once relevant drivers will support
> - * memory bandwidth management.
> - */
> - stat->busy_time = readl_relaxed(tegra->regs + MC_STAT_EMC_COUNT);
> - stat->total_time = readl_relaxed(tegra->regs + MC_STAT_EMC_CLOCKS) / 8;
> - stat->current_frequency = clk_get_rate(tegra->emc_clock);
> -
> - writel_relaxed(EMC_GATHER_CLEAR, tegra->regs + MC_STAT_CONTROL);
> - writel_relaxed(EMC_GATHER_ENABLE, tegra->regs + MC_STAT_CONTROL);
> -
> - return 0;
> -}
> -
> -static struct devfreq_dev_profile tegra_devfreq_profile = {
> - .polling_ms = 500,
> - .target = tegra_devfreq_target,
> - .get_dev_status = tegra_devfreq_get_dev_status,
> -};
> -
> -static struct tegra_mc *tegra_get_memory_controller(void)
> -{
> - struct platform_device *pdev;
> - struct device_node *np;
> - struct tegra_mc *mc;
> -
> - np = of_find_compatible_node(NULL, NULL, "nvidia,tegra20-mc-gart");
> - if (!np)
> - return ERR_PTR(-ENOENT);
> -
> - pdev = of_find_device_by_node(np);
> - of_node_put(np);
> - if (!pdev)
> - return ERR_PTR(-ENODEV);
> -
> - mc = platform_get_drvdata(pdev);
> - if (!mc)
> - return ERR_PTR(-EPROBE_DEFER);
> -
> - return mc;
> -}
> -
> -static int tegra_devfreq_probe(struct platform_device *pdev)
> -{
> - struct tegra_devfreq *tegra;
> - struct tegra_mc *mc;
> - unsigned long max_rate;
> - unsigned long rate;
> - int err;
> -
> - mc = tegra_get_memory_controller();
> - if (IS_ERR(mc)) {
> - err = PTR_ERR(mc);
> - dev_err(&pdev->dev, "failed to get memory controller: %d\n",
> - err);
> - return err;
> - }
> -
> - tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
> - if (!tegra)
> - return -ENOMEM;
> -
> - /* EMC is a system-critical clock that is always enabled */
> - tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
> - if (IS_ERR(tegra->emc_clock))
> - return dev_err_probe(&pdev->dev, PTR_ERR(tegra->emc_clock),
> - "failed to get emc clock\n");
> -
> - tegra->regs = mc->regs;
> -
> - max_rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
> -
> - for (rate = 0; rate <= max_rate; rate++) {
> - rate = clk_round_rate(tegra->emc_clock, rate);
> -
> - err = dev_pm_opp_add(&pdev->dev, rate, 0);
> - if (err) {
> - dev_err(&pdev->dev, "failed to add opp: %d\n", err);
> - goto remove_opps;
> - }
> - }
> -
> - /*
> - * Reset statistic gathers state, select global bandwidth for the
> - * statistics collection mode and set clocks counter saturation
> - * limit to maximum.
> - */
> - writel_relaxed(0x00000000, tegra->regs + MC_STAT_CONTROL);
> - writel_relaxed(0x00000000, tegra->regs + MC_STAT_EMC_CONTROL);
> - writel_relaxed(0xffffffff, tegra->regs + MC_STAT_EMC_CLOCK_LIMIT);
> -
> - platform_set_drvdata(pdev, tegra);
> -
> - tegra->devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
> - DEVFREQ_GOV_SIMPLE_ONDEMAND, NULL);
> - if (IS_ERR(tegra->devfreq)) {
> - err = PTR_ERR(tegra->devfreq);
> - goto remove_opps;
> - }
> -
> - return 0;
> -
> -remove_opps:
> - dev_pm_opp_remove_all_dynamic(&pdev->dev);
> -
> - return err;
> -}
> -
> -static int tegra_devfreq_remove(struct platform_device *pdev)
> -{
> - struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
> -
> - devfreq_remove_device(tegra->devfreq);
> - dev_pm_opp_remove_all_dynamic(&pdev->dev);
> -
> - return 0;
> -}
> -
> -static struct platform_driver tegra_devfreq_driver = {
> - .probe = tegra_devfreq_probe,
> - .remove = tegra_devfreq_remove,
> - .driver = {
> - .name = "tegra20-devfreq",
> - },
> -};
> -module_platform_driver(tegra_devfreq_driver);
> -
> -MODULE_ALIAS("platform:tegra20-devfreq");
> -MODULE_AUTHOR("Dmitry Osipenko <[email protected]>");
> -MODULE_DESCRIPTION("NVIDIA Tegra20 devfreq driver");
> -MODULE_LICENSE("GPL v2");
>
Applied it. Thanks.
--
Best Regards,
Chanwoo Choi
Samsung Electronics
23.11.2020 09:32, Georgi Djakov пишет:
> Hi Dmitry,
>
> On 23.11.20 2:27, Dmitry Osipenko wrote:
>> Now Internal and External memory controllers are memory interconnection
>> providers. This allows us to use interconnect API for tuning of memory
>> configuration. EMC driver now supports OPPs and DVFS. MC driver now
>> supports tuning of memory arbitration latency, which needs to be done
>> for ISO memory clients, like a Display client for example.
>>
>> Tested-by: Peter Geis <[email protected]>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>
> Acked-by: Georgi Djakov <[email protected]>
>
> Thank you for the continuous work on this patchset!
Hello Georgi,
Thank you for reviewing the patches!
23.11.2020 10:02, Chanwoo Choi пишет:
> Hi Dmitry,
>
> On 11/23/20 9:27 AM, Dmitry Osipenko wrote:
>> This patch moves ACTMON driver away from generating OPP table by itself,
>> transitioning it to use the table which comes from device-tree. This
>> change breaks compatibility with older device-trees and brings support
>> for the interconnect framework to the driver. This is a mandatory change
>> which needs to be done in order to implement interconnect-based memory
>> DVFS, i.e. device-trees need to be updated. Now ACTMON issues a memory
>> bandwidth requests using dev_pm_opp_set_bw() instead of driving EMC clock
>> rate directly.
>>
>> Tested-by: Peter Geis <[email protected]>
>> Tested-by: Nicolas Chauvet <[email protected]>
>> Acked-by: Chanwoo Choi <[email protected]>
>> Signed-off-by: Dmitry Osipenko <[email protected]>
>> ---
>> drivers/devfreq/tegra30-devfreq.c | 79 +++++++++++++++----------------
>> 1 file changed, 37 insertions(+), 42 deletions(-)
>>
>
> (snip)
Hello Chanwoo,
> When the patches related to icc are merged, I'll merge patch9/10.
> Thanks for your work.
>
Alright, thank you!
23.11.2020 09:17, Viresh Kumar пишет:
> On 23-11-20, 03:27, Dmitry Osipenko wrote:
>> This series brings initial support for memory interconnect to Tegra20,
>> Tegra30 and Tegra124 SoCs.
>>
>> For the starter only display controllers and devfreq devices are getting
>> interconnect API support, others could be supported later on. The display
>> controllers have the biggest demand for interconnect API right now because
>> dynamic memory frequency scaling can't be done safely without taking into
>> account bandwidth requirement from the displays. In particular this series
>> fixes distorted display output on T30 Ouya and T124 TK1 devices.
>>
>> Changelog:
>>
>> v10 - In a longer run it will be much nicer if we could support EMC
>> hardware versioning on Tegra20 and it's not late to support it now.
>> Hence I added these new patches:
>>
>> dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
>> memory: tegra20: Support hardware versioning and clean up OPP table initialization
>>
>> - Removed error message from tegra30-devfreq driver about missing OPP
>> properties in a device-tree because EMC driver already prints that
>> message and it uses OPP API error code instead of checking DT directly,
>> which is a more correct way of doing that.
>
> Looks good to me (from OPP APIs usage perspective). Thanks for
> continuing with this and fixing all the issues Dmitry.
>
Hello Viresh,
Thank you for all the clarifications and for reviewing of the patches!
Hi Dmitry,
The v5.10-rc6 was released from linus git tree.
Generally, I will send the pull-quest about devfreq to linux-pm.git maintainer
after releasing the v5.1-rc7 for the integration test on linux-pm.git.
The icc patches in this patch have not yet merged. If these patches
are not merged before v5.10-rc7, Maybe, I'll apply the devfreq patches
for v5.12-rc1.
Best Regards,
Chanwoo Choi
On 11/23/20 9:27 AM, Dmitry Osipenko wrote:
> This series brings initial support for memory interconnect to Tegra20,
> Tegra30 and Tegra124 SoCs.
>
> For the starter only display controllers and devfreq devices are getting
> interconnect API support, others could be supported later on. The display
> controllers have the biggest demand for interconnect API right now because
> dynamic memory frequency scaling can't be done safely without taking into
> account bandwidth requirement from the displays. In particular this series
> fixes distorted display output on T30 Ouya and T124 TK1 devices.
>
> Changelog:
>
> v10 - In a longer run it will be much nicer if we could support EMC
> hardware versioning on Tegra20 and it's not late to support it now.
> Hence I added these new patches:
>
> dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
> memory: tegra20: Support hardware versioning and clean up OPP table initialization
>
> - Removed error message from tegra30-devfreq driver about missing OPP
> properties in a device-tree because EMC driver already prints that
> message and it uses OPP API error code instead of checking DT directly,
> which is a more correct way of doing that.
>
> v9: - Squashed "memory: tegra30-emc: Factor out clk initialization" into
> patch "tegra30: Support interconnect framework".
> Suggested by Krzysztof Kozlowski.
>
> - Improved Kconfig in the patch "memory: tegra124-emc: Make driver modular"
> by adding CONFIG_TEGRA124_CLK_EMC entry, which makes clk-driver changes
> to look a bit more cleaner. Suggested by Krzysztof Kozlowski.
>
> - Dropped voltage regulator support from ICC and DT patches for now
> because there is a new discussion about using a power domain abstraction
> for controlling the regulator, which is likely to happen.
>
> - Replaced direct "operating-points-v2" property checking in EMC drivers
> with checking of a returned error code from dev_pm_opp_of_add_table().
> Note that I haven't touched T20 EMC driver because it's very likely
> that we'll replace that code with a common helper soon anyways.
> Suggested by Viresh Kumar.
>
> - The T30 DT patches now include EMC OPP changes for Ouya board, which
> is available now in linux-next.
>
> Dmitry Osipenko (19):
> dt-bindings: memory: tegra20: emc: Document opp-supported-hw property
> memory: tegra20: Support hardware versioning and clean up OPP table
> initialization
> memory: tegra30: Support interconnect framework
> memory: tegra124-emc: Make driver modular
> memory: tegra124-emc: Continue probing if timings are missing in
> device-tree
> memory: tegra124: Support interconnect framework
> drm/tegra: dc: Support memory bandwidth management
> drm/tegra: dc: Extend debug stats with total number of events
> PM / devfreq: tegra30: Support interconnect and OPPs from device-tree
> PM / devfreq: tegra30: Separate configurations per-SoC generation
> PM / devfreq: tegra20: Deprecate in a favor of emc-stat based driver
> ARM: tegra: Correct EMC registers size in Tegra20 device-tree
> ARM: tegra: Add interconnect properties to Tegra20 device-tree
> ARM: tegra: Add interconnect properties to Tegra30 device-tree
> ARM: tegra: Add interconnect properties to Tegra124 device-tree
> ARM: tegra: Add nvidia,memory-controller phandle to Tegra20 EMC
> device-tree
> ARM: tegra: Add EMC OPP properties to Tegra20 device-trees
> ARM: tegra: Add EMC OPP and ICC properties to Tegra30 EMC and ACTMON
> device-tree nodes
> ARM: tegra: Add EMC OPP and ICC properties to Tegra124 EMC and ACTMON
> device-tree nodes
>
> .../memory-controllers/nvidia,tegra20-emc.txt | 6 +
> MAINTAINERS | 1 -
> arch/arm/boot/dts/tegra124-apalis-emc.dtsi | 8 +
> .../arm/boot/dts/tegra124-jetson-tk1-emc.dtsi | 8 +
> arch/arm/boot/dts/tegra124-nyan-big-emc.dtsi | 10 +
> .../arm/boot/dts/tegra124-nyan-blaze-emc.dtsi | 10 +
> .../boot/dts/tegra124-peripherals-opp.dtsi | 419 ++++++++++++++++++
> arch/arm/boot/dts/tegra124.dtsi | 31 ++
> .../boot/dts/tegra20-acer-a500-picasso.dts | 5 +
> arch/arm/boot/dts/tegra20-colibri.dtsi | 4 +
> arch/arm/boot/dts/tegra20-paz00.dts | 4 +
> .../arm/boot/dts/tegra20-peripherals-opp.dtsi | 109 +++++
> arch/arm/boot/dts/tegra20.dtsi | 33 +-
> ...30-asus-nexus7-grouper-memory-timings.dtsi | 12 +
> arch/arm/boot/dts/tegra30-ouya.dts | 8 +
> .../arm/boot/dts/tegra30-peripherals-opp.dtsi | 383 ++++++++++++++++
> arch/arm/boot/dts/tegra30.dtsi | 33 +-
> drivers/clk/tegra/Kconfig | 3 +
> drivers/clk/tegra/Makefile | 2 +-
> drivers/clk/tegra/clk-tegra124-emc.c | 41 +-
> drivers/clk/tegra/clk-tegra124.c | 26 +-
> drivers/clk/tegra/clk.h | 18 +-
> drivers/devfreq/Kconfig | 10 -
> drivers/devfreq/Makefile | 1 -
> drivers/devfreq/tegra20-devfreq.c | 210 ---------
> drivers/devfreq/tegra30-devfreq.c | 147 +++---
> drivers/gpu/drm/tegra/Kconfig | 1 +
> drivers/gpu/drm/tegra/dc.c | 359 +++++++++++++++
> drivers/gpu/drm/tegra/dc.h | 19 +
> drivers/gpu/drm/tegra/drm.c | 14 +
> drivers/gpu/drm/tegra/hub.c | 3 +
> drivers/gpu/drm/tegra/plane.c | 121 +++++
> drivers/gpu/drm/tegra/plane.h | 15 +
> drivers/memory/tegra/Kconfig | 5 +-
> drivers/memory/tegra/tegra124-emc.c | 377 ++++++++++++++--
> drivers/memory/tegra/tegra124.c | 82 +++-
> drivers/memory/tegra/tegra20-emc.c | 48 +-
> drivers/memory/tegra/tegra30-emc.c | 344 +++++++++++++-
> drivers/memory/tegra/tegra30.c | 173 +++++++-
> include/linux/clk/tegra.h | 8 +
> include/soc/tegra/emc.h | 16 -
> 41 files changed, 2725 insertions(+), 402 deletions(-)
> create mode 100644 arch/arm/boot/dts/tegra124-peripherals-opp.dtsi
> create mode 100644 arch/arm/boot/dts/tegra20-peripherals-opp.dtsi
> create mode 100644 arch/arm/boot/dts/tegra30-peripherals-opp.dtsi
> delete mode 100644 drivers/devfreq/tegra20-devfreq.c
> delete mode 100644 include/soc/tegra/emc.h
>
On Mon, Nov 30, 2020 at 05:44:39PM +0900, Chanwoo Choi wrote:
> Hi Dmitry,
>
> The v5.10-rc6 was released from linus git tree.
> Generally, I will send the pull-quest about devfreq to linux-pm.git maintainer
> after releasing the v5.1-rc7 for the integration test on linux-pm.git.
>
> The icc patches in this patch have not yet merged. If these patches
> are not merged before v5.10-rc7, Maybe, I'll apply the devfreq patches
> for v5.12-rc1.
None of the patches here are going to be merged to Linus' in the current
cycle. They will only go to the next so if there is dependency,
everything will be broken and non-bisectable.
However no such dependencies or merging requirements were mention in the
cover letter.
Best regards,
Krzysztof
On 11/30/20 5:36 PM, Krzysztof Kozlowski wrote:
> On Mon, Nov 30, 2020 at 05:44:39PM +0900, Chanwoo Choi wrote:
>> Hi Dmitry,
>>
>> The v5.10-rc6 was released from linus git tree.
>> Generally, I will send the pull-quest about devfreq to linux-pm.git maintainer
>> after releasing the v5.1-rc7 for the integration test on linux-pm.git.
>>
>> The icc patches in this patch have not yet merged. If these patches
>> are not merged before v5.10-rc7, Maybe, I'll apply the devfreq patches
>> for v5.12-rc1.
>
> None of the patches here are going to be merged to Linus' in the current
> cycle. They will only go to the next so if there is dependency,
> everything will be broken and non-bisectable.
>
> However no such dependencies or merging requirements were mention in the
> cover letter.
Thanks for reply. The devfreq patch depends on the icc changes.
I'll apply the devfreq patches on next time (v5.12-rc1). Thanks.
--
Best Regards,
Chanwoo Choi
Samsung Electronics
On 23.11.20 2:27, Dmitry Osipenko wrote:
> Document opp-supported-hw property, which is not strictly necessary to
> have on Tegra20, but it's very convenient to have because all other SoC
> core devices will use hardware versioning, and thus, it's good to maintain
> the consistency.
Hi Dmitry,
I believe Krzysztof is waiting for Ack on the binding before merging
this patch (and the rest), but unfortunately it was not sent to the
DT mailing list for review.
Thanks,
Georgi
>
> Signed-off-by: Dmitry Osipenko <[email protected]>
> ---
> .../bindings/memory-controllers/nvidia,tegra20-emc.txt | 6 ++++++
> 1 file changed, 6 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
> index 67ac8d1297da..fe99ce1013bd 100644
> --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
> +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra20-emc.txt
> @@ -16,6 +16,12 @@ Properties:
> - #interconnect-cells : Should be 0.
> - operating-points-v2: See ../bindings/opp/opp.txt for details.
>
> +For each opp entry in 'operating-points-v2' table:
> +- opp-supported-hw: One bitfield indicating SoC process ID mask
> +
> + A bitwise AND is performed against this value and if any bit
> + matches, the OPP gets enabled.
> +
> Optional properties:
> - core-supply: Phandle of voltage regulator of the SoC "core" power domain.
>
>
On Mon, Nov 30, 2020 at 11:48:18AM +0200, Georgi Djakov wrote:
> On 23.11.20 2:27, Dmitry Osipenko wrote:
> > Document opp-supported-hw property, which is not strictly necessary to
> > have on Tegra20, but it's very convenient to have because all other SoC
> > core devices will use hardware versioning, and thus, it's good to maintain
> > the consistency.
>
> Hi Dmitry,
>
> I believe Krzysztof is waiting for Ack on the binding before merging
> this patch (and the rest), but unfortunately it was not sent to the
> DT mailing list for review.
Indeed I am still waiting for Rob's and Thierry's acks for this and the
following patches. It has been just a week so I'll give it few more
days.
Best regards,
Krzysztof
30.11.2020 21:23, Krzysztof Kozlowski пишет:
> On Mon, Nov 30, 2020 at 11:48:18AM +0200, Georgi Djakov wrote:
>> On 23.11.20 2:27, Dmitry Osipenko wrote:
>>> Document opp-supported-hw property, which is not strictly necessary to
>>> have on Tegra20, but it's very convenient to have because all other SoC
>>> core devices will use hardware versioning, and thus, it's good to maintain
>>> the consistency.
>>
>> Hi Dmitry,
>>
>> I believe Krzysztof is waiting for Ack on the binding before merging
>> this patch (and the rest), but unfortunately it was not sent to the
>> DT mailing list for review.
Good catch, thank you.
> Indeed I am still waiting for Rob's and Thierry's acks for this and the
> following patches. It has been just a week so I'll give it few more
> days.
Rob doesn't review patches which aren't sent to the DT ML, which isn't
cc'ed in v10 by accident. I'll make v11.