Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * Raspberry Pi driver for firmware controlled clocks
4 : : *
5 : : * Even though clk-bcm2835 provides an interface to the hardware registers for
6 : : * the system clocks we've had to factor out 'pllb' as the firmware 'owns' it.
7 : : * We're not allowed to change it directly as we might race with the
8 : : * over-temperature and under-voltage protections provided by the firmware.
9 : : *
10 : : * Copyright (C) 2019 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
11 : : */
12 : :
13 : : #include <linux/clkdev.h>
14 : : #include <linux/clk-provider.h>
15 : : #include <linux/io.h>
16 : : #include <linux/module.h>
17 : : #include <linux/platform_device.h>
18 : :
19 : : #include <soc/bcm2835/raspberrypi-firmware.h>
20 : :
21 : : enum rpi_firmware_clk_id {
22 : : RPI_FIRMWARE_EMMC_CLK_ID = 1,
23 : : RPI_FIRMWARE_UART_CLK_ID,
24 : : RPI_FIRMWARE_ARM_CLK_ID,
25 : : RPI_FIRMWARE_CORE_CLK_ID,
26 : : RPI_FIRMWARE_V3D_CLK_ID,
27 : : RPI_FIRMWARE_H264_CLK_ID,
28 : : RPI_FIRMWARE_ISP_CLK_ID,
29 : : RPI_FIRMWARE_SDRAM_CLK_ID,
30 : : RPI_FIRMWARE_PIXEL_CLK_ID,
31 : : RPI_FIRMWARE_PWM_CLK_ID,
32 : : RPI_FIRMWARE_HEVC_CLK_ID,
33 : : RPI_FIRMWARE_EMMC2_CLK_ID,
34 : : RPI_FIRMWARE_M2MC_CLK_ID,
35 : : RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
36 : : RPI_FIRMWARE_NUM_CLK_ID,
37 : : };
38 : :
39 : : static char *rpi_firmware_clk_names[] = {
40 : : [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc",
41 : : [RPI_FIRMWARE_UART_CLK_ID] = "uart",
42 : : [RPI_FIRMWARE_ARM_CLK_ID] = "arm",
43 : : [RPI_FIRMWARE_CORE_CLK_ID] = "core",
44 : : [RPI_FIRMWARE_V3D_CLK_ID] = "v3d",
45 : : [RPI_FIRMWARE_H264_CLK_ID] = "h264",
46 : : [RPI_FIRMWARE_ISP_CLK_ID] = "isp",
47 : : [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram",
48 : : [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel",
49 : : [RPI_FIRMWARE_PWM_CLK_ID] = "pwm",
50 : : [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc",
51 : : [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2",
52 : : [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc",
53 : : [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
54 : : };
55 : :
56 : : #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
57 : : #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1)
58 : :
59 : : /*
60 : : * Even though the firmware interface alters 'pllb' the frequencies are
61 : : * provided as per 'pllb_arm'. We need to scale before passing them trough.
62 : : */
63 : : #define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2
64 : :
65 : : #define A2W_PLL_FRAC_BITS 20
66 : :
67 : : struct raspberrypi_clk {
68 : : struct device *dev;
69 : : struct rpi_firmware *firmware;
70 : : struct platform_device *cpufreq;
71 : : };
72 : :
73 : : struct raspberrypi_clk_data {
74 : : struct clk_hw hw;
75 : : unsigned id;
76 : :
77 : : struct raspberrypi_clk *rpi;
78 : : };
79 : :
80 : : /*
81 : : * Structure of the message passed to Raspberry Pi's firmware in order to
82 : : * change clock rates. The 'disable_turbo' option is only available to the ARM
83 : : * clock (pllb) which we enable by default as turbo mode will alter multiple
84 : : * clocks at once.
85 : : *
86 : : * Even though we're able to access the clock registers directly we're bound to
87 : : * use the firmware interface as the firmware ultimately takes care of
88 : : * mitigating overheating/undervoltage situations and we would be changing
89 : : * frequencies behind his back.
90 : : *
91 : : * For more information on the firmware interface check:
92 : : * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
93 : : */
94 : : struct raspberrypi_firmware_prop {
95 : : __le32 id;
96 : : __le32 val;
97 : : __le32 disable_turbo;
98 : : } __packed;
99 : :
100 : 45964 : static int raspberrypi_clock_property(struct rpi_firmware *firmware,
101 : : const struct raspberrypi_clk_data *data,
102 : : u32 tag, u32 *val)
103 : : {
104 : 91928 : struct raspberrypi_firmware_prop msg = {
105 : 45964 : .id = cpu_to_le32(data->id),
106 : 45964 : .val = cpu_to_le32(*val),
107 : : .disable_turbo = cpu_to_le32(0),
108 : : };
109 : : int ret;
110 : :
111 : 45964 : ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg));
112 [ + - ]: 45962 : if (ret)
113 : : return ret;
114 : :
115 : 45962 : *val = le32_to_cpu(msg.val);
116 : :
117 : 45962 : return 0;
118 : : }
119 : :
120 : 828 : static int raspberrypi_fw_is_prepared(struct clk_hw *hw)
121 : : {
122 : : struct raspberrypi_clk_data *data =
123 : : container_of(hw, struct raspberrypi_clk_data, hw);
124 : 828 : struct raspberrypi_clk *rpi = data->rpi;
125 : 828 : u32 val = 0;
126 : : int ret;
127 : :
128 : 828 : ret = raspberrypi_clock_property(rpi->firmware, data,
129 : : RPI_FIRMWARE_GET_CLOCK_STATE, &val);
130 [ + - ]: 828 : if (ret)
131 : : return 0;
132 : :
133 : 828 : return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
134 : : }
135 : :
136 : :
137 : 29469 : static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw,
138 : : unsigned long parent_rate)
139 : : {
140 : : struct raspberrypi_clk_data *data =
141 : : container_of(hw, struct raspberrypi_clk_data, hw);
142 : 29469 : struct raspberrypi_clk *rpi = data->rpi;
143 : 29469 : u32 val = 0;
144 : : int ret;
145 : :
146 : 29469 : ret = raspberrypi_clock_property(rpi->firmware, data,
147 : : RPI_FIRMWARE_GET_CLOCK_RATE, &val);
148 [ - + ]: 29469 : if (ret)
149 : 0 : return ret;
150 : :
151 : 29469 : return val;
152 : : }
153 : :
154 : 14011 : static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate,
155 : : unsigned long parent_rate)
156 : : {
157 : : struct raspberrypi_clk_data *data =
158 : : container_of(hw, struct raspberrypi_clk_data, hw);
159 : 14011 : struct raspberrypi_clk *rpi = data->rpi;
160 : 14011 : u32 _rate = rate;
161 : : int ret;
162 : :
163 : 14011 : ret = raspberrypi_clock_property(rpi->firmware, data,
164 : : RPI_FIRMWARE_SET_CLOCK_RATE, &_rate);
165 [ - + ]: 14009 : if (ret)
166 [ # # ]: 0 : dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
167 : : clk_hw_get_name(hw), ret);
168 : :
169 : 14009 : return ret;
170 : : }
171 : :
172 : 42447 : static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw,
173 : : struct clk_rate_request *req)
174 : : {
175 : : /*
176 : : * The firmware will do the rounding but that isn't part of
177 : : * the interface with the firmware, so we just do our best
178 : : * here.
179 : : */
180 : 42447 : req->rate = clamp(req->rate, req->min_rate, req->max_rate);
181 : 42447 : return 0;
182 : : }
183 : :
184 : : static const struct clk_ops raspberrypi_firmware_clk_ops = {
185 : : .is_prepared = raspberrypi_fw_is_prepared,
186 : : .recalc_rate = raspberrypi_fw_get_rate,
187 : : .determine_rate = raspberrypi_fw_dumb_determine_rate,
188 : : .set_rate = raspberrypi_fw_set_rate,
189 : : };
190 : :
191 : 828 : static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
192 : : unsigned int parent,
193 : : unsigned int id)
194 : : {
195 : : struct raspberrypi_clk_data *data;
196 : 828 : struct clk_init_data init = {};
197 : : u32 min_rate, max_rate;
198 : : int ret;
199 : :
200 : 828 : data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
201 [ + - ]: 828 : if (!data)
202 : : return ERR_PTR(-ENOMEM);
203 : 828 : data->rpi = rpi;
204 : 828 : data->id = id;
205 : :
206 : 828 : init.name = devm_kasprintf(rpi->dev, GFP_KERNEL,
207 : : "fw-clk-%s",
208 : : rpi_firmware_clk_names[id]);
209 : 828 : init.ops = &raspberrypi_firmware_clk_ops;
210 : 828 : init.flags = CLK_GET_RATE_NOCACHE;
211 : :
212 : 828 : data->hw.init = &init;
213 : :
214 : 828 : ret = raspberrypi_clock_property(rpi->firmware, data,
215 : : RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
216 : : &min_rate);
217 [ - + ]: 828 : if (ret) {
218 : 0 : dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
219 : : init.name, ret);
220 : 0 : return ERR_PTR(ret);
221 : : }
222 : :
223 : 828 : ret = raspberrypi_clock_property(rpi->firmware, data,
224 : : RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
225 : : &max_rate);
226 [ - + ]: 828 : if (ret) {
227 : 0 : dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
228 : : init.name, ret);
229 : 0 : return ERR_PTR(ret);
230 : : }
231 : :
232 : 828 : ret = devm_clk_hw_register(rpi->dev, &data->hw);
233 [ - + ]: 828 : if (ret)
234 : 0 : return ERR_PTR(ret);
235 : :
236 : 828 : clk_hw_set_rate_range(&data->hw, min_rate, max_rate);
237 : :
238 [ + + ]: 828 : if (id == RPI_FIRMWARE_ARM_CLK_ID) {
239 : 207 : ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw,
240 : : NULL, "cpu0");
241 [ - + ]: 207 : if (ret) {
242 : 0 : dev_err(rpi->dev, "Failed to initialize clkdev\n");
243 : 0 : return ERR_PTR(ret);
244 : : }
245 : : }
246 : :
247 : 828 : return &data->hw;
248 : : }
249 : :
250 : 207 : static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
251 : : struct clk_hw_onecell_data *data)
252 : : {
253 : : struct rpi_firmware_get_clocks_response *clks;
254 : : int ret;
255 : :
256 : 207 : clks = devm_kcalloc(rpi->dev,
257 : : sizeof(*clks), RPI_FIRMWARE_NUM_CLK_ID,
258 : : GFP_KERNEL);
259 [ + - ]: 207 : if (!clks)
260 : : return -ENOMEM;
261 : :
262 : 207 : ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS,
263 : : clks,
264 : : sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID);
265 [ + - ]: 207 : if (ret)
266 : : return ret;
267 : :
268 [ + + ]: 3105 : while (clks->id) {
269 : : struct clk_hw *hw;
270 : :
271 [ + + ]: 2898 : switch (clks->id) {
272 : : case RPI_FIRMWARE_ARM_CLK_ID:
273 : : case RPI_FIRMWARE_CORE_CLK_ID:
274 : : case RPI_FIRMWARE_M2MC_CLK_ID:
275 : : case RPI_FIRMWARE_V3D_CLK_ID:
276 : 828 : hw = raspberrypi_clk_register(rpi, clks->parent,
277 : : clks->id);
278 [ - + ]: 828 : if (IS_ERR(hw))
279 : 0 : return PTR_ERR(hw);
280 : :
281 : 828 : data->hws[clks->id] = hw;
282 : 828 : data->num = clks->id + 1;
283 : : fallthrough;
284 : :
285 : : default:
286 : 2898 : clks++;
287 : 2898 : break;
288 : : }
289 : : }
290 : :
291 : : return 0;
292 : : }
293 : :
294 : 207 : static int raspberrypi_clk_probe(struct platform_device *pdev)
295 : : {
296 : : struct clk_hw_onecell_data *clk_data;
297 : : struct device_node *firmware_node;
298 : 207 : struct device *dev = &pdev->dev;
299 : : struct rpi_firmware *firmware;
300 : : struct raspberrypi_clk *rpi;
301 : : int ret;
302 : :
303 : : /*
304 : : * We can be probed either through the an old-fashioned
305 : : * platform device registration or through a DT node that is a
306 : : * child of the firmware node. Handle both cases.
307 : : */
308 [ + - ]: 207 : if (dev->of_node)
309 : 207 : firmware_node = of_get_parent(dev->of_node);
310 : : else
311 : 0 : firmware_node = of_find_compatible_node(NULL, NULL,
312 : : "raspberrypi,bcm2835-firmware");
313 [ - + ]: 207 : if (!firmware_node) {
314 : 0 : dev_err(dev, "Missing firmware node\n");
315 : 0 : return -ENOENT;
316 : : }
317 : :
318 : 207 : firmware = rpi_firmware_get(firmware_node);
319 : 207 : of_node_put(firmware_node);
320 [ + - ]: 207 : if (!firmware)
321 : : return -EPROBE_DEFER;
322 : :
323 : : rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL);
324 [ + - ]: 207 : if (!rpi)
325 : : return -ENOMEM;
326 : :
327 : 207 : rpi->dev = dev;
328 : 207 : rpi->firmware = firmware;
329 : : platform_set_drvdata(pdev, rpi);
330 : :
331 : 207 : clk_data = devm_kzalloc(dev, struct_size(clk_data, hws,
332 : : RPI_FIRMWARE_NUM_CLK_ID),
333 : : GFP_KERNEL);
334 [ + - ]: 207 : if (!clk_data)
335 : : return -ENOMEM;
336 : :
337 : 207 : ret = raspberrypi_discover_clocks(rpi, clk_data);
338 [ + - ]: 207 : if (ret)
339 : : return ret;
340 : :
341 : 207 : ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
342 : : clk_data);
343 [ + - ]: 207 : if (ret)
344 : : return ret;
345 : :
346 : 207 : rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq",
347 : : -1, NULL, 0);
348 : :
349 : 207 : return 0;
350 : : }
351 : :
352 : 0 : static int raspberrypi_clk_remove(struct platform_device *pdev)
353 : : {
354 : : struct raspberrypi_clk *rpi = platform_get_drvdata(pdev);
355 : :
356 : 0 : platform_device_unregister(rpi->cpufreq);
357 : :
358 : 0 : return 0;
359 : : }
360 : :
361 : : static const struct of_device_id raspberrypi_clk_match[] = {
362 : : { .compatible = "raspberrypi,firmware-clocks" },
363 : : { },
364 : : };
365 : : MODULE_DEVICE_TABLE(of, raspberrypi_clk_match);
366 : :
367 : : static struct platform_driver raspberrypi_clk_driver = {
368 : : .driver = {
369 : : .name = "raspberrypi-clk",
370 : : .of_match_table = raspberrypi_clk_match,
371 : : },
372 : : .probe = raspberrypi_clk_probe,
373 : : .remove = raspberrypi_clk_remove,
374 : : };
375 : 207 : module_platform_driver(raspberrypi_clk_driver);
376 : :
377 : : MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
378 : : MODULE_DESCRIPTION("Raspberry Pi firmware clock driver");
379 : : MODULE_LICENSE("GPL");
380 : : MODULE_ALIAS("platform:raspberrypi-clk");
|