/* * Copyright (C) 2013 Allwinnertech, kevin.z.m * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include "clk-factors.h" #include "clk-periph.h" #include "clk-debugfs.h" static struct testclk_data testclk_priv; static void clktest_reg_dump(void) { unsigned int i; char dumpline[256]; pr_info("Dump Base 0x%lx Regs %d as Follow:\n", (unsigned long __force)sunxi_clk_base, (unsigned int __force)sunxi_clk_maxreg); for (i = 0; i <= sunxi_clk_maxreg;) { if (i + 12 <= sunxi_clk_maxreg) sprintf( dumpline, "[Reg %08x] %08x %08x %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4)), *((unsigned int *)((char *)sunxi_clk_base + i + 8)), *((unsigned int *)((char *)sunxi_clk_base + i + 12))); else if (i + 8 <= sunxi_clk_maxreg) sprintf( dumpline, "[Reg %08x] %08x %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4)), *((unsigned int *)((char *)sunxi_clk_base + i + 8))); else if (i + 4 <= sunxi_clk_maxreg) sprintf(dumpline, "[Reg %08x] %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4))); else sprintf( dumpline, "[Reg %08x] %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i))); pr_info("%s\n", dumpline); i = i + 16; } pr_info("\n"); if (cpus_clk_maxreg) { pr_info("Dump CPUS Base 0x%lx Regs %d as Follow:\n", (unsigned long __force)sunxi_clk_cpus_base, (unsigned int)cpus_clk_maxreg); for (i = 0; i <= cpus_clk_maxreg;) { if (i + 12 <= cpus_clk_maxreg) sprintf( dumpline, "[Reg %08x] %08x %08x %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4)), *((unsigned int *)((char *)sunxi_clk_base + i + 8)), *((unsigned int *)((char *)sunxi_clk_base + i + 12))); else if (i + 8 <= cpus_clk_maxreg) sprintf( dumpline, "[Reg %08x] %08x %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4)), *((unsigned int *)((char *)sunxi_clk_base + i + 8))); else if (i + 4 <= cpus_clk_maxreg) sprintf( dumpline, "[Reg %08x] %08x %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i)), *((unsigned int *)((char *)sunxi_clk_base + i + 4))); else sprintf( dumpline, "[Reg %08x] %08x ", i, *((unsigned int *)((char *)sunxi_clk_base + i))); pr_info("%s\n", dumpline); i = i + 16; } } pr_info("\n"); } static void clktest_process(void) { int i, j, enabled, command = 0xff; unsigned long rate; struct clk *cur_clk = NULL; struct clk *parent_clk = NULL; int ret; unsigned long start = 0; ret = kstrtoul(testclk_priv.start, 0, (unsigned long *)&start); if (start == 1) { if (!strcmp(testclk_priv.command, "getparents")) command = 1; else if (!strcmp(testclk_priv.command, "getparent")) command = 2; else if (!strcmp(testclk_priv.command, "setparent")) command = 3; else if (!strcmp(testclk_priv.command, "getrate")) command = 4; else if (!strcmp(testclk_priv.command, "setrate")) command = 5; else if (!strcmp(testclk_priv.command, "is_enabled")) command = 6; else if (!strcmp(testclk_priv.command, "disable")) command = 7; else if (!strcmp(testclk_priv.command, "enable")) command = 8; else if (!strcmp(testclk_priv.command, "dumpreg")) command = 9; else if (!strcmp(testclk_priv.command, "assert")) command = 10; else if (!strcmp(testclk_priv.command, "deassert")) command = 11; else { pr_info("Error Not support command %s\n", testclk_priv.command); command = 0xff; } if ((command != 0xff) && (command != 9)) { cur_clk = clk_get(NULL, testclk_priv.name); if (!cur_clk || IS_ERR(cur_clk)) { cur_clk = NULL; pr_info("Error Found clk %s\n", testclk_priv.name); strcpy(testclk_priv.info, "Error"); return; } switch (command) { case 1: /* getparents */ { j = 0; memset(testclk_priv.info, 0x0, sizeof(testclk_priv.info)); for (i = 0; i < cur_clk->core->num_parents; i++) { memcpy(&testclk_priv.info[j], cur_clk->core->parent_names[i], strlen(cur_clk->core ->parent_names[i])); j += strlen( cur_clk->core->parent_names[i]); testclk_priv.info[j] = ' '; j++; } testclk_priv.info[j] = 0; break; } case 2: /* getparent */ { parent_clk = clk_get_parent(cur_clk); if (!parent_clk || IS_ERR(parent_clk)) { pr_info("Error Found parent of %s\n", cur_clk->core->name); strcpy(testclk_priv.info, "Error"); } else strcpy(testclk_priv.info, parent_clk->core->name); break; } case 3: /* setparent */ { if (cur_clk->core->parent) { parent_clk = clk_get(NULL, testclk_priv.param); if (!parent_clk || IS_ERR(parent_clk)) { pr_info( "Error Found parent %s\n", testclk_priv.param); strcpy(testclk_priv.info, "Error"); } else { clk_set_parent(cur_clk, parent_clk); strcpy(testclk_priv.info, cur_clk->core->parent ->name); clk_put(parent_clk); } } else strcpy(testclk_priv.info, "Error"); break; } case 4: /* getrate */ { rate = clk_get_rate(cur_clk); if (rate) sprintf(testclk_priv.info, "%d", (unsigned int)rate); else strcpy(testclk_priv.info, "Error"); break; } case 5: /* setrate */ { ret = kstrtoul(testclk_priv.param, 0, (unsigned long *)&rate); if (rate) { clk_set_rate(cur_clk, rate); sprintf(testclk_priv.info, "%d", (unsigned int)rate); } else strcpy(testclk_priv.info, "Error"); break; } case 6: /* is_enabled */ { if (!cur_clk->core->ops->is_enabled) enabled = cur_clk->core->enable_count ? 1 : 0; else enabled = cur_clk->core->ops->is_enabled( cur_clk->core->hw); if (enabled) strcpy(testclk_priv.info, "enabled"); else strcpy(testclk_priv.info, "disabled"); break; } case 7: /* disable */ { if (!cur_clk->core->ops->is_enabled) enabled = cur_clk->core->enable_count ? 1 : 0; else enabled = cur_clk->core->ops->is_enabled( cur_clk->core->hw); if (enabled) clk_disable_unprepare(cur_clk); strcpy(testclk_priv.info, "disabled"); break; } case 8: /* enable */ { clk_prepare_enable(cur_clk); strcpy(testclk_priv.info, "enabled"); break; } case 10: /* assert */ { if (!sunxi_periph_reset_assert(cur_clk)) strcpy(testclk_priv.info, "asserted"); else strcpy(testclk_priv.info, "failed"); break; } case 11: /* deassert */ { if (!sunxi_periph_reset_deassert(cur_clk)) strcpy(testclk_priv.info, "deasserted"); else strcpy(testclk_priv.info, "failed"); break; } default: break; } if (cur_clk) clk_put(cur_clk); } else if (command == 9) { clktest_reg_dump(); strcpy(testclk_priv.info, "OK"); } } } /*##########command###############*/ static int ccudbg_command_open(struct inode *inode, struct file *file) { return 0; } static int ccudbg_command_release(struct inode *inode, struct file *file) { return 0; } static ssize_t ccudbg_command_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int len = strlen(testclk_priv.command); strcpy(testclk_priv.tmpbuf, testclk_priv.command); testclk_priv.tmpbuf[len] = 0x0A; testclk_priv.tmpbuf[len + 1] = 0x0; len = strlen(testclk_priv.tmpbuf); if (len) { if (*ppos >= len) return 0; if (count >= len) count = len; if (count > (len - *ppos)) count = (len - *ppos); if (copy_to_user((void __user *)buf, (const void *)testclk_priv.tmpbuf, (unsigned long)len)) return -EFAULT; *ppos += count; } else count = 0; return count; } static ssize_t ccudbg_command_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { if (count >= sizeof(testclk_priv.command)) return 0; if (copy_from_user(testclk_priv.command, buf, count)) return -EFAULT; if (testclk_priv.command[count - 1] == 0x0A) testclk_priv.command[count - 1] = 0; else testclk_priv.command[count] = 0; return count; } /*##########name###############*/ static int ccudbg_name_open(struct inode *inode, struct file *file) { return 0; } static int ccudbg_name_release(struct inode *inode, struct file *file) { return 0; } static ssize_t ccudbg_name_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int len = strlen(testclk_priv.name); strcpy(testclk_priv.tmpbuf, testclk_priv.name); testclk_priv.tmpbuf[len] = 0x0A; testclk_priv.tmpbuf[len + 1] = 0x0; len = strlen(testclk_priv.tmpbuf); if (len) { if (*ppos >= len) return 0; if (count >= len) count = len; if (count > (len - *ppos)) count = (len - *ppos); if (copy_to_user((void __user *)buf, (const void *)testclk_priv.tmpbuf, (unsigned long)len)) return -EFAULT; *ppos += count; } else count = 0; return count; } static ssize_t ccudbg_name_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { if (count >= sizeof(testclk_priv.name)) return 0; if (copy_from_user(testclk_priv.name, buf, count)) return -EFAULT; if (testclk_priv.name[count - 1] == 0x0A) testclk_priv.name[count - 1] = 0; else testclk_priv.name[count] = 0; return count; } /*##########start###############*/ static int ccudbg_start_open(struct inode *inode, struct file *file) { return 0; } static int ccudbg_start_release(struct inode *inode, struct file *file) { return 0; } static ssize_t ccudbg_start_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int len = strlen(testclk_priv.start); strcpy(testclk_priv.tmpbuf, testclk_priv.start); testclk_priv.tmpbuf[len] = 0x0A; testclk_priv.tmpbuf[len + 1] = 0x0; len = strlen(testclk_priv.tmpbuf); if (len) { if (*ppos >= len) return 0; if (count >= len) count = len; if (count > (len - *ppos)) count = (len - *ppos); if (copy_to_user((void __user *)buf, (const void *)testclk_priv.tmpbuf, (unsigned long)len)) return -EFAULT; *ppos += count; } else count = 0; return count; } static ssize_t ccudbg_start_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { if (count >= sizeof(testclk_priv.start)) return 0; if (copy_from_user(testclk_priv.start, buf, count)) return -EFAULT; if (testclk_priv.start[count - 1] == 0x0A) testclk_priv.start[count - 1] = 0; else testclk_priv.start[count] = 0; clktest_process(); return count; } /*##########param###############*/ static int ccudbg_param_open(struct inode *inode, struct file *file) { return 0; } static int ccudbg_param_release(struct inode *inode, struct file *file) { return 0; } static ssize_t ccudbg_param_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int len = strlen(testclk_priv.param); strcpy(testclk_priv.tmpbuf, testclk_priv.param); testclk_priv.tmpbuf[len] = 0x0A; testclk_priv.tmpbuf[len + 1] = 0x0; len = strlen(testclk_priv.tmpbuf); if (len) { if (*ppos >= len) return 0; if (count >= len) count = len; if (count > (len - *ppos)) count = (len - *ppos); if (copy_to_user((void __user *)buf, (const void *)testclk_priv.tmpbuf, (unsigned long)len)) return -EFAULT; *ppos += count; } else count = 0; return count; } static ssize_t ccudbg_param_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { if (count >= sizeof(testclk_priv.param)) return 0; if (copy_from_user(testclk_priv.param, buf, count)) return -EFAULT; if (testclk_priv.param[count - 1] == 0x0A) testclk_priv.param[count - 1] = 0; else testclk_priv.param[count] = 0; return count; } /*##########info###############*/ static int ccudbg_info_open(struct inode *inode, struct file *file) { return 0; } static int ccudbg_info_release(struct inode *inode, struct file *file) { return 0; } static ssize_t ccudbg_info_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { int len = strlen(testclk_priv.info); strcpy(testclk_priv.tmpbuf, testclk_priv.info); testclk_priv.tmpbuf[len] = 0x0A; testclk_priv.tmpbuf[len + 1] = 0x0; len = strlen(testclk_priv.tmpbuf); if (len) { if (*ppos >= len) return 0; if (count >= len) count = len; if (count > (len - *ppos)) count = (len - *ppos); if (copy_to_user((void __user *)buf, (const void *)testclk_priv.tmpbuf, (unsigned long)len)) return -EFAULT; *ppos += count; } else count = 0; return count; } static ssize_t ccudbg_info_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { if (count >= sizeof(testclk_priv.info)) return 0; if (copy_from_user(testclk_priv.info, buf, count)) return -EFAULT; if (testclk_priv.info[count - 1] == 0x0A) testclk_priv.info[count - 1] = 0; else testclk_priv.info[count] = 0; return count; } static const struct file_operations command_ops = { .write = ccudbg_command_write, .read = ccudbg_command_read, .open = ccudbg_command_open, .release = ccudbg_command_release, }; static const struct file_operations name_ops = { .write = ccudbg_name_write, .read = ccudbg_name_read, .open = ccudbg_name_open, .release = ccudbg_name_release, }; static const struct file_operations start_ops = { .write = ccudbg_start_write, .read = ccudbg_start_read, .open = ccudbg_start_open, .release = ccudbg_start_release, }; static const struct file_operations param_ops = { .write = ccudbg_param_write, .read = ccudbg_param_read, .open = ccudbg_param_open, .release = ccudbg_param_release, }; static const struct file_operations info_ops = { .write = ccudbg_info_write, .read = ccudbg_info_read, .open = ccudbg_info_open, .release = ccudbg_info_release, }; static int __init debugfs_test_init(void) { my_ccudbg_root = debugfs_create_dir("ccudbg", NULL); if (!debugfs_create_file("command", 0644, my_ccudbg_root, NULL, &command_ops)) goto Fail; if (!debugfs_create_file("name", 0644, my_ccudbg_root, NULL, &name_ops)) goto Fail; if (!debugfs_create_file("start", 0644, my_ccudbg_root, NULL, &start_ops)) goto Fail; if (!debugfs_create_file("param", 0644, my_ccudbg_root, NULL, ¶m_ops)) goto Fail; if (!debugfs_create_file("info", 0644, my_ccudbg_root, NULL, &info_ops)) goto Fail; return 0; Fail: debugfs_remove_recursive(my_ccudbg_root); my_ccudbg_root = NULL; return -ENOENT; } late_initcall(debugfs_test_init);