diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Discover-the-firmware-clocks.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Discover-the-firmware-clocks.patch | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Discover-the-firmware-clocks.patch b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Discover-the-firmware-clocks.patch new file mode 100644 index 0000000000..eaa4a81141 --- /dev/null +++ b/target/linux/bcm27xx/patches-5.4/950-0531-clk-bcm-rpi-Discover-the-firmware-clocks.patch @@ -0,0 +1,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); |