Here's a series to add a real clock provider on Raspberry Pi.
Previously, we've been using a mix of fixed clocks from clk-bcm2835.c
(though some of them failed to get used by their intended consumers),
and fixed-clock nodes in the DT.
This driver gives us the ability to enable/disable our clocks, and
also lets us get and set frequency values. This will let us build a
cpufreq driver (Andrea Merello has done some work on this front, and
has a couple of options available), and get correct frequencies on the
Raspberry Pi 2 without hardcoding new values.
This branch can be found at:
https://github.com/anholt/linux/tree/rpi-dt-clocks
which also has a patch for the MMC clock which is not included yet due
to a bug in the sdhci driver. The series applies on top of
rpi-mailbox, whose code is currently out for review:
http://lists.infradead.org/pipermail/linux-rpi-kernel/2015-May/001762.html
The hardware clocks are not controllable by the ARM, so we have to
make requests to the firmware to do so from the VPU side. This will
let us replace fixed clocks in our DT with actual clock control (and
correct frequency information).
Signed-off-by: Eric Anholt <[email protected]>
---
.../bindings/clock/raspberrypi,firmware-clocks.txt | 24 ++++++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.txt
diff --git a/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.txt b/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.txt
new file mode 100644
index 0000000..9e1f21b
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/raspberrypi,firmware-clocks.txt
@@ -0,0 +1,24 @@
+Raspberry Pi firmware clock provider.
+
+The Raspberry Pi architecture doesn't provide direct access to the
+CLOCKMAN peripheral from the ARM side, so Linux has to make requests
+to the VPU firmware to program them.
+
+This binding uses the common clock binding:
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible: Should be "raspberrypi,firmware-clocks"
+
+- #clock-cells: Shall have value <1>. The permitted clock-specifier values
+ can be found in include/dt-bindings/clk/raspberrypi.h.
+
+- firmware: Phandle to the firmware driver node.
+
+Example:
+
+firmware_clocks: firmware-clocks {
+ compatible = "raspberrypi,firmware-clocks";
+ #clock-cells = <1>;
+ firmware = <&firmware>;
+};
--
2.1.4
Unfortunately, the clock manager's registers are not accessible by the
ARM, so we have to request that the firmware modify our clocks for us.
This driver only registers the clocks at the point they are requested
by a client driver. This is partially to support returning
-EPROBE_DEFER when the firmware driver isn't supported yet, but it
also avoids issues with disabling "unused" clocks due to them not yet
being connected to their consumers in the DT.
Signed-off-by: Eric Anholt <[email protected]>
---
drivers/clk/Makefile | 1 +
drivers/clk/clk-raspberrypi.c | 241 ++++++++++++++++++++++++++++++++++
include/dt-bindings/clk/raspberrypi.h | 23 ++++
3 files changed, 265 insertions(+)
create mode 100644 drivers/clk/clk-raspberrypi.c
create mode 100644 include/dt-bindings/clk/raspberrypi.h
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3d00c25..6a5dafa 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_ARCH_AXXIA) += clk-axm5516.o
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
+obj-$(CONFIG_ARCH_BCM2835) += clk-raspberrypi.o
obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
diff --git a/drivers/clk/clk-raspberrypi.c b/drivers/clk/clk-raspberrypi.c
new file mode 100644
index 0000000..5745875
--- /dev/null
+++ b/drivers/clk/clk-raspberrypi.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Implements a clock provider for the clocks controlled by the
+ * firmware on Raspberry Pi.
+ *
+ * These clocks are controlled by the CLOCKMAN peripheral in the
+ * hardware, but the ARM doesn't have access to the registers for
+ * them. As a result, we have to call into the firmware to get it to
+ * enable, disable, and set their frequencies.
+ *
+ * We don't have an interface for getting the set of frequencies
+ * available from the hardware. We can request a min/max, but other
+ * than that we have to request a frequency and take what it gives us.
+ */
+
+#include <dt-bindings/clk/raspberrypi.h>
+#include <linux/clk-provider.h>
+#include <soc/bcm2835/raspberrypi-firmware-property.h>
+
+struct rpi_firmware_clock {
+ /* Clock definitions in our static struct. */
+ int clock_id;
+ const char *name;
+
+ /* The rest are filled in at init time. */
+ struct clk_hw hw;
+ struct device *dev;
+ struct device_node *firmware_node;
+};
+
+static struct rpi_firmware_clock rpi_clocks[] = {
+ [RPI_CLOCK_EMMC] = { 1, "emmc" },
+ [RPI_CLOCK_UART0] = { 2, "uart0" },
+ [RPI_CLOCK_ARM] = { 3, "arm" },
+ [RPI_CLOCK_CORE] = { 4, "core" },
+ [RPI_CLOCK_V3D] = { 5, "v3d" },
+ [RPI_CLOCK_H264] = { 6, "h264" },
+ [RPI_CLOCK_ISP] = { 7, "isp" },
+ [RPI_CLOCK_SDRAM] = { 8, "sdram" },
+ [RPI_CLOCK_PIXEL] = { 9, "pixel" },
+ [RPI_CLOCK_PWM] = { 10, "pwm" },
+};
+
+static int rpi_clk_is_on(struct clk_hw *hw)
+{
+ struct rpi_firmware_clock *rpi_clk =
+ container_of(hw, struct rpi_firmware_clock, hw);
+ u32 packet[2];
+ int ret;
+
+ packet[0] = rpi_clk->clock_id;
+ packet[1] = 0;
+ ret = rpi_firmware_property(rpi_clk->firmware_node,
+ RPI_FIRMWARE_GET_CLOCK_STATE,
+ &packet, sizeof(packet));
+ if (ret) {
+ dev_err(rpi_clk->dev, "Failed to get clock state\n");
+ return 0;
+ }
+
+ return packet[1] != 0;
+}
+
+static int rpi_clk_set_state(struct rpi_firmware_clock *rpi_clk, bool on)
+{
+ u32 packet[2];
+ int ret;
+
+ packet[0] = rpi_clk->clock_id;
+ packet[1] = on;
+ ret = rpi_firmware_property(rpi_clk->firmware_node,
+ RPI_FIRMWARE_SET_CLOCK_STATE,
+ &packet, sizeof(packet));
+ if (ret || (packet[1] & (1 << 1))) {
+ dev_err(rpi_clk->dev, "Failed to set clock state\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rpi_clk_on(struct clk_hw *hw)
+{
+ struct rpi_firmware_clock *rpi_clk =
+ container_of(hw, struct rpi_firmware_clock, hw);
+
+ return rpi_clk_set_state(rpi_clk, true);
+}
+
+static void rpi_clk_off(struct clk_hw *hw)
+{
+ struct rpi_firmware_clock *rpi_clk =
+ container_of(hw, struct rpi_firmware_clock, hw);
+
+ rpi_clk_set_state(rpi_clk, false);
+}
+
+static unsigned long rpi_clk_get_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct rpi_firmware_clock *rpi_clk =
+ container_of(hw, struct rpi_firmware_clock, hw);
+ u32 packet[2];
+ int ret;
+
+ packet[0] = rpi_clk->clock_id;
+ packet[1] = 0;
+ ret = rpi_firmware_property(rpi_clk->firmware_node,
+ RPI_FIRMWARE_GET_CLOCK_RATE,
+ &packet, sizeof(packet));
+ if (ret) {
+ dev_err(rpi_clk->dev, "Failed to get clock rate\n");
+ return 0;
+ }
+
+ return packet[1];
+}
+
+static int rpi_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+{
+ struct rpi_firmware_clock *rpi_clk =
+ container_of(hw, struct rpi_firmware_clock, hw);
+ u32 packet[2];
+ int ret;
+
+ packet[0] = rpi_clk->clock_id;
+ packet[1] = rate;
+ ret = rpi_firmware_property(rpi_clk->firmware_node,
+ RPI_FIRMWARE_SET_CLOCK_RATE,
+ &packet, sizeof(packet));
+ if (ret) {
+ dev_err(rpi_clk->dev, "Failed to set clock rate\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static long rpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ /*
+ * The firmware will end up rounding our rate to something,
+ * but we don't have an interface for it. Just return the
+ * requested value, and it'll get updated after the clock gets
+ * set.
+ */
+ return rate;
+}
+
+static struct clk_ops rpi_clk_ops = {
+ .is_prepared = rpi_clk_is_on,
+ .prepare = rpi_clk_on,
+ .unprepare = rpi_clk_off,
+ .recalc_rate = rpi_clk_get_rate,
+ .set_rate = rpi_clk_set_rate,
+ .round_rate = rpi_clk_round_rate,
+};
+
+DEFINE_MUTEX(delayed_clock_init);
+static struct clk *rpi_firmware_delayed_get_clk(struct of_phandle_args *clkspec,
+ void *_data)
+{
+ struct device_node *of_node = _data;
+ struct platform_device *pdev = of_find_device_by_node(of_node);
+ struct device *dev = &pdev->dev;
+ struct device_node *firmware_node;
+ struct clk_init_data init;
+ struct rpi_firmware_clock *rpi_clk;
+ struct clk *ret_clk;
+ int ret;
+
+ if (clkspec->args_count != 1) {
+ dev_err(dev, "clock phandle should have 1 argument\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ if (clkspec->args[0] >= ARRAY_SIZE(rpi_clocks)) {
+ dev_err(dev, "clock phandle index %d too large\n",
+ clkspec->args[0]);
+ return ERR_PTR(-ENODEV);
+ }
+
+ rpi_clk = &rpi_clocks[clkspec->args[0]];
+
+ firmware_node = of_parse_phandle(of_node, "firmware", 0);
+ if (!firmware_node) {
+ dev_err(dev, "%s: Missing firmware node\n", rpi_clk->name);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Try a no-op transaction to see if the driver is loaded yet. */
+ ret = rpi_firmware_property_list(firmware_node, NULL, 0);
+ if (ret)
+ return ERR_PTR(ret);
+
+ mutex_lock(&delayed_clock_init);
+ if (rpi_clk->hw.clk) {
+ mutex_unlock(&delayed_clock_init);
+ return rpi_clk->hw.clk;
+ }
+ memset(&init, 0, sizeof(init));
+ init.ops = &rpi_clk_ops;
+
+ rpi_clk->firmware_node = firmware_node;
+ rpi_clk->dev = dev;
+ rpi_clk->hw.init = &init;
+ init.name = rpi_clk->name;
+ init.flags = CLK_IS_ROOT;
+
+ ret_clk = clk_register(dev, &rpi_clk->hw);
+ mutex_unlock(&delayed_clock_init);
+ if (!IS_ERR(ret_clk))
+ dev_info(dev, "registered %s clock\n", rpi_clk->name);
+ else {
+ dev_err(dev, "%s clock failed to init: %ld\n", rpi_clk->name,
+ PTR_ERR(ret_clk));
+ }
+ return ret_clk;
+}
+
+void __init rpi_firmware_init_clock_provider(struct device_node *node)
+{
+ /* We delay construction of our struct clks until get time,
+ * because we need to be able to return -EPROBE_DEFER if the
+ * firmware driver isn't up yet. clk core doesn't support
+ * re-probing on -EPROBE_DEFER, but callers of clk_get can.
+ */
+ of_clk_add_provider(node, rpi_firmware_delayed_get_clk, node);
+}
+
+CLK_OF_DECLARE(rpi_firmware_clocks, "raspberrypi,firmware-clocks",
+ rpi_firmware_init_clock_provider);
diff --git a/include/dt-bindings/clk/raspberrypi.h b/include/dt-bindings/clk/raspberrypi.h
new file mode 100644
index 0000000..c9fa85c
--- /dev/null
+++ b/include/dt-bindings/clk/raspberrypi.h
@@ -0,0 +1,23 @@
+#/*
+ * Copyright © 2015 Broadcom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _DT_BINDINGS_CLK_RASPBERRYPI_H
+#define _DT_BINDINGS_CLK_RASPBERRYPI_H
+
+#define RPI_CLOCK_EMMC 0
+#define RPI_CLOCK_UART0 1
+#define RPI_CLOCK_ARM 2
+#define RPI_CLOCK_CORE 3
+#define RPI_CLOCK_V3D 4
+#define RPI_CLOCK_H264 5
+#define RPI_CLOCK_ISP 6
+#define RPI_CLOCK_SDRAM 7
+#define RPI_CLOCK_PIXEL 8
+#define RPI_CLOCK_PWM 9
+
+#endif
--
2.1.4
Signed-off-by: Eric Anholt <[email protected]>
---
arch/arm/boot/dts/bcm2835-rpi.dtsi | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index ace33f6..ac5f84c 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -20,6 +20,12 @@
compatible = "raspberrypi,firmware";
mboxes = <&mailbox>;
};
+
+ firmware_clocks: firmware-clocks {
+ compatible = "raspberrypi,firmware-clocks";
+ #clock-cells = <1>;
+ firmware = <&firmware>;
+ };
};
};
--
2.1.4
This appears to have been copy-and-paste from another serial driver's
DT. The driver has never used this value -- instead, the pl011 driver
is getting the fixed 20201000.uart clock from clk-bcm2835.c.
Signed-off-by: Eric Anholt <[email protected]>
---
arch/arm/boot/dts/bcm2835.dtsi | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 301c73f..32f5830 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -96,7 +96,6 @@
compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
reg = <0x7e201000 0x1000>;
interrupts = <2 25>;
- clock-frequency = <3000000>;
arm,primecell-periphid = <0x00241011>;
};
--
2.1.4
Nothing uses it, and I can't find any evidence that anything ever has.
Signed-off-by: Eric Anholt <[email protected]>
---
drivers/clk/clk-bcm2835.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/clk/clk-bcm2835.c b/drivers/clk/clk-bcm2835.c
index 6b950ca..dd295e4 100644
--- a/drivers/clk/clk-bcm2835.c
+++ b/drivers/clk/clk-bcm2835.c
@@ -32,11 +32,6 @@ void __init bcm2835_init_clocks(void)
struct clk *clk;
int ret;
- clk = clk_register_fixed_rate(NULL, "sys_pclk", NULL, CLK_IS_ROOT,
- 250000000);
- if (IS_ERR(clk))
- pr_err("sys_pclk not registered\n");
-
clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT,
126000000);
if (IS_ERR(clk))
--
2.1.4
This gets us a correct apb_pclk, which previously was accidentally
using the "20201000.uart" clock from clk-bcm2835.c, due to the
fallback clk_get_sys() path.
Signed-off-by: Eric Anholt <[email protected]>
---
arch/arm/boot/dts/bcm2835-rpi.dtsi | 7 +++++++
arch/arm/boot/dts/bcm2835.dtsi | 2 +-
2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index ac5f84c..53285a3 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -1,3 +1,4 @@
+#include <dt-bindings/clk/raspberrypi.h>
#include "bcm2835.dtsi"
/ {
@@ -62,3 +63,9 @@
status = "okay";
bus-width = <4>;
};
+
+&uart0 {
+ clocks = <&firmware_clocks RPI_CLOCK_UART0>,
+ <&firmware_clocks RPI_CLOCK_CORE>;
+ clock-names = "uartclk", "apb_pclk";
+};
diff --git a/arch/arm/boot/dts/bcm2835.dtsi b/arch/arm/boot/dts/bcm2835.dtsi
index 32f5830..5be2862 100644
--- a/arch/arm/boot/dts/bcm2835.dtsi
+++ b/arch/arm/boot/dts/bcm2835.dtsi
@@ -92,7 +92,7 @@
#interrupt-cells = <2>;
};
- uart@7e201000 {
+ uart0: uart@7e201000 {
compatible = "brcm,bcm2835-pl011", "arm,pl011", "arm,primecell";
reg = <0x7e201000 0x1000>;
interrupts = <2 25>;
--
2.1.4
We were previously using a fixed clock declared in the 2835 DT, but
it's actually the core clock, and it might not be the same if you had
adjusted it using the firmware's config.txt.
Signed-off-by: Eric Anholt <[email protected]>
---
This is the only patch in the series I haven't really tested, since I
don't have any SPI devices.
arch/arm/boot/dts/bcm2835-rpi.dtsi | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/bcm2835-rpi.dtsi b/arch/arm/boot/dts/bcm2835-rpi.dtsi
index 53285a3..994c8e7 100644
--- a/arch/arm/boot/dts/bcm2835-rpi.dtsi
+++ b/arch/arm/boot/dts/bcm2835-rpi.dtsi
@@ -69,3 +69,7 @@
<&firmware_clocks RPI_CLOCK_CORE>;
clock-names = "uartclk", "apb_pclk";
};
+
+&spi {
+ clocks = <&firmware_clocks RPI_CLOCK_CORE>;
+};
--
2.1.4
Hi Eric,
On Mon, May 18, 2015 at 12:43:34PM -0700, Eric Anholt wrote:
> +DEFINE_MUTEX(delayed_clock_init);
Static?
baruch
--
http://baruch.siach.name/blog/ ~. .~ Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
- [email protected] - tel: +972.2.679.5364, http://www.tkos.co.il -
On 05/29/2015 03:02 PM, Eric Anholt wrote:
> Stephen Warren <[email protected]> writes:
>
>> On 05/18/2015 01:43 PM, Eric Anholt wrote:
>>> +static struct clk *rpi_firmware_delayed_get_clk(struct
>>> of_phandle_args *clkspec, + void *_data)
>>
>>> + rpi_clk = &rpi_clocks[clkspec->args[0]]; + + firmware_node =
>>> of_parse_phandle(of_node, "firmware", 0); + if (!firmware_node)
>>> { + dev_err(dev, "%s: Missing firmware node\n",
>>> rpi_clk->name); + return ERR_PTR(-ENODEV); + } + + /* Try a
>>> no-op transaction to see if the driver is loaded yet. */ + ret
>>> = rpi_firmware_property_list(firmware_node, NULL, 0); + if
>>> (ret) + return ERR_PTR(ret);
>>
>> I would move all that into this driver's probe().
>
> We can't move all this into the driver's probe, because this is
> where we're returning -EPROBE_DEFER. We could potentially do just
> the phandle parse up front and allocate some memory to pass it and
> our own device node to this function through the _data arg, but I
> don't see much point.
Well, once the clock core correctly supports deferred probe, that can
be moved.
Aside from that, I think all your other replies to my replies in this
thread/series make sense.