1 // SPDX-License-Identifier: GPL-2.0-only
4 #include <linux/clk-provider.h>
5 #include <linux/mod_devicetable.h>
6 #include <linux/mutex.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>
13 #include <soc/tegra/common.h>
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().
23 struct tegra_clk_device
{
24 struct notifier_block clk_nb
;
30 static int tegra_clock_set_pd_state(struct tegra_clk_device
*clk_dev
,
33 struct device
*dev
= clk_dev
->dev
;
34 struct dev_pm_opp
*opp
;
37 opp
= dev_pm_opp_find_freq_ceil(dev
, &rate
);
38 if (opp
== ERR_PTR(-ERANGE
)) {
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.
47 dev_dbg(dev
, "failed to find ceil OPP for %luHz\n", rate
);
48 opp
= dev_pm_opp_find_freq_floor(dev
, &rate
);
52 dev_err(dev
, "failed to find OPP for %luHz: %pe\n", rate
, opp
);
56 pstate
= dev_pm_opp_get_required_pstate(opp
, 0);
59 return dev_pm_genpd_set_performance_state(dev
, pstate
);
62 static int tegra_clock_change_notify(struct notifier_block
*nb
,
63 unsigned long msg
, void *data
)
65 struct clk_notifier_data
*cnd
= data
;
66 struct tegra_clk_device
*clk_dev
;
69 clk_dev
= container_of(nb
, struct tegra_clk_device
, clk_nb
);
71 mutex_lock(&clk_dev
->lock
);
74 if (cnd
->new_rate
> cnd
->old_rate
)
75 err
= tegra_clock_set_pd_state(clk_dev
, cnd
->new_rate
);
78 case ABORT_RATE_CHANGE
:
79 err
= tegra_clock_set_pd_state(clk_dev
, cnd
->old_rate
);
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
);
90 mutex_unlock(&clk_dev
->lock
);
92 return notifier_from_errno(err
);
95 static int tegra_clock_sync_pd_state(struct tegra_clk_device
*clk_dev
)
100 mutex_lock(&clk_dev
->lock
);
102 rate
= clk_hw_get_rate(clk_dev
->hw
);
103 ret
= tegra_clock_set_pd_state(clk_dev
, rate
);
105 mutex_unlock(&clk_dev
->lock
);
110 static int tegra_clock_probe(struct platform_device
*pdev
)
112 struct tegra_core_opp_params opp_params
= {};
113 struct tegra_clk_device
*clk_dev
;
114 struct device
*dev
= &pdev
->dev
;
121 clk_dev
= devm_kzalloc(dev
, sizeof(*clk_dev
), GFP_KERNEL
);
125 clk
= devm_clk_get(dev
, NULL
);
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
);
134 platform_set_drvdata(pdev
, clk_dev
);
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.
143 err
= devm_tegra_core_dev_init_opp_table(dev
, &opp_params
);
147 err
= clk_notifier_register(clk
, &clk_dev
->clk_nb
);
149 dev_err(dev
, "failed to register clk notifier: %d\n", err
);
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.
158 err
= tegra_clock_sync_pd_state(clk_dev
);
165 clk_notifier_unregister(clk
, &clk_dev
->clk_nb
);
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.
177 static const struct dev_pm_ops tegra_clock_pm
= {
178 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_resume_and_get
, pm_runtime_put
)
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" },
190 static struct platform_driver tegra_clock_driver
= {
192 .name
= "tegra-clock",
193 .of_match_table
= tegra_clock_match
,
194 .pm
= &tegra_clock_pm
,
195 .suppress_bind_attrs
= true,
197 .probe
= tegra_clock_probe
,
199 builtin_platform_driver(tegra_clock_driver
);