clk: tegra: Changes for v5.17-rc1

This contains a simple fix for the VDE clock on Tegra114 and some
 preparation work to support runtime PM and generic power domains.
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAmG8r9ITHHRyZWRpbmdA
 bnZpZGlhLmNvbQAKCRDdI6zXfz6zoTZZD/4sXPJ3KRt+KHi2KzNlw1F1VuzL54cy
 8mvxxHnvQzoHAhzciphCbNphn39gQ06nZP9Wmdlj4WmEY7zzpDcKTdM+dQwgoGrM
 IEqg4B13qNi7gaFpahoN0Ygjp/C6x8R2b18KKrytp4UemLuEH7IPL1utHm7YWzye
 srUymc20Fs49qbHwOyyyLp7lg9Kpn3oSRSxVSZbpmUK+r465MnXlxgrHtur+UaKF
 dV7I3UQuNJxLng5E6t8/LgFo2FwPKrYUit8/vhwUiGO1IbfSOlVzKcWn7J1Yfo7y
 IDRfrYMXu/TYObCzE3qTiMSEhUWz+uhfDonUhFNHXI0qCpx94k0MhjEGhDyZknJG
 8alwumFP8NX96N/CC+GWnJhuGf6M32gte6O1Yh9aa6jA4OwsuDXdx8YRu/R3ABYz
 7rgfENGarl5UvgcNr8smNc03r8BZrkMl2wkLU6owUI+CY3ToY8R99ncvfuKE7ZFq
 ATezvxWmjjlcfdSxDjVb7PJvBmLNwPGOv/dTSKXcRcrnAFyqZgnW7zGKXSJdBIbB
 QYHeo7GpLbZoiJvqUsn7AT85NZyIIPwD5kkRMTg2TlV1To7H/sKzKsVNl2y9j4Nd
 WHxc2zawuU8a09fz2RQDe7sQTR0Kn9WchYSnITTpunhymqbPa7nc8B56LTuMoKAN
 0ywl+4UfbDRvYQ==
 =aQXh
 -----END PGP SIGNATURE-----

Merge tag 'for-5.17-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into clk-nvidia

Pull Tegra clk driver updates from Thierry Reding:

This contains a simple fix for the VDE clock on Tegra114 and some
preparation work to support runtime PM and generic power domains.

* tag 'for-5.17-clk' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux:
  clk: tegra: Support runtime PM and power domain
  clk: tegra: Make vde a child of pll_p on tegra114
This commit is contained in:
Stephen Boyd 2021-12-28 21:47:05 -08:00
commit fcfc6ea4a4
9 changed files with 422 additions and 56 deletions

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += clk.o
obj-y += clk-audio-sync.o
obj-y += clk-device.o
obj-y += clk-dfll.o
obj-y += clk-divider.o
obj-y += clk-periph.o

View File

@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <soc/tegra/common.h>
#include "clk.h"
/*
* This driver manages performance state of the core power domain for the
* independent PLLs and system clocks. We created a virtual clock device
* for such clocks, see tegra_clk_dev_register().
*/
struct tegra_clk_device {
struct notifier_block clk_nb;
struct device *dev;
struct clk_hw *hw;
struct mutex lock;
};
static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev,
unsigned long rate)
{
struct device *dev = clk_dev->dev;
struct dev_pm_opp *opp;
unsigned int pstate;
opp = dev_pm_opp_find_freq_ceil(dev, &rate);
if (opp == ERR_PTR(-ERANGE)) {
/*
* Some clocks may be unused by a particular board and they
* may have uninitiated clock rate that is overly high. In
* this case clock is expected to be disabled, but still we
* need to set up performance state of the power domain and
* not error out clk initialization. A typical example is
* a PCIe clock on Android tablets.
*/
dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate);
opp = dev_pm_opp_find_freq_floor(dev, &rate);
}
if (IS_ERR(opp)) {
dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp);
return PTR_ERR(opp);
}
pstate = dev_pm_opp_get_required_pstate(opp, 0);
dev_pm_opp_put(opp);
return dev_pm_genpd_set_performance_state(dev, pstate);
}
static int tegra_clock_change_notify(struct notifier_block *nb,
unsigned long msg, void *data)
{
struct clk_notifier_data *cnd = data;
struct tegra_clk_device *clk_dev;
int err = 0;
clk_dev = container_of(nb, struct tegra_clk_device, clk_nb);
mutex_lock(&clk_dev->lock);
switch (msg) {
case PRE_RATE_CHANGE:
if (cnd->new_rate > cnd->old_rate)
err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
break;
case ABORT_RATE_CHANGE:
err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate);
break;
case POST_RATE_CHANGE:
if (cnd->new_rate < cnd->old_rate)
err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
break;
default:
break;
}
mutex_unlock(&clk_dev->lock);
return notifier_from_errno(err);
}
static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev)
{
unsigned long rate;
int ret;
mutex_lock(&clk_dev->lock);
rate = clk_hw_get_rate(clk_dev->hw);
ret = tegra_clock_set_pd_state(clk_dev, rate);
mutex_unlock(&clk_dev->lock);
return ret;
}
static int tegra_clock_probe(struct platform_device *pdev)
{
struct tegra_core_opp_params opp_params = {};
struct tegra_clk_device *clk_dev;
struct device *dev = &pdev->dev;
struct clk *clk;
int err;
if (!dev->pm_domain)
return -EINVAL;
clk_dev = devm_kzalloc(dev, sizeof(*clk_dev), GFP_KERNEL);
if (!clk_dev)
return -ENOMEM;
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
clk_dev->dev = dev;
clk_dev->hw = __clk_get_hw(clk);
clk_dev->clk_nb.notifier_call = tegra_clock_change_notify;
mutex_init(&clk_dev->lock);
platform_set_drvdata(pdev, clk_dev);
/*
* Runtime PM was already enabled for this device by the parent clk
* driver and power domain state should be synced under clk_dev lock,
* hence we don't use the common OPP helper that initializes OPP
* state. For some clocks common OPP helper may fail to find ceil
* rate, it's handled by this driver.
*/
err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
if (err)
return err;
err = clk_notifier_register(clk, &clk_dev->clk_nb);
if (err) {
dev_err(dev, "failed to register clk notifier: %d\n", err);
return err;
}
/*
* The driver is attaching to a potentially active/resumed clock, hence
* we need to sync the power domain performance state in a accordance to
* the clock rate if clock is resumed.
*/
err = tegra_clock_sync_pd_state(clk_dev);
if (err)
goto unreg_clk;
return 0;
unreg_clk:
clk_notifier_unregister(clk, &clk_dev->clk_nb);
return err;
}
/*
* Tegra GENPD driver enables clocks during NOIRQ phase. It can't be done
* for clocks served by this driver because runtime PM is unavailable in
* NOIRQ phase. We will keep clocks resumed during suspend to mitigate this
* problem. In practice this makes no difference from a power management
* perspective since voltage is kept at a nominal level during suspend anyways.
*/
static const struct dev_pm_ops tegra_clock_pm = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_resume_and_get, pm_runtime_put)
};
static const struct of_device_id tegra_clock_match[] = {
{ .compatible = "nvidia,tegra20-sclk" },
{ .compatible = "nvidia,tegra30-sclk" },
{ .compatible = "nvidia,tegra30-pllc" },
{ .compatible = "nvidia,tegra30-plle" },
{ .compatible = "nvidia,tegra30-pllm" },
{ }
};
static struct platform_driver tegra_clock_driver = {
.driver = {
.name = "tegra-clock",
.of_match_table = tegra_clock_match,
.pm = &tegra_clock_pm,
.suppress_bind_attrs = true,
},
.probe = tegra_clock_probe,
};
builtin_platform_driver(tegra_clock_driver);

View File

@ -1914,7 +1914,7 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
/* Data in .init is copied by clk_register(), so stack variable OK */
pll->hw.init = &init;
return clk_register(NULL, &pll->hw);
return tegra_clk_dev_register(&pll->hw);
}
struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,

View File

@ -226,7 +226,7 @@ struct clk *tegra_clk_register_super_mux(const char *name,
/* Data in .init is copied by clk_register(), so stack variable OK */
super->hw.init = &init;
clk = clk_register(NULL, &super->hw);
clk = tegra_clk_dev_register(&super->hw);
if (IS_ERR(clk))
kfree(super);

View File

@ -1158,7 +1158,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0 },
{ TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0 },
{ TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0 },
{ TEGRA114_CLK_VDE, TEGRA114_CLK_CLK_MAX, 600000000, 0 },
{ TEGRA114_CLK_VDE, TEGRA114_CLK_PLL_P, 408000000, 0 },
{ TEGRA114_CLK_SPDIF_IN_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA114_CLK_I2S0_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
{ TEGRA114_CLK_I2S1_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },

View File

@ -6,8 +6,11 @@
#include <linux/io.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk/tegra.h>
#include <linux/delay.h>
#include <dt-bindings/clock/tegra20-car.h>
@ -414,7 +417,7 @@ static struct tegra_clk_pll_params pll_e_params = {
.fixed_rate = 100000000,
};
static struct tegra_devclk devclks[] __initdata = {
static struct tegra_devclk devclks[] = {
{ .con_id = "pll_c", .dt_id = TEGRA20_CLK_PLL_C },
{ .con_id = "pll_c_out1", .dt_id = TEGRA20_CLK_PLL_C_OUT1 },
{ .con_id = "pll_p", .dt_id = TEGRA20_CLK_PLL_P },
@ -710,13 +713,6 @@ static void tegra20_super_clk_init(void)
NULL);
clks[TEGRA20_CLK_CCLK] = clk;
/* SCLK */
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
ARRAY_SIZE(sclk_parents),
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
clks[TEGRA20_CLK_SCLK] = clk;
/* twd */
clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4);
clks[TEGRA20_CLK_TWD] = clk;
@ -1014,7 +1010,7 @@ static struct tegra_cpu_car_ops tegra20_cpu_car_ops = {
#endif
};
static struct tegra_clk_init_table init_table[] __initdata = {
static struct tegra_clk_init_table init_table[] = {
{ TEGRA20_CLK_PLL_P, TEGRA20_CLK_CLK_MAX, 216000000, 1 },
{ TEGRA20_CLK_PLL_P_OUT1, TEGRA20_CLK_CLK_MAX, 28800000, 1 },
{ TEGRA20_CLK_PLL_P_OUT2, TEGRA20_CLK_CLK_MAX, 48000000, 1 },
@ -1052,11 +1048,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0 },
};
static void __init tegra20_clock_apply_init_table(void)
{
tegra_init_from_table(init_table, clks, TEGRA20_CLK_CLK_MAX);
}
/*
* Some clocks may be used by different drivers depending on the board
* configuration. List those here to register them twice in the clock lookup
@ -1076,6 +1067,8 @@ static const struct of_device_id pmc_match[] __initconst = {
{ },
};
static bool tegra20_car_initialized;
static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
void *data)
{
@ -1083,6 +1076,16 @@ static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
struct clk_hw *hw;
struct clk *clk;
/*
* Timer clocks are needed early, the rest of the clocks shouldn't be
* available to device drivers until clock tree is fully initialized.
*/
if (clkspec->args[0] != TEGRA20_CLK_RTC &&
clkspec->args[0] != TEGRA20_CLK_TWD &&
clkspec->args[0] != TEGRA20_CLK_TIMER &&
!tegra20_car_initialized)
return ERR_PTR(-EPROBE_DEFER);
clk = of_clk_src_onecell_get(clkspec, data);
if (IS_ERR(clk))
return clk;
@ -1149,10 +1152,48 @@ static void __init tegra20_clock_init(struct device_node *np)
tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX);
tegra_add_of_provider(np, tegra20_clk_src_onecell_get);
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
tegra_clk_apply_init_table = tegra20_clock_apply_init_table;
tegra_cpu_car_ops = &tegra20_cpu_car_ops;
}
CLK_OF_DECLARE(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
CLK_OF_DECLARE_DRIVER(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
/*
* Clocks that use runtime PM can't be created at the tegra20_clock_init
* time because drivers' base isn't initialized yet, and thus platform
* devices can't be created for the clocks. Hence we need to split the
* registration of the clocks into two phases. The first phase registers
* essential clocks which don't require RPM and are actually used during
* early boot. The second phase registers clocks which use RPM and this
* is done when device drivers' core API is ready.
*/
static int tegra20_car_probe(struct platform_device *pdev)
{
struct clk *clk;
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
ARRAY_SIZE(sclk_parents),
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
clks[TEGRA20_CLK_SCLK] = clk;
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
tegra_init_from_table(init_table, clks, TEGRA20_CLK_CLK_MAX);
tegra20_car_initialized = true;
return 0;
}
static const struct of_device_id tegra20_car_match[] = {
{ .compatible = "nvidia,tegra20-car" },
{ }
};
static struct platform_driver tegra20_car_driver = {
.driver = {
.name = "tegra20-car",
.of_match_table = tegra20_car_match,
.suppress_bind_attrs = true,
},
.probe = tegra20_car_probe,
};
builtin_platform_driver(tegra20_car_driver);

View File

@ -7,8 +7,11 @@
#include <linux/delay.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/clk/tegra.h>
#include <soc/tegra/pmc.h>
@ -532,7 +535,7 @@ static unsigned long tegra30_input_freq[] = {
[12] = 26000000,
};
static struct tegra_devclk devclks[] __initdata = {
static struct tegra_devclk devclks[] = {
{ .con_id = "pll_c", .dt_id = TEGRA30_CLK_PLL_C },
{ .con_id = "pll_c_out1", .dt_id = TEGRA30_CLK_PLL_C_OUT1 },
{ .con_id = "pll_p", .dt_id = TEGRA30_CLK_PLL_P },
@ -812,11 +815,6 @@ static void __init tegra30_pll_init(void)
{
struct clk *clk;
/* PLLC */
clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
&pll_c_params, NULL);
clks[TEGRA30_CLK_PLL_C] = clk;
/* PLLC_OUT1 */
clk = tegra_clk_register_divider("pll_c_out1_div", "pll_c",
clk_base + PLLC_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@ -826,11 +824,6 @@ static void __init tegra30_pll_init(void)
0, NULL);
clks[TEGRA30_CLK_PLL_C_OUT1] = clk;
/* PLLM */
clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
CLK_SET_RATE_GATE, &pll_m_params, NULL);
clks[TEGRA30_CLK_PLL_M] = clk;
/* PLLM_OUT1 */
clk = tegra_clk_register_divider("pll_m_out1_div", "pll_m",
clk_base + PLLM_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
@ -880,9 +873,6 @@ static void __init tegra30_pll_init(void)
ARRAY_SIZE(pll_e_parents),
CLK_SET_RATE_NO_REPARENT,
clk_base + PLLE_AUX, 2, 1, 0, NULL);
clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
clks[TEGRA30_CLK_PLL_E] = clk;
}
static const char *cclk_g_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
@ -971,14 +961,6 @@ static void __init tegra30_super_clk_init(void)
NULL);
clks[TEGRA30_CLK_CCLK_LP] = clk;
/* SCLK */
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
ARRAY_SIZE(sclk_parents),
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY,
0, 4, 0, 0, NULL);
clks[TEGRA30_CLK_SCLK] = clk;
/* twd */
clk = clk_register_fixed_factor(NULL, "twd", "cclk_g",
CLK_SET_RATE_PARENT, 1, 2);
@ -1214,7 +1196,7 @@ static struct tegra_cpu_car_ops tegra30_cpu_car_ops = {
#endif
};
static struct tegra_clk_init_table init_table[] __initdata = {
static struct tegra_clk_init_table init_table[] = {
{ TEGRA30_CLK_UARTA, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTB, TEGRA30_CLK_PLL_P, 408000000, 0 },
{ TEGRA30_CLK_UARTC, TEGRA30_CLK_PLL_P, 408000000, 0 },
@ -1259,11 +1241,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
{ TEGRA30_CLK_CLK_MAX, TEGRA30_CLK_CLK_MAX, 0, 0 },
};
static void __init tegra30_clock_apply_init_table(void)
{
tegra_init_from_table(init_table, clks, TEGRA30_CLK_CLK_MAX);
}
/*
* Some clocks may be used by different drivers depending on the board
* configuration. List those here to register them twice in the clock lookup
@ -1294,12 +1271,24 @@ static struct tegra_audio_clk_info tegra30_audio_plls[] = {
{ "pll_a", &pll_a_params, tegra_clk_pll_a, "pll_p_out1" },
};
static bool tegra30_car_initialized;
static struct clk *tegra30_clk_src_onecell_get(struct of_phandle_args *clkspec,
void *data)
{
struct clk_hw *hw;
struct clk *clk;
/*
* Timer clocks are needed early, the rest of the clocks shouldn't be
* available to device drivers until clock tree is fully initialized.
*/
if (clkspec->args[0] != TEGRA30_CLK_RTC &&
clkspec->args[0] != TEGRA30_CLK_TWD &&
clkspec->args[0] != TEGRA30_CLK_TIMER &&
!tegra30_car_initialized)
return ERR_PTR(-EPROBE_DEFER);
clk = of_clk_src_onecell_get(clkspec, data);
if (IS_ERR(clk))
return clk;
@ -1357,10 +1346,75 @@ 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, tegra30_clk_src_onecell_get);
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
tegra_cpu_car_ops = &tegra30_cpu_car_ops;
}
CLK_OF_DECLARE(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
CLK_OF_DECLARE_DRIVER(tegra30, "nvidia,tegra30-car", tegra30_clock_init);
/*
* Clocks that use runtime PM can't be created at the tegra30_clock_init
* time because drivers' base isn't initialized yet, and thus platform
* devices can't be created for the clocks. Hence we need to split the
* registration of the clocks into two phases. The first phase registers
* essential clocks which don't require RPM and are actually used during
* early boot. The second phase registers clocks which use RPM and this
* is done when device drivers' core API is ready.
*/
static int tegra30_car_probe(struct platform_device *pdev)
{
struct clk *clk;
/* PLLC */
clk = tegra_clk_register_pll("pll_c", "pll_ref", clk_base, pmc_base, 0,
&pll_c_params, NULL);
clks[TEGRA30_CLK_PLL_C] = clk;
/* PLLE */
clk = tegra_clk_register_plle("pll_e", "pll_e_mux", clk_base, pmc_base,
CLK_GET_RATE_NOCACHE, &pll_e_params, NULL);
clks[TEGRA30_CLK_PLL_E] = clk;
/* PLLM */
clk = tegra_clk_register_pll("pll_m", "pll_ref", clk_base, pmc_base,
CLK_SET_RATE_GATE, &pll_m_params, NULL);
clks[TEGRA30_CLK_PLL_M] = clk;
/* SCLK */
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
ARRAY_SIZE(sclk_parents),
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
clk_base + SCLK_BURST_POLICY,
0, 4, 0, 0, NULL);
clks[TEGRA30_CLK_SCLK] = clk;
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
tegra_init_from_table(init_table, clks, TEGRA30_CLK_CLK_MAX);
tegra30_car_initialized = true;
return 0;
}
static const struct of_device_id tegra30_car_match[] = {
{ .compatible = "nvidia,tegra30-car" },
{ }
};
static struct platform_driver tegra30_car_driver = {
.driver = {
.name = "tegra30-car",
.of_match_table = tegra30_car_match,
.suppress_bind_attrs = true,
},
.probe = tegra30_car_probe,
};
/*
* Clock driver must be registered before memory controller driver,
* which doesn't support deferred probing for today and is registered
* from arch init-level.
*/
static int tegra30_car_init(void)
{
return platform_driver_register(&tegra30_car_driver);
}
postcore_initcall(tegra30_car_init);

View File

@ -9,14 +9,19 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/clk/tegra.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset-controller.h>
#include <linux/string.h>
#include <soc/tegra/fuse.h>
#include "clk.h"
/* Global data of Tegra CPU CAR ops */
static struct device_node *tegra_car_np;
static struct tegra_cpu_car_ops dummy_car_ops;
struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
@ -261,8 +266,8 @@ void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
}
}
void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
struct clk *clks[], int clk_max)
void tegra_init_from_table(struct tegra_clk_init_table *tbl,
struct clk *clks[], int clk_max)
{
struct clk *clk;
@ -320,6 +325,8 @@ void __init tegra_add_of_provider(struct device_node *np,
{
int i;
tegra_car_np = np;
for (i = 0; i < clk_num; i++) {
if (IS_ERR(clks[i])) {
pr_err
@ -348,7 +355,7 @@ void __init tegra_init_special_resets(unsigned int num,
special_reset_deassert = deassert;
}
void __init tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
void tegra_register_devclks(struct tegra_devclk *dev_clks, int num)
{
int i;
@ -372,6 +379,68 @@ struct clk ** __init tegra_lookup_dt_id(int clk_id,
return NULL;
}
static struct device_node *tegra_clk_get_of_node(struct clk_hw *hw)
{
struct device_node *np;
char *node_name;
node_name = kstrdup(hw->init->name, GFP_KERNEL);
if (!node_name)
return NULL;
strreplace(node_name, '_', '-');
for_each_child_of_node(tegra_car_np, np) {
if (!strcmp(np->name, node_name))
break;
}
kfree(node_name);
return np;
}
struct clk *tegra_clk_dev_register(struct clk_hw *hw)
{
struct platform_device *pdev, *parent;
const char *dev_name = NULL;
struct device *dev = NULL;
struct device_node *np;
np = tegra_clk_get_of_node(hw);
if (!of_device_is_available(np))
goto put_node;
dev_name = kasprintf(GFP_KERNEL, "tegra_clk_%s", hw->init->name);
if (!dev_name)
goto put_node;
parent = of_find_device_by_node(tegra_car_np);
if (parent) {
pdev = of_platform_device_create(np, dev_name, &parent->dev);
put_device(&parent->dev);
if (!pdev) {
pr_err("%s: failed to create device for %pOF\n",
__func__, np);
goto free_name;
}
dev = &pdev->dev;
pm_runtime_enable(dev);
} else {
WARN(1, "failed to find device for %pOF\n", tegra_car_np);
}
free_name:
kfree(dev_name);
put_node:
of_node_put(np);
return clk_register(dev, hw);
}
tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
static int __init tegra_clocks_apply_init_table(void)

View File

@ -927,4 +927,6 @@ struct clk *tegra20_clk_register_emc(void __iomem *ioaddr, bool low_jitter);
struct clk *tegra210_clk_register_emc(struct device_node *np,
void __iomem *regs);
struct clk *tegra_clk_dev_register(struct clk_hw *hw);
#endif /* TEGRA_CLK_H */