summaryrefslogtreecommitdiff
path: root/drivers/memory/tegra/tegra186-emc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/memory/tegra/tegra186-emc.c')
-rw-r--r--drivers/memory/tegra/tegra186-emc.c185
1 files changed, 92 insertions, 93 deletions
diff --git a/drivers/memory/tegra/tegra186-emc.c b/drivers/memory/tegra/tegra186-emc.c
index 6ad8a4023dd7..dfddceecdd1a 100644
--- a/drivers/memory/tegra/tegra186-emc.c
+++ b/drivers/memory/tegra/tegra186-emc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2019 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2019-2025 NVIDIA CORPORATION. All rights reserved.
*/
#include <linux/clk.h>
@@ -35,11 +35,6 @@ struct tegra186_emc {
struct icc_provider provider;
};
-static inline struct tegra186_emc *to_tegra186_emc(struct icc_provider *provider)
-{
- return container_of(provider, struct tegra186_emc, provider);
-}
-
/*
* debugfs interface
*
@@ -155,21 +150,88 @@ DEFINE_DEBUGFS_ATTRIBUTE(tegra186_emc_debug_max_rate_fops,
tegra186_emc_debug_max_rate_get,
tegra186_emc_debug_max_rate_set, "%llu\n");
+static int tegra186_emc_get_emc_dvfs_latency(struct tegra186_emc *emc)
+{
+ struct mrq_emc_dvfs_latency_response response;
+ struct tegra_bpmp_message msg;
+ unsigned int i;
+ int err;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.mrq = MRQ_EMC_DVFS_LATENCY;
+ msg.tx.data = NULL;
+ msg.tx.size = 0;
+ msg.rx.data = &response;
+ msg.rx.size = sizeof(response);
+
+ err = tegra_bpmp_transfer(emc->bpmp, &msg);
+ if (err < 0) {
+ dev_err(emc->dev, "failed to EMC DVFS pairs: %d\n", err);
+ return err;
+ }
+ if (msg.rx.ret < 0) {
+ dev_err(emc->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret);
+ return -EINVAL;
+ }
+
+ emc->debugfs.min_rate = ULONG_MAX;
+ emc->debugfs.max_rate = 0;
+
+ emc->num_dvfs = response.num_pairs;
+
+ emc->dvfs = devm_kmalloc_array(emc->dev, emc->num_dvfs, sizeof(*emc->dvfs), GFP_KERNEL);
+ if (!emc->dvfs)
+ return -ENOMEM;
+
+ dev_dbg(emc->dev, "%u DVFS pairs:\n", emc->num_dvfs);
+
+ for (i = 0; i < emc->num_dvfs; i++) {
+ emc->dvfs[i].rate = response.pairs[i].freq * 1000;
+ emc->dvfs[i].latency = response.pairs[i].latency;
+
+ if (emc->dvfs[i].rate < emc->debugfs.min_rate)
+ emc->debugfs.min_rate = emc->dvfs[i].rate;
+
+ if (emc->dvfs[i].rate > emc->debugfs.max_rate)
+ emc->debugfs.max_rate = emc->dvfs[i].rate;
+
+ dev_dbg(emc->dev, " %2u: %lu Hz -> %lu us\n", i,
+ emc->dvfs[i].rate, emc->dvfs[i].latency);
+ }
+
+ err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate, emc->debugfs.max_rate);
+ if (err < 0) {
+ dev_err(emc->dev, "failed to set rate range [%lu-%lu] for %pC\n",
+ emc->debugfs.min_rate, emc->debugfs.max_rate, emc->clk);
+ return err;
+ }
+
+ emc->debugfs.root = debugfs_create_dir("emc", NULL);
+ debugfs_create_file("available_rates", 0444, emc->debugfs.root, emc,
+ &tegra186_emc_debug_available_rates_fops);
+ debugfs_create_file("min_rate", 0644, emc->debugfs.root, emc,
+ &tegra186_emc_debug_min_rate_fops);
+ debugfs_create_file("max_rate", 0644, emc->debugfs.root, emc,
+ &tegra186_emc_debug_max_rate_fops);
+
+ return 0;
+}
+
/*
- * tegra_emc_icc_set_bw() - Set BW api for EMC provider
+ * tegra186_emc_icc_set_bw() - Set BW api for EMC provider
* @src: ICC node for External Memory Controller (EMC)
* @dst: ICC node for External Memory (DRAM)
*
* Do nothing here as info to BPMP-FW is now passed in the BW set function
* of the MC driver. BPMP-FW sets the final Freq based on the passed values.
*/
-static int tegra_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst)
+static int tegra186_emc_icc_set_bw(struct icc_node *src, struct icc_node *dst)
{
return 0;
}
static struct icc_node *
-tegra_emc_of_icc_xlate(struct of_phandle_args *spec, void *data)
+tegra186_emc_of_icc_xlate(const struct of_phandle_args *spec, void *data)
{
struct icc_provider *provider = data;
struct icc_node *node;
@@ -185,7 +247,7 @@ tegra_emc_of_icc_xlate(struct of_phandle_args *spec, void *data)
return ERR_PTR(-EPROBE_DEFER);
}
-static int tegra_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
+static int tegra186_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
{
*avg = 0;
*peak = 0;
@@ -193,7 +255,7 @@ static int tegra_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)
return 0;
}
-static int tegra_emc_interconnect_init(struct tegra186_emc *emc)
+static int tegra186_emc_interconnect_init(struct tegra186_emc *emc)
{
struct tegra_mc *mc = dev_get_drvdata(emc->dev->parent);
const struct tegra_mc_soc *soc = mc->soc;
@@ -201,20 +263,18 @@ static int tegra_emc_interconnect_init(struct tegra186_emc *emc)
int err;
emc->provider.dev = emc->dev;
- emc->provider.set = tegra_emc_icc_set_bw;
+ emc->provider.set = tegra186_emc_icc_set_bw;
emc->provider.data = &emc->provider;
emc->provider.aggregate = soc->icc_ops->aggregate;
- emc->provider.xlate = tegra_emc_of_icc_xlate;
- emc->provider.get_bw = tegra_emc_icc_get_init_bw;
+ emc->provider.xlate = tegra186_emc_of_icc_xlate;
+ emc->provider.get_bw = tegra186_emc_icc_get_init_bw;
icc_provider_init(&emc->provider);
/* create External Memory Controller node */
node = icc_node_create(TEGRA_ICC_EMC);
- if (IS_ERR(node)) {
- err = PTR_ERR(node);
- goto err_msg;
- }
+ if (IS_ERR(node))
+ return PTR_ERR(node);
node->name = "External Memory Controller";
icc_node_add(node, &emc->provider);
@@ -242,19 +302,14 @@ static int tegra_emc_interconnect_init(struct tegra186_emc *emc)
remove_nodes:
icc_nodes_remove(&emc->provider);
-err_msg:
- dev_err(emc->dev, "failed to initialize ICC: %d\n", err);
- return err;
+ return dev_err_probe(emc->dev, err, "failed to initialize ICC\n");
}
static int tegra186_emc_probe(struct platform_device *pdev)
{
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
- struct mrq_emc_dvfs_latency_response response;
- struct tegra_bpmp_message msg;
struct tegra186_emc *emc;
- unsigned int i;
int err;
emc = devm_kzalloc(&pdev->dev, sizeof(*emc), GFP_KERNEL);
@@ -263,82 +318,25 @@ static int tegra186_emc_probe(struct platform_device *pdev)
emc->bpmp = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(emc->bpmp))
- return dev_err_probe(&pdev->dev, PTR_ERR(emc->bpmp), "failed to get BPMP\n");
+ return dev_err_probe(&pdev->dev, PTR_ERR(emc->bpmp),
+ "failed to get BPMP\n");
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);
+ err = dev_err_probe(&pdev->dev, PTR_ERR(emc->clk),
+ "failed to get EMC clock\n");
goto put_bpmp;
}
platform_set_drvdata(pdev, emc);
emc->dev = &pdev->dev;
- memset(&msg, 0, sizeof(msg));
- msg.mrq = MRQ_EMC_DVFS_LATENCY;
- msg.tx.data = NULL;
- msg.tx.size = 0;
- msg.rx.data = &response;
- msg.rx.size = sizeof(response);
-
- err = tegra_bpmp_transfer(emc->bpmp, &msg);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to EMC DVFS pairs: %d\n", err);
- goto put_bpmp;
- }
- if (msg.rx.ret < 0) {
- err = -EINVAL;
- dev_err(&pdev->dev, "EMC DVFS MRQ failed: %d (BPMP error code)\n", msg.rx.ret);
- goto put_bpmp;
- }
-
- emc->debugfs.min_rate = ULONG_MAX;
- emc->debugfs.max_rate = 0;
-
- emc->num_dvfs = response.num_pairs;
-
- emc->dvfs = devm_kmalloc_array(&pdev->dev, emc->num_dvfs,
- sizeof(*emc->dvfs), GFP_KERNEL);
- if (!emc->dvfs) {
- err = -ENOMEM;
- goto put_bpmp;
- }
-
- dev_dbg(&pdev->dev, "%u DVFS pairs:\n", emc->num_dvfs);
-
- for (i = 0; i < emc->num_dvfs; i++) {
- emc->dvfs[i].rate = response.pairs[i].freq * 1000;
- emc->dvfs[i].latency = response.pairs[i].latency;
-
- if (emc->dvfs[i].rate < emc->debugfs.min_rate)
- emc->debugfs.min_rate = emc->dvfs[i].rate;
-
- if (emc->dvfs[i].rate > emc->debugfs.max_rate)
- emc->debugfs.max_rate = emc->dvfs[i].rate;
-
- dev_dbg(&pdev->dev, " %2u: %lu Hz -> %lu us\n", i,
- emc->dvfs[i].rate, emc->dvfs[i].latency);
- }
-
- err = clk_set_rate_range(emc->clk, emc->debugfs.min_rate,
- emc->debugfs.max_rate);
- if (err < 0) {
- dev_err(&pdev->dev,
- "failed to set rate range [%lu-%lu] for %pC\n",
- emc->debugfs.min_rate, emc->debugfs.max_rate,
- emc->clk);
- goto put_bpmp;
+ if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_EMC_DVFS_LATENCY)) {
+ err = tegra186_emc_get_emc_dvfs_latency(emc);
+ if (err)
+ goto put_bpmp;
}
- emc->debugfs.root = debugfs_create_dir("emc", NULL);
- debugfs_create_file("available_rates", S_IRUGO, emc->debugfs.root,
- emc, &tegra186_emc_debug_available_rates_fops);
- debugfs_create_file("min_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
- emc, &tegra186_emc_debug_min_rate_fops);
- debugfs_create_file("max_rate", S_IRUGO | S_IWUSR, emc->debugfs.root,
- emc, &tegra186_emc_debug_max_rate_fops);
-
if (mc && mc->soc->icc_ops) {
if (tegra_bpmp_mrq_is_supported(emc->bpmp, MRQ_BWMGR_INT)) {
mc->bwmgr_mrq_supported = true;
@@ -358,7 +356,7 @@ static int tegra186_emc_probe(struct platform_device *pdev)
* EINVAL instead of passing the request to BPMP-FW later when the BW
* request is made by client with 'icc_set_bw()' call.
*/
- err = tegra_emc_interconnect_init(emc);
+ err = tegra186_emc_interconnect_init(emc);
if (err) {
mc->bpmp = NULL;
goto put_bpmp;
@@ -372,7 +370,7 @@ put_bpmp:
return err;
}
-static int tegra186_emc_remove(struct platform_device *pdev)
+static void tegra186_emc_remove(struct platform_device *pdev)
{
struct tegra_mc *mc = dev_get_drvdata(pdev->dev.parent);
struct tegra186_emc *emc = platform_get_drvdata(pdev);
@@ -381,8 +379,6 @@ static int tegra186_emc_remove(struct platform_device *pdev)
mc->bpmp = NULL;
tegra_bpmp_put(emc->bpmp);
-
- return 0;
}
static const struct of_device_id tegra186_emc_of_match[] = {
@@ -395,6 +391,9 @@ static const struct of_device_id tegra186_emc_of_match[] = {
#if defined(CONFIG_ARCH_TEGRA_234_SOC)
{ .compatible = "nvidia,tegra234-emc" },
#endif
+#if defined(CONFIG_ARCH_TEGRA_264_SOC)
+ { .compatible = "nvidia,tegra264-emc" },
+#endif
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tegra186_emc_of_match);