Skip to content

Commit fcfc6ea

Browse files
committed
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
2 parents fa55b7d + b1bc04a commit fcfc6ea

File tree

9 files changed

+421
-55
lines changed

9 files changed

+421
-55
lines changed

drivers/clk/tegra/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-License-Identifier: GPL-2.0
22
obj-y += clk.o
33
obj-y += clk-audio-sync.o
4+
obj-y += clk-device.o
45
obj-y += clk-dfll.o
56
obj-y += clk-divider.o
67
obj-y += clk-periph.o

drivers/clk/tegra/clk-device.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/clk.h>
4+
#include <linux/clk-provider.h>
5+
#include <linux/mutex.h>
6+
#include <linux/of_device.h>
7+
#include <linux/platform_device.h>
8+
#include <linux/pm_domain.h>
9+
#include <linux/pm_opp.h>
10+
#include <linux/pm_runtime.h>
11+
#include <linux/slab.h>
12+
13+
#include <soc/tegra/common.h>
14+
15+
#include "clk.h"
16+
17+
/*
18+
* This driver manages performance state of the core power domain for the
19+
* independent PLLs and system clocks. We created a virtual clock device
20+
* for such clocks, see tegra_clk_dev_register().
21+
*/
22+
23+
struct tegra_clk_device {
24+
struct notifier_block clk_nb;
25+
struct device *dev;
26+
struct clk_hw *hw;
27+
struct mutex lock;
28+
};
29+
30+
static int tegra_clock_set_pd_state(struct tegra_clk_device *clk_dev,
31+
unsigned long rate)
32+
{
33+
struct device *dev = clk_dev->dev;
34+
struct dev_pm_opp *opp;
35+
unsigned int pstate;
36+
37+
opp = dev_pm_opp_find_freq_ceil(dev, &rate);
38+
if (opp == ERR_PTR(-ERANGE)) {
39+
/*
40+
* Some clocks may be unused by a particular board and they
41+
* may have uninitiated clock rate that is overly high. In
42+
* this case clock is expected to be disabled, but still we
43+
* need to set up performance state of the power domain and
44+
* not error out clk initialization. A typical example is
45+
* a PCIe clock on Android tablets.
46+
*/
47+
dev_dbg(dev, "failed to find ceil OPP for %luHz\n", rate);
48+
opp = dev_pm_opp_find_freq_floor(dev, &rate);
49+
}
50+
51+
if (IS_ERR(opp)) {
52+
dev_err(dev, "failed to find OPP for %luHz: %pe\n", rate, opp);
53+
return PTR_ERR(opp);
54+
}
55+
56+
pstate = dev_pm_opp_get_required_pstate(opp, 0);
57+
dev_pm_opp_put(opp);
58+
59+
return dev_pm_genpd_set_performance_state(dev, pstate);
60+
}
61+
62+
static int tegra_clock_change_notify(struct notifier_block *nb,
63+
unsigned long msg, void *data)
64+
{
65+
struct clk_notifier_data *cnd = data;
66+
struct tegra_clk_device *clk_dev;
67+
int err = 0;
68+
69+
clk_dev = container_of(nb, struct tegra_clk_device, clk_nb);
70+
71+
mutex_lock(&clk_dev->lock);
72+
switch (msg) {
73+
case PRE_RATE_CHANGE:
74+
if (cnd->new_rate > cnd->old_rate)
75+
err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
76+
break;
77+
78+
case ABORT_RATE_CHANGE:
79+
err = tegra_clock_set_pd_state(clk_dev, cnd->old_rate);
80+
break;
81+
82+
case POST_RATE_CHANGE:
83+
if (cnd->new_rate < cnd->old_rate)
84+
err = tegra_clock_set_pd_state(clk_dev, cnd->new_rate);
85+
break;
86+
87+
default:
88+
break;
89+
}
90+
mutex_unlock(&clk_dev->lock);
91+
92+
return notifier_from_errno(err);
93+
}
94+
95+
static int tegra_clock_sync_pd_state(struct tegra_clk_device *clk_dev)
96+
{
97+
unsigned long rate;
98+
int ret;
99+
100+
mutex_lock(&clk_dev->lock);
101+
102+
rate = clk_hw_get_rate(clk_dev->hw);
103+
ret = tegra_clock_set_pd_state(clk_dev, rate);
104+
105+
mutex_unlock(&clk_dev->lock);
106+
107+
return ret;
108+
}
109+
110+
static int tegra_clock_probe(struct platform_device *pdev)
111+
{
112+
struct tegra_core_opp_params opp_params = {};
113+
struct tegra_clk_device *clk_dev;
114+
struct device *dev = &pdev->dev;
115+
struct clk *clk;
116+
int err;
117+
118+
if (!dev->pm_domain)
119+
return -EINVAL;
120+
121+
clk_dev = devm_kzalloc(dev, sizeof(*clk_dev), GFP_KERNEL);
122+
if (!clk_dev)
123+
return -ENOMEM;
124+
125+
clk = devm_clk_get(dev, NULL);
126+
if (IS_ERR(clk))
127+
return PTR_ERR(clk);
128+
129+
clk_dev->dev = dev;
130+
clk_dev->hw = __clk_get_hw(clk);
131+
clk_dev->clk_nb.notifier_call = tegra_clock_change_notify;
132+
mutex_init(&clk_dev->lock);
133+
134+
platform_set_drvdata(pdev, clk_dev);
135+
136+
/*
137+
* Runtime PM was already enabled for this device by the parent clk
138+
* driver and power domain state should be synced under clk_dev lock,
139+
* hence we don't use the common OPP helper that initializes OPP
140+
* state. For some clocks common OPP helper may fail to find ceil
141+
* rate, it's handled by this driver.
142+
*/
143+
err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
144+
if (err)
145+
return err;
146+
147+
err = clk_notifier_register(clk, &clk_dev->clk_nb);
148+
if (err) {
149+
dev_err(dev, "failed to register clk notifier: %d\n", err);
150+
return err;
151+
}
152+
153+
/*
154+
* The driver is attaching to a potentially active/resumed clock, hence
155+
* we need to sync the power domain performance state in a accordance to
156+
* the clock rate if clock is resumed.
157+
*/
158+
err = tegra_clock_sync_pd_state(clk_dev);
159+
if (err)
160+
goto unreg_clk;
161+
162+
return 0;
163+
164+
unreg_clk:
165+
clk_notifier_unregister(clk, &clk_dev->clk_nb);
166+
167+
return err;
168+
}
169+
170+
/*
171+
* Tegra GENPD driver enables clocks during NOIRQ phase. It can't be done
172+
* for clocks served by this driver because runtime PM is unavailable in
173+
* NOIRQ phase. We will keep clocks resumed during suspend to mitigate this
174+
* problem. In practice this makes no difference from a power management
175+
* perspective since voltage is kept at a nominal level during suspend anyways.
176+
*/
177+
static const struct dev_pm_ops tegra_clock_pm = {
178+
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_resume_and_get, pm_runtime_put)
179+
};
180+
181+
static const struct of_device_id tegra_clock_match[] = {
182+
{ .compatible = "nvidia,tegra20-sclk" },
183+
{ .compatible = "nvidia,tegra30-sclk" },
184+
{ .compatible = "nvidia,tegra30-pllc" },
185+
{ .compatible = "nvidia,tegra30-plle" },
186+
{ .compatible = "nvidia,tegra30-pllm" },
187+
{ }
188+
};
189+
190+
static struct platform_driver tegra_clock_driver = {
191+
.driver = {
192+
.name = "tegra-clock",
193+
.of_match_table = tegra_clock_match,
194+
.pm = &tegra_clock_pm,
195+
.suppress_bind_attrs = true,
196+
},
197+
.probe = tegra_clock_probe,
198+
};
199+
builtin_platform_driver(tegra_clock_driver);

drivers/clk/tegra/clk-pll.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1914,7 +1914,7 @@ static struct clk *_tegra_clk_register_pll(struct tegra_clk_pll *pll,
19141914
/* Data in .init is copied by clk_register(), so stack variable OK */
19151915
pll->hw.init = &init;
19161916

1917-
return clk_register(NULL, &pll->hw);
1917+
return tegra_clk_dev_register(&pll->hw);
19181918
}
19191919

19201920
struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,

drivers/clk/tegra/clk-super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ struct clk *tegra_clk_register_super_mux(const char *name,
226226
/* Data in .init is copied by clk_register(), so stack variable OK */
227227
super->hw.init = &init;
228228

229-
clk = clk_register(NULL, &super->hw);
229+
clk = tegra_clk_dev_register(&super->hw);
230230
if (IS_ERR(clk))
231231
kfree(super);
232232

drivers/clk/tegra/clk-tegra114.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1158,7 +1158,7 @@ static struct tegra_clk_init_table init_table[] __initdata = {
11581158
{ TEGRA114_CLK_XUSB_HS_SRC, TEGRA114_CLK_XUSB_SS_DIV2, 61200000, 0 },
11591159
{ TEGRA114_CLK_XUSB_FALCON_SRC, TEGRA114_CLK_PLL_P, 204000000, 0 },
11601160
{ TEGRA114_CLK_XUSB_HOST_SRC, TEGRA114_CLK_PLL_P, 102000000, 0 },
1161-
{ TEGRA114_CLK_VDE, TEGRA114_CLK_CLK_MAX, 600000000, 0 },
1161+
{ TEGRA114_CLK_VDE, TEGRA114_CLK_PLL_P, 408000000, 0 },
11621162
{ TEGRA114_CLK_SPDIF_IN_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
11631163
{ TEGRA114_CLK_I2S0_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },
11641164
{ TEGRA114_CLK_I2S1_SYNC, TEGRA114_CLK_CLK_MAX, 24000000, 0 },

drivers/clk/tegra/clk-tegra20.c

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
#include <linux/io.h>
77
#include <linux/clk-provider.h>
88
#include <linux/clkdev.h>
9+
#include <linux/init.h>
910
#include <linux/of.h>
1011
#include <linux/of_address.h>
12+
#include <linux/of_device.h>
13+
#include <linux/platform_device.h>
1114
#include <linux/clk/tegra.h>
1215
#include <linux/delay.h>
1316
#include <dt-bindings/clock/tegra20-car.h>
@@ -414,7 +417,7 @@ static struct tegra_clk_pll_params pll_e_params = {
414417
.fixed_rate = 100000000,
415418
};
416419

417-
static struct tegra_devclk devclks[] __initdata = {
420+
static struct tegra_devclk devclks[] = {
418421
{ .con_id = "pll_c", .dt_id = TEGRA20_CLK_PLL_C },
419422
{ .con_id = "pll_c_out1", .dt_id = TEGRA20_CLK_PLL_C_OUT1 },
420423
{ .con_id = "pll_p", .dt_id = TEGRA20_CLK_PLL_P },
@@ -710,13 +713,6 @@ static void tegra20_super_clk_init(void)
710713
NULL);
711714
clks[TEGRA20_CLK_CCLK] = clk;
712715

713-
/* SCLK */
714-
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
715-
ARRAY_SIZE(sclk_parents),
716-
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
717-
clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
718-
clks[TEGRA20_CLK_SCLK] = clk;
719-
720716
/* twd */
721717
clk = clk_register_fixed_factor(NULL, "twd", "cclk", 0, 1, 4);
722718
clks[TEGRA20_CLK_TWD] = clk;
@@ -1014,7 +1010,7 @@ static struct tegra_cpu_car_ops tegra20_cpu_car_ops = {
10141010
#endif
10151011
};
10161012

1017-
static struct tegra_clk_init_table init_table[] __initdata = {
1013+
static struct tegra_clk_init_table init_table[] = {
10181014
{ TEGRA20_CLK_PLL_P, TEGRA20_CLK_CLK_MAX, 216000000, 1 },
10191015
{ TEGRA20_CLK_PLL_P_OUT1, TEGRA20_CLK_CLK_MAX, 28800000, 1 },
10201016
{ TEGRA20_CLK_PLL_P_OUT2, TEGRA20_CLK_CLK_MAX, 48000000, 1 },
@@ -1052,11 +1048,6 @@ static struct tegra_clk_init_table init_table[] __initdata = {
10521048
{ TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0 },
10531049
};
10541050

1055-
static void __init tegra20_clock_apply_init_table(void)
1056-
{
1057-
tegra_init_from_table(init_table, clks, TEGRA20_CLK_CLK_MAX);
1058-
}
1059-
10601051
/*
10611052
* Some clocks may be used by different drivers depending on the board
10621053
* configuration. List those here to register them twice in the clock lookup
@@ -1076,13 +1067,25 @@ static const struct of_device_id pmc_match[] __initconst = {
10761067
{ },
10771068
};
10781069

1070+
static bool tegra20_car_initialized;
1071+
10791072
static struct clk *tegra20_clk_src_onecell_get(struct of_phandle_args *clkspec,
10801073
void *data)
10811074
{
10821075
struct clk_hw *parent_hw;
10831076
struct clk_hw *hw;
10841077
struct clk *clk;
10851078

1079+
/*
1080+
* Timer clocks are needed early, the rest of the clocks shouldn't be
1081+
* available to device drivers until clock tree is fully initialized.
1082+
*/
1083+
if (clkspec->args[0] != TEGRA20_CLK_RTC &&
1084+
clkspec->args[0] != TEGRA20_CLK_TWD &&
1085+
clkspec->args[0] != TEGRA20_CLK_TIMER &&
1086+
!tegra20_car_initialized)
1087+
return ERR_PTR(-EPROBE_DEFER);
1088+
10861089
clk = of_clk_src_onecell_get(clkspec, data);
10871090
if (IS_ERR(clk))
10881091
return clk;
@@ -1149,10 +1152,48 @@ static void __init tegra20_clock_init(struct device_node *np)
11491152
tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX);
11501153

11511154
tegra_add_of_provider(np, tegra20_clk_src_onecell_get);
1152-
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
1153-
1154-
tegra_clk_apply_init_table = tegra20_clock_apply_init_table;
11551155

11561156
tegra_cpu_car_ops = &tegra20_cpu_car_ops;
11571157
}
1158-
CLK_OF_DECLARE(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
1158+
CLK_OF_DECLARE_DRIVER(tegra20, "nvidia,tegra20-car", tegra20_clock_init);
1159+
1160+
/*
1161+
* Clocks that use runtime PM can't be created at the tegra20_clock_init
1162+
* time because drivers' base isn't initialized yet, and thus platform
1163+
* devices can't be created for the clocks. Hence we need to split the
1164+
* registration of the clocks into two phases. The first phase registers
1165+
* essential clocks which don't require RPM and are actually used during
1166+
* early boot. The second phase registers clocks which use RPM and this
1167+
* is done when device drivers' core API is ready.
1168+
*/
1169+
static int tegra20_car_probe(struct platform_device *pdev)
1170+
{
1171+
struct clk *clk;
1172+
1173+
clk = tegra_clk_register_super_mux("sclk", sclk_parents,
1174+
ARRAY_SIZE(sclk_parents),
1175+
CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
1176+
clk_base + SCLK_BURST_POLICY, 0, 4, 0, 0, NULL);
1177+
clks[TEGRA20_CLK_SCLK] = clk;
1178+
1179+
tegra_register_devclks(devclks, ARRAY_SIZE(devclks));
1180+
tegra_init_from_table(init_table, clks, TEGRA20_CLK_CLK_MAX);
1181+
tegra20_car_initialized = true;
1182+
1183+
return 0;
1184+
}
1185+
1186+
static const struct of_device_id tegra20_car_match[] = {
1187+
{ .compatible = "nvidia,tegra20-car" },
1188+
{ }
1189+
};
1190+
1191+
static struct platform_driver tegra20_car_driver = {
1192+
.driver = {
1193+
.name = "tegra20-car",
1194+
.of_match_table = tegra20_car_match,
1195+
.suppress_bind_attrs = true,
1196+
},
1197+
.probe = tegra20_car_probe,
1198+
};
1199+
builtin_platform_driver(tegra20_car_driver);

0 commit comments

Comments
 (0)