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