Merge tag 'memory-controller-drv-tegra-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.12 - Tegra SoC

1. Make the tegra124-emc driver modular.
2. Add interconnect support to Tegra124 memory controller drivers.
3. Minor fixes.

* tag 'memory-controller-drv-tegra-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl:
  memory: tegra: Remove calls to dev_pm_opp_set_clkname()
  memory: tegra: Check whether reset is already asserted
  memory: tegra124: Support interconnect framework
  memory: tegra124-emc: Continue probing if timings are missing in device-tree
  memory: tegra124-emc: Make driver modular

Link: https://lore.kernel.org/r/20210131180109.11510-2-krzk@kernel.org
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann
2021-02-02 22:20:34 +01:00
13 changed files with 508 additions and 93 deletions

View File

@@ -7,3 +7,6 @@ config TEGRA_CLK_DFLL
depends on ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC depends on ARCH_TEGRA_124_SOC || ARCH_TEGRA_210_SOC
select PM_OPP select PM_OPP
def_bool y def_bool y
config TEGRA124_CLK_EMC
bool

View File

@@ -22,7 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o obj-$(CONFIG_TEGRA_CLK_DFLL) += clk-tegra124-dfll-fcpu.o
obj-$(CONFIG_TEGRA124_EMC) += clk-tegra124-emc.o obj-$(CONFIG_TEGRA124_CLK_EMC) += clk-tegra124-emc.o
obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o
obj-y += cvb.o obj-y += cvb.o
obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o

View File

@@ -11,7 +11,9 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/clk/tegra.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/export.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_address.h> #include <linux/of_address.h>
@@ -21,7 +23,6 @@
#include <linux/string.h> #include <linux/string.h>
#include <soc/tegra/fuse.h> #include <soc/tegra/fuse.h>
#include <soc/tegra/emc.h>
#include "clk.h" #include "clk.h"
@@ -80,6 +81,9 @@ struct tegra_clk_emc {
int num_timings; int num_timings;
struct emc_timing *timings; struct emc_timing *timings;
spinlock_t *lock; spinlock_t *lock;
tegra124_emc_prepare_timing_change_cb *prepare_timing_change;
tegra124_emc_complete_timing_change_cb *complete_timing_change;
}; };
/* Common clock framework callback implementations */ /* Common clock framework callback implementations */
@@ -176,6 +180,9 @@ static struct tegra_emc *emc_ensure_emc_driver(struct tegra_clk_emc *tegra)
if (tegra->emc) if (tegra->emc)
return tegra->emc; return tegra->emc;
if (!tegra->prepare_timing_change || !tegra->complete_timing_change)
return NULL;
if (!tegra->emc_node) if (!tegra->emc_node)
return NULL; return NULL;
@@ -241,7 +248,7 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
div = timing->parent_rate / (timing->rate / 2) - 2; div = timing->parent_rate / (timing->rate / 2) - 2;
err = tegra_emc_prepare_timing_change(emc, timing->rate); err = tegra->prepare_timing_change(emc, timing->rate);
if (err) if (err)
return err; return err;
@@ -259,7 +266,7 @@ static int emc_set_timing(struct tegra_clk_emc *tegra,
spin_unlock_irqrestore(tegra->lock, flags); spin_unlock_irqrestore(tegra->lock, flags);
tegra_emc_complete_timing_change(emc, timing->rate); tegra->complete_timing_change(emc, timing->rate);
clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent)); clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent));
clk_disable_unprepare(tegra->prev_parent); clk_disable_unprepare(tegra->prev_parent);
@@ -473,8 +480,8 @@ static const struct clk_ops tegra_clk_emc_ops = {
.get_parent = emc_get_parent, .get_parent = emc_get_parent,
}; };
struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
spinlock_t *lock) spinlock_t *lock)
{ {
struct tegra_clk_emc *tegra; struct tegra_clk_emc *tegra;
struct clk_init_data init; struct clk_init_data init;
@@ -538,3 +545,27 @@ struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np,
return clk; return clk;
}; };
void tegra124_clk_set_emc_callbacks(tegra124_emc_prepare_timing_change_cb *prep_cb,
tegra124_emc_complete_timing_change_cb *complete_cb)
{
struct clk *clk = __clk_lookup("emc");
struct tegra_clk_emc *tegra;
struct clk_hw *hw;
if (clk) {
hw = __clk_get_hw(clk);
tegra = container_of(hw, struct tegra_clk_emc, hw);
tegra->prepare_timing_change = prep_cb;
tegra->complete_timing_change = complete_cb;
}
}
EXPORT_SYMBOL_GPL(tegra124_clk_set_emc_callbacks);
bool tegra124_clk_emc_driver_available(struct clk_hw *hw)
{
struct tegra_clk_emc *tegra = container_of(hw, struct tegra_clk_emc, hw);
return tegra->prepare_timing_change && tegra->complete_timing_change;
}

View File

@@ -1500,6 +1500,26 @@ static void __init tegra124_132_clock_init_pre(struct device_node *np)
writel(plld_base, clk_base + PLLD_BASE); writel(plld_base, clk_base + PLLD_BASE);
} }
static struct clk *tegra124_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] == TEGRA124_CLK_EMC) {
if (!tegra124_clk_emc_driver_available(hw))
return ERR_PTR(-EPROBE_DEFER);
}
return clk;
}
/** /**
* tegra124_132_clock_init_post - clock initialization postamble for T124/T132 * tegra124_132_clock_init_post - clock initialization postamble for T124/T132
* @np: struct device_node * of the DT node for the SoC CAR IP block * @np: struct device_node * of the DT node for the SoC CAR IP block
@@ -1516,10 +1536,10 @@ static void __init tegra124_132_clock_init_post(struct device_node *np)
&pll_x_params); &pll_x_params);
tegra_init_special_resets(1, tegra124_reset_assert, tegra_init_special_resets(1, tegra124_reset_assert,
tegra124_reset_deassert); tegra124_reset_deassert);
tegra_add_of_provider(np, of_clk_src_onecell_get); tegra_add_of_provider(np, tegra124_clk_src_onecell_get);
clks[TEGRA124_CLK_EMC] = tegra_clk_register_emc(clk_base, np, clks[TEGRA124_CLK_EMC] = tegra124_clk_register_emc(clk_base, np,
&emc_lock); &emc_lock);
tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_register_devclks(devclks, ARRAY_SIZE(devclks));

View File

@@ -881,16 +881,22 @@ void tegra_super_clk_gen5_init(void __iomem *clk_base,
void __iomem *pmc_base, struct tegra_clk *tegra_clks, void __iomem *pmc_base, struct tegra_clk *tegra_clks,
struct tegra_clk_pll_params *pll_params); struct tegra_clk_pll_params *pll_params);
#ifdef CONFIG_TEGRA124_EMC #ifdef CONFIG_TEGRA124_CLK_EMC
struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, struct clk *tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
spinlock_t *lock); spinlock_t *lock);
bool tegra124_clk_emc_driver_available(struct clk_hw *emc_hw);
#else #else
static inline struct clk *tegra_clk_register_emc(void __iomem *base, static inline struct clk *
struct device_node *np, tegra124_clk_register_emc(void __iomem *base, struct device_node *np,
spinlock_t *lock) spinlock_t *lock)
{ {
return NULL; return NULL;
} }
static inline bool tegra124_clk_emc_driver_available(struct clk_hw *emc_hw)
{
return false;
}
#endif #endif
void tegra114_clock_tune_cpu_trimmers_high(void); void tegra114_clock_tune_cpu_trimmers_high(void);

View File

@@ -32,9 +32,11 @@ config TEGRA30_EMC
external memory. external memory.
config TEGRA124_EMC config TEGRA124_EMC
bool "NVIDIA Tegra124 External Memory Controller driver" tristate "NVIDIA Tegra124 External Memory Controller driver"
default y default y
depends on TEGRA_MC && ARCH_TEGRA_124_SOC depends on TEGRA_MC && ARCH_TEGRA_124_SOC
select TEGRA124_CLK_EMC
select PM_OPP
help help
This driver is for the External Memory Controller (EMC) found on This driver is for the External Memory Controller (EMC) found on
Tegra124 chips. The EMC controls the external DRAM on the board. Tegra124 chips. The EMC controls the external DRAM on the board.

View File

@@ -176,6 +176,13 @@ static int tegra_mc_hotreset_assert(struct reset_controller_dev *rcdev,
if (!rst_ops) if (!rst_ops)
return -ENODEV; return -ENODEV;
/* DMA flushing will fail if reset is already asserted */
if (rst_ops->reset_status) {
/* check whether reset is asserted */
if (rst_ops->reset_status(mc, rst))
return 0;
}
if (rst_ops->block_dma) { if (rst_ops->block_dma) {
/* block clients DMA requests */ /* block clients DMA requests */
err = rst_ops->block_dma(mc, rst); err = rst_ops->block_dma(mc, rst);

View File

@@ -9,22 +9,29 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clkdev.h> #include <linux/clkdev.h>
#include <linux/clk/tegra.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interconnect-provider.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <linux/string.h> #include <linux/string.h>
#include <soc/tegra/emc.h>
#include <soc/tegra/fuse.h> #include <soc/tegra/fuse.h>
#include <soc/tegra/mc.h> #include <soc/tegra/mc.h>
#include "mc.h"
#define EMC_FBIO_CFG5 0x104 #define EMC_FBIO_CFG5 0x104
#define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3 #define EMC_FBIO_CFG5_DRAM_TYPE_MASK 0x3
#define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT 0 #define EMC_FBIO_CFG5_DRAM_TYPE_SHIFT 0
#define EMC_FBIO_CFG5_DRAM_WIDTH_X64 BIT(4)
#define EMC_INTSTATUS 0x0 #define EMC_INTSTATUS 0x0
#define EMC_INTSTATUS_CLKCHANGE_COMPLETE BIT(4) #define EMC_INTSTATUS_CLKCHANGE_COMPLETE BIT(4)
@@ -460,6 +467,17 @@ struct emc_timing {
u32 emc_zcal_interval; 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 tegra_emc {
struct device *dev; struct device *dev;
@@ -470,6 +488,7 @@ struct tegra_emc {
struct clk *clk; struct clk *clk;
enum emc_dram_type dram_type; enum emc_dram_type dram_type;
unsigned int dram_bus_width;
unsigned int dram_num; unsigned int dram_num;
struct emc_timing last_timing; struct emc_timing last_timing;
@@ -481,6 +500,17 @@ struct tegra_emc {
unsigned long min_rate; unsigned long min_rate;
unsigned long max_rate; unsigned long max_rate;
} debugfs; } 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 */ /* Timing change sequence functions */
@@ -562,8 +592,8 @@ static struct emc_timing *tegra_emc_find_timing(struct tegra_emc *emc,
return timing; return timing;
} }
int tegra_emc_prepare_timing_change(struct tegra_emc *emc, static int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
unsigned long rate) unsigned long rate)
{ {
struct emc_timing *timing = tegra_emc_find_timing(emc, rate); struct emc_timing *timing = tegra_emc_find_timing(emc, rate);
struct emc_timing *last = &emc->last_timing; struct emc_timing *last = &emc->last_timing;
@@ -790,8 +820,8 @@ int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
return 0; return 0;
} }
void tegra_emc_complete_timing_change(struct tegra_emc *emc, static void tegra_emc_complete_timing_change(struct tegra_emc *emc,
unsigned long rate) unsigned long rate)
{ {
struct emc_timing *timing = tegra_emc_find_timing(emc, rate); struct emc_timing *timing = tegra_emc_find_timing(emc, rate);
struct emc_timing *last = &emc->last_timing; struct emc_timing *last = &emc->last_timing;
@@ -869,6 +899,14 @@ static void emc_read_current_timing(struct tegra_emc *emc,
static int emc_init(struct tegra_emc *emc) static int emc_init(struct tegra_emc *emc)
{ {
emc->dram_type = readl(emc->regs + EMC_FBIO_CFG5); 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_MASK;
emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT; emc->dram_type >>= EMC_FBIO_CFG5_DRAM_TYPE_SHIFT;
@@ -987,6 +1025,7 @@ static const struct of_device_id tegra_emc_of_match[] = {
{ .compatible = "nvidia,tegra132-emc" }, { .compatible = "nvidia,tegra132-emc" },
{} {}
}; };
MODULE_DEVICE_TABLE(of, tegra_emc_of_match);
static struct device_node * static struct device_node *
tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code) tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
@@ -1007,6 +1046,83 @@ tegra_emc_find_node_by_ram_code(struct device_node *node, u32 ram_code)
return NULL; 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 * debugfs interface
* *
@@ -1079,7 +1195,7 @@ static int tegra_emc_debug_min_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate)) if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL; return -EINVAL;
err = clk_set_min_rate(emc->clk, rate); err = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0) if (err < 0)
return err; return err;
@@ -1109,7 +1225,7 @@ static int tegra_emc_debug_max_rate_set(void *data, u64 rate)
if (!tegra_emc_validate_rate(emc, rate)) if (!tegra_emc_validate_rate(emc, rate))
return -EINVAL; return -EINVAL;
err = clk_set_max_rate(emc->clk, rate); err = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);
if (err < 0) if (err < 0)
return err; return err;
@@ -1127,15 +1243,6 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
unsigned int i; unsigned int i;
int err; 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.min_rate = ULONG_MAX;
emc->debugfs.max_rate = 0; emc->debugfs.max_rate = 0;
@@ -1175,6 +1282,168 @@ static void emc_debugfs_init(struct device *dev, struct tegra_emc *emc)
emc, &tegra_emc_debug_max_rate_fops); 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 *hw_opp_table;
int 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);
return err;
}
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);
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) static int tegra_emc_probe(struct platform_device *pdev)
{ {
struct device_node *np; struct device_node *np;
@@ -1186,6 +1455,7 @@ static int tegra_emc_probe(struct platform_device *pdev)
if (!emc) if (!emc)
return -ENOMEM; return -ENOMEM;
mutex_init(&emc->rate_lock);
emc->dev = &pdev->dev; emc->dev = &pdev->dev;
emc->regs = devm_platform_ioremap_resource(pdev, 0); emc->regs = devm_platform_ioremap_resource(pdev, 0);
@@ -1199,23 +1469,15 @@ static int tegra_emc_probe(struct platform_device *pdev)
ram_code = tegra_read_ram_code(); ram_code = tegra_read_ram_code();
np = tegra_emc_find_node_by_ram_code(pdev->dev.of_node, ram_code); np = tegra_emc_find_node_by_ram_code(pdev->dev.of_node, ram_code);
if (!np) { if (np) {
dev_err(&pdev->dev, err = tegra_emc_load_timings_from_dt(emc, np);
"no memory timings for RAM code %u found in DT\n", of_node_put(np);
ram_code); if (err)
return -ENOENT; return err;
} } else {
dev_info(&pdev->dev,
err = tegra_emc_load_timings_from_dt(emc, np); "no memory timings for RAM code %u found in DT\n",
of_node_put(np); ram_code);
if (err)
return err;
if (emc->num_timings == 0) {
dev_err(&pdev->dev,
"no memory timings for RAM code %u registered\n",
ram_code);
return -ENOENT;
} }
err = emc_init(emc); err = emc_init(emc);
@@ -1226,9 +1488,39 @@ static int tegra_emc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, emc); platform_set_drvdata(pdev, emc);
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)) if (IS_ENABLED(CONFIG_DEBUG_FS))
emc_debugfs_init(&pdev->dev, emc); 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
* this driver.
*/
try_module_get(THIS_MODULE);
return 0; return 0;
}; };
@@ -1238,11 +1530,11 @@ static struct platform_driver tegra_emc_driver = {
.name = "tegra-emc", .name = "tegra-emc",
.of_match_table = tegra_emc_of_match, .of_match_table = tegra_emc_of_match,
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
.sync_state = icc_sync_state,
}, },
}; };
module_platform_driver(tegra_emc_driver);
static int tegra_emc_init(void) MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
{ MODULE_DESCRIPTION("NVIDIA Tegra124 EMC driver");
return platform_driver_register(&tegra_emc_driver); MODULE_LICENSE("GPL v2");
}
subsys_initcall(tegra_emc_init);

View File

@@ -4,7 +4,8 @@
*/ */
#include <linux/of.h> #include <linux/of.h>
#include <linux/mm.h> #include <linux/of_device.h>
#include <linux/slab.h>
#include <dt-bindings/memory/tegra124-mc.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), 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 #ifdef CONFIG_ARCH_TEGRA_124_SOC
static const unsigned long tegra124_mc_emem_regs[] = { static const unsigned long tegra124_mc_emem_regs[] = {
MC_EMEM_ARB_CFG, MC_EMEM_ARB_CFG,
@@ -1061,6 +1139,7 @@ const struct tegra_mc_soc tegra124_mc_soc = {
.reset_ops = &tegra_mc_reset_ops_common, .reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets, .resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets), .num_resets = ARRAY_SIZE(tegra124_mc_resets),
.icc_ops = &tegra124_mc_icc_ops,
}; };
#endif /* CONFIG_ARCH_TEGRA_124_SOC */ #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, .reset_ops = &tegra_mc_reset_ops_common,
.resets = tegra124_mc_resets, .resets = tegra124_mc_resets,
.num_resets = ARRAY_SIZE(tegra124_mc_resets), .num_resets = ARRAY_SIZE(tegra124_mc_resets),
.icc_ops = &tegra124_mc_icc_ops,
}; };
#endif /* CONFIG_ARCH_TEGRA_132_SOC */ #endif /* CONFIG_ARCH_TEGRA_132_SOC */

View File

@@ -911,21 +911,14 @@ err_msg:
static int tegra_emc_opp_table_init(struct tegra_emc *emc) static int tegra_emc_opp_table_init(struct tegra_emc *emc)
{ {
u32 hw_version = BIT(tegra_sku_info.soc_process_id); u32 hw_version = BIT(tegra_sku_info.soc_process_id);
struct opp_table *clk_opp_table, *hw_opp_table; struct opp_table *hw_opp_table;
int err; 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); hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
err = PTR_ERR_OR_ZERO(hw_opp_table); err = PTR_ERR_OR_ZERO(hw_opp_table);
if (err) { if (err) {
dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err); dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
goto put_clk_table; return err;
} }
err = dev_pm_opp_of_add_table(emc->dev); err = dev_pm_opp_of_add_table(emc->dev);
@@ -954,8 +947,6 @@ remove_table:
dev_pm_opp_of_remove_table(emc->dev); dev_pm_opp_of_remove_table(emc->dev);
put_hw_table: put_hw_table:
dev_pm_opp_put_supported_hw(hw_opp_table); dev_pm_opp_put_supported_hw(hw_opp_table);
put_clk_table:
dev_pm_opp_put_clkname(clk_opp_table);
return err; return err;
} }

View File

@@ -1483,21 +1483,14 @@ err_msg:
static int tegra_emc_opp_table_init(struct tegra_emc *emc) static int tegra_emc_opp_table_init(struct tegra_emc *emc)
{ {
u32 hw_version = BIT(tegra_sku_info.soc_speedo_id); u32 hw_version = BIT(tegra_sku_info.soc_speedo_id);
struct opp_table *clk_opp_table, *hw_opp_table; struct opp_table *hw_opp_table;
int err; 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); hw_opp_table = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);
err = PTR_ERR_OR_ZERO(hw_opp_table); err = PTR_ERR_OR_ZERO(hw_opp_table);
if (err) { if (err) {
dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err); dev_err(emc->dev, "failed to set OPP supported HW: %d\n", err);
goto put_clk_table; return err;
} }
err = dev_pm_opp_of_add_table(emc->dev); err = dev_pm_opp_of_add_table(emc->dev);
@@ -1526,8 +1519,6 @@ remove_table:
dev_pm_opp_of_remove_table(emc->dev); dev_pm_opp_of_remove_table(emc->dev);
put_hw_table: put_hw_table:
dev_pm_opp_put_supported_hw(hw_opp_table); dev_pm_opp_put_supported_hw(hw_opp_table);
put_clk_table:
dev_pm_opp_put_clkname(clk_opp_table);
return err; return err;
} }

View File

@@ -136,6 +136,7 @@ extern void tegra210_clk_emc_dll_update_setting(u32 emc_dll_src_value);
extern void tegra210_clk_emc_update_setting(u32 emc_src_value); extern void tegra210_clk_emc_update_setting(u32 emc_src_value);
struct clk; struct clk;
struct tegra_emc;
typedef long (tegra20_clk_emc_round_cb)(unsigned long rate, typedef long (tegra20_clk_emc_round_cb)(unsigned long rate,
unsigned long min_rate, unsigned long min_rate,
@@ -146,6 +147,13 @@ void tegra20_clk_set_emc_round_callback(tegra20_clk_emc_round_cb *round_cb,
void *cb_arg); void *cb_arg);
int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same); int tegra20_clk_prepare_emc_mc_same_freq(struct clk *emc_clk, bool same);
typedef int (tegra124_emc_prepare_timing_change_cb)(struct tegra_emc *emc,
unsigned long rate);
typedef void (tegra124_emc_complete_timing_change_cb)(struct tegra_emc *emc,
unsigned long rate);
void tegra124_clk_set_emc_callbacks(tegra124_emc_prepare_timing_change_cb *prep_cb,
tegra124_emc_complete_timing_change_cb *complete_cb);
struct tegra210_clk_emc_config { struct tegra210_clk_emc_config {
unsigned long rate; unsigned long rate;
bool same_freq; bool same_freq;

View File

@@ -1,16 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 NVIDIA Corporation. All rights reserved.
*/
#ifndef __SOC_TEGRA_EMC_H__
#define __SOC_TEGRA_EMC_H__
struct tegra_emc;
int tegra_emc_prepare_timing_change(struct tegra_emc *emc,
unsigned long rate);
void tegra_emc_complete_timing_change(struct tegra_emc *emc,
unsigned long rate);
#endif /* __SOC_TEGRA_EMC_H__ */