Hi, Since the whole DRM/HDMI support began to grow fairly big, I've chosen to split away the two discussions between the firmware clocks and the HDMI support. Let me know what you think, Maxime Cc: bcm-kernel-feedback-list@broadcom.com Cc: devicetree@vger.kernel.org Cc: Kamal Dasu <kdasu.kdev@gmail.com> Cc: linux-clk@vger.kernel.org Cc: Michael Turquette <mturquette@baylibre.com> Cc: Rob Herring <robh+dt@kernel.org> Cc: Stephen Boyd <sboyd@kernel.org> Changes from v2: - Rebased on top of next-20200526 - Split away from the HDMI series - Fixed an of_node leakage in the firmware driver - Fixed an of_node leakage in the firmware clocks driver - Added the min/max rate retrieval to all the firmware clocks - Added proper name for the firmware clocks - Removed the PLLB setup from the firmware clocks and moved it back to the MMIO driver Florian Fainelli (1): dt-bindings: arm: bcm: Convert BCM2835 firmware binding to YAML Maxime Ripard (24): dt-bindings: clock: Add a binding for the RPi Firmware clocks firmware: rpi: Only create clocks device if we don't have a node for it clk: bcm: rpi: Allow the driver to be probed by DT clk: bcm: rpi: Statically init clk_init_data clk: bcm: rpi: Use clk_hw_register for pllb_arm clk: bcm: rpi: Remove global pllb_arm clock pointer clk: bcm: rpi: Make sure pllb_arm is removed clk: bcm: rpi: Remove pllb_arm_lookup global pointer clk: bcm: rpi: Switch to clk_hw_register_clkdev clk: bcm: rpi: Make sure the clkdev lookup is removed clk: bcm: rpi: Use CCF boundaries instead of rolling our own clk: bcm: rpi: Create a data structure for the clocks clk: bcm: rpi: Add clock id to data clk: bcm: rpi: Pass the clocks data to the firmware function clk: bcm: rpi: Rename is_prepared function clk: bcm: rpi: Split pllb clock hooks clk: bcm: rpi: Make the PLLB registration function return a clk_hw clk: bcm: rpi: Add DT provider for the clocks clk: bcm: rpi: Add an enum for the firmware clocks clk: bcm: rpi: Discover the firmware clocks clk: bcm: rpi: Give firmware clocks a name Revert "clk: bcm2835: remove pllb" clk: bcm: rpi: Remove the quirks for the CPU clock ARM: dts: bcm2711: Add firmware clocks node Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt | 14 +--- Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml | 59 ++++++++++++++- arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 5 +- drivers/clk/bcm/clk-bcm2835.c | 30 ++++++- drivers/clk/bcm/clk-raspberrypi.c | 299 ++++++++++++++++++++++++++++++++++++++++++++---------------------------- drivers/firmware/raspberrypi.c | 14 +++- include/soc/bcm2835/raspberrypi-firmware.h | 5 +- 7 files changed, 293 insertions(+), 133 deletions(-) delete mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.txt create mode 100644 Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml base-commit: b0523c7b1c9d0edcd6c0fe6d2cb558a9ad5c60a8 -- git-series 0.9.1
The firmware running on the RPi VideoCore can be used to discover and change the various clocks running in the BCM2711. Since devices will need to use them through the DT, let's add a pretty simple binding. Cc: Michael Turquette <mturquette@baylibre.com> Cc: Stephen Boyd <sboyd@kernel.org> Cc: Rob Herring <robh+dt@kernel.org> Cc: linux-clk@vger.kernel.org Cc: devicetree@vger.kernel.org Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml index cec540c052b6..b48ed875eb8e 100644 --- a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml @@ -22,6 +22,25 @@ properties: Phandle to the firmware device's Mailbox. (See: ../mailbox/mailbox.txt for more information) + clocks: + type: object + + properties: + compatible: + const: raspberrypi,firmware-clocks + + "#clock-cells": + const: 1 + description: > + The argument is the ID of the clocks contained by the + firmware messages. + + required: + - compatible + - "#clock-cells" + + additionalProperties: false + required: - compatible - mboxes @@ -31,5 +50,10 @@ examples: firmware { compatible = "raspberrypi,bcm2835-firmware", "simple-bus"; mboxes = <&mailbox>; + + firmware_clocks: clocks { + compatible = "raspberrypi,firmware-clocks"; + #clock-cells = <1>; + }; }; ... -- git-series 0.9.1
The current firmware clock driver for the RaspberryPi can only be probed by manually registering an associated platform_device. While this works fine for cpufreq where the device gets attached a clkdev lookup, it would be tedious to maintain a table of all the devices using one of the clocks exposed by the firmware. Since the DT on the other hand is the perfect place to store those associations, make the firmware clocks driver probe-able through the device tree so that we can represent it as a node. 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 | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 1654fd0eedc9..8610355bda47 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -255,8 +255,16 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct raspberrypi_clk *rpi; int ret; - firmware_node = of_find_compatible_node(NULL, NULL, - "raspberrypi,bcm2835-firmware"); + /* + * We can be probed either through the an old-fashioned + * platform device registration or through a DT node that is a + * child of the firmware node. Handle both cases. + */ + if (dev->of_node) + firmware_node = of_get_parent(dev->of_node); + else + firmware_node = of_find_compatible_node(NULL, NULL, + "raspberrypi,bcm2835-firmware"); if (!firmware_node) { dev_err(dev, "Missing firmware node\n"); return -ENOENT; @@ -300,9 +308,16 @@ static int raspberrypi_clk_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id raspberrypi_clk_match[] = { + { .compatible = "raspberrypi,firmware-clocks" }, + { }, +}; +MODULE_DEVICE_TABLE(of, raspberrypi_clk_match); + static struct platform_driver raspberrypi_clk_driver = { .driver = { .name = "raspberrypi-clk", + .of_match_table = raspberrypi_clk_match, }, .probe = raspberrypi_clk_probe, .remove = raspberrypi_clk_remove, -- git-series 0.9.1
Instead of declaring the clk_init_data and then calling memset on it, just initialise properly. Cc: Michael Turquette <mturquette@baylibre.com> Cc: Stephen Boyd <sboyd@kernel.org> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 8610355bda47..ddc72207212e 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -175,11 +175,10 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { + struct clk_init_data init = {}; u32 min_rate = 0, max_rate = 0; - struct clk_init_data init; int ret; - memset(&init, 0, sizeof(init)); /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; -- git-series 0.9.1
The pllb_arm clock is defined as a fixed factor clock with the pllb clock as a parent. However, all its configuration is entirely static, and thus we don't really need to call clk_hw_register_fixed_factor() but can simply call clk_hw_register() with a static clk_fixed_factor structure. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index ddc72207212e..5f0d4875e145 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -225,16 +225,28 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) return devm_clk_hw_register(rpi->dev, &rpi->pllb); } +static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { + .mult = 1, + .div = 2, + .hw.init = &(struct clk_init_data) { + .name = "pllb_arm", + .parent_names = (const char *[]){ "pllb" }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + }, +}; + static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { - rpi->pllb_arm = clk_hw_register_fixed_factor(rpi->dev, - "pllb_arm", "pllb", - CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, - 1, 2); - if (IS_ERR(rpi->pllb_arm)) { + int ret; + + ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return PTR_ERR(rpi->pllb_arm); + return ret; } + rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { -- git-series 0.9.1
The pllb_arm clk_hw pointer in the raspberry_clk structure isn't used anywhere but in the raspberrypi_register_pllb_arm. Let's remove it, this will make our lives easier in future patches. Cc: Michael Turquette <mturquette@baylibre.com> Cc: Stephen Boyd <sboyd@kernel.org> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 5f0d4875e145..b21dd6ddc4fe 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -40,7 +40,6 @@ struct raspberrypi_clk { unsigned long max_rate; struct clk_hw pllb; - struct clk_hw *pllb_arm; struct clk_lookup *pllb_arm_lookup; }; @@ -246,12 +245,12 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); return ret; } - rpi->pllb_arm = &raspberrypi_clk_pllb_arm.hw; - rpi->pllb_arm_lookup = clkdev_hw_create(rpi->pllb_arm, NULL, "cpu0"); + rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - clk_hw_unregister_fixed_factor(rpi->pllb_arm); + clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); return -ENOMEM; } -- git-series 0.9.1
The pllb_arm clock was created at probe time, but was never removed if something went wrong later in probe, or if the driver was ever removed from the system. Now that we are using clk_hw_register(), we can just use its managed variant to take care of that for us. Cc: Michael Turquette <mturquette@baylibre.com> Cc: Stephen Boyd <sboyd@kernel.org> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index b21dd6ddc4fe..d62605861028 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -240,7 +240,7 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { int ret; - ret = clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); + ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); return ret; @@ -250,7 +250,6 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) NULL, "cpu0"); if (!rpi->pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - clk_hw_unregister_fixed_factor(&raspberrypi_clk_pllb_arm.hw); return -ENOMEM; } -- git-series 0.9.1
The pllb_arm_lookup pointer in the struct raspberrypi_clk is not used for anything but to store the returned pointer to clkdev_hw_create, and is not used anywhere else in the driver. Let's remove that global pointer from the structure. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index d62605861028..5a06c4991c7f 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -40,7 +40,6 @@ struct raspberrypi_clk { unsigned long max_rate; struct clk_hw pllb; - struct clk_lookup *pllb_arm_lookup; }; /* @@ -238,6 +237,7 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { + struct clk_lookup *pllb_arm_lookup; int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); @@ -246,9 +246,9 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - rpi->pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); - if (!rpi->pllb_arm_lookup) { + pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); + if (!pllb_arm_lookup) { dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); return -ENOMEM; } -- git-series 0.9.1
Since we don't care about retrieving the clk_lookup structure pointer returned by clkdev_hw_create, we can just use the clk_hw_register_clkdev function. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 5a06c4991c7f..23f06618a356 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -237,7 +237,6 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { - struct clk_lookup *pllb_arm_lookup; int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); @@ -246,11 +245,11 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - pllb_arm_lookup = clkdev_hw_create(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); - if (!pllb_arm_lookup) { - dev_err(rpi->dev, "Failed to initialize pllb_arm_lookup\n"); - return -ENOMEM; + ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + return ret; } return 0; -- git-series 0.9.1
The clkdev lookup created for the cpufreq device is never removed if there's an issue later in probe or at module removal time. Let's convert to the managed variant of the clk_hw_register_clkdev function to make sure it happens. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 23f06618a356..a20492fade6a 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -245,8 +245,9 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return ret; } - ret = clk_hw_register_clkdev(&raspberrypi_clk_pllb_arm.hw, - NULL, "cpu0"); + ret = devm_clk_hw_register_clkdev(rpi->dev, + &raspberrypi_clk_pllb_arm.hw, + NULL, "cpu0"); if (ret) { dev_err(rpi->dev, "Failed to initialize clkdev\n"); return ret; -- git-series 0.9.1
So far the driver has really only been providing a single clock, and stored both the data associated to that clock in particular with the data associated to the "controller". Since we will change that in the future, let's decouple the clock data from the provider data. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index e135ad28d38d..00735704eabc 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -35,8 +35,11 @@ struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; struct platform_device *cpufreq; +}; - struct clk_hw pllb; +struct raspberrypi_clk_data { + struct clk_hw hw; + struct raspberrypi_clk *rpi; }; /* @@ -80,8 +83,9 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; @@ -98,8 +102,9 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 val = 0; int ret; @@ -116,8 +121,9 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { - struct raspberrypi_clk *rpi = container_of(hw, struct raspberrypi_clk, - pllb); + struct raspberrypi_clk_data *data = + container_of(hw, struct raspberrypi_clk_data, hw); + struct raspberrypi_clk *rpi = data->rpi; u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; int ret; @@ -168,10 +174,15 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { + struct raspberrypi_clk_data *data; struct clk_init_data init = {}; u32 min_rate = 0, max_rate = 0; int ret; + data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->rpi = rpi; /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; @@ -210,11 +221,11 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", min_rate, max_rate); - rpi->pllb.init = &init; + data->hw.init = &init; - ret = devm_clk_hw_register(rpi->dev, &rpi->pllb); + ret = devm_clk_hw_register(rpi->dev, &data->hw); if (!ret) - clk_hw_set_rate_range(&rpi->pllb, + clk_hw_set_rate_range(&data->hw, min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); -- git-series 0.9.1
The driver has really only supported one clock so far and has hardcoded the ID used in communications with the firmware in all the functions implementing the clock framework hooks. Let's store that in the clock data structure so that we can support more clocks later on. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 00735704eabc..97ac04604b0a 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -39,6 +39,9 @@ struct raspberrypi_clk { struct raspberrypi_clk_data { struct clk_hw hw; + + unsigned int id; + struct raspberrypi_clk *rpi; }; @@ -91,7 +94,7 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCK_STATE, - RPI_FIRMWARE_ARM_CLK_ID, &val); + data->id, &val); if (ret) return 0; @@ -110,8 +113,7 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &val); + data->id, &val); if (ret) return ret; @@ -129,8 +131,7 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_SET_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &new_rate); + data->id, &new_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -183,6 +184,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (!data) return -ENOMEM; data->rpi = rpi; + data->id = RPI_FIRMWARE_ARM_CLK_ID; /* All of the PLLs derive from the external oscillator. */ init.parent_names = (const char *[]){ "osc" }; @@ -194,8 +196,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) /* Get min & max rates set by the firmware */ ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &min_rate); + data->id, &min_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); @@ -204,8 +205,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) ret = raspberrypi_clock_property(rpi->firmware, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - RPI_FIRMWARE_ARM_CLK_ID, - &max_rate); + data->id, &max_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); -- git-series 0.9.1
The raspberry_clock_property only takes the clock ID as an argument, but now that we have a clock data structure it makes more sense to just pass that structure instead. Cc: Michael Turquette <mturquette@baylibre.com> Cc: Stephen Boyd <sboyd@kernel.org> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 97ac04604b0a..3fce49a65a79 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -65,11 +65,12 @@ struct raspberrypi_firmware_prop { __le32 disable_turbo; } __packed; -static int raspberrypi_clock_property(struct rpi_firmware *firmware, u32 tag, - u32 clk, u32 *val) +static int raspberrypi_clock_property(struct rpi_firmware *firmware, + const struct raspberrypi_clk_data *data, + u32 tag, u32 *val) { struct raspberrypi_firmware_prop msg = { - .id = cpu_to_le32(clk), + .id = cpu_to_le32(data->id), .val = cpu_to_le32(*val), .disable_turbo = cpu_to_le32(1), }; @@ -92,9 +93,8 @@ static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_STATE, - data->id, &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_STATE, &val); if (ret) return 0; @@ -111,9 +111,8 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, u32 val = 0; int ret; - ret = raspberrypi_clock_property(rpi->firmware, - RPI_FIRMWARE_GET_CLOCK_RATE, - data->id, &val); + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_CLOCK_RATE, &val); if (ret) return ret; @@ -129,9 +128,9 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; int ret; - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_SET_CLOCK_RATE, - data->id, &new_rate); + &new_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -194,18 +193,18 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) init.flags = CLK_GET_RATE_NOCACHE | CLK_IGNORE_UNUSED; /* Get min & max rates set by the firmware */ - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MIN_CLOCK_RATE, - data->id, &min_rate); + &min_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); return ret; } - ret = raspberrypi_clock_property(rpi->firmware, + ret = raspberrypi_clock_property(rpi->firmware, data, RPI_FIRMWARE_GET_MAX_CLOCK_RATE, - data->id, &max_rate); + &max_rate); if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); -- git-series 0.9.1
The raspberrypi_fw_pll_is_on function doesn't only apply to PLL registered in the driver, but any clock exposed by the firmware. Since we also implement the is_prepared hook, make the function consistent with the other function names. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 3fce49a65a79..58ac1b104429 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -85,7 +85,7 @@ static int raspberrypi_clock_property(struct rpi_firmware *firmware, return 0; } -static int raspberrypi_fw_pll_is_on(struct clk_hw *hw) +static int raspberrypi_fw_is_prepared(struct clk_hw *hw) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); @@ -166,7 +166,7 @@ static int raspberrypi_pll_determine_rate(struct clk_hw *hw, } static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { - .is_prepared = raspberrypi_fw_pll_is_on, + .is_prepared = raspberrypi_fw_is_prepared, .recalc_rate = raspberrypi_fw_pll_get_rate, .set_rate = raspberrypi_fw_pll_set_rate, .determine_rate = raspberrypi_pll_determine_rate, -- git-series 0.9.1
The driver only supports the pllb for now and all the clock framework hooks are a mix of the generic firmware interface and the specifics of the pllb. Since we will support more clocks in the future let's split the generic and specific hooks Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 58ac1b104429..19571602ba64 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -102,8 +102,8 @@ static int raspberrypi_fw_is_prepared(struct clk_hw *hw) } -static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, - unsigned long parent_rate) +static unsigned long raspberrypi_fw_get_rate(struct clk_hw *hw, + unsigned long parent_rate) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); @@ -116,21 +116,27 @@ static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, if (ret) return ret; - return val * RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + return val; } -static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, - unsigned long parent_rate) +static unsigned long raspberrypi_fw_pll_get_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return raspberrypi_fw_get_rate(hw, parent_rate) * + RPI_FIRMWARE_PLLB_ARM_DIV_RATE; +} + +static int raspberrypi_fw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) { struct raspberrypi_clk_data *data = container_of(hw, struct raspberrypi_clk_data, hw); struct raspberrypi_clk *rpi = data->rpi; - u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + u32 _rate = rate; int ret; ret = raspberrypi_clock_property(rpi->firmware, data, - RPI_FIRMWARE_SET_CLOCK_RATE, - &new_rate); + RPI_FIRMWARE_SET_CLOCK_RATE, &_rate); if (ret) dev_err_ratelimited(rpi->dev, "Failed to change %s frequency: %d", clk_hw_get_name(hw), ret); @@ -138,6 +144,14 @@ static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, return ret; } +static int raspberrypi_fw_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u32 new_rate = rate / RPI_FIRMWARE_PLLB_ARM_DIV_RATE; + + return raspberrypi_fw_set_rate(hw, new_rate, parent_rate); +} + /* * Sadly there is no firmware rate rounding interface. We borrowed it from * clk-bcm2835. -- git-series 0.9.1
The raspberrypi_register_pllb has been returning an integer so far to notify whether the functions has exited successfully or not. However, the OF provider functions in the clock framework require access to the clk_hw structure so that we can expose those clocks to device tree consumers. Since we'll want that for the future clocks, let's return a clk_hw pointer instead of the return code. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 46 ++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index 19571602ba64..d2cb90c086a7 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -186,7 +186,7 @@ static const struct clk_ops raspberrypi_firmware_pll_clk_ops = { .determine_rate = raspberrypi_pll_determine_rate, }; -static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) +static struct clk_hw *raspberrypi_register_pllb(struct raspberrypi_clk *rpi) { struct raspberrypi_clk_data *data; struct clk_init_data init = {}; @@ -195,7 +195,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) data = devm_kzalloc(rpi->dev, sizeof(*data), GFP_KERNEL); if (!data) - return -ENOMEM; + return ERR_PTR(-ENOMEM); data->rpi = rpi; data->id = RPI_FIRMWARE_ARM_CLK_ID; @@ -213,7 +213,7 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (ret) { dev_err(rpi->dev, "Failed to get %s min freq: %d\n", init.name, ret); - return ret; + return ERR_PTR(ret); } ret = raspberrypi_clock_property(rpi->firmware, data, @@ -222,13 +222,13 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) if (ret) { dev_err(rpi->dev, "Failed to get %s max freq: %d\n", init.name, ret); - return ret; + return ERR_PTR(ret); } if (!min_rate || !max_rate) { dev_err(rpi->dev, "Unexpected frequency range: min %u, max %u\n", min_rate, max_rate); - return -EINVAL; + return ERR_PTR(-EINVAL); } dev_info(rpi->dev, "CPU frequency range: min %u, max %u\n", @@ -237,12 +237,14 @@ static int raspberrypi_register_pllb(struct raspberrypi_clk *rpi) data->hw.init = &init; ret = devm_clk_hw_register(rpi->dev, &data->hw); - if (!ret) - clk_hw_set_rate_range(&data->hw, - min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, - max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); + if (ret) + return ERR_PTR(ret); - return ret; + clk_hw_set_rate_range(&data->hw, + min_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE, + max_rate * RPI_FIRMWARE_PLLB_ARM_DIV_RATE); + + return &data->hw; } static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { @@ -257,14 +259,14 @@ static struct clk_fixed_factor raspberrypi_clk_pllb_arm = { }, }; -static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) +static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) { int ret; ret = devm_clk_hw_register(rpi->dev, &raspberrypi_clk_pllb_arm.hw); if (ret) { dev_err(rpi->dev, "Failed to initialize pllb_arm\n"); - return ret; + return ERR_PTR(ret); } ret = devm_clk_hw_register_clkdev(rpi->dev, @@ -272,10 +274,10 @@ static int raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) NULL, "cpu0"); if (ret) { dev_err(rpi->dev, "Failed to initialize clkdev\n"); - return ret; + return ERR_PTR(ret); } - return 0; + return &raspberrypi_clk_pllb_arm.hw; } static int raspberrypi_clk_probe(struct platform_device *pdev) @@ -284,7 +286,7 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; - int ret; + struct clk_hw *hw; /* * We can be probed either through the an old-fashioned @@ -314,15 +316,15 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); - ret = raspberrypi_register_pllb(rpi); - if (ret) { - dev_err(dev, "Failed to initialize pllb, %d\n", ret); - return ret; + 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); } - ret = raspberrypi_register_pllb_arm(rpi); - if (ret) - return ret; + hw = raspberrypi_register_pllb_arm(rpi); + if (IS_ERR(hw)) + return PTR_ERR(hw); rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", -1, NULL, 0); -- git-series 0.9.1
For the upcoming registration of the clocks provided by the firmware, make sure it's exposed to the device tree providers. Cc: Michael Turquette <mturquette@baylibre.com> Cc: linux-clk@vger.kernel.org Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index d2cb90c086a7..5f4e2d49432f 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -31,6 +31,8 @@ #define A2W_PLL_FRAC_BITS 20 +#define NUM_FW_CLKS 16 + struct raspberrypi_clk { struct device *dev; struct rpi_firmware *firmware; @@ -282,11 +284,13 @@ static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) static int raspberrypi_clk_probe(struct platform_device *pdev) { + struct clk_hw_onecell_data *clk_data; struct device_node *firmware_node; struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; struct clk_hw *hw; + int ret; /* * We can be probed either through the an old-fashioned @@ -316,6 +320,11 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) rpi->firmware = firmware; platform_set_drvdata(pdev, rpi); + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, NUM_FW_CLKS), + GFP_KERNEL); + 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)); @@ -325,6 +334,13 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) 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 = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + clk_data); + if (ret) + return ret; rpi->cpufreq = platform_device_register_data(dev, "raspberrypi-cpufreq", -1, NULL, 0); -- git-series 0.9.1
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. Since the firmware implements DVFS, this rate change can have a number of side-effects, including adjusting the various PLL voltages or the PLL parents. The firmware also implements thermal throttling, so even some thermal pressure can change those parameters behind Linux back. DVFS is currently implemented on the arm, core, h264, v3d, isp and hevc clocks, so updating any of them using the MMIO driver (and thus behind the firmware's back) can lead to troubles, the arm clock obviously being the most problematic. 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. However, the firmware doesn't seem to provide some equivalents to their MMIO counterparts, so we can't really replace that driver entirely. Fortunately, the 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 Reviewed-by: Stephen Boyd <sboyd@kernel.org> Signed-off-by: Maxime Ripard <maxime@cerno.tech> --- drivers/clk/bcm/clk-raspberrypi.c | 151 ++++++++++++++++++++-- include/soc/bcm2835/raspberrypi-firmware.h | 5 +- 2 files changed, 144 insertions(+), 12 deletions(-) diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk-raspberrypi.c index eebd16040f8a..8d3acf3ee053 100644 --- a/drivers/clk/bcm/clk-raspberrypi.c +++ b/drivers/clk/bcm/clk-raspberrypi.c @@ -296,6 +296,142 @@ static struct clk_hw *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) return &raspberrypi_clk_pllb_arm.hw; } +static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + /* + * The firmware will do the rounding but that isn't part of + * the interface with the firmware, so we just do our best + * here. + */ + req->rate = clamp(req->rate, req->min_rate, req->max_rate); + return 0; +} + +static const struct clk_ops raspberrypi_firmware_clk_ops = { + .is_prepared = raspberrypi_fw_is_prepared, + .recalc_rate = raspberrypi_fw_get_rate, + .determine_rate = raspberrypi_fw_dumb_determine_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 = {}; + u32 min_rate, max_rate; + 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 = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, + &min_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get clock %d min freq: %d", + id, ret); + return ERR_PTR(ret); + } + + ret = raspberrypi_clock_property(rpi->firmware, data, + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, + &max_rate); + if (ret) { + dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", + id, ret); + return ERR_PTR(ret); + } + + dev_info(rpi->dev, "Clock %d frequency range: min %u, max %u\n", + id, min_rate, max_rate); + + ret = devm_clk_hw_register(rpi->dev, &data->hw); + if (ret) + return ERR_PTR(ret); + + clk_hw_set_rate_range(&data->hw, min_rate, max_rate); + + if (id == RPI_FIRMWARE_ARM_CLK_ID) { + ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, + NULL, "cpu0"); + if (ret) { + dev_err(rpi->dev, "Failed to initialize clkdev\n"); + 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), RPI_FIRMWARE_NUM_CLK_ID, + GFP_KERNEL); + if (!clks) + return -ENOMEM; + + ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, + clks, + sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); + if (ret) + return ret; + + while (clks->id) { + struct clk_hw *hw; + + switch (clks->id) { + case RPI_FIRMWARE_ARM_CLK_ID: + case RPI_FIRMWARE_CORE_CLK_ID: + case RPI_FIRMWARE_M2MC_CLK_ID: + case RPI_FIRMWARE_V3D_CLK_ID: + 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; + fallthrough; + + default: + clks++; + break; + } + } + + return 0; +} + static int raspberrypi_clk_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -303,7 +439,6 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rpi_firmware *firmware; struct raspberrypi_clk *rpi; - struct clk_hw *hw; int ret; /* @@ -340,17 +475,9 @@ static int raspberrypi_clk_probe(struct platform_device *pdev) 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); diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h index 3025aca3c358..1c48f8269eab 100644 --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -136,6 +136,11 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, }; +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); -- git-series 0.9.1
On Wed, 27 May 2020 17:44:58 +0200, Maxime Ripard wrote:
> The firmware running on the RPi VideoCore can be used to discover and
> change the various clocks running in the BCM2711. Since devices will
> need to use them through the DT, let's add a pretty simple binding.
>
> Cc: Michael Turquette <mturquette@baylibre.com>
> Cc: Stephen Boyd <sboyd@kernel.org>
> Cc: Rob Herring <robh+dt@kernel.org>
> Cc: linux-clk@vger.kernel.org
> Cc: devicetree@vger.kernel.org
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
> Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml | 24 ++++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
>
Reviewed-by: Rob Herring <robh@kernel.org>
Quoting Maxime Ripard (2020-05-27 08:44:58) > The firmware running on the RPi VideoCore can be used to discover and > change the various clocks running in the BCM2711. Since devices will > need to use them through the DT, let's add a pretty simple binding. > > Cc: Michael Turquette <mturquette@baylibre.com> > Cc: Stephen Boyd <sboyd@kernel.org> > Cc: Rob Herring <robh+dt@kernel.org> > Cc: linux-clk@vger.kernel.org > Cc: devicetree@vger.kernel.org > Signed-off-by: Maxime Ripard <maxime@cerno.tech> > --- Reviewed-by: Stephen Boyd <sboyd@kernel.org> > diff --git a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml > index cec540c052b6..b48ed875eb8e 100644 > --- a/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml > +++ b/Documentation/devicetree/bindings/arm/bcm/raspberrypi,bcm2835-firmware.yaml > @@ -22,6 +22,25 @@ properties: > Phandle to the firmware device's Mailbox. > (See: ../mailbox/mailbox.txt for more information) > > + clocks: > + type: object > + > + properties: > + compatible: > + const: raspberrypi,firmware-clocks > + > + "#clock-cells": > + const: 1 > + description: > Just curious if this is the same as the | syntax? I haven't seen this used before. > + The argument is the ID of the clocks contained by the > + firmware messages. > +
Quoting Maxime Ripard (2020-05-27 08:45:00)
> The current firmware clock driver for the RaspberryPi can only be probed by
> manually registering an associated platform_device.
>
> While this works fine for cpufreq where the device gets attached a clkdev
> lookup, it would be tedious to maintain a table of all the devices using
> one of the clocks exposed by the firmware.
>
> Since the DT on the other hand is the perfect place to store those
> associations, make the firmware clocks driver probe-able through the device
> tree so that we can represent it as a node.
>
> 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>
> ---
Reviewed-by: Stephen Boyd <sboyd@kernel.org>
[-- Attachment #1: Type: text/plain, Size: 445 bytes --] Hi Stephen, Thanks for your review :) On Fri, May 29, 2020 at 02:17:16PM -0700, Stephen Boyd wrote: > > + "#clock-cells": > > + const: 1 > > + description: > > > Just curious if this is the same as the | syntax? I haven't seen this > used before. It differs on how it keeps the formatting of text below. | will keep the formatting as is, > will leave the formatting to whatever is going to use it. Maxime [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 228 bytes --]
[-- Attachment #1: Type: text/plain, Size: 867 bytes --] On Wed, 2020-05-27 at 17:45 +0200, Maxime Ripard wrote: > The current firmware clock driver for the RaspberryPi can only be probed by > manually registering an associated platform_device. > > While this works fine for cpufreq where the device gets attached a clkdev > lookup, it would be tedious to maintain a table of all the devices using > one of the clocks exposed by the firmware. > > Since the DT on the other hand is the perfect place to store those > associations, make the firmware clocks driver probe-able through the device > tree so that we can represent it as a node. > > 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> > --- Reviewed-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Regards, Nicolas [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #1: Type: text/plain, Size: 590 bytes --] On Wed, 2020-05-27 at 17:45 +0200, Maxime Ripard wrote: > The driver only supports the pllb for now and all the clock framework hooks > are a mix of the generic firmware interface and the specifics of the pllb. > Since we will support more clocks in the future let's split the generic and > specific hooks > > Cc: Michael Turquette <mturquette@baylibre.com> > Cc: linux-clk@vger.kernel.org > Reviewed-by: Stephen Boyd <sboyd@kernel.org> > Signed-off-by: Maxime Ripard <maxime@cerno.tech> > --- Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Regards, Nicolas [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #1: Type: text/plain, Size: 757 bytes --] On Wed, 2020-05-27 at 17:45 +0200, Maxime Ripard wrote: > The raspberrypi_register_pllb has been returning an integer so far to > notify whether the functions has exited successfully or not. > > However, the OF provider functions in the clock framework require access to > the clk_hw structure so that we can expose those clocks to device tree > consumers. > > Since we'll want that for the future clocks, let's return a clk_hw pointer > instead of the return code. > > Cc: Michael Turquette <mturquette@baylibre.com> > Cc: linux-clk@vger.kernel.org > Reviewed-by: Stephen Boyd <sboyd@kernel.org> > Signed-off-by: Maxime Ripard <maxime@cerno.tech> > --- Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Regards, Nicolas [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #1: Type: text/plain, Size: 465 bytes --] On Wed, 2020-05-27 at 17:45 +0200, Maxime Ripard wrote: > For the upcoming registration of the clocks provided by the firmware, make > sure it's exposed to the device tree providers. > > Cc: Michael Turquette <mturquette@baylibre.com> > Cc: linux-clk@vger.kernel.org > Reviewed-by: Stephen Boyd <sboyd@kernel.org> > Signed-off-by: Maxime Ripard <maxime@cerno.tech> > --- Acked-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de> Regards, Nicolas [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 488 bytes --]
[-- Attachment #1: Type: text/plain, Size: 12850 bytes --] Hi Maxime, On Wed, 2020-05-27 at 17:45 +0200, Maxime Ripard wrote: > 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. > > Since the firmware implements DVFS, this rate change can have a number of > side-effects, including adjusting the various PLL voltages or the PLL > parents. The firmware also implements thermal throttling, so even some > thermal pressure can change those parameters behind Linux back. > > DVFS is currently implemented on the arm, core, h264, v3d, isp and hevc > clocks, so updating any of them using the MMIO driver (and thus behind the > firmware's back) can lead to troubles, the arm clock obviously being the > most problematic. > > 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. However, > the firmware doesn't seem to provide some equivalents to their MMIO > counterparts, so we can't really replace that driver entirely. > > Fortunately, the 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 > Reviewed-by: Stephen Boyd <sboyd@kernel.org> > Signed-off-by: Maxime Ripard <maxime@cerno.tech> > --- > drivers/clk/bcm/clk-raspberrypi.c | 151 ++++++++++++++++++++-- > include/soc/bcm2835/raspberrypi-firmware.h | 5 +- > 2 files changed, 144 insertions(+), 12 deletions(-) > > diff --git a/drivers/clk/bcm/clk-raspberrypi.c b/drivers/clk/bcm/clk- > raspberrypi.c > index eebd16040f8a..8d3acf3ee053 100644 > --- a/drivers/clk/bcm/clk-raspberrypi.c > +++ b/drivers/clk/bcm/clk-raspberrypi.c > @@ -296,6 +296,142 @@ static struct clk_hw > *raspberrypi_register_pllb_arm(struct raspberrypi_clk *rpi) > return &raspberrypi_clk_pllb_arm.hw; > } > > +static int raspberrypi_fw_dumb_determine_rate(struct clk_hw *hw, > + struct clk_rate_request *req) > +{ > + /* > + * The firmware will do the rounding but that isn't part of > + * the interface with the firmware, so we just do our best > + * here. > + */ > + req->rate = clamp(req->rate, req->min_rate, req->max_rate); > + return 0; > +} > + > +static const struct clk_ops raspberrypi_firmware_clk_ops = { > + .is_prepared = raspberrypi_fw_is_prepared, > + .recalc_rate = raspberrypi_fw_get_rate, > + .determine_rate = raspberrypi_fw_dumb_determine_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 = {}; > + u32 min_rate, max_rate; > + 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 = raspberrypi_clock_property(rpi->firmware, data, > + RPI_FIRMWARE_GET_MIN_CLOCK_RATE, > + &min_rate); > + if (ret) { > + dev_err(rpi->dev, "Failed to get clock %d min freq: %d", > + id, ret); > + return ERR_PTR(ret); > + } > + > + ret = raspberrypi_clock_property(rpi->firmware, data, > + RPI_FIRMWARE_GET_MAX_CLOCK_RATE, > + &max_rate); > + if (ret) { > + dev_err(rpi->dev, "Failed to get clock %d max freq: %d\n", > + id, ret); > + return ERR_PTR(ret); > + } > + > + dev_info(rpi->dev, "Clock %d frequency range: min %u, max %u\n", > + id, min_rate, max_rate); This outputs: [ 9.071965] raspberrypi-clk soc:firmware:clocks: Clock arm frequency range: min 600000000, max 1500000000 [ 9.086115] raspberrypi-clk soc:firmware:clocks: Clock core frequency range: min 200000000, max 500000000 [ 9.102418] raspberrypi-clk soc:firmware:clocks: Clock v3d frequency range: min 250000000, max 500000000 [ 9.120260] raspberrypi-clk soc:firmware:clocks: Clock m2mc frequency range: min 0, max 600000000 I think, arm aside, it's pretty useless. I'd either print it only for arm or drop it altogether. > + > + ret = devm_clk_hw_register(rpi->dev, &data->hw); > + if (ret) > + return ERR_PTR(ret); > + > + clk_hw_set_rate_range(&data->hw, min_rate, max_rate); > + > + if (id == RPI_FIRMWARE_ARM_CLK_ID) { > + ret = devm_clk_hw_register_clkdev(rpi->dev, &data->hw, > + NULL, "cpu0"); > + if (ret) { > + dev_err(rpi->dev, "Failed to initialize clkdev\n"); > + 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), RPI_FIRMWARE_NUM_CLK_ID, > + GFP_KERNEL); > + if (!clks) > + return -ENOMEM; > + > + ret = rpi_firmware_property(rpi->firmware, RPI_FIRMWARE_GET_CLOCKS, > + clks, > + sizeof(*clks) * RPI_FIRMWARE_NUM_CLK_ID); > + if (ret) > + return ret; > + > + while (clks->id) { > + struct clk_hw *hw; > + > + switch (clks->id) { > + case RPI_FIRMWARE_ARM_CLK_ID: > + case RPI_FIRMWARE_CORE_CLK_ID: > + case RPI_FIRMWARE_M2MC_CLK_ID: > + case RPI_FIRMWARE_V3D_CLK_ID: > + 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; > + fallthrough; > + > + default: > + clks++; > + break; > + } > + } > + > + return 0; > +} > + > static int raspberrypi_clk_probe(struct platform_device *pdev) > { > struct clk_hw_onecell_data *clk_data; > @@ -303,7 +439,6 @@ static int raspberrypi_clk_probe(struct platform_device > *pdev) > struct device *dev = &pdev->dev; > struct rpi_firmware *firmware; > struct raspberrypi_clk *rpi; > - struct clk_hw *hw; > int ret; > > /* > @@ -340,17 +475,9 @@ static int raspberrypi_clk_probe(struct platform_device > *pdev) > 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); > diff --git a/include/soc/bcm2835/raspberrypi-firmware.h > b/include/soc/bcm2835/raspberrypi-firmware.h > index 3025aca3c358..1c48f8269eab 100644 > --- a/include/soc/bcm2835/raspberrypi-firmware.h > +++ b/include/soc/bcm2835/raspberrypi-firmware.h > @@ -136,6 +136,11 @@ enum rpi_firmware_property_tag { > RPI_FIRMWARE_GET_DMA_CHANNELS = 0x00060001, > }; > > +struct rpi_firmware_get_clocks_response { > + __le32 parent; > + __le32 id; > +}; > + As per other firmware consumer drivers, this should be moved into clk-raspberrypi.c. Also I'd switch to using u32. It's pretty clear this will be used only on this platform, so no need to worry about endianness (also common practice among rpi firmware consumer drivers). > #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE) > int rpi_firmware_property(struct rpi_firmware *fw, > u32 tag, void *data, size_t len); Here's the clk_summary output: enable prepare protect duty clock count count count rate accuracy phase cycle --------------------------------------------------------------------------------------------- fw-clk-m2mc 0 0 0 149984992 0 0 50000 fw-clk-v3d 0 0 0 250000000 0 0 50000 fw-clk-core 0 0 0 200000000 0 0 50000 fw-clk-arm 0 0 0 1500000000 0 0 50000 otg 0 0 0 480000000 0 0 50000 osc 2 2 2 54000000 0 0 50000 tsens 0 0 0 3375000 0 0 50000 otp 0 0 0 13500000 0 0 50000 timer 0 0 0 1000000 0 0 50000 plld 1 1 1 2999999988 0 0 50000 plld_dsi1 0 0 0 11718750 0 0 50000 plld_dsi0 0 0 0 11718750 0 0 50000 plld_per 5 5 4 749999997 0 0 50000 pwm 1 1 1 9999999 0 0 50000 emmc2 1 1 1 99999999 0 0 50000 emmc 1 1 1 249999999 0 0 50000 uart 1 1 1 47999999 0 0 50000 plld_core 0 0 0 599999998 0 0 50000 pllc 1 1 1 3000000091 0 0 50000 pllc_per 0 0 0 600000019 0 0 50000 pllc_core2 0 0 0 11718751 0 0 50000 pllc_core1 0 0 0 11718751 0 0 50000 pllc_core0 1 1 1 1000000031 0 0 50000 vpu 5 5 2 200000006 0 0 50000 fe804000.i2c_div 1 1 1 99901 0 0 50000 fe205000.i2c_div 1 1 1 99901 0 0 50000 aux_spi2 0 0 0 200000006 0 0 50000 aux_spi1 0 0 0 200000006 0 0 50000 aux_uart 1 1 0 200000006 0 0 50000 peri_image 0 0 0 200000006 0 0 50000 pllb 0 0 0 2999999988 0 0 50000 pllb_arm 0 0 0 599999998 0 0 50000 plla 0 0 0 2999999988 0 0 50000 plla_ccp2 0 0 0 11718750 0 0 50000 plla_dsi0 0 0 0 11718750 0 0 50000 plla_per 0 0 0 11718750 0 0 50000 plla_core 0 0 0 499999998 0 0 50000 h264 0 0 0 249999999 0 0 50000 isp 0 0 0 249999999 0 0 50000 v3d 0 0 0 249999999 0 0 50000 [...] arm clocks don't match. CCF is caching the mmio version of the clocks, I think we need to add CLK_GET_RATE_NOCACHE to every clock that's being handled by firmware. Otherwise I verified that cpufreq registers/behaves as expected. Regards, Nicolas [-- Attachment #2: This is a digitally signed message part --] [-- Type: application/pgp-signature, Size: 488 bytes --]