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 | static char *rpi_firmware_clk_names[] = { |
22 | [RPI_FIRMWARE_EMMC_CLK_ID] = "emmc" , |
23 | [RPI_FIRMWARE_UART_CLK_ID] = "uart" , |
24 | [RPI_FIRMWARE_ARM_CLK_ID] = "arm" , |
25 | [RPI_FIRMWARE_CORE_CLK_ID] = "core" , |
26 | [RPI_FIRMWARE_V3D_CLK_ID] = "v3d" , |
27 | [RPI_FIRMWARE_H264_CLK_ID] = "h264" , |
28 | [RPI_FIRMWARE_ISP_CLK_ID] = "isp" , |
29 | [RPI_FIRMWARE_SDRAM_CLK_ID] = "sdram" , |
30 | [RPI_FIRMWARE_PIXEL_CLK_ID] = "pixel" , |
31 | [RPI_FIRMWARE_PWM_CLK_ID] = "pwm" , |
32 | [RPI_FIRMWARE_HEVC_CLK_ID] = "hevc" , |
33 | [RPI_FIRMWARE_EMMC2_CLK_ID] = "emmc2" , |
34 | [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc" , |
35 | [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb" , |
36 | [RPI_FIRMWARE_VEC_CLK_ID] = "vec" , |
37 | }; |
38 | |
39 | #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0) |
40 | #define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1) |
41 | |
42 | struct raspberrypi_clk_variant; |
43 | |
44 | struct raspberrypi_clk { |
45 | struct device *dev; |
46 | struct rpi_firmware *firmware; |
47 | struct platform_device *cpufreq; |
48 | }; |
49 | |
50 | struct raspberrypi_clk_data { |
51 | struct clk_hw hw; |
52 | |
53 | unsigned int id; |
54 | struct raspberrypi_clk_variant *variant; |
55 | |
56 | struct raspberrypi_clk *rpi; |
57 | }; |
58 | |
59 | struct raspberrypi_clk_variant { |
60 | bool export; |
61 | char *clkdev; |
62 | unsigned long min_rate; |
63 | bool minimize; |
64 | }; |
65 | |
66 | static struct raspberrypi_clk_variant |
67 | raspberrypi_clk_variants[RPI_FIRMWARE_NUM_CLK_ID] = { |
68 | [RPI_FIRMWARE_ARM_CLK_ID] = { |
69 | .export = true, |
70 | .clkdev = "cpu0" , |
71 | }, |
72 | [RPI_FIRMWARE_CORE_CLK_ID] = { |
73 | .export = true, |
74 | |
75 | /* |
76 | * The clock is shared between the HVS and the CSI |
77 | * controllers, on the BCM2711 and will change depending |
78 | * on the pixels composited on the HVS and the capture |
79 | * resolution on Unicam. |
80 | * |
81 | * Since the rate can get quite large, and we need to |
82 | * coordinate between both driver instances, let's |
83 | * always use the minimum the drivers will let us. |
84 | */ |
85 | .minimize = true, |
86 | }, |
87 | [RPI_FIRMWARE_M2MC_CLK_ID] = { |
88 | .export = true, |
89 | |
90 | /* |
91 | * If we boot without any cable connected to any of the |
92 | * HDMI connector, the firmware will skip the HSM |
93 | * initialization and leave it with a rate of 0, |
94 | * resulting in a bus lockup when we're accessing the |
95 | * registers even if it's enabled. |
96 | * |
97 | * Let's put a sensible default so that we don't end up |
98 | * in this situation. |
99 | */ |
100 | .min_rate = 120000000, |
101 | |
102 | /* |
103 | * The clock is shared between the two HDMI controllers |
104 | * on the BCM2711 and will change depending on the |
105 | * resolution output on each. Since the rate can get |
106 | * quite large, and we need to coordinate between both |
107 | * driver instances, let's always use the minimum the |
108 | * drivers will let us. |
109 | */ |
110 | .minimize = true, |
111 | }, |
112 | [RPI_FIRMWARE_V3D_CLK_ID] = { |
113 | .export = true, |
114 | }, |
115 | [RPI_FIRMWARE_PIXEL_CLK_ID] = { |
116 | .export = true, |
117 | }, |
118 | [RPI_FIRMWARE_HEVC_CLK_ID] = { |
119 | .export = true, |
120 | }, |
121 | [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = { |
122 | .export = true, |
123 | }, |
124 | [RPI_FIRMWARE_VEC_CLK_ID] = { |
125 | .export = true, |
126 | }, |
127 | }; |
128 | |
129 | /* |
130 | * Structure of the message passed to Raspberry Pi's firmware in order to |
131 | * change clock rates. The 'disable_turbo' option is only available to the ARM |
132 | * clock (pllb) which we enable by default as turbo mode will alter multiple |
133 | * clocks at once. |
134 | * |
135 | * Even though we're able to access the clock registers directly we're bound to |
136 | * use the firmware interface as the firmware ultimately takes care of |
137 | * mitigating overheating/undervoltage situations and we would be changing |
138 | * frequencies behind his back. |
139 | * |
140 | * For more information on the firmware interface check: |
141 | * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface |
142 | */ |
143 | struct raspberrypi_firmware_prop { |
144 | __le32 id; |
145 | __le32 val; |
146 | __le32 disable_turbo; |
147 | } __packed; |
148 | |
149 | static int raspberrypi_clock_property(struct rpi_firmware *firmware, |
150 | const struct raspberrypi_clk_data *data, |
151 | u32 tag, u32 *val) |
152 | { |
153 | struct raspberrypi_firmware_prop msg = { |
154 | .id = cpu_to_le32(data->id), |
155 | .val = cpu_to_le32(*val), |
156 | .disable_turbo = cpu_to_le32(1), |
157 | }; |
158 | int ret; |
159 | |
160 | ret = rpi_firmware_property(fw: firmware, tag, data: &msg, len: sizeof(msg)); |
161 | if (ret) |
162 | return ret; |
163 | |
164 | *val = le32_to_cpu(msg.val); |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | static int raspberrypi_fw_is_prepared(struct clk_hw *hw) |
170 | { |
171 | struct raspberrypi_clk_data *data = |
172 | container_of(hw, struct raspberrypi_clk_data, hw); |
173 | struct raspberrypi_clk *rpi = data->rpi; |
174 | u32 val = 0; |
175 | int ret; |
176 | |
177 | ret = raspberrypi_clock_property(firmware: rpi->firmware, data, |
178 | tag: RPI_FIRMWARE_GET_CLOCK_STATE, val: &val); |
179 | if (ret) |
180 | return 0; |
181 | |
182 | return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT); |
183 | } |
184 | |
185 | |
186 | static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, |
187 | unsigned long parent_rate) |
188 | { |
189 | struct raspberrypi_clk_data *data = |
190 | container_of(hw, struct raspberrypi_clk_data, hw); |
191 | struct raspberrypi_clk *rpi = data->rpi; |
192 | u32 val = 0; |
193 | int ret; |
194 | |
195 | ret = raspberrypi_clock_property(firmware: rpi->firmware, data, |
196 | tag: RPI_FIRMWARE_GET_CLOCK_RATE, val: &val); |
197 | if (ret) |
198 | return 0; |
199 | |
200 | return val; |
201 | } |
202 | |
203 | static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, |
204 | unsigned long parent_rate) |
205 | { |
206 | struct raspberrypi_clk_data *data = |
207 | container_of(hw, struct raspberrypi_clk_data, hw); |
208 | struct raspberrypi_clk *rpi = data->rpi; |
209 | u32 _rate = rate; |
210 | int ret; |
211 | |
212 | ret = raspberrypi_clock_property(firmware: rpi->firmware, data, |
213 | tag: RPI_FIRMWARE_SET_CLOCK_RATE, val: &_rate); |
214 | if (ret) |
215 | dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d\n" , |
216 | clk_hw_get_name(hw), ret); |
217 | |
218 | return ret; |
219 | } |
220 | |
221 | static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, |
222 | struct clk_rate_request *req) |
223 | { |
224 | struct raspberrypi_clk_data *data = |
225 | container_of(hw, struct raspberrypi_clk_data, hw); |
226 | struct raspberrypi_clk_variant *variant = data->variant; |
227 | |
228 | /* |
229 | * The firmware will do the rounding but that isn't part of |
230 | * the interface with the firmware, so we just do our best |
231 | * here. |
232 | */ |
233 | |
234 | req->rate = clamp(req->rate, req->min_rate, req->max_rate); |
235 | |
236 | /* |
237 | * We want to aggressively reduce the clock rate here, so let's |
238 | * just ignore the requested rate and return the bare minimum |
239 | * rate we can get away with. |
240 | */ |
241 | if (variant->minimize && req->min_rate > 0) |
242 | req->rate = req->min_rate; |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static const struct clk_ops raspberrypi_firmware_clk_ops = { |
248 | .is_prepared = raspberrypi_fw_is_prepared, |
249 | .recalc_rate = raspberrypi_fw_get_rate, |
250 | .determine_rate = raspberrypi_fw_dumb_determine_rate, |
251 | .set_rate = raspberrypi_fw_set_rate, |
252 | }; |
253 | |
254 | static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi, |
255 | unsigned int parent, |
256 | unsigned int id, |
257 | struct raspberrypi_clk_variant *variant) |
258 | { |
259 | struct raspberrypi_clk_data *data; |
260 | struct clk_init_data init = {}; |
261 | u32 min_rate, max_rate; |
262 | int ret; |
263 | |
264 | data = devm_kzalloc(dev: rpi->dev, size: sizeof(*data), GFP_KERNEL); |
265 | if (!data) |
266 | return ERR_PTR(error: -ENOMEM); |
267 | data->rpi = rpi; |
268 | data->id = id; |
269 | data->variant = variant; |
270 | |
271 | init.name = devm_kasprintf(dev: rpi->dev, GFP_KERNEL, |
272 | fmt: "fw-clk-%s" , |
273 | rpi_firmware_clk_names[id]); |
274 | init.ops = &raspberrypi_firmware_clk_ops; |
275 | init.flags = CLK_GET_RATE_NOCACHE; |
276 | |
277 | data->hw.init = &init; |
278 | |
279 | ret = raspberrypi_clock_property(firmware: rpi->firmware, data, |
280 | tag: RPI_FIRMWARE_GET_MIN_CLOCK_RATE, |
281 | val: &min_rate); |
282 | if (ret) { |
283 | dev_err(rpi->dev, "Failed to get clock %d min freq: %d\n" , |
284 | id, ret); |
285 | return ERR_PTR(error: ret); |
286 | } |
287 | |
288 | ret = raspberrypi_clock_property(firmware: rpi->firmware, data, |
289 | tag: RPI_FIRMWARE_GET_MAX_CLOCK_RATE, |
290 | val: &max_rate); |
291 | if (ret) { |
292 | dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n" , |
293 | id, ret); |
294 | return ERR_PTR(error: ret); |
295 | } |
296 | |
297 | ret = devm_clk_hw_register(dev: rpi->dev, hw: &data->hw); |
298 | if (ret) |
299 | return ERR_PTR(error: ret); |
300 | |
301 | clk_hw_set_rate_range(hw: &data->hw, min_rate, max_rate); |
302 | |
303 | if (variant->clkdev) { |
304 | ret = devm_clk_hw_register_clkdev(dev: rpi->dev, hw: &data->hw, |
305 | NULL, dev_id: variant->clkdev); |
306 | if (ret) { |
307 | dev_err(rpi->dev, "Failed to initialize clkdev\n" ); |
308 | return ERR_PTR(error: ret); |
309 | } |
310 | } |
311 | |
312 | if (variant->min_rate) { |
313 | unsigned long rate; |
314 | |
315 | clk_hw_set_rate_range(hw: &data->hw, min_rate: variant->min_rate, max_rate); |
316 | |
317 | rate = raspberrypi_fw_get_rate(hw: &data->hw, parent_rate: 0); |
318 | if (rate < variant->min_rate) { |
319 | ret = raspberrypi_fw_set_rate(hw: &data->hw, rate: variant->min_rate, parent_rate: 0); |
320 | if (ret) |
321 | return ERR_PTR(error: ret); |
322 | } |
323 | } |
324 | |
325 | return &data->hw; |
326 | } |
327 | |
328 | struct rpi_firmware_get_clocks_response { |
329 | u32 parent; |
330 | u32 id; |
331 | }; |
332 | |
333 | static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi, |
334 | struct clk_hw_onecell_data *data) |
335 | { |
336 | struct rpi_firmware_get_clocks_response *clks; |
337 | int ret; |
338 | |
339 | /* |
340 | * The firmware doesn't guarantee that the last element of |
341 | * RPI_FIRMWARE_GET_CLOCKS is zeroed. So allocate an additional |
342 | * zero element as sentinel. |
343 | */ |
344 | clks = devm_kcalloc(dev: rpi->dev, |
345 | n: RPI_FIRMWARE_NUM_CLK_ID + 1, size: sizeof(*clks), |
346 | GFP_KERNEL); |
347 | if (!clks) |
348 | return -ENOMEM; |
349 | |
350 | ret = rpi_firmware_property(fw: rpi->firmware, tag: RPI_FIRMWARE_GET_CLOCKS, |
351 | data: clks, |
352 | len: sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); |
353 | if (ret) |
354 | return ret; |
355 | |
356 | while (clks->id) { |
357 | struct raspberrypi_clk_variant *variant; |
358 | |
359 | if (clks->id >= RPI_FIRMWARE_NUM_CLK_ID) { |
360 | dev_err(rpi->dev, "Unknown clock id: %u (max: %u)\n" , |
361 | clks->id, RPI_FIRMWARE_NUM_CLK_ID - 1); |
362 | return -EINVAL; |
363 | } |
364 | |
365 | variant = &raspberrypi_clk_variants[clks->id]; |
366 | if (variant->export) { |
367 | struct clk_hw *hw; |
368 | |
369 | hw = raspberrypi_clk_register(rpi, parent: clks->parent, |
370 | id: clks->id, variant); |
371 | if (IS_ERR(ptr: hw)) |
372 | return PTR_ERR(ptr: hw); |
373 | |
374 | data->hws[clks->id] = hw; |
375 | data->num = clks->id + 1; |
376 | } |
377 | |
378 | clks++; |
379 | } |
380 | |
381 | return 0; |
382 | } |
383 | |
384 | static int raspberrypi_clk_probe(struct platform_device *pdev) |
385 | { |
386 | struct clk_hw_onecell_data *clk_data; |
387 | struct device_node *firmware_node; |
388 | struct device *dev = &pdev->dev; |
389 | struct rpi_firmware *firmware; |
390 | struct raspberrypi_clk *rpi; |
391 | int ret; |
392 | |
393 | /* |
394 | * We can be probed either through the an old-fashioned |
395 | * platform device registration or through a DT node that is a |
396 | * child of the firmware node. Handle both cases. |
397 | */ |
398 | if (dev->of_node) |
399 | firmware_node = of_get_parent(node: dev->of_node); |
400 | else |
401 | firmware_node = of_find_compatible_node(NULL, NULL, |
402 | compat: "raspberrypi,bcm2835-firmware" ); |
403 | if (!firmware_node) { |
404 | dev_err(dev, "Missing firmware node\n" ); |
405 | return -ENOENT; |
406 | } |
407 | |
408 | firmware = devm_rpi_firmware_get(dev: &pdev->dev, firmware_node); |
409 | of_node_put(node: firmware_node); |
410 | if (!firmware) |
411 | return -EPROBE_DEFER; |
412 | |
413 | rpi = devm_kzalloc(dev, size: sizeof(*rpi), GFP_KERNEL); |
414 | if (!rpi) |
415 | return -ENOMEM; |
416 | |
417 | rpi->dev = dev; |
418 | rpi->firmware = firmware; |
419 | platform_set_drvdata(pdev, data: rpi); |
420 | |
421 | clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, |
422 | RPI_FIRMWARE_NUM_CLK_ID), |
423 | GFP_KERNEL); |
424 | if (!clk_data) |
425 | return -ENOMEM; |
426 | |
427 | ret = raspberrypi_discover_clocks(rpi, data: clk_data); |
428 | if (ret) |
429 | return ret; |
430 | |
431 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_onecell_get, |
432 | data: clk_data); |
433 | if (ret) |
434 | return ret; |
435 | |
436 | rpi->cpufreq = platform_device_register_data(parent: dev, name: "raspberrypi-cpufreq" , |
437 | id: -1, NULL, size: 0); |
438 | |
439 | return 0; |
440 | } |
441 | |
442 | static void raspberrypi_clk_remove(struct platform_device *pdev) |
443 | { |
444 | struct raspberrypi_clk *rpi = platform_get_drvdata(pdev); |
445 | |
446 | platform_device_unregister(rpi->cpufreq); |
447 | } |
448 | |
449 | static const struct of_device_id raspberrypi_clk_match[] = { |
450 | { .compatible = "raspberrypi,firmware-clocks" }, |
451 | { }, |
452 | }; |
453 | MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); |
454 | |
455 | static struct platform_driver raspberrypi_clk_driver = { |
456 | .driver = { |
457 | .name = "raspberrypi-clk" , |
458 | .of_match_table = raspberrypi_clk_match, |
459 | }, |
460 | .probe = raspberrypi_clk_probe, |
461 | .remove_new = raspberrypi_clk_remove, |
462 | }; |
463 | module_platform_driver(raspberrypi_clk_driver); |
464 | |
465 | MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>" ); |
466 | MODULE_DESCRIPTION("Raspberry Pi firmware clock driver" ); |
467 | MODULE_LICENSE("GPL" ); |
468 | MODULE_ALIAS("platform:raspberrypi-clk" ); |
469 | |