forked from Minki/linux
memory: tegra: Changes for v5.5-rc1
This contains a couple of fixes and adds support for EMC frequency scaling on Tegra30. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAl3Jbz4THHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoZjGEAC17V/1w6n0uYZTCN/EAR8F8HW+la8V 7MdLl22O5+V7HlY3ZUJ5InMo1SLRlI8H9KC87fqDr25j06DpusyoWI7mNkVgVxJX zwJbQ7IZM9UkodayI8wW5/RGdNPn5IJCQuWgnBsH9TsVWff+yk2zBJnYSYc2ORKX UGWh3XPVfyY/cJvEcchR6bmAa+KVzwjgCaTcF0J0x1BqaHnBQ/8rsyHljXkx1Di1 t8bqaufQ0PYdYrvUpY2lUCYBZb1ygX2k0liRvqTA6gfF4mA7cyaG6ZS48hn9AQHu Q7TnkcokddkHUjCJ+fq/XUfH4GF71crj2F5+UdgYyEvf/8Rf6A4CfWUU/gXxUSSZ 5UFfoxIwubWjyz0Dy9Y/NZh+HBgJOaaDr8ErcFaoEvfNR0JvAaLuZjbxTN8BjHM4 H+2gSSy+ZI/M4JnLosGVKxJxr59mtB7vOeQo8S6RvQNbs5V/HouiCF1V32uZtnzr rQdx2tCkGZud4dgmm9gX2S/500A6Vp1/0CUoHbZNrGk3XRAX9XmcHkRTZbTqYEDr FJFK48m0Leni1o5furwfibCZ/RuQ86C1Tx9U2nQGtBAl1nDavw4+dAy1hrCFphyy 26IzulfQjeGZEI8nEAb9lpk/wMRjkSbRXBSM5krP5iDSOyR/UTsUHf4+zGKsH1I9 bqWS5vfMAK9ELA== =DLzB -----END PGP SIGNATURE----- Merge tag 'tegra-for-5.5-memory-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/drivers memory: tegra: Changes for v5.5-rc1 This contains a couple of fixes and adds support for EMC frequency scaling on Tegra30. * tag 'tegra-for-5.5-memory-v2' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: memory: tegra: Consolidate registers definition into common header memory: tegra: Ensure timing control debug features are disabled memory: tegra: Introduce Tegra30 EMC driver memory: tegra: Do not handle error from wait_for_completion_timeout() memory: tegra: Increase handshake timeout on Tegra20 memory: tegra: Print a brief info message about EMC timings memory: tegra: Pre-configure debug register on Tegra20 memory: tegra: Include io.h instead of iopoll.h memory: tegra: Adapt for Tegra20 clock driver changes memory: tegra: Don't set EMC rate to maximum on probe for Tegra20 memory: tegra: Add gr2d and gr3d to DRM IOMMU group memory: tegra: Set DMA mask based on supported address bits clk: tegra: Add Tegra20/30 EMC clock implementation Link: https://lore.kernel.org/r/20191111143836.4027200-1-thierry.reding@gmail.com Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
commit
57a54dfe48
@ -17,7 +17,9 @@ obj-y += clk-tegra-fixed.o
|
||||
obj-y += clk-tegra-super-gen4.o
|
||||
obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
|
||||
obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
|
||||
|
293
drivers/clk/tegra/clk-tegra20-emc.c
Normal file
293
drivers/clk/tegra/clk-tegra20-emc.c
Normal file
@ -0,0 +1,293 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Based on drivers/clk/tegra/clk-emc.c
|
||||
* Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author: Dmitry Osipenko <digetx@gmail.com>
|
||||
* Copyright (C) 2019 GRATE-DRIVER project
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "tegra-emc-clk: " fmt
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "clk.h"
|
||||
|
||||
#define CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK GENMASK(7, 0)
|
||||
#define CLK_SOURCE_EMC_2X_CLK_SRC_MASK GENMASK(31, 30)
|
||||
#define CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT 30
|
||||
|
||||
#define MC_EMC_SAME_FREQ BIT(16)
|
||||
#define USE_PLLM_UD BIT(29)
|
||||
|
||||
#define EMC_SRC_PLL_M 0
|
||||
#define EMC_SRC_PLL_C 1
|
||||
#define EMC_SRC_PLL_P 2
|
||||
#define EMC_SRC_CLK_M 3
|
||||
|
||||
static const char * const emc_parent_clk_names[] = {
|
||||
"pll_m", "pll_c", "pll_p", "clk_m",
|
||||
};
|
||||
|
||||
struct tegra_clk_emc {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
bool mc_same_freq;
|
||||
bool want_low_jitter;
|
||||
|
||||
tegra20_clk_emc_round_cb *round_cb;
|
||||
void *cb_arg;
|
||||
};
|
||||
|
||||
static inline struct tegra_clk_emc *to_tegra_clk_emc(struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct tegra_clk_emc, hw);
|
||||
}
|
||||
|
||||
static unsigned long emc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
|
||||
return DIV_ROUND_UP(parent_rate * 2, div + 2);
|
||||
}
|
||||
|
||||
static u8 emc_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
|
||||
return readl_relaxed(emc->reg) >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
}
|
||||
|
||||
static int emc_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
|
||||
val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
div = val & CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
unsigned int index;
|
||||
u32 val, div;
|
||||
|
||||
div = div_frac_get(rate, parent_rate, 8, 1, 0);
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
val |= div;
|
||||
|
||||
index = val >> CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_set_rate_and_parent(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate,
|
||||
u8 index)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
u32 val, div;
|
||||
|
||||
div = div_frac_get(rate, parent_rate, 8, 1, 0);
|
||||
|
||||
val = readl_relaxed(emc->reg);
|
||||
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_SRC_MASK;
|
||||
val |= index << CLK_SOURCE_EMC_2X_CLK_SRC_SHIFT;
|
||||
|
||||
val &= ~CLK_SOURCE_EMC_2X_CLK_DIVISOR_MASK;
|
||||
val |= div;
|
||||
|
||||
if (index == EMC_SRC_PLL_M && div == 0 && emc->want_low_jitter)
|
||||
val |= USE_PLLM_UD;
|
||||
else
|
||||
val &= ~USE_PLLM_UD;
|
||||
|
||||
if (emc->mc_same_freq)
|
||||
val |= MC_EMC_SAME_FREQ;
|
||||
else
|
||||
val &= ~MC_EMC_SAME_FREQ;
|
||||
|
||||
writel_relaxed(val, emc->reg);
|
||||
|
||||
fence_udelay(1, emc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
||||
{
|
||||
struct tegra_clk_emc *emc = to_tegra_clk_emc(hw);
|
||||
struct clk_hw *parent_hw;
|
||||
unsigned long divided_rate;
|
||||
unsigned long parent_rate;
|
||||
unsigned int i;
|
||||
long emc_rate;
|
||||
int div;
|
||||
|
||||
emc_rate = emc->round_cb(req->rate, req->min_rate, req->max_rate,
|
||||
emc->cb_arg);
|
||||
if (emc_rate < 0)
|
||||
return emc_rate;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) {
|
||||
parent_hw = clk_hw_get_parent_by_index(hw, i);
|
||||
|
||||
if (req->best_parent_hw == parent_hw)
|
||||
parent_rate = req->best_parent_rate;
|
||||
else
|
||||
parent_rate = clk_hw_get_rate(parent_hw);
|
||||
|
||||
if (emc_rate > parent_rate)
|
||||
continue;
|
||||
|
||||
div = div_frac_get(emc_rate, parent_rate, 8, 1, 0);
|
||||
divided_rate = DIV_ROUND_UP(parent_rate * 2, div + 2);
|
||||
|
||||
if (divided_rate != emc_rate)
|
||||
continue;
|
||||
|
||||
req->best_parent_rate = parent_rate;
|
||||
req->best_parent_hw = parent_hw;
|
||||
req->rate = emc_rate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(emc_parent_clk_names)) {
|
||||
pr_err_once("can't find parent for rate %lu emc_rate %lu\n",
|
||||
req->rate, emc_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops tegra_clk_emc_ops = {
|
||||
.recalc_rate = emc_recalc_rate,
|
||||
.get_parent = emc_get_parent,
|
||||
.set_parent = emc_set_parent,
|
||||
.set_rate = emc_set_rate,
|
||||
.set_rate_and_parent = emc_set_rate_and_parent,
|
||||
.determine_rate = emc_determine_rate,
|
||||
};
|
||||
|
||||
void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct clk *clk = __clk_lookup("emc");
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (clk) {
|
||||
hw = __clk_get_hw(clk);
|
||||
emc = to_tegra_clk_emc(hw);
|
||||
|
||||
emc->round_cb = round_cb;
|
||||
emc->cb_arg = cb_arg;
|
||||
}
|
||||
}
|
||||
|
||||
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw)
|
||||
{
|
||||
return to_tegra_clk_emc(emc_hw)->round_cb != NULL;
|
||||
}
|
||||
|
||||
struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter)
|
||||
{
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
|
||||
emc = kzalloc(sizeof(*emc), GFP_KERNEL);
|
||||
if (!emc)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* EMC stands for External Memory Controller.
|
||||
*
|
||||
* We don't want EMC clock to be disabled ever by gating its
|
||||
* parent and whatnot because system is busted immediately in that
|
||||
* case, hence the clock is marked as critical.
|
||||
*/
|
||||
init.name = "emc";
|
||||
init.ops = &tegra_clk_emc_ops;
|
||||
init.flags = CLK_IS_CRITICAL;
|
||||
init.parent_names = emc_parent_clk_names;
|
||||
init.num_parents = ARRAY_SIZE(emc_parent_clk_names);
|
||||
|
||||
emc->reg = ioaddr;
|
||||
emc->hw.init = &init;
|
||||
emc->want_low_jitter = low_jitter;
|
||||
|
||||
clk = clk_register(NULL, &emc->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
kfree(emc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same)
|
||||
{
|
||||
struct tegra_clk_emc *emc;
|
||||
struct clk_hw *hw;
|
||||
|
||||
if (!emc_clk)
|
||||
return -EINVAL;
|
||||
|
||||
hw = __clk_get_hw(emc_clk);
|
||||
emc = to_tegra_clk_emc(hw);
|
||||
emc->mc_same_freq = same;
|
||||
|
||||
return 0;
|
||||
}
|
@ -130,8 +130,6 @@ static struct cpu_clk_suspend_context {
|
||||
static void __iomem *clk_base;
|
||||
static void __iomem *pmc_base;
|
||||
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
TEGRA_INIT_DATA(_name, NULL, NULL, _parents, _offset, \
|
||||
@ -760,7 +758,6 @@ static const char *pwm_parents[] = { "pll_p", "pll_c", "audio", "clk_m",
|
||||
static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
|
||||
static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c",
|
||||
"clk_m" };
|
||||
static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
|
||||
|
||||
static struct tegra_periph_init_data tegra_periph_clk_list[] = {
|
||||
TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1),
|
||||
@ -787,41 +784,6 @@ static struct tegra_periph_init_data tegra_periph_nodiv_clk_list[] = {
|
||||
TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2),
|
||||
};
|
||||
|
||||
static void __init tegra20_emc_clk_init(void)
|
||||
{
|
||||
const u32 use_pllm_ud = BIT(29);
|
||||
struct clk *clk;
|
||||
u32 emc_reg;
|
||||
|
||||
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
clk_base + CLK_SOURCE_EMC,
|
||||
30, 2, 0, &emc_lock);
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
|
||||
&emc_lock);
|
||||
clks[TEGRA20_CLK_MC] = clk;
|
||||
|
||||
/* un-divided pll_m_out0 is currently unsupported */
|
||||
emc_reg = readl_relaxed(clk_base + CLK_SOURCE_EMC);
|
||||
if (emc_reg & use_pllm_ud) {
|
||||
pr_err("%s: un-divided PllM_out0 used as clock source\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that 'emc_mux' source and 'emc' rate shouldn't be changed at
|
||||
* the same time due to a HW bug, this won't happen because we're
|
||||
* defining 'emc_mux' and 'emc' as distinct clocks.
|
||||
*/
|
||||
clk = tegra_clk_register_divider("emc", "emc_mux",
|
||||
clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL,
|
||||
TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock);
|
||||
clks[TEGRA20_CLK_EMC] = clk;
|
||||
}
|
||||
|
||||
static void __init tegra20_periph_clk_init(void)
|
||||
{
|
||||
struct tegra_periph_init_data *data;
|
||||
@ -835,7 +797,13 @@ static void __init tegra20_periph_clk_init(void)
|
||||
clks[TEGRA20_CLK_AC97] = clk;
|
||||
|
||||
/* emc */
|
||||
tegra20_emc_clk_init();
|
||||
clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false);
|
||||
|
||||
clks[TEGRA20_CLK_EMC] = clk;
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
|
||||
NULL);
|
||||
clks[TEGRA20_CLK_MC] = clk;
|
||||
|
||||
/* dsi */
|
||||
clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0,
|
||||
@ -1115,6 +1083,8 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
/*
|
||||
* Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent
|
||||
* clock is created by the pinctrl driver. It is possible for clk user
|
||||
@ -1124,13 +1094,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
||||
*/
|
||||
if (clkspec->args[0] == TEGRA20_CLK_CDEV1 ||
|
||||
clkspec->args[0] == TEGRA20_CLK_CDEV2) {
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
parent_hw = clk_hw_get_parent(hw);
|
||||
if (!parent_hw)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
if (clkspec->args[0] == TEGRA20_CLK_EMC) {
|
||||
if (!tegra20_clk_emc_driver_available(hw))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,6 @@ static unsigned long input_freq;
|
||||
|
||||
static DEFINE_SPINLOCK(cml_lock);
|
||||
static DEFINE_SPINLOCK(pll_d_lock);
|
||||
static DEFINE_SPINLOCK(emc_lock);
|
||||
|
||||
#define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \
|
||||
_clk_num, _gate_flags, _clk_id) \
|
||||
@ -808,7 +807,7 @@ static struct tegra_clk tegra30_clks[tegra_clk_max] __initdata = {
|
||||
[tegra_clk_pll_a] = { .dt_id = TEGRA30_CLK_PLL_A, .present = true },
|
||||
[tegra_clk_pll_a_out0] = { .dt_id = TEGRA30_CLK_PLL_A_OUT0, .present = true },
|
||||
[tegra_clk_cec] = { .dt_id = TEGRA30_CLK_CEC, .present = true },
|
||||
[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = true },
|
||||
[tegra_clk_emc] = { .dt_id = TEGRA30_CLK_EMC, .present = false },
|
||||
};
|
||||
|
||||
static const char *pll_e_parents[] = { "pll_ref", "pll_p" };
|
||||
@ -995,7 +994,6 @@ static void __init tegra30_super_clk_init(void)
|
||||
static const char *mux_pllacp_clkm[] = { "pll_a_out0", "unused", "pll_p",
|
||||
"clk_m" };
|
||||
static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" };
|
||||
static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" };
|
||||
static const char *spdif_out_parents[] = { "pll_a_out0", "spdif_2x", "pll_p",
|
||||
"clk_m" };
|
||||
static const char *mux_pllmcpa[] = { "pll_m", "pll_c", "pll_p", "pll_a_out0" };
|
||||
@ -1044,14 +1042,12 @@ static void __init tegra30_periph_clk_init(void)
|
||||
clks[TEGRA30_CLK_AFI] = clk;
|
||||
|
||||
/* emc */
|
||||
clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm,
|
||||
ARRAY_SIZE(mux_pllmcp_clkm),
|
||||
CLK_SET_RATE_NO_REPARENT,
|
||||
clk_base + CLK_SOURCE_EMC,
|
||||
30, 2, 0, &emc_lock);
|
||||
clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, true);
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC,
|
||||
&emc_lock);
|
||||
clks[TEGRA30_CLK_EMC] = clk;
|
||||
|
||||
clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC,
|
||||
NULL);
|
||||
clks[TEGRA30_CLK_MC] = clk;
|
||||
|
||||
/* cml0 */
|
||||
@ -1302,6 +1298,26 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
|
||||
{ "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
|
||||
};
|
||||
|
||||
static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
|
||||
void *data)
|
||||
{
|
||||
struct clk_hw *hw;
|
||||
struct clk *clk;
|
||||
|
||||
clk = of_clk_src_onecell_get(clkspec, data);
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
hw = __clk_get_hw(clk);
|
||||
|
||||
if (clkspec->args[0] == TEGRA30_CLK_EMC) {
|
||||
if (!tegra20_clk_emc_driver_available(hw))
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
}
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init tegra30_clock_init(struct device_node *np)
|
||||
{
|
||||
struct device_node *node;
|
||||
@ -1345,7 +1361,7 @@ static void __init tegra30_clock_init(struct device_node *np)
|
||||
|
||||
tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA30_CLK_CLK_MAX);
|
||||
|
||||
tegra_add_of_provider(np, of_clk_src_onecell_get);
|
||||
tegra_add_of_provider(np, tegra30_clk_src_onecell_get);
|
||||
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
|
||||
|
||||
tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
|
||||
|
@ -838,4 +838,7 @@ int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width,
|
||||
udelay(delay); \
|
||||
} while (0)
|
||||
|
||||
bool tegra20_clk_emc_driver_available(struct clk_hw *emc_hw);
|
||||
struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
|
||||
|
||||
#endif /* TEGRA_CLK_H */
|
||||
|
@ -17,6 +17,16 @@ config TEGRA20_EMC
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA30_EMC
|
||||
bool "NVIDIA Tegra30 External Memory Controller driver"
|
||||
default y
|
||||
depends on TEGRA_MC && ARCH_TEGRA_3x_SOC
|
||||
help
|
||||
This driver is for the External Memory Controller (EMC) found on
|
||||
Tegra30 chips. The EMC controls the external DRAM on the board.
|
||||
This driver is required to change memory timings / clock rate for
|
||||
external memory.
|
||||
|
||||
config TEGRA124_EMC
|
||||
bool "NVIDIA Tegra124 External Memory Controller driver"
|
||||
default y
|
||||
|
@ -11,5 +11,6 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o
|
||||
obj-$(CONFIG_TEGRA_MC) += tegra-mc.o
|
||||
|
||||
obj-$(CONFIG_TEGRA20_EMC) += tegra20-emc.o
|
||||
obj-$(CONFIG_TEGRA30_EMC) += tegra30-emc.o
|
||||
obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o
|
||||
obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -18,39 +19,6 @@
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
#define MC_INTSTATUS 0x000
|
||||
|
||||
#define MC_INTMASK 0x004
|
||||
|
||||
#define MC_ERR_STATUS 0x08
|
||||
#define MC_ERR_STATUS_TYPE_SHIFT 28
|
||||
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (6 << MC_ERR_STATUS_TYPE_SHIFT)
|
||||
#define MC_ERR_STATUS_TYPE_MASK (0x7 << MC_ERR_STATUS_TYPE_SHIFT)
|
||||
#define MC_ERR_STATUS_READABLE (1 << 27)
|
||||
#define MC_ERR_STATUS_WRITABLE (1 << 26)
|
||||
#define MC_ERR_STATUS_NONSECURE (1 << 25)
|
||||
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
|
||||
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
|
||||
#define MC_ERR_STATUS_SECURITY (1 << 17)
|
||||
#define MC_ERR_STATUS_RW (1 << 16)
|
||||
|
||||
#define MC_ERR_ADR 0x0c
|
||||
|
||||
#define MC_GART_ERROR_REQ 0x30
|
||||
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
|
||||
#define MC_SECURITY_VIOLATION_STATUS 0x74
|
||||
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) (((x) & 0x1ff) << 0)
|
||||
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
|
||||
#define MC_EMEM_ADR_CFG 0x54
|
||||
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
|
||||
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
#define MC_TIMING_UPDATE BIT(0)
|
||||
|
||||
static const struct of_device_id tegra_mc_of_match[] = {
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
{ .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc },
|
||||
@ -307,7 +275,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
|
||||
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
|
||||
{
|
||||
unsigned int i;
|
||||
struct tegra_mc_timing *timing = NULL;
|
||||
@ -322,11 +290,13 @@ void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate)
|
||||
if (!timing) {
|
||||
dev_err(mc->dev, "no memory timing registered for rate %lu\n",
|
||||
rate);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < mc->soc->num_emem_regs; ++i)
|
||||
mc_writel(mc, timing->emem_data[i], mc->soc->emem_regs[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc)
|
||||
@ -626,6 +596,7 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
struct tegra_mc *mc;
|
||||
void *isr;
|
||||
u64 mask;
|
||||
int err;
|
||||
|
||||
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
|
||||
@ -637,6 +608,14 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
mc->soc = of_device_get_match_data(&pdev->dev);
|
||||
mc->dev = &pdev->dev;
|
||||
|
||||
mask = DMA_BIT_MASK(mc->soc->num_address_bits);
|
||||
|
||||
err = dma_coerce_mask_and_coherent(&pdev->dev, mask);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* length of MC tick in nanoseconds */
|
||||
mc->tick = 30;
|
||||
|
||||
@ -658,6 +637,9 @@ static int tegra_mc_probe(struct platform_device *pdev)
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* ensure that debug features are disabled */
|
||||
mc_writel(mc, 0x00000000, MC_TIMING_CONTROL_DBG);
|
||||
|
||||
err = tegra_mc_setup_latency_allowance(mc);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
|
@ -6,20 +6,76 @@
|
||||
#ifndef MEMORY_TEGRA_MC_H
|
||||
#define MEMORY_TEGRA_MC_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <soc/tegra/mc.h>
|
||||
|
||||
#define MC_INT_DECERR_MTS (1 << 16)
|
||||
#define MC_INT_SECERR_SEC (1 << 13)
|
||||
#define MC_INT_DECERR_VPR (1 << 12)
|
||||
#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
|
||||
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
|
||||
#define MC_INT_ARBITRATION_EMEM (1 << 9)
|
||||
#define MC_INT_SECURITY_VIOLATION (1 << 8)
|
||||
#define MC_INT_INVALID_GART_PAGE (1 << 7)
|
||||
#define MC_INT_DECERR_EMEM (1 << 6)
|
||||
#define MC_INTSTATUS 0x00
|
||||
#define MC_INTMASK 0x04
|
||||
#define MC_ERR_STATUS 0x08
|
||||
#define MC_ERR_ADR 0x0c
|
||||
#define MC_GART_ERROR_REQ 0x30
|
||||
#define MC_EMEM_ADR_CFG 0x54
|
||||
#define MC_DECERR_EMEM_OTHERS_STATUS 0x58
|
||||
#define MC_SECURITY_VIOLATION_STATUS 0x74
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
|
||||
#define MC_EMEM_ARB_OVERRIDE 0xe8
|
||||
#define MC_TIMING_CONTROL_DBG 0xf8
|
||||
#define MC_TIMING_CONTROL 0xfc
|
||||
|
||||
#define MC_INT_DECERR_MTS BIT(16)
|
||||
#define MC_INT_SECERR_SEC BIT(13)
|
||||
#define MC_INT_DECERR_VPR BIT(12)
|
||||
#define MC_INT_INVALID_APB_ASID_UPDATE BIT(11)
|
||||
#define MC_INT_INVALID_SMMU_PAGE BIT(10)
|
||||
#define MC_INT_ARBITRATION_EMEM BIT(9)
|
||||
#define MC_INT_SECURITY_VIOLATION BIT(8)
|
||||
#define MC_INT_INVALID_GART_PAGE BIT(7)
|
||||
#define MC_INT_DECERR_EMEM BIT(6)
|
||||
|
||||
#define MC_ERR_STATUS_TYPE_SHIFT 28
|
||||
#define MC_ERR_STATUS_TYPE_INVALID_SMMU_PAGE (0x6 << 28)
|
||||
#define MC_ERR_STATUS_TYPE_MASK (0x7 << 28)
|
||||
#define MC_ERR_STATUS_READABLE BIT(27)
|
||||
#define MC_ERR_STATUS_WRITABLE BIT(26)
|
||||
#define MC_ERR_STATUS_NONSECURE BIT(25)
|
||||
#define MC_ERR_STATUS_ADR_HI_SHIFT 20
|
||||
#define MC_ERR_STATUS_ADR_HI_MASK 0x3
|
||||
#define MC_ERR_STATUS_SECURITY BIT(17)
|
||||
#define MC_ERR_STATUS_RW BIT(16)
|
||||
|
||||
#define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0)
|
||||
|
||||
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(x) ((x) & 0x1ff)
|
||||
#define MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK 0x1ff
|
||||
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ_MAX_MASK 0x1ff
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ_HOLDOFF_OVERRIDE BIT(30)
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ_LIMIT_ENABLE BIT(31)
|
||||
|
||||
#define MC_EMEM_ARB_OVERRIDE_EACK_MASK 0x3
|
||||
|
||||
#define MC_TIMING_UPDATE BIT(0)
|
||||
|
||||
static inline u32 mc_readl(struct tegra_mc *mc, unsigned long offset)
|
||||
{
|
||||
|
@ -909,16 +909,18 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = {
|
||||
{ .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 },
|
||||
};
|
||||
|
||||
static const unsigned int tegra114_group_display[] = {
|
||||
static const unsigned int tegra114_group_drm[] = {
|
||||
TEGRA_SWGROUP_DC,
|
||||
TEGRA_SWGROUP_DCB,
|
||||
TEGRA_SWGROUP_G2,
|
||||
TEGRA_SWGROUP_NV,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_group_soc tegra114_groups[] = {
|
||||
{
|
||||
.name = "display",
|
||||
.swgroups = tegra114_group_display,
|
||||
.num_swgroups = ARRAY_SIZE(tegra114_group_display),
|
||||
.name = "drm",
|
||||
.swgroups = tegra114_group_drm,
|
||||
.num_swgroups = ARRAY_SIZE(tegra114_group_drm),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -10,26 +10,6 @@
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
#define MC_EMEM_ARB_CFG 0x90
|
||||
#define MC_EMEM_ARB_OUTSTANDING_REQ 0x94
|
||||
#define MC_EMEM_ARB_TIMING_RCD 0x98
|
||||
#define MC_EMEM_ARB_TIMING_RP 0x9c
|
||||
#define MC_EMEM_ARB_TIMING_RC 0xa0
|
||||
#define MC_EMEM_ARB_TIMING_RAS 0xa4
|
||||
#define MC_EMEM_ARB_TIMING_FAW 0xa8
|
||||
#define MC_EMEM_ARB_TIMING_RRD 0xac
|
||||
#define MC_EMEM_ARB_TIMING_RAP2PRE 0xb0
|
||||
#define MC_EMEM_ARB_TIMING_WAP2PRE 0xb4
|
||||
#define MC_EMEM_ARB_TIMING_R2R 0xb8
|
||||
#define MC_EMEM_ARB_TIMING_W2W 0xbc
|
||||
#define MC_EMEM_ARB_TIMING_R2W 0xc0
|
||||
#define MC_EMEM_ARB_TIMING_W2R 0xc4
|
||||
#define MC_EMEM_ARB_DA_TURNS 0xd0
|
||||
#define MC_EMEM_ARB_DA_COVERS 0xd4
|
||||
#define MC_EMEM_ARB_MISC0 0xd8
|
||||
#define MC_EMEM_ARB_MISC1 0xdc
|
||||
#define MC_EMEM_ARB_RING1_THROTTLE 0xe0
|
||||
|
||||
static const struct tegra_mc_client tegra124_mc_clients[] = {
|
||||
{
|
||||
.id = 0x00,
|
||||
@ -974,16 +954,18 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = {
|
||||
{ .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 },
|
||||
};
|
||||
|
||||
static const unsigned int tegra124_group_display[] = {
|
||||
static const unsigned int tegra124_group_drm[] = {
|
||||
TEGRA_SWGROUP_DC,
|
||||
TEGRA_SWGROUP_DCB,
|
||||
TEGRA_SWGROUP_GPU,
|
||||
TEGRA_SWGROUP_VIC,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_group_soc tegra124_groups[] = {
|
||||
{
|
||||
.name = "display",
|
||||
.swgroups = tegra124_group_display,
|
||||
.num_swgroups = ARRAY_SIZE(tegra124_group_display),
|
||||
.name = "drm",
|
||||
.swgroups = tegra124_group_drm,
|
||||
.num_swgroups = ARRAY_SIZE(tegra124_group_drm),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -6,10 +6,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk/tegra.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
@ -21,6 +22,7 @@
|
||||
|
||||
#define EMC_INTSTATUS 0x000
|
||||
#define EMC_INTMASK 0x004
|
||||
#define EMC_DBG 0x008
|
||||
#define EMC_TIMING_CONTROL 0x028
|
||||
#define EMC_RC 0x02c
|
||||
#define EMC_RFC 0x030
|
||||
@ -79,6 +81,12 @@
|
||||
#define EMC_REFRESH_OVERFLOW_INT BIT(3)
|
||||
#define EMC_CLKCHANGE_COMPLETE_INT BIT(4)
|
||||
|
||||
#define EMC_DBG_READ_MUX_ASSEMBLY BIT(0)
|
||||
#define EMC_DBG_WRITE_MUX_ACTIVE BIT(1)
|
||||
#define EMC_DBG_FORCE_UPDATE BIT(2)
|
||||
#define EMC_DBG_READ_DQM_CTRL BIT(9)
|
||||
#define EMC_DBG_CFG_PRIORITY BIT(24)
|
||||
|
||||
static const u16 emc_timing_registers[] = {
|
||||
EMC_RC,
|
||||
EMC_RFC,
|
||||
@ -137,9 +145,6 @@ struct tegra_emc {
|
||||
struct device *dev;
|
||||
struct completion clk_handshake_complete;
|
||||
struct notifier_block clk_nb;
|
||||
struct clk *backup_clk;
|
||||
struct clk *emc_mux;
|
||||
struct clk *pll_m;
|
||||
struct clk *clk;
|
||||
void __iomem *regs;
|
||||
|
||||
@ -219,7 +224,7 @@ static int emc_prepare_timing_change(struct tegra_emc *emc, unsigned long rate)
|
||||
|
||||
static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
||||
{
|
||||
long timeout;
|
||||
unsigned long timeout;
|
||||
|
||||
dev_dbg(emc->dev, "%s: flush %d\n", __func__, flush);
|
||||
|
||||
@ -231,14 +236,10 @@ static int emc_complete_timing_change(struct tegra_emc *emc, bool flush)
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_timeout(&emc->clk_handshake_complete,
|
||||
usecs_to_jiffies(100));
|
||||
msecs_to_jiffies(100));
|
||||
if (timeout == 0) {
|
||||
dev_err(emc->dev, "EMC-CAR handshake failed\n");
|
||||
return -EIO;
|
||||
} else if (timeout < 0) {
|
||||
dev_err(emc->dev, "failed to wait for EMC-CAR handshake: %ld\n",
|
||||
timeout);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -363,6 +364,13 @@ static int tegra_emc_load_timings_from_dt(struct tegra_emc *emc,
|
||||
sort(emc->timings, emc->num_timings, sizeof(*timing), cmp_timings,
|
||||
NULL);
|
||||
|
||||
dev_info(emc->dev,
|
||||
"got %u timings for RAM code %u (min %luMHz max %luMHz)\n",
|
||||
emc->num_timings,
|
||||
tegra_read_ram_code(),
|
||||
emc->timings[0].rate / 1000000,
|
||||
emc->timings[emc->num_timings - 1].rate / 1000000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -398,7 +406,7 @@ tegra_emc_find_node_by_ram_code(struct device *dev)
|
||||
static int emc_setup_hw(struct tegra_emc *emc)
|
||||
{
|
||||
u32 intmask = EMC_REFRESH_OVERFLOW_INT | EMC_CLKCHANGE_COMPLETE_INT;
|
||||
u32 emc_cfg;
|
||||
u32 emc_cfg, emc_dbg;
|
||||
|
||||
emc_cfg = readl_relaxed(emc->regs + EMC_CFG_2);
|
||||
|
||||
@ -421,42 +429,53 @@ static int emc_setup_hw(struct tegra_emc *emc)
|
||||
writel_relaxed(intmask, emc->regs + EMC_INTMASK);
|
||||
writel_relaxed(intmask, emc->regs + EMC_INTSTATUS);
|
||||
|
||||
/* ensure that unwanted debug features are disabled */
|
||||
emc_dbg = readl_relaxed(emc->regs + EMC_DBG);
|
||||
emc_dbg |= EMC_DBG_CFG_PRIORITY;
|
||||
emc_dbg &= ~EMC_DBG_READ_MUX_ASSEMBLY;
|
||||
emc_dbg &= ~EMC_DBG_WRITE_MUX_ACTIVE;
|
||||
emc_dbg &= ~EMC_DBG_FORCE_UPDATE;
|
||||
writel_relaxed(emc_dbg, emc->regs + EMC_DBG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc_init(struct tegra_emc *emc, unsigned long rate)
|
||||
static long emc_round_rate(unsigned long rate,
|
||||
unsigned long min_rate,
|
||||
unsigned long max_rate,
|
||||
void *arg)
|
||||
{
|
||||
int err;
|
||||
struct emc_timing *timing = NULL;
|
||||
struct tegra_emc *emc = arg;
|
||||
unsigned int i;
|
||||
|
||||
err = clk_set_parent(emc->emc_mux, emc->backup_clk);
|
||||
if (err) {
|
||||
dev_err(emc->dev,
|
||||
"failed to reparent to backup source: %d\n", err);
|
||||
return err;
|
||||
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;
|
||||
}
|
||||
|
||||
err = clk_set_rate(emc->pll_m, rate);
|
||||
if (err) {
|
||||
dev_err(emc->dev,
|
||||
"failed to change pll_m rate: %d\n", err);
|
||||
return err;
|
||||
if (!timing) {
|
||||
dev_err(emc->dev, "no timing for rate %lu min %lu max %lu\n",
|
||||
rate, min_rate, max_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = clk_set_parent(emc->emc_mux, emc->pll_m);
|
||||
if (err) {
|
||||
dev_err(emc->dev,
|
||||
"failed to reparent to pll_m: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_set_rate(emc->clk, rate);
|
||||
if (err) {
|
||||
dev_err(emc->dev,
|
||||
"failed to change emc rate: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return timing->rate;
|
||||
}
|
||||
|
||||
static int tegra_emc_probe(struct platform_device *pdev)
|
||||
@ -515,57 +534,26 @@ 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;
|
||||
}
|
||||
|
||||
emc->pll_m = clk_get_sys(NULL, "pll_m");
|
||||
if (IS_ERR(emc->pll_m)) {
|
||||
err = PTR_ERR(emc->pll_m);
|
||||
dev_err(&pdev->dev, "failed to get pll_m clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
emc->backup_clk = clk_get_sys(NULL, "pll_p");
|
||||
if (IS_ERR(emc->backup_clk)) {
|
||||
err = PTR_ERR(emc->backup_clk);
|
||||
dev_err(&pdev->dev, "failed to get pll_p clock: %d\n", err);
|
||||
goto put_pll_m;
|
||||
}
|
||||
|
||||
emc->emc_mux = clk_get_parent(emc->clk);
|
||||
if (IS_ERR(emc->emc_mux)) {
|
||||
err = PTR_ERR(emc->emc_mux);
|
||||
dev_err(&pdev->dev, "failed to get emc_mux clock: %d\n", err);
|
||||
goto put_backup;
|
||||
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);
|
||||
goto put_backup;
|
||||
}
|
||||
|
||||
/* set DRAM clock rate to maximum */
|
||||
err = emc_init(emc, emc->timings[emc->num_timings - 1].rate);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to initialize EMC clock rate: %d\n",
|
||||
err);
|
||||
goto unreg_notifier;
|
||||
goto unset_cb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unreg_notifier:
|
||||
clk_notifier_unregister(emc->clk, &emc->clk_nb);
|
||||
put_backup:
|
||||
clk_put(emc->backup_clk);
|
||||
put_pll_m:
|
||||
clk_put(emc->pll_m);
|
||||
unset_cb:
|
||||
tegra20_clk_set_emc_round_callback(NULL, NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
1232
drivers/memory/tegra/tegra30-emc.c
Normal file
1232
drivers/memory/tegra/tegra30-emc.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,27 @@
|
||||
|
||||
#include "mc.h"
|
||||
|
||||
static const unsigned long tegra30_mc_emem_regs[] = {
|
||||
MC_EMEM_ARB_CFG,
|
||||
MC_EMEM_ARB_OUTSTANDING_REQ,
|
||||
MC_EMEM_ARB_TIMING_RCD,
|
||||
MC_EMEM_ARB_TIMING_RP,
|
||||
MC_EMEM_ARB_TIMING_RC,
|
||||
MC_EMEM_ARB_TIMING_RAS,
|
||||
MC_EMEM_ARB_TIMING_FAW,
|
||||
MC_EMEM_ARB_TIMING_RRD,
|
||||
MC_EMEM_ARB_TIMING_RAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_WAP2PRE,
|
||||
MC_EMEM_ARB_TIMING_R2R,
|
||||
MC_EMEM_ARB_TIMING_W2W,
|
||||
MC_EMEM_ARB_TIMING_R2W,
|
||||
MC_EMEM_ARB_TIMING_W2R,
|
||||
MC_EMEM_ARB_DA_TURNS,
|
||||
MC_EMEM_ARB_DA_COVERS,
|
||||
MC_EMEM_ARB_MISC0,
|
||||
MC_EMEM_ARB_RING1_THROTTLE,
|
||||
};
|
||||
|
||||
static const struct tegra_mc_client tegra30_mc_clients[] = {
|
||||
{
|
||||
.id = 0x00,
|
||||
@ -931,16 +952,19 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = {
|
||||
{ .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 },
|
||||
};
|
||||
|
||||
static const unsigned int tegra30_group_display[] = {
|
||||
static const unsigned int tegra30_group_drm[] = {
|
||||
TEGRA_SWGROUP_DC,
|
||||
TEGRA_SWGROUP_DCB,
|
||||
TEGRA_SWGROUP_G2,
|
||||
TEGRA_SWGROUP_NV,
|
||||
TEGRA_SWGROUP_NV2,
|
||||
};
|
||||
|
||||
static const struct tegra_smmu_group_soc tegra30_groups[] = {
|
||||
{
|
||||
.name = "display",
|
||||
.swgroups = tegra30_group_display,
|
||||
.num_swgroups = ARRAY_SIZE(tegra30_group_display),
|
||||
.name = "drm",
|
||||
.swgroups = tegra30_group_drm,
|
||||
.num_swgroups = ARRAY_SIZE(tegra30_group_drm),
|
||||
},
|
||||
};
|
||||
|
||||
@ -994,6 +1018,8 @@ const struct tegra_mc_soc tegra30_mc_soc = {
|
||||
.atom_size = 16,
|
||||
.client_id_mask = 0x7f,
|
||||
.smmu = &tegra30_smmu_soc,
|
||||
.emem_regs = tegra30_mc_emem_regs,
|
||||
.num_emem_regs = ARRAY_SIZE(tegra30_mc_emem_regs),
|
||||
.intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
|
||||
MC_INT_DECERR_EMEM,
|
||||
.reset_ops = &tegra_mc_reset_ops_common,
|
||||
|
@ -119,4 +119,15 @@ extern void tegra210_put_utmipll_in_iddq(void);
|
||||
extern void tegra210_put_utmipll_out_iddq(void);
|
||||
extern int tegra210_clk_handle_mbist_war(unsigned int id);
|
||||
|
||||
struct clk;
|
||||
|
||||
typedef long (tegra20_clk_emc_round_cb)(unsigned long rate,
|
||||
unsigned long min_rate,
|
||||
unsigned long max_rate,
|
||||
void *arg);
|
||||
|
||||
void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
|
||||
void *cb_arg);
|
||||
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same);
|
||||
|
||||
#endif /* __LINUX_CLK_TEGRA_H_ */
|
||||
|
@ -181,7 +181,7 @@ struct tegra_mc {
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
void tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
|
||||
int tegra_mc_write_emem_configuration(struct tegra_mc *mc, unsigned long rate);
|
||||
unsigned int tegra_mc_get_emem_device_count(struct tegra_mc *mc);
|
||||
|
||||
#endif /* __SOC_TEGRA_MC_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user