diff options
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 407f6919604c..341710d3f868 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include <linux/pm_runtime.h> #include <linux/sched.h> #include <linux/clkdev.h> +#include <linux/uaccess.h> #include "clk.h" @@ -3193,6 +3194,116 @@ static int clk_max_rate_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(clk_max_rate); +static ssize_t clock_freq_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char buf[32]; + unsigned int len; + struct clk_core *core = file->f_inode->i_private; + u32 clk_cur_frq = clk_core_get_rate_recalc(core); + + len = snprintf(buf, sizeof(buf), "%u\n", clk_cur_frq); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t clock_freq_set(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[32]; + ssize_t len; + struct clk_core *core = file->f_inode->i_private; + u32 new_clk_frq; + /* No parent */ + unsigned long parent_rate = 0; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + /* Make the buffer a valid string that we can not overrun */ + buf[len] = '\0'; + if (kstrtouint(buf, 0, &new_clk_frq)) + return -EINVAL; + + core->ops->set_rate(core->hw, new_clk_frq, parent_rate); + return count; +} + +static int clock_freq_dummy(struct seq_file *s, void *unused) +{ + return 0; +} + +static int clock_freq_open(struct inode *inode, struct file *file) +{ + return single_open(file, clock_freq_dummy, + inode->i_private); +} + +/* File operations for reading or setting various device clock frequencies */ +static const struct file_operations clock_freq_fops = { + .open = clock_freq_open, + .read = clock_freq_get, + .write = clock_freq_set, + .llseek = seq_lseek, + .release = single_release, +}; + +int clk_core_get_available_rate(struct clk_core *core, u64 *rates) +{ + if (core->ops->get_available_rates) + return core->ops->get_available_rates(core->hw, rates); + return 0; +} + +static ssize_t available_clock_freq_get(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buf[256]; + u64 rates_buf[16]; + u32 index = 0, i; + int no_of_freqs; + + struct clk_core *core = file->f_inode->i_private; + + no_of_freqs = clk_core_get_available_rate(core, rates_buf); + + /* No freq found */ + if (no_of_freqs <= 0 || no_of_freqs > 16) { + pr_err("Fails to get available frequencies\n"); + return -EINVAL; + } + + for (i = 0; i < no_of_freqs; i++) + index += snprintf(&buf[index], + sizeof(buf), "%llu ", rates_buf[i]); + + index += snprintf(&buf[index], sizeof(buf), "\n"); + return simple_read_from_buffer(user_buf, count, ppos, buf, index); +} + +static int available_clock_freq_dummy(struct seq_file *s, void *unused) +{ + return 0; +} + +static int available_clock_freq_open(struct inode *inode, struct file *file) +{ + return single_open(file, available_clock_freq_dummy, + inode->i_private); +} + +/* + * File operations for reading all valid possible frequencies the device + * clock could support. + */ +static const struct file_operations available_clock_freq_fops = { + .open = available_clock_freq_open, + .read = available_clock_freq_get, + .llseek = seq_lseek, + .release = single_release, +}; + static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { struct dentry *root; @@ -3205,6 +3316,8 @@ static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) debugfs_create_file("clk_rate", clk_rate_mode, root, core, &clk_rate_fops); + debugfs_create_file("clk_available_freq", 0444, root, core, + &available_clock_freq_fops); debugfs_create_file("clk_min_rate", 0444, root, core, &clk_min_rate_fops); debugfs_create_file("clk_max_rate", 0444, root, core, &clk_max_rate_fops); debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy); |