1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2013 Xilinx, Inc.
4  */
5 #include <command.h>
6 #include <clk.h>
7 #include <dm.h>
8 #include <dm/device.h>
9 #include <dm/root.h>
10 #include <dm/device-internal.h>
11 #include <linux/clk-provider.h>
12 
show_clks(struct udevice * dev,int depth,int last_flag)13 static void show_clks(struct udevice *dev, int depth, int last_flag)
14 {
15 	int i, is_last;
16 	struct udevice *child;
17 	struct clk *clkp, *parent;
18 	u32 rate;
19 
20 	clkp = dev_get_clk_ptr(dev);
21 	if (clkp) {
22 		parent = clk_get_parent(clkp);
23 		if (!IS_ERR(parent) && depth == -1)
24 			return;
25 		depth++;
26 		rate = clk_get_rate(clkp);
27 
28 		printf(" %-12u  %8d        ", rate, clkp->enable_count);
29 
30 		for (i = depth; i >= 0; i--) {
31 			is_last = (last_flag >> i) & 1;
32 			if (i) {
33 				if (is_last)
34 					printf("    ");
35 				else
36 					printf("|   ");
37 			} else {
38 				if (is_last)
39 					printf("`-- ");
40 				else
41 					printf("|-- ");
42 			}
43 		}
44 
45 		printf("%s\n", dev->name);
46 	}
47 
48 	device_foreach_child_probe(child, dev) {
49 		if (device_get_uclass_id(child) != UCLASS_CLK)
50 			continue;
51 		if (child == dev)
52 			continue;
53 		is_last = list_is_last(&child->sibling_node, &dev->child_head);
54 		show_clks(child, depth, (last_flag << 1) | is_last);
55 	}
56 }
57 
soc_clk_dump(void)58 static int soc_clk_dump(void)
59 {
60 	struct udevice *dev;
61 	const struct clk_ops *ops;
62 
63 	printf(" Rate               Usecnt      Name\n");
64 	printf("------------------------------------------\n");
65 
66 	uclass_foreach_dev_probe(UCLASS_CLK, dev)
67 		show_clks(dev, -1, 0);
68 
69 	uclass_foreach_dev_probe(UCLASS_CLK, dev) {
70 		ops = dev_get_driver_ops(dev);
71 		if (ops && ops->dump) {
72 			printf("\n%s %s:\n", dev->driver->name, dev->name);
73 			ops->dump(dev);
74 		}
75 	}
76 
77 	return 0;
78 }
79 
do_clk_dump(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])80 static int do_clk_dump(struct cmd_tbl *cmdtp, int flag, int argc,
81 		       char *const argv[])
82 {
83 	int ret;
84 
85 	ret = soc_clk_dump();
86 	if (ret < 0) {
87 		printf("Clock dump error %d\n", ret);
88 		ret = CMD_RET_FAILURE;
89 	}
90 
91 	return ret;
92 }
93 
do_clk_setfreq(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])94 static int do_clk_setfreq(struct cmd_tbl *cmdtp, int flag, int argc,
95 			  char *const argv[])
96 {
97 	struct clk *clk = NULL;
98 	s32 freq;
99 	struct udevice *dev;
100 
101 	if (argc != 3)
102 		return CMD_RET_USAGE;
103 
104 	freq = dectoul(argv[2], NULL);
105 
106 	if (!uclass_get_device_by_name(UCLASS_CLK, argv[1], &dev))
107 		clk = dev_get_clk_ptr(dev);
108 
109 	if (!clk) {
110 		printf("clock '%s' not found.\n", argv[1]);
111 		return CMD_RET_FAILURE;
112 	}
113 
114 	freq = clk_set_rate(clk, freq);
115 	if (freq < 0) {
116 		printf("set_rate failed: %d\n", freq);
117 		return CMD_RET_FAILURE;
118 	}
119 
120 	printf("set_rate returns %u\n", freq);
121 	return 0;
122 }
123 
124 static struct cmd_tbl cmd_clk_sub[] = {
125 	U_BOOT_CMD_MKENT(dump, 1, 1, do_clk_dump, "", ""),
126 	U_BOOT_CMD_MKENT(setfreq, 3, 1, do_clk_setfreq, "", ""),
127 };
128 
do_clk(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])129 static int do_clk(struct cmd_tbl *cmdtp, int flag, int argc,
130 		  char *const argv[])
131 {
132 	struct cmd_tbl *c;
133 
134 	if (argc < 2)
135 		return CMD_RET_USAGE;
136 
137 	/* Strip off leading 'clk' command argument */
138 	argc--;
139 	argv++;
140 
141 	c = find_cmd_tbl(argv[0], &cmd_clk_sub[0], ARRAY_SIZE(cmd_clk_sub));
142 
143 	if (c)
144 		return c->cmd(cmdtp, flag, argc, argv);
145 	else
146 		return CMD_RET_USAGE;
147 }
148 
149 U_BOOT_LONGHELP(clk,
150 	"dump - Print clock frequencies\n"
151 	"clk setfreq [clk] [freq] - Set clock frequency");
152 
153 U_BOOT_CMD(clk, 4, 1, do_clk, "CLK sub-system", clk_help_text);
154