| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * cpufreq-dbg-lite.c - eem debug driver |
| * |
| * Copyright (c) 2020 MediaTek Inc. |
| * Tungchen Shih <tungchen.shih@mediatek.com> |
| */ |
| |
| /* system includes */ |
| #include <linux/cpu.h> |
| #include <linux/cpufreq.h> |
| #include <linux/delay.h> |
| #include <linux/init.h> |
| #include <linux/io.h> |
| #include <linux/kernel.h> |
| #include <linux/kobject.h> |
| #include <linux/module.h> |
| #include <linux/of_address.h> |
| #include <linux/of_device.h> |
| #include <linux/of_platform.h> |
| #include <linux/platform_device.h> |
| #include <linux/pm_opp.h> |
| #include <linux/proc_fs.h> |
| #include <linux/sched.h> |
| #include <linux/seq_file.h> |
| #include <linux/uaccess.h> |
| #include "../mcupm/include/mcupm_driver.h" |
| #include "../mcupm/include/mcupm_ipi_id.h" |
| #include "cpufreq-dbg-lite.h" |
| |
| #ifdef pr_fmt |
| #undef pr_fmt |
| #endif |
| #define pr_fmt(fmt) "[cpudvfs]: " fmt |
| |
| u32 *g_cpufreq_debug; |
| |
| unsigned int cpufreq_debug_cpu; |
| static int cpufreq_debug_proc_show(struct seq_file *m, void *v) |
| { |
| struct cpufreq_policy *policy; |
| |
| if (cpufreq_debug_cpu >= 8) { |
| seq_printf(m, "cpu%u is invalid!\n", cpufreq_debug_cpu); |
| return 0; |
| } |
| |
| policy = cpufreq_cpu_get(cpufreq_debug_cpu); |
| if (policy == NULL) { |
| seq_printf(m, "policy of cpu%u is null!\n", cpufreq_debug_cpu); |
| return 0; |
| } |
| |
| seq_printf(m, "cpu%u freq[%u]: policy min[%u] max[%u], cpuinfo min[%u] max[%u]\n", |
| cpufreq_debug_cpu, |
| policy->cur, |
| policy->min, |
| policy->max, |
| policy->cpuinfo.min_freq, |
| policy->cpuinfo.max_freq); |
| cpufreq_cpu_put(policy); |
| |
| return 0; |
| } |
| |
| static ssize_t cpufreq_debug_proc_write(struct file *file, |
| const char __user *buffer, size_t count, loff_t *pos) |
| { |
| int cpu = 0, min = 0, max = 0; |
| unsigned long MHz; |
| unsigned long mHz; |
| char *buf = (char *) __get_free_page(GFP_USER); |
| struct device *cpu_dev = get_cpu_device(cpu); |
| struct cpufreq_policy *policy; |
| |
| if (copy_from_user(buf, buffer, count)) { |
| free_page((unsigned long)buf); |
| return -EINVAL; |
| } |
| |
| if (sscanf(buf, "%d %d %d", &cpu, &min, &max) != 3) { |
| cpufreq_debug_cpu = cpu; |
| free_page((unsigned long)buf); |
| return -EINVAL; |
| } |
| |
| free_page((unsigned long)buf); |
| if (min <= 0 || max <= 0) |
| return -EINVAL; |
| MHz = max; |
| mHz = min; |
| MHz = MHz * 1000; |
| mHz = mHz * 1000; |
| dev_pm_opp_find_freq_floor(cpu_dev, &MHz); |
| dev_pm_opp_find_freq_floor(cpu_dev, &mHz); |
| |
| max = (unsigned int)(MHz / 1000); |
| min = (unsigned int)(mHz / 1000); |
| |
| policy = cpufreq_cpu_get(cpu); |
| if (policy == NULL) |
| return count; |
| down_write(&policy->rwsem); |
| policy->cpuinfo.max_freq = max; |
| policy->cpuinfo.min_freq = min; |
| up_write(&policy->rwsem); |
| cpufreq_cpu_put(policy); |
| /* cpufreq_update_limits(cpu); */ |
| return count; |
| } |
| |
| PROC_FOPS_RW(cpufreq_debug); |
| |
| static int create_cpufreq_debug_fs(void) |
| { |
| struct proc_dir_entry *dir = NULL; |
| int i; |
| |
| struct pentry { |
| const char *name; |
| const struct proc_ops *fops; |
| void *data; |
| }; |
| |
| const struct pentry entries[] = { |
| PROC_ENTRY_DATA(cpufreq_debug), |
| }; |
| |
| /* create /proc/cpudvfs */ |
| dir = proc_mkdir("cpudvfs", NULL); |
| if (!dir) { |
| pr_info("fail to create /proc/cpudvfs @ %s()\n", |
| __func__); |
| return -ENOMEM; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(entries); i++) { |
| if (!proc_create_data |
| (entries[i].name, 0664, |
| dir, entries[i].fops, NULL)) |
| pr_info("%s(), create /proc/cpudvfs/%s failed\n", |
| __func__, entries[0].name); |
| } |
| return 0; |
| } |
| |
| static int mtk_cpudvfs_init(void) |
| { |
| int ret = 0; |
| struct device_node *dvfs_node; |
| struct platform_device *pdev; |
| |
| dvfs_node = of_find_node_by_name(NULL, "cpuhvfs"); |
| if (dvfs_node == NULL) { |
| pr_info("failed to find node @ %s\n", __func__); |
| return -ENODEV; |
| } |
| |
| pdev = of_find_device_by_node(dvfs_node); |
| if (pdev == NULL) { |
| pr_info("failed to find pdev @ %s\n", __func__); |
| return -EINVAL; |
| } |
| |
| create_cpufreq_debug_fs(); |
| #ifdef EEM_DBG_LITE |
| ret = mtk_eem_init(pdev); |
| if (ret) |
| pr_info("eem dbg init fail: %d\n", ret); |
| #endif |
| mtk_devinfo_init(pdev); |
| return 0; |
| } |
| module_init(mtk_cpudvfs_init); |
| |
| static void mtk_cpudvfs_exit(void) |
| { |
| } |
| module_exit(mtk_cpudvfs_exit); |
| |
| MODULE_DESCRIPTION("MTK CPU DVFS Platform Driver Helper v0.1.1"); |
| MODULE_AUTHOR("Tungchen Shih <tungchen.shih@mediatek.com>"); |
| MODULE_LICENSE("GPL v2"); |