Commit 281462e5 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Krzysztof Kozlowski

memory: tegra124-emc: Make driver modular

Add modularization support to the Tegra124 EMC driver, which now can be
compiled as a loadable kernel module.

Note that EMC clock must be registered at clk-init time, otherwise PLLM
will be disabled as unused clock at boot time if EMC driver is compiled
as a module. Hence add a prepare/complete callbacks. similarly to what is
done for the Tegra20/30 EMC drivers.
Tested-by: default avatarNicolas Chauvet <kwizart@gmail.com>
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Link: https://lore.kernel.org/r/20201228154920.18846-2-digetx@gmail.comSigned-off-by: default avatarKrzysztof Kozlowski <krzk@kernel.org>
parent 5c8fe583
...@@ -7,3 +7,6 @@ config TEGRA_CLK_DFLL ...@@ -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
...@@ -22,7 +22,7 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra20-emc.o ...@@ -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
......
...@@ -11,7 +11,9 @@ ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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 = { ...@@ -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, ...@@ -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;
}
...@@ -1500,6 +1500,26 @@ static void __init tegra124_132_clock_init_pre(struct device_node *np) ...@@ -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) ...@@ -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));
......
...@@ -881,16 +881,22 @@ void tegra_super_clk_gen5_init(void __iomem *clk_base, ...@@ -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);
......
...@@ -32,9 +32,10 @@ config TEGRA30_EMC ...@@ -32,9 +32,10 @@ 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
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.
......
...@@ -9,16 +9,17 @@ ...@@ -9,16 +9,17 @@
#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/io.h> #include <linux/io.h>
#include <linux/module.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/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>
...@@ -562,8 +563,8 @@ static struct emc_timing *tegra_emc_find_timing(struct tegra_emc *emc, ...@@ -562,8 +563,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 +791,8 @@ int tegra_emc_prepare_timing_change(struct tegra_emc *emc, ...@@ -790,8 +791,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;
...@@ -987,6 +988,7 @@ static const struct of_device_id tegra_emc_of_match[] = { ...@@ -987,6 +988,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)
...@@ -1226,9 +1228,19 @@ static int tegra_emc_probe(struct platform_device *pdev) ...@@ -1226,9 +1228,19 @@ 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);
if (IS_ENABLED(CONFIG_DEBUG_FS)) if (IS_ENABLED(CONFIG_DEBUG_FS))
emc_debugfs_init(&pdev->dev, emc); emc_debugfs_init(&pdev->dev, 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;
}; };
...@@ -1240,9 +1252,8 @@ static struct platform_driver tegra_emc_driver = { ...@@ -1240,9 +1252,8 @@ static struct platform_driver tegra_emc_driver = {
.suppress_bind_attrs = true, .suppress_bind_attrs = true,
}, },
}; };
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);
...@@ -136,6 +136,7 @@ extern void tegra210_clk_emc_dll_update_setting(u32 emc_dll_src_value); ...@@ -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, ...@@ -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;
......
/* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment