clk: add a clk debug command

Add a command and associated op to allow clock drivers to implement
their own debug commands. Some clock controller hardware implements
debug features like support for measuring clock output rates. This
allows using a common entrypoint to driver-specific clock debugging
functionality.

Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
diff --git a/cmd/clk.c b/cmd/clk.c
index 491f214..f1e7e91 100644
--- a/cmd/clk.c
+++ b/cmd/clk.c
@@ -10,6 +10,7 @@
 #include <dm/device.h>
 #include <dm/root.h>
 #include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
 #include <linux/clk-provider.h>
 #endif
 
@@ -95,6 +96,36 @@
 	return ret;
 }
 
+static int do_clk_debug(struct cmd_tbl *cmdtp, int flag, int argc,
+		       char *const argv[])
+{
+	int ret;
+	struct udevice *dev;
+
+	for (ret = 0; ret < argc; ret++) {
+		printf("%s ", argv[ret]);
+	}
+	printf("\n\n");
+
+	if (argc < 2) {
+		printf("clk debug <device>\n");
+		return 0;
+	}
+
+	ret = uclass_find_device_by_name(UCLASS_CLK, argv[1], &dev);
+	if (ret < 0) {
+		ret = uclass_find_device_by_seq(UCLASS_CLK, dectoul(argv[1], NULL), &dev);
+		if (ret < 0) {
+			printf("Can't find device '%s'\n", argv[1]);
+			return ret;
+		}
+	}
+
+	clk_debug_clks(dev, argc-1, argv+1);
+
+	return ret;
+}
+
 #if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(CLK)
 static int do_clk_setfreq(struct cmd_tbl *cmdtp, int flag, int argc,
 			  char *const argv[])
@@ -129,6 +160,9 @@
 
 static struct cmd_tbl cmd_clk_sub[] = {
 	U_BOOT_CMD_MKENT(dump, 1, 1, do_clk_dump, "", ""),
+	U_BOOT_CMD_MKENT(debug, CONFIG_SYS_MAXARGS, 1, do_clk_debug,
+		"debug <device name> [args...]",
+		"Call the debug handler for the given clock device"),
 #if CONFIG_IS_ENABLED(DM) && CONFIG_IS_ENABLED(CLK)
 	U_BOOT_CMD_MKENT(setfreq, 3, 1, do_clk_setfreq, "", ""),
 #endif
@@ -156,6 +190,7 @@
 
 U_BOOT_LONGHELP(clk,
 	"dump - Print clock frequencies\n"
+	"debug - invoke platform specific debug commands\n"
 	"clk setfreq [clk] [freq] - Set clock frequency");
 
-U_BOOT_CMD(clk, 4, 1, do_clk, "CLK sub-system", clk_help_text);
+U_BOOT_CMD(clk, CONFIG_SYS_MAXARGS, 1, do_clk, "CLK sub-system", clk_help_text);
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 2d7d8fe..807c326 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -682,6 +682,17 @@
 		ops->dump_clks(dev);
 }
 
+void clk_debug_clks(struct udevice *dev, int argc, char *const argv[])
+{
+	const struct clk_ops *ops;
+
+	ops = clk_dev_ops(dev);
+	if (ops && ops->debug_clks)
+		ops->debug_clks(dev, argc, argv);
+	else
+		printf("No debug op for %s\n", dev->name);
+}
+
 int clk_enable_bulk(struct clk_bulk *bulk)
 {
 	int i, ret;
diff --git a/include/clk-uclass.h b/include/clk-uclass.h
index e26fce3..a56ed25 100644
--- a/include/clk-uclass.h
+++ b/include/clk-uclass.h
@@ -40,6 +40,7 @@
 	int (*enable)(struct clk *clk);
 	int (*disable)(struct clk *clk);
 	void (*dump_clks)(struct udevice *dev);
+	void (*debug_clks)(struct udevice *dev, int argc, char *const argv[]);
 };
 
 #if 0 /* For documentation only */
diff --git a/include/clk.h b/include/clk.h
index bfdad96..3c19e82 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -551,6 +551,8 @@
 
 void clk_dump_clks(struct udevice *dev);
 
+void clk_debug_clks(struct udevice *dev, int argc, char *const argv[]);
+
 /**
  * clk_disable_bulk() - Disable (turn off) all clocks in a clock bulk struct.
  * @bulk:	A clock bulk struct that was previously successfully requested