diff mbox series

[1/4] clk/qcom: implement clk dump

Message ID 20240819-b4-qcom-clk-dump-v1-1-182cf77c23a2@linaro.org
State Accepted
Commit ba0598bdc830327f4234355ba3b9a9c6eb22f367
Headers show
Series clk/qcom: implement clk dump callback | expand

Commit Message

Caleb Connolly Aug. 19, 2024, 7:34 p.m. UTC
Add support for dumping a few of the clocks used on Qualcomm platforms.
Naming the Global PLL's, Root Clock Generators, and gate clocks.

This helps a lot with platform bringup and feature enablement by making
it easy to sanity check that the clocks are programmed correctly.

== Usage ==

Enable CONFIG_CMD_CLK and "#define LOG_DEBUG" at the top of
qcom-<soc>.c.

The "clk dump" command should print the states of all the gates, GPLLs
and RCGs for your SoC.

== Glossary ==

RCG: Root Clock Generator
* Takes in some fairly arbitrary high freq clock (configurable clock
source and options for taking just even pulses and other things)
* Output frequency = input_freq * (m/n) * (1/d) where m/n are arbitrary
8 or 16-bit values (depending on the RCG), and d is a number (with
support for .5 offsets).

GPLL: Global Phase Locked Loop
* Crystal as input
* integer multiplier + exponent part (2^-40)

Gate: Simple on/off clock
* Put between RCGs and the peripherals they power
* Required to allow for correct power sequencing

If you do the maths manually using the equations from "clk dump", the
numbers should roughly line up by they're likely to be out by a handful
of MHz. They output is formatted so that it can be pasted directly into
the python interpreter.

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
---
 drivers/clk/qcom/clock-qcom.c | 117 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/qcom/clock-qcom.h |   6 +++
 2 files changed, 123 insertions(+)
diff mbox series

Patch

diff --git a/drivers/clk/qcom/clock-qcom.c b/drivers/clk/qcom/clock-qcom.c
index 79c7606a2253..5727d055e95a 100644
--- a/drivers/clk/qcom/clock-qcom.c
+++ b/drivers/clk/qcom/clock-qcom.c
@@ -12,8 +12,9 @@ 
  * Based on Little Kernel driver, simplified
  */
 
 #include <clk-uclass.h>
+#include <linux/clk-provider.h>
 #include <dm.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
 #include <errno.h>
@@ -214,11 +215,127 @@  static int msm_clk_enable(struct clk *clk)
 
 	return 0;
 }
 
+static void dump_gplls(struct udevice *dev, phys_addr_t base)
+{
+	struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(dev);
+	u32 i;
+	bool locked;
+	u64 l, a, xo_rate = 19200000;
+	struct clk *clk = NULL;
+	struct udevice *xodev;
+	const phys_addr_t *gplls = data->dbg_pll_addrs;
+
+	uclass_foreach_dev_probe(UCLASS_CLK, xodev) {
+		if (!strcmp(xodev->name, "xo-board") || !strcmp(xodev->name, "xo_board")) {
+			clk = dev_get_clk_ptr(xodev);
+			break;
+		}
+	}
+
+	if (clk) {
+		xo_rate = clk_get_rate(clk);
+
+		/* On SDM845 this needs to be divided by 2 for some reason */
+		if (xo_rate && of_machine_is_compatible("qcom,sdm845"))
+			xo_rate /= 2;
+	} else {
+		printf("Can't find XO clock, XO_BOARD rate may be wrong\n");
+	}
+
+	printf("GPLL clocks:\n");
+	printf("| GPLL   | LOCKED | XO_BOARD  |  PLL_L     | ALPHA          |\n");
+	printf("+--------+--------+-----------+------------+----------------+\n");
+	for (i = 0; i < data->num_plls; i++) {
+		locked = !!(readl(gplls[i]) & BIT(31));
+		l = readl(gplls[i] + 4) & (BIT(16) - 1);
+		a = readq(gplls[i] + 40) & (BIT(16) - 1);
+		printf("| GPLL%-2d | %-6s | %9llu * (%#-9llx + %#-13llx  * 2 ** -40 ) / 1000000\n",
+		       i, locked ? "X" : "", xo_rate, l, a);
+	}
+}
+
+static void dump_rcgs(struct udevice *dev)
+{
+	struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(dev);
+	int i;
+	u32 cmd;
+	u32 cfg;
+	u32 not_n_minus_m;
+	u32 src, m, n, div;
+	bool root_on, d_odd;
+
+	printf("\nRCGs:\n");
+
+	/*
+	 * Which GPLL SRC corresponds to depends on the parent map, see gcc-<soc>.c in Linux
+	 * and find the parent map associated with the clock. Note that often there are multiple
+	 * outputs from a single GPLL where one is actually half the rate of the other (_EVEN).
+	 * intput_freq = associated GPLL output freq (potentially divided depending on SRC).
+	 */
+	printf("| NAME                             | ON | SRC | OUT_FREQ = input_freq * (m/n) * (1/d) | [CMD REG   ] |\n");
+	printf("+----------------------------------+----+-----+---------------------------------------+--------------+\n");
+	for (i = 0; i < data->num_rcgs; i++) {
+		cmd = readl(data->dbg_rcg_addrs[i]);
+		cfg = readl(data->dbg_rcg_addrs[i] + 0x4);
+		m = readl(data->dbg_rcg_addrs[i] + 0x8);
+		n = 0;
+		not_n_minus_m = readl(data->dbg_rcg_addrs[i] + 0xc);
+
+		root_on = !(cmd & BIT(31)); // ROOT_OFF
+		src = (cfg >> 8) & 7;
+
+		if (not_n_minus_m) {
+			n = (~not_n_minus_m & 0xffff);
+
+			/* A clumsy assumption that this is an 8-bit MND RCG */
+			if ((n & 0xff00) == 0xff00)
+				n = n & 0xff;
+
+			n += m;
+		}
+
+		div = ((cfg & 0b11111) + 1) / 2;
+		d_odd = ((cfg & 0b11111) + 1) % 2 == 1;
+		printf("%-34s | %-2s | %3d | input_freq * (%4d/%5d) * (1/%1d%-2s)   | [%#010x]\n",
+		       data->dbg_rcg_names[i], root_on ? "X" : "", src,
+		       m ?: 1, n ?: 1, div, d_odd ? ".5" : "", cmd);
+	}
+
+	printf("\n");
+}
+
+static void msm_dump_clks(struct udevice *dev)
+{
+	struct msm_clk_data *data = (struct msm_clk_data *)dev_get_driver_data(dev);
+	struct msm_clk_priv *priv = dev_get_priv(dev);
+	const struct gate_clk *sclk;
+	int val, i;
+
+	if (!data->clks) {
+		printf("No clocks\n");
+		return;
+	}
+
+	printf("Gate Clocks:\n");
+	for (i = 0; i < data->num_clks; i++) {
+		sclk = &data->clks[i];
+		if (!sclk->name)
+			continue;
+		printf("%-32s: ", sclk->name);
+		val = readl(priv->base + sclk->reg) & sclk->en_val;
+		printf("%s\n", val ? "ON" : "");
+	}
+
+	dump_gplls(dev, priv->base);
+	dump_rcgs(dev);
+}
+
 static struct clk_ops msm_clk_ops = {
 	.set_rate = msm_clk_set_rate,
 	.enable = msm_clk_enable,
+	.dump = msm_dump_clks,
 };
 
 U_BOOT_DRIVER(qcom_clk) = {
 	.name		= "qcom_clk",
diff --git a/drivers/clk/qcom/clock-qcom.h b/drivers/clk/qcom/clock-qcom.h
index f6445c8f566f..040c0cf29943 100644
--- a/drivers/clk/qcom/clock-qcom.h
+++ b/drivers/clk/qcom/clock-qcom.h
@@ -75,8 +75,14 @@  struct msm_clk_data {
 	unsigned long			num_resets;
 	const struct gate_clk		*clks;
 	unsigned long			num_clks;
 
+	const phys_addr_t		*dbg_pll_addrs;
+	unsigned long			num_plls;
+	const phys_addr_t		*dbg_rcg_addrs;
+	unsigned long			num_rcgs;
+	const char * const		*dbg_rcg_names;
+
 	int (*enable)(struct clk *clk);
 	unsigned long (*set_rate)(struct clk *clk, unsigned long rate);
 };