1 From 54276fe20c0735dd18d298891b71b664ea54962d Mon Sep 17 00:00:00 2001
2 From: Maxime Ripard <maxime@cerno.tech>
3 Date: Mon, 10 Feb 2020 14:06:09 +0100
4 Subject: [PATCH] clk: bcm: rpi: Discover the firmware clocks
6 The RaspberryPi4 firmware actually exposes more clocks than are currently
7 handled by the driver and we will need to change some of them directly
8 based on the pixel rate for the display related clocks, or the load for the
11 This rate change can have a number of side-effects, including adjusting the
12 various PLL voltages or the PLL parents. The firmware will also update
13 those clocks by itself for example if the SoC runs too hot.
15 In order to make Linux play as nice as possible with those constraints, it
16 makes sense to rely on the firmware clocks as much as possible.
18 Fortunately,t he firmware has an interface to discover the clocks it
21 Let's use it to discover, register the clocks in the clocks framework and
22 then expose them through the device tree for consumers to use them.
24 Cc: Michael Turquette <mturquette@baylibre.com>
25 Cc: Stephen Boyd <sboyd@kernel.org>
26 Cc: linux-clk@vger.kernel.org
27 Signed-off-by: Maxime Ripard <maxime@cerno.tech>
29 drivers/clk/bcm/clk-raspberrypi.c | 104 ++++++++++++++++++---
30 include/soc/bcm2835/raspberrypi-firmware.h | 5 +
31 2 files changed, 97 insertions(+), 12 deletions(-)
33 --- a/drivers/clk/bcm/clk-raspberrypi.c
34 +++ b/drivers/clk/bcm/clk-raspberrypi.c
35 @@ -285,6 +285,95 @@ static struct clk_hw *raspberrypi_regist
36 return &raspberrypi_clk_pllb_arm.hw;
39 +static long raspberrypi_fw_dumb_round_rate(struct clk_hw *hw,
41 + unsigned long *parent_rate)
44 + * The firmware will do the rounding but that isn't part of
45 + * the interface with the firmware, so we just do our best
51 +static const struct clk_ops raspberrypi_firmware_clk_ops = {
52 + .is_prepared = raspberrypi_fw_is_prepared,
53 + .recalc_rate = raspberrypi_fw_get_rate,
54 + .round_rate = raspberrypi_fw_dumb_round_rate,
55 + .set_rate = raspberrypi_fw_set_rate,
58 +static struct clk_hw *raspberrypi_clk_register(struct raspberrypi_clk *rpi,
59 + unsigned int parent,
62 + struct raspberrypi_clk_data *data;
63 + struct clk_init_data init = {};
66 + if (id == RPI_FIRMWARE_ARM_CLK_ID) {
69 + hw = raspberrypi_register_pllb(rpi);
71 + dev_err(rpi->dev, "Failed to initialize pllb, %ld\n",
76 + return raspberrypi_register_pllb_arm(rpi);
79 + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL);
81 + return ERR_PTR(-ENOMEM);
85 + init.name = devm_kasprintf(rpi->dev, GFP_KERNEL, "fw-clk-%u", id);
86 + init.ops = &raspberrypi_firmware_clk_ops;
87 + init.flags = CLK_GET_RATE_NOCACHE;
89 + data->hw.init = &init;
91 + ret = devm_clk_hw_register(rpi->dev, &data->hw);
93 + return ERR_PTR(ret);
98 +static int raspberrypi_discover_clocks(struct raspberrypi_clk *rpi,
99 + struct clk_hw_onecell_data *data)
101 + struct rpi_firmware_get_clocks_response *clks;
104 + clks = devm_kcalloc(rpi->dev, sizeof(*clks), NUM_FW_CLKS, GFP_KERNEL);
108 + ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS,
109 + clks, sizeof(*clks) * NUM_FW_CLKS);
116 + hw = raspberrypi_clk_register(rpi, clks->parent, clks->id);
118 + return PTR_ERR(hw);
120 + data->hws[clks->id] = hw;
121 + data->num = clks->id + 1;
128 static int raspberrypi_clk_probe(struct platform_device *pdev)
130 struct clk_hw_onecell_data *clk_data;
131 @@ -292,7 +381,6 @@ static int raspberrypi_clk_probe(struct
132 struct device *dev = &pdev->dev;
133 struct rpi_firmware *firmware;
134 struct raspberrypi_clk *rpi;
138 firmware_node = of_parse_phandle(dev->of_node, "raspberrypi,firmware", 0);
139 @@ -318,17 +406,9 @@ static int raspberrypi_clk_probe(struct
143 - hw = raspberrypi_register_pllb(rpi);
145 - dev_err(dev, "Failed to initialize pllb, %ld\n", PTR_ERR(hw));
146 - return PTR_ERR(hw);
149 - hw = raspberrypi_register_pllb_arm(rpi);
151 - return PTR_ERR(hw);
152 - clk_data->hws[RPI_FIRMWARE_ARM_CLK_ID] = hw;
153 - clk_data->num = RPI_FIRMWARE_ARM_CLK_ID + 1;
154 + ret = raspberrypi_discover_clocks(rpi, clk_data);
158 ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
160 --- a/include/soc/bcm2835/raspberrypi-firmware.h
161 +++ b/include/soc/bcm2835/raspberrypi-firmware.h
162 @@ -160,6 +160,11 @@ enum rpi_firmware_property_tag {
164 #define GET_DISPLAY_SETTINGS_PAYLOAD_SIZE 64
166 +struct rpi_firmware_get_clocks_response {
171 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
172 int rpi_firmware_property(struct rpi_firmware *fw,
173 u32 tag, void *data, size_t len);