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