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