1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <ctype.h>
6 #include <dirent.h>
7 #include <fcntl.h>
8 #include <getopt.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <zircon/device/clk.h>
15
usage(const char * cmd)16 int usage(const char* cmd) {
17 fprintf(
18 stderr,
19 "\nInteract with clocks on the SOC:\n"
20 " %s measure Measures all clock values\n"
21 " %s measure -idx <idx> Measure CLK idx\n"
22 " %s help Print this message\n",
23 cmd,
24 cmd,
25 cmd);
26 return -1;
27 }
28
29 // Returns "true" if the argument matches the prefix.
30 // In this case, moves the argument past the prefix.
prefix_match(const char ** arg,const char * prefix)31 bool prefix_match(const char** arg, const char* prefix) {
32 if (!strncmp(*arg, prefix, strlen(prefix))) {
33 *arg += strlen(prefix);
34 return true;
35 }
36 return false;
37 }
38
39 // Gets the value of a particular field passed through
40 // command line.
getValue(int argc,char ** argv,const char * field)41 const char* getValue(int argc, char** argv, const char* field) {
42 int i = 1;
43 while (i < argc - 1 && strcmp(argv[i], field) != 0) {
44 ++i;
45 }
46 if (i >= argc - 1) {
47 printf("NULL\n");
48 return NULL;
49 } else {
50 return argv[i + 1];
51 }
52 }
53
guess_dev(void)54 char* guess_dev(void) {
55 char path[21]; // strlen("/dev/class/clock/###") + 1
56 DIR* d = opendir("/dev/class/clock");
57 if (!d) {
58 return NULL;
59 }
60
61 struct dirent* de;
62 while ((de = readdir(d)) != NULL) {
63 if (strlen(de->d_name) != 3) {
64 continue;
65 }
66
67 if (isdigit(de->d_name[0]) &&
68 isdigit(de->d_name[1]) &&
69 isdigit(de->d_name[2])) {
70 sprintf(path, "/dev/class/clock/%.3s", de->d_name);
71 closedir(d);
72 return strdup(path);
73 }
74 }
75
76 closedir(d);
77 return NULL;
78 }
79
measure_clk_util(int fd,uint32_t idx)80 int measure_clk_util(int fd, uint32_t idx) {
81 clk_freq_info_t info;
82 ssize_t rc = ioctl_clk_measure(fd, &idx, &info);
83 if (rc < 0) {
84 fprintf(stderr, "ERROR: Failed to measure clock: %zd\n", rc);
85 return rc;
86 }
87 printf("[%4d][%4d MHz] %s\n", idx, info.clk_freq, info.clk_name);
88 return 0;
89 }
90
measure_clk(const char * path,uint32_t idx,bool clk)91 int measure_clk(const char* path, uint32_t idx, bool clk) {
92 int fd = open(path, O_RDWR);
93 if (fd < 0) {
94 fprintf(stderr, "ERROR: Failed to open clock device: %d\n", fd);
95 return -1;
96 }
97
98 uint32_t num_clocks = 0;
99 ssize_t rc = ioctl_clk_get_count(fd, &num_clocks);
100 if (rc < 0) {
101 fprintf(stderr, "ERROR: Failed to get num_clocks: %zd\n", rc);
102 return rc;
103 }
104
105 if (clk) {
106 if (idx > num_clocks) {
107 fprintf(stderr, "ERROR: Invalid clock index.\n");
108 return -1;
109 }
110 return measure_clk_util(fd, idx);
111 } else {
112 for (uint32_t i = 0; i < num_clocks; i++) {
113 rc = measure_clk_util(fd, i);
114 if (rc < 0) {
115 return rc;
116 }
117 }
118 }
119 return 0;
120 }
121
main(int argc,char ** argv)122 int main(int argc, char** argv) {
123 int err = 0;
124 const char* cmd = basename(argv[0]);
125 char* path = NULL;
126 const char* index = NULL;
127 bool measure = false;
128 bool clk = false;
129 uint32_t idx = 0;
130
131 // If no arguments passed, bail out after dumping
132 // usage information.
133 if (argc == 1) {
134 return usage(cmd);
135 }
136
137 // Parse all args.
138 while (argc > 1) {
139 const char* arg = argv[1];
140 if (prefix_match(&arg, "measure")) {
141 measure = true;
142 }
143 if (prefix_match(&arg, "-idx")) {
144 index = getValue(argc, argv, "-idx");
145 clk = true;
146 if (index) {
147 idx = atoi(index);
148 } else {
149 fprintf(stderr, "Enter Valid CLK IDX.\n");
150 }
151 }
152 if (prefix_match(&arg, "help")) {
153 return usage(cmd);
154 }
155 argc--;
156 argv++;
157 }
158
159 // Get the device path.
160 path = guess_dev();
161 if (!path) {
162 fprintf(stderr, "No CLK device found.\n");
163 return usage(cmd);
164 }
165
166 // Measure the clocks.
167 if (measure) {
168 err = measure_clk(path, idx, clk);
169 if (err) {
170 printf("Measure CLK failed.\n");
171 }
172 }
173 return 0;
174 }
175