]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
clk/qcom: implement clk dump
authorCaleb Connolly <caleb.connolly@linaro.org>
Mon, 19 Aug 2024 19:34:17 +0000 (21:34 +0200)
committerCaleb Connolly <caleb.connolly@linaro.org>
Fri, 4 Oct 2024 12:57:03 +0000 (14:57 +0200)
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
drivers/clk/qcom/clock-qcom.h

index 79c7606a2253d43d4e42f6100f91d4b82c2193a7..25ca67e537d112dda236837d3d3984f9b666365e 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <clk-uclass.h>
+#include <linux/clk-provider.h>
 #include <dm.h>
 #include <dm/device-internal.h>
 #include <dm/lists.h>
@@ -215,9 +216,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 __maybe_unused 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,
+#if IS_ENABLED(CONFIG_CMD_CLK)
+       .dump = msm_dump_clks,
+#endif
 };
 
 U_BOOT_DRIVER(qcom_clk) = {
index 7aa6ca59aad553ad2551c624d662482468f02e7e..78d9b1d81ece1b3dd96b7bd0ab1a69fa016523b6 100644 (file)
@@ -77,6 +77,12 @@ struct msm_clk_data {
        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);
 };