53428fcbda0db34db2e4a557043bee1bb1a9877d
[oweals/openwrt.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");