53428fcbda0db34db2e4a557043bee1bb1a9877d
[openwrt/staging/lynxis.git] /
1 From 35306211392a8c17ba6cde2a6b5d1beb61ca7d54 Mon Sep 17 00:00:00 2001
2 From: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
3 Date: Wed, 12 Jun 2019 20:24:54 +0200
4 Subject: [PATCH] clk: bcm283x: add driver interfacing with Raspberry
5 Pi's firmware
6
7 Commit 4e85e535e6cc6e8a96350e8ee684d0f22eb8629e upstream.
8
9 Raspberry Pi's firmware offers an interface though which update it's
10 clock's frequencies. This is specially useful in order to change the CPU
11 clock (pllb_arm) which is 'owned' by the firmware and we're unable to
12 scale using the register interface provided by clk-bcm2835.
13
14 Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
15 Acked-by: Eric Anholt <eric@anholt.net>
16 Signed-off-by: Stephen Boyd <sboyd@kernel.org>
17 ---
18 drivers/clk/bcm/Kconfig | 7 +
19 drivers/clk/bcm/Makefile | 1 +
20 drivers/clk/bcm/clk-raspberrypi.c | 300 ++++++++++++++++++++++++++++++
21 3 files changed, 308 insertions(+)
22 create mode 100644 drivers/clk/bcm/clk-raspberrypi.c
23
24 --- a/drivers/clk/bcm/Kconfig
25 +++ b/drivers/clk/bcm/Kconfig
26 @@ -63,3 +63,10 @@ config CLK_BCM_SR
27 default ARCH_BCM_IPROC
28 help
29 Enable common clock framework support for the Broadcom Stingray SoC
30 +
31 +config CLK_RASPBERRYPI
32 + tristate "Raspberry Pi firmware based clock support"
33 + depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
34 + help
35 + Enable common clock framework support for Raspberry Pi's firmware
36 + dependent clocks
37 --- a/drivers/clk/bcm/Makefile
38 +++ b/drivers/clk/bcm/Makefile
39 @@ -7,6 +7,7 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm216
40 obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o
41 obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
42 obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835-aux.o
43 +obj-$(CONFIG_CLK_RASPBERRYPI) += clk-raspberrypi.o
44 obj-$(CONFIG_ARCH_BCM_53573) += clk-bcm53573-ilp.o
45 obj-$(CONFIG_CLK_BCM_CYGNUS) += clk-cygnus.o
46 obj-$(CONFIG_CLK_BCM_HR2) += clk-hr2.o
47 --- /dev/null
48 +++ b/drivers/clk/bcm/clk-raspberrypi.c
49 @@ -0,0 +1,300 @@
50 +// SPDX-License-Identifier: GPL-2.0+
51 +/*
52 + * Raspberry Pi driver for firmware controlled clocks
53 + *
54 + * Even though clk-bcm2835 provides an interface to the hardware registers for
55 + * the system clocks we've had to factor out 'pllb' as the firmware 'owns' it.
56 + * We're not allowed to change it directly as we might race with the
57 + * over-temperature and under-voltage protections provided by the firmware.
58 + *
59 + * Copyright (C) 2019 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
60 + */
61 +
62 +#include <linux/clkdev.h>
63 +#include <linux/clk-provider.h>
64 +#include <linux/io.h>
65 +#include <linux/module.h>
66 +#include <linux/platform_device.h>
67 +
68 +#include <soc/bcm2835/raspberrypi-firmware.h>
69 +
70 +#define RPI_FIRMWARE_ARM_CLK_ID 0x00000003
71 +
72 +#define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
73 +#define RPI_FIRMWARE_STATE_WAIT_BIT BIT(1)
74 +
75 +/*
76 + * Even though the firmware interface alters 'pllb' the frequencies are
77 + * provided as per 'pllb_arm'. We need to scale before passing them trough.
78 + */
79 +#define RPI_FIRMWARE_PLLB_ARM_DIV_RATE 2
80 +
81 +#define A2W_PLL_FRAC_BITS 20
82 +
83 +struct raspberrypi_clk {
84 + struct device *dev;
85 + struct rpi_firmware *firmware;
86 +
87 + unsigned long min_rate;
88 + unsigned long max_rate;
89 +
90 + struct clk_hw pllb;
91 + struct clk_hw *pllb_arm;
92 + struct clk_lookup *pllb_arm_lookup;
93 +};
94 +
95 +/*
96 + * Structure of the message passed to Raspberry Pi's firmware in order to
97 + * change clock rates. The 'disable_turbo' option is only available to the ARM
98 + * clock (pllb) which we enable by default as turbo mode will alter multiple
99 + * clocks at once.
100 + *
101 + * Even though we're able to access the clock registers directly we're bound to
102 + * use the firmware interface as the firmware ultimately takes care of
103 + * mitigating overheating/undervoltage situations and we would be changing
104 + * frequencies behind his back.
105 + *
106 + * For more information on the firmware interface check:
107 + * https://github.com/raspberrypi/firmware/wiki/Mailbox-property-interface
108 + */
109 +struct raspberrypi_firmware_prop {
110 + __le32 id;
111 + __le32 val;
112 + __le32 disable_turbo;
113 +} __packed;
114 +
115 +static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag,
116 + u32 clk, u32 *val)
117 +{
118 + struct raspberrypi_firmware_prop msg = {
119 + .id = cpu_to_le32(clk),
120 + .val = cpu_to_le32(*val),
121 + .disable_turbo = cpu_to_le32(1),
122 + };
123 + int ret;
124 +
125 + ret = rpi_firmware_property(firmware, tag, &msg, sizeof(msg));
126 + if (ret)
127 + return ret;
128 +
129 + *val = le32_to_cpu(msg.val);
130 +
131 + return 0;
132 +}
133 +
134 +static int raspberrypi_fw_pll_is_on(struct clk_hw *hw)
135 +{
136 + struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
137 + pllb);
138 + u32 val = 0;
139 + int ret;
140 +
141 + ret = raspberrypi_clock_property(rpi->firmware,
142 + RPI_FIRMWARE_GET_CLOCK_STATE,
143 + RPI_FIRMWARE_ARM_CLK_ID, &val);
144 + if (ret)
145 + return 0;
146 +
147 + return !!(val & RPI_FIRMWARE_STATE_ENABLE_BIT);
148 +}
149 +
150 +
151 +static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw,
152 + unsigned long parent_rate)
153 +{
154 + struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
155 + pllb);
156 + u32 val = 0;
157 + int ret;
158 +
159 + ret = raspberrypi_clock_property(rpi->firmware,
160 + RPI_FIRMWARE_GET_CLOCK_RATE,
161 + RPI_FIRMWARE_ARM_CLK_ID,
162 + &val);
163 + if (ret)
164 + return ret;
165 +
166 + return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
167 +}
168 +
169 +static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate,
170 + unsigned long parent_rate)
171 +{
172 + struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
173 + pllb);
174 + u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
175 + int ret;
176 +
177 + ret = raspberrypi_clock_property(rpi->firmware,
178 + RPI_FIRMWARE_SET_CLOCK_RATE,
179 + RPI_FIRMWARE_ARM_CLK_ID,
180 + &new_rate);
181 + if (ret)
182 + dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d",
183 + clk_hw_get_name(hw), ret);
184 +
185 + return ret;
186 +}
187 +
188 +/*
189 + * Sadly there is no firmware rate rounding interface. We borrowed it from
190 + * clk-bcm2835.
191 + */
192 +static int raspberrypi_pll_determine_rate(struct clk_hw *hw,
193 + struct clk_rate_request *req)
194 +{
195 + struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk,
196 + pllb);
197 + u64 div, final_rate;
198 + u32 ndiv, fdiv;
199 +
200 + /* We can't use req->rate directly as it would overflow */
201 + final_rate = clamp(req->rate, rpi->min_rate, rpi->max_rate);
202 +
203 + div = (u64)final_rate << A2W_PLL_FRAC_BITS;
204 + do_div(div, req->best_parent_rate);
205 +
206 + ndiv = div >> A2W_PLL_FRAC_BITS;
207 + fdiv = div & ((1 << A2W_PLL_FRAC_BITS) - 1);
208 +
209 + final_rate = ((u64)req->best_parent_rate *
210 + ((ndiv << A2W_PLL_FRAC_BITS) + fdiv));
211 +
212 + req->rate = final_rate >> A2W_PLL_FRAC_BITS;
213 +
214 + return 0;
215 +}
216 +
217 +static const struct clk_ops raspberrypi_firmware_pll_clk_ops = {
218 + .is_prepared = raspberrypi_fw_pll_is_on,
219 + .recalc_rate = raspberrypi_fw_pll_get_rate,
220 + .set_rate = raspberrypi_fw_pll_set_rate,
221 + .determine_rate = raspberrypi_pll_determine_rate,
222 +};
223 +
224 +static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi)
225 +{
226 + u32 min_rate = 0, max_rate = 0;
227 + struct clk_init_data init;
228 + int ret;
229 +
230 + memset(&init, 0, sizeof(init));
231 +
232 + /* All of the PLLs derive from the external oscillator. */
233 + init.parent_names = (const char *[]){ "osc" };
234 + init.num_parents = 1;
235 + init.name = "pllb";
236 + init.ops = &raspberrypi_firmware_pll_clk_ops;
237 + init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED;
238 +
239 + /* Get min & max rates set by the firmware */
240 + ret = raspberrypi_clock_property(rpi->firmware,
241 + RPI_FIRMWARE_GET_MIN_CLOCK_RATE,
242 + RPI_FIRMWARE_ARM_CLK_ID,
243 + &min_rate);
244 + if (ret) {
245 + dev_err(rpi->dev, "Failed to get %s min freq: %d\n",
246 + init.name, ret);
247 + return ret;
248 + }
249 +
250 + ret = raspberrypi_clock_property(rpi->firmware,
251 + RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
252 + RPI_FIRMWARE_ARM_CLK_ID,
253 + &max_rate);
254 + if (ret) {
255 + dev_err(rpi->dev, "Failed to get %s max freq: %d\n",
256 + init.name, ret);
257 + return ret;
258 + }
259 +
260 + if (!min_rate || !max_rate) {
261 + dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n",
262 + min_rate, max_rate);
263 + return -EINVAL;
264 + }
265 +
266 + dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n",
267 + min_rate, max_rate);
268 +
269 + rpi->min_rate = min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
270 + rpi->max_rate = max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE;
271 +
272 + rpi->pllb.init = &init;
273 +
274 + return devm_clk_hw_register(rpi->dev, &rpi->pllb);
275 +}
276 +
277 +static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi)
278 +{
279 + rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev,
280 + "pllb_arm", "pllb",
281 + CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE,
282 + 1, 2);
283 + if (IS_ERR(rpi->pllb_arm)) {
284 + dev_err(rpi->dev, "Failed to initialize pllb_arm\n");
285 + return PTR_ERR(rpi->pllb_arm);
286 + }
287 +
288 + rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0");
289 + if (!rpi->pllb_arm_lookup) {
290 + dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n");
291 + clk_hw_unregister_fixed_factor(rpi->pllb_arm);
292 + return -ENOMEM;
293 + }
294 +
295 + return 0;
296 +}
297 +
298 +static int raspberrypi_clk_probe(struct platform_device *pdev)
299 +{
300 + struct device_node *firmware_node;
301 + struct device *dev = &pdev->dev;
302 + struct rpi_firmware *firmware;
303 + struct raspberrypi_clk *rpi;
304 + int ret;
305 +
306 + firmware_node = of_find_compatible_node(NULL, NULL,
307 + "raspberrypi,bcm2835-firmware");
308 + if (!firmware_node) {
309 + dev_err(dev, "Missing firmware node\n");
310 + return -ENOENT;
311 + }
312 +
313 + firmware = rpi_firmware_get(firmware_node);
314 + of_node_put(firmware_node);
315 + if (!firmware)
316 + return -EPROBE_DEFER;
317 +
318 + rpi = devm_kzalloc(dev, sizeof(*rpi), GFP_KERNEL);
319 + if (!rpi)
320 + return -ENOMEM;
321 +
322 + rpi->dev = dev;
323 + rpi->firmware = firmware;
324 +
325 + ret = raspberrypi_register_pllb(rpi);
326 + if (ret) {
327 + dev_err(dev, "Failed to initialize pllb, %d\n", ret);
328 + return ret;
329 + }
330 +
331 + ret = raspberrypi_register_pllb_arm(rpi);
332 + if (ret)
333 + return ret;
334 +
335 + return 0;
336 +}
337 +
338 +static struct platform_driver raspberrypi_clk_driver = {
339 + .driver = {
340 + .name = "raspberrypi-clk",
341 + },
342 + .probe = raspberrypi_clk_probe,
343 +};
344 +module_platform_driver(raspberrypi_clk_driver);
345 +
346 +MODULE_AUTHOR("Nicolas Saenz Julienne <nsaenzjulienne@suse.de>");
347 +MODULE_DESCRIPTION("Raspberry Pi firmware clock driver");
348 +MODULE_LICENSE("GPL");
349 +MODULE_ALIAS("platform:raspberrypi-clk");