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