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:
Olof Johansson 2019-11-11 13:13:22 -08:00
commit 57a54dfe48
16 changed files with 1781 additions and 204 deletions

View File

@ -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

View 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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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)
{

View File

@ -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),
},
};

View File

@ -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),
},
};

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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,

View File

@ -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_ */

View File

@ -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__ */