summaryrefslogtreecommitdiffstats
path: root/drivers/memory/tegra
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2019-08-12 00:00:31 +0300
committerThierry Reding <treding@nvidia.com>2019-11-11 14:55:25 +0100
commit77ab499dca5d7426a84f76988b3f84e7c9db7e12 (patch)
tree4124d510d4dd3f7bf789c6aeff5d3fdd530ba030 /drivers/memory/tegra
parentfa6749d40e99d3e780b60f2a2e0a3ad97af45b3c (diff)
downloadlinux-77ab499dca5d7426a84f76988b3f84e7c9db7e12.tar.gz
linux-77ab499dca5d7426a84f76988b3f84e7c9db7e12.tar.bz2
linux-77ab499dca5d7426a84f76988b3f84e7c9db7e12.zip
memory: tegra: Adapt for Tegra20 clock driver changes
Now Tegra20 and Tegra30 EMC drivers should provide clock-rounding functionality using the new Tegra clock driver API. Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/memory/tegra')
-rw-r--r--drivers/memory/tegra/tegra20-emc.c50
1 files changed, 48 insertions, 2 deletions
diff --git a/drivers/memory/tegra/tegra20-emc.c b/drivers/memory/tegra/tegra20-emc.c
index da8fa592b071..b519f02b0ee9 100644
--- a/drivers/memory/tegra/tegra20-emc.c
+++ b/drivers/memory/tegra/tegra20-emc.c
@@ -6,6 +6,7 @@
*/
#include <linux/clk.h>
+#include <linux/clk/tegra.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/interrupt.h>
@@ -421,6 +422,44 @@ static int emc_setup_hw(struct tegra_emc *emc)
return 0;
}
+static long emc_round_rate(unsigned long rate,
+ unsigned long min_rate,
+ unsigned long max_rate,
+ void *arg)
+{
+ struct emc_timing *timing = NULL;
+ struct tegra_emc *emc = arg;
+ unsigned int i;
+
+ min_rate = min(min_rate, emc->timings[emc->num_timings - 1].rate);
+
+ for (i = 0; i < emc->num_timings; i++) {
+ if (emc->timings[i].rate < rate && i != emc->num_timings - 1)
+ continue;
+
+ if (emc->timings[i].rate > max_rate) {
+ i = max(i, 1u) - 1;
+
+ if (emc->timings[i].rate < min_rate)
+ break;
+ }
+
+ if (emc->timings[i].rate < min_rate)
+ continue;
+
+ timing = &emc->timings[i];
+ break;
+ }
+
+ if (!timing) {
+ dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
+ rate, min_rate, max_rate);
+ return -EINVAL;
+ }
+
+ return timing->rate;
+}
+
static int tegra_emc_probe(struct platform_device *pdev)
{
struct device_node *np;
@@ -477,21 +516,28 @@ 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);
- return err;
+ goto unset_cb;
}
err = clk_notifier_register(emc->clk, &emc->clk_nb);
if (err) {
dev_err(&pdev->dev, "failed to register clk notifier: %d\n",
err);
- return err;
+ goto unset_cb;
}
return 0;
+
+unset_cb:
+ tegra20_clk_set_emc_round_callback(NULL, NULL);
+
+ return err;
}
static const struct of_device_id tegra_emc_of_match[] = {