bcm2708: boot tested on RPi B+ v1.2
bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G
bcm2710: boot tested on RPi 3B v1.2
bcm2711: boot tested on RPi 4B v1.1 4G
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
(cherry-picked from commit f07e572f64)
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
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);
 |