mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 12:43:29 +02:00
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: Nicolas Chauvet <kwizart@gmail.com> Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Link: https://lore.kernel.org/r/20201228154920.18846-2-digetx@gmail.com Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
This commit is contained in:
committed by
Krzysztof Kozlowski
parent
5c8fe583cc
commit
281462e593
@@ -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
|
|||||||
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 @@
|
|||||||
#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;
|
||||||
|
}
|
||||||
|
@@ -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));
|
||||||
|
|
||||||
|
@@ -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
|
|||||||
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 @@
|
|||||||
#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,
|
|||||||
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,
|
|||||||
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[] = {
|
|||||||
{ .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)
|
|||||||
|
|
||||||
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 = {
|
|||||||
.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);
|
|||||||
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;
|
||||||
|
@@ -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__ */
|
|
Reference in New Issue
Block a user