// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include int usage(const char* cmd) { fprintf( stderr, "\nInteract with clocks on the SOC:\n" " %s measure Measures all clock values\n" " %s measure -idx Measure CLK idx\n" " %s help Print this message\n", cmd, cmd, cmd); return -1; } // Returns "true" if the argument matches the prefix. // In this case, moves the argument past the prefix. bool prefix_match(const char** arg, const char* prefix) { if (!strncmp(*arg, prefix, strlen(prefix))) { *arg += strlen(prefix); return true; } return false; } // Gets the value of a particular field passed through // command line. const char* getValue(int argc, char** argv, const char* field) { int i = 1; while (i < argc - 1 && strcmp(argv[i], field) != 0) { ++i; } if (i >= argc - 1) { printf("NULL\n"); return NULL; } else { return argv[i + 1]; } } char* guess_dev(void) { char path[21]; // strlen("/dev/class/clock/###") + 1 DIR* d = opendir("/dev/class/clock"); if (!d) { return NULL; } struct dirent* de; while ((de = readdir(d)) != NULL) { if (strlen(de->d_name) != 3) { continue; } if (isdigit(de->d_name[0]) && isdigit(de->d_name[1]) && isdigit(de->d_name[2])) { sprintf(path, "/dev/class/clock/%.3s", de->d_name); closedir(d); return strdup(path); } } closedir(d); return NULL; } int measure_clk_util(int fd, uint32_t idx) { clk_freq_info_t info; ssize_t rc = ioctl_clk_measure(fd, &idx, &info); if (rc < 0) { fprintf(stderr, "ERROR: Failed to measure clock: %zd\n", rc); return rc; } printf("[%4d][%4d MHz] %s\n", idx, info.clk_freq, info.clk_name); return 0; } int measure_clk(const char* path, uint32_t idx, bool clk) { int fd = open(path, O_RDWR); if (fd < 0) { fprintf(stderr, "ERROR: Failed to open clock device: %d\n", fd); return -1; } uint32_t num_clocks = 0; ssize_t rc = ioctl_clk_get_count(fd, &num_clocks); if (rc < 0) { fprintf(stderr, "ERROR: Failed to get num_clocks: %zd\n", rc); return rc; } if (clk) { if (idx > num_clocks) { fprintf(stderr, "ERROR: Invalid clock index.\n"); return -1; } return measure_clk_util(fd, idx); } else { for (uint32_t i = 0; i < num_clocks; i++) { rc = measure_clk_util(fd, i); if (rc < 0) { return rc; } } } return 0; } int main(int argc, char** argv) { int err = 0; const char* cmd = basename(argv[0]); char* path = NULL; const char* index = NULL; bool measure = false; bool clk = false; uint32_t idx = 0; // If no arguments passed, bail out after dumping // usage information. if (argc == 1) { return usage(cmd); } // Parse all args. while (argc > 1) { const char* arg = argv[1]; if (prefix_match(&arg, "measure")) { measure = true; } if (prefix_match(&arg, "-idx")) { index = getValue(argc, argv, "-idx"); clk = true; if (index) { idx = atoi(index); } else { fprintf(stderr, "Enter Valid CLK IDX.\n"); } } if (prefix_match(&arg, "help")) { return usage(cmd); } argc--; argv++; } // Get the device path. path = guess_dev(); if (!path) { fprintf(stderr, "No CLK device found.\n"); return usage(cmd); } // Measure the clocks. if (measure) { err = measure_clk(path, idx, clk); if (err) { printf("Measure CLK failed.\n"); } } return 0; }