From: chenxing Date: Wed, 20 Mar 2013 06:29:15 +0000 (+0800) Subject: rk: add /sys/pm_tests/tools support X-Git-Tag: firefly_0821_release~7380 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=62e801f1f8018d14f2fcf00fee3ee2ecd277a025;p=firefly-linux-kernel-4.4.55.git rk: add /sys/pm_tests/tools support --- diff --git a/arch/arm/plat-rk/Kconfig b/arch/arm/plat-rk/Kconfig index 8d7aba6a812c..88f3964b6136 100755 --- a/arch/arm/plat-rk/Kconfig +++ b/arch/arm/plat-rk/Kconfig @@ -113,6 +113,8 @@ config RK_CLOCK_PROC bool "/proc/clocks support" depends on PROC_FS +source arch/arm/plat-rk/rk_pm_tests/Kconfig + if !ARCH_RK29 menu "Support for RK power manage" diff --git a/arch/arm/plat-rk/Makefile b/arch/arm/plat-rk/Makefile index 2544a3253553..0dc74c95bc74 100755 --- a/arch/arm/plat-rk/Makefile +++ b/arch/arm/plat-rk/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_DDR_FREQ) += ddr_freq.o obj-$(CONFIG_DVFS) += dvfs.o obj-y += pwm.o obj-$(CONFIG_RK_TIMER) += rk_timer.o +obj-$(CONFIG_RK_PM_TESTS) += rk_pm_tests/ diff --git a/arch/arm/plat-rk/rk_pm_tests/Kconfig b/arch/arm/plat-rk/rk_pm_tests/Kconfig new file mode 100755 index 000000000000..a9ad24fc655d --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/Kconfig @@ -0,0 +1,29 @@ +menuconfig RK_PM_TESTS + bool "/sys/pm_tests/ support" + help + PM tests tools + +if RK_PM_TESTS + +config PM_TEST_CLK_RATE + bool "Scale clock rate" + default y + help + Use to set clock rate + +config PM_TEST_CLK_VOLT + bool "Scale voltage" + default y + help + Use to set voltage domain's voltage + +config PM_TEST_FREQ_LIMIT + bool "Limit clocks' frequency" + help + Use to limit clocks' frequency + +config PM_TEST_CPU_USAGE + bool "Set cpu running at a fixed frequency" + help + Use to Set cpu running at a fixed frequency +endif diff --git a/arch/arm/plat-rk/rk_pm_tests/Makefile b/arch/arm/plat-rk/rk_pm_tests/Makefile new file mode 100644 index 000000000000..1f742c866a94 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_RK_PM_TESTS) += rk_pm_tests.o + +# Optional functions +obj-$(CONFIG_PM_TEST_CLK_RATE) += clk_rate.o +obj-$(CONFIG_PM_TEST_CLK_VOLT) += clk_volt.o +obj-$(CONFIG_PM_TEST_FREQ_LIMIT) += freq_limit.o +obj-$(CONFIG_PM_TEST_CPU_USAGE) += cpu_usage.o + diff --git a/arch/arm/plat-rk/rk_pm_tests/clk_rate.c b/arch/arm/plat-rk/rk_pm_tests/clk_rate.c new file mode 100644 index 000000000000..3d102b21130a --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/clk_rate.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "rk_pm_tests.h" +#include "clk_rate.h" +/***************************************************************************/ +#define FILE_GOV_MODE "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" +#define FILE_SETSPEED "/sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed" +#define FILE_CUR_FREQ "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" + +static int file_read(char *file_path, char *buf) +{ + struct file *file = NULL; + mm_segment_t old_fs; + loff_t offset = 0; + + PM_DBG("read %s\n", file_path); + file = filp_open(file_path, O_RDONLY, 0); + + if (IS_ERR(file)) { + PM_ERR("%s error open file %s\n", __func__, file_path); + return -1; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + file->f_op->read(file, (char *)buf, 32, &offset); + sscanf(buf, "%s", buf); + + set_fs(old_fs); + filp_close(file, NULL); + + file = NULL; + + return 0; + +} + +static int file_write(char *file_path, char *buf) +{ + struct file *file = NULL; + mm_segment_t old_fs; + loff_t offset = 0; + + PM_DBG("write %s %s size = %d\n", file_path, buf, strlen(buf)); + file = filp_open(file_path, O_RDWR, 0); + + if (IS_ERR(file)) { + PM_ERR("%s error open file %s\n", __func__, file_path); + return -1; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + file->f_op->write(file, (char *)buf, strlen(buf), &offset); + + set_fs(old_fs); + filp_close(file, NULL); + + file = NULL; + + return 0; + +} + +ssize_t clk_rate_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *str = buf; + str += sprintf(str, "set [clk_name] [rate(Hz)]\n" + "reset [clk_name] [rate(Hz) = 0]\n"); + if (str != buf) + *(str - 1) = '\n'; + return (str - buf); + +} + +ssize_t clk_rate_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + char cmd[20], clk_name[20], msg[50]; + int rate; + struct clk *clk; + sscanf(buf, "%s %s %d", cmd, clk_name, &rate); + + + clk = clk_get(NULL, clk_name); + if (IS_ERR(clk)) { + PM_ERR("%s get clk %s error\n", __func__, clk_name); + return n; + } + + if (0 == strncmp(cmd, "set", strlen("set"))) { + PM_DBG("Get command set %s %dHz\n", clk_name, rate); + if (file_read(FILE_GOV_MODE, msg) != 0) { + PM_ERR("read current governor error\n"); + return n; + } else { + PM_DBG("current governor = %s\n", msg); + } + + strcpy(msg, "userspace"); + if (file_write(FILE_GOV_MODE, msg) != 0) { + PM_ERR("set current governor error\n"); + return n; + } + + dvfs_clk_enable_limit(clk, rate, rate); + clk_set_rate(clk, rate); + + } else if (0 == strncmp(cmd, "reset", strlen("reset"))) { + PM_DBG("Get command reset %s\n", clk_name); + if (file_read(FILE_GOV_MODE, msg) != 0) { + PM_ERR("read current governor error\n"); + return n; + } else { + PM_DBG("current governor = %s\n", msg); + } + + strcpy(msg, "interactive"); + if (file_write(FILE_GOV_MODE, msg) != 0) { + PM_ERR("set current governor error\n"); + return n; + } + + dvfs_clk_disable_limit(clk); + } + + return n; +} + diff --git a/arch/arm/plat-rk/rk_pm_tests/clk_rate.h b/arch/arm/plat-rk/rk_pm_tests/clk_rate.h new file mode 100644 index 000000000000..bfb649d09a36 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/clk_rate.h @@ -0,0 +1,8 @@ +#ifndef _CLK_RATE_H_ +#define _CLK_RATE_H_ +ssize_t clk_rate_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); +ssize_t clk_rate_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); + +#endif diff --git a/arch/arm/plat-rk/rk_pm_tests/clk_volt.c b/arch/arm/plat-rk/rk_pm_tests/clk_volt.c new file mode 100644 index 000000000000..ef4e7a4a10f8 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/clk_volt.c @@ -0,0 +1,60 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk_pm_tests.h" +#include "clk_volt.h" +/***************************************************************************/ +ssize_t clk_volt_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *str = buf; + str += sprintf(str, "[regulaotr_name] [voltage(uV)]\n"); + if (str != buf) + *(str - 1) = '\n'; + return (str - buf); + +} + +ssize_t clk_volt_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + char regulator_name[20]; + unsigned int volt; + int ret = 0; + struct regulator *regulator; + + sscanf(buf, "%s %u", regulator_name, &volt); + + regulator = dvfs_get_regulator(regulator_name); + if (IS_ERR(regulator)) { + PM_ERR("%s get dvfs_regulator %s error\n", __func__, regulator_name); + return n; + } + + ret = regulator_set_voltage(regulator, volt, volt); + PM_DBG("ret = %d\n", ret); + + +// if (0 == strncmp(cmd, "enable", strlen("enable"))) { + return n; +} + diff --git a/arch/arm/plat-rk/rk_pm_tests/clk_volt.h b/arch/arm/plat-rk/rk_pm_tests/clk_volt.h new file mode 100644 index 000000000000..346bd20bfea2 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/clk_volt.h @@ -0,0 +1,8 @@ +#ifndef _CLK_VOLT_H_ +#define _CLK_VOLT_H_ +ssize_t clk_volt_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); +ssize_t clk_volt_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); + +#endif diff --git a/arch/arm/plat-rk/rk_pm_tests/cpu_usage.c b/arch/arm/plat-rk/rk_pm_tests/cpu_usage.c new file mode 100644 index 000000000000..192484e8b4f1 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/cpu_usage.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "rk_pm_tests.h" +#include "cpu_usage.h" +/***************************************************************************/ +#define cru_readl(offset) readl_relaxed(RK2928_CRU_BASE + offset) +#define cru_writel(v, offset) do { writel_relaxed(v, RK2928_CRU_BASE + offset); dsb(); } while (0) + +struct workqueue_struct *workqueue_cpu_usage0; +struct work_struct work_cpu_usage0; +struct workqueue_struct *workqueue_cpu_usage1; +struct work_struct work_cpu_usage1; + + +int cpu_usage_run = 0; +int cpu_usage_percent = 0; + +struct timer_list arm_mode_timer; +#define ARM_MODE_TIMER_MSEC 100 +static void arm_mode_timer_handler(unsigned long data) +{ + int i; + PM_DBG("enter %s\n", __func__); + if (cpu_usage_run == 0) { + PM_DBG("STOP\n"); + return ; + } + + arm_mode_timer.expires = jiffies + msecs_to_jiffies(ARM_MODE_TIMER_MSEC); + add_timer(&arm_mode_timer); + for(i = 0; i < cpu_usage_percent; i++) { + mdelay(1); + } +} + +static void handler_cpu_usage(struct work_struct *work) +{ +#if 1 + while(cpu_usage_run) { + barrier(); + } +#else + del_timer(&arm_mode_timer); + arm_mode_timer.expires = jiffies + msecs_to_jiffies(ARM_MODE_TIMER_MSEC); + add_timer(&arm_mode_timer); +#endif +} +ssize_t cpu_usage_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *str = buf; + str += sprintf(str, "HELLO\n"); + if (str != buf) + *(str - 1) = '\n'; + return (str - buf); + +} +ssize_t cpu_usage_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + char cmd[20]; + int usage = 0; + sscanf(buf, "%s %d", cmd, &usage); + + if((strncmp(cmd, "start", strlen("start")) == 0)) { + PM_DBG("get cmd start\n"); + cpu_usage_run = 1; + + cpu_usage_percent = (ARM_MODE_TIMER_MSEC * usage) / 100; + if (workqueue_cpu_usage0 == NULL) { + PM_ERR("workqueue NULL\n"); + return n; + } + if (workqueue_cpu_usage1 == NULL) { + PM_ERR("workqueue NULL\n"); + return n; + } + + queue_work_on(0, workqueue_cpu_usage0, &work_cpu_usage0); + queue_work_on(1, workqueue_cpu_usage1, &work_cpu_usage1); +#if 0 + del_timer(&arm_mode_timer); + arm_mode_timer.expires = jiffies + msecs_to_jiffies(ARM_MODE_TIMER_MSEC); + add_timer(&arm_mode_timer); +#endif + + } else if (strncmp(cmd, "stop", strlen("stop")) == 0) { + PM_DBG("get cmd stop\n"); + cpu_usage_run = 0; + } + + return n; +} + +static int __init cpu_usage_init(void) +{ + workqueue_cpu_usage0 = create_singlethread_workqueue("workqueue_cpu_usage0"); + INIT_WORK(&work_cpu_usage0, handler_cpu_usage); + workqueue_cpu_usage1 = create_singlethread_workqueue("workqueue_cpu_usage1"); + INIT_WORK(&work_cpu_usage1, handler_cpu_usage); + setup_timer(&arm_mode_timer, arm_mode_timer_handler, (unsigned int)NULL); + return 0; +} +late_initcall(cpu_usage_init); diff --git a/arch/arm/plat-rk/rk_pm_tests/cpu_usage.h b/arch/arm/plat-rk/rk_pm_tests/cpu_usage.h new file mode 100644 index 000000000000..0c45ad4f4846 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/cpu_usage.h @@ -0,0 +1,8 @@ +#ifndef _CPU_USAGE_H_ +#define _CPU_USAGE_H_ +ssize_t cpu_usage_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); +ssize_t cpu_usage_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); + +#endif diff --git a/arch/arm/plat-rk/rk_pm_tests/freq_limit.c b/arch/arm/plat-rk/rk_pm_tests/freq_limit.c new file mode 100644 index 000000000000..02582adde316 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/freq_limit.c @@ -0,0 +1,65 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk_pm_tests.h" +#include "freq_limit.h" +/***************************************************************************/ +ssize_t freq_limit_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *str = buf; + str += sprintf(str, "command(enable/disable) clk_name min_rate max_rate\n"); + if (str != buf) + *(str - 1) = '\n'; + return (str - buf); + +} + +ssize_t freq_limit_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + char cmd[20], clk_name[20]; + unsigned int min_rate, max_rate; + struct clk *clk; + sscanf(buf, "%s %s %u %u", cmd, clk_name, &min_rate, &max_rate); + + clk = clk_get(NULL, clk_name); + if (IS_ERR(clk)) { + PM_ERR("get clk %s error\n", __func__); + return n; + } + + if (0 == strncmp(cmd, "enable", strlen("enable"))) { + PM_DBG("limit enable clk(%s) min(%d) max(%d)\n", clk_name, min_rate, max_rate); + dvfs_clk_enable_limit(clk, min_rate, max_rate); + clk_set_rate(clk, min_rate); + + } else if (0 == strncmp(cmd, "disable", strlen("disable"))) { + PM_DBG("limit disable clk(%s)\n", clk_name); + dvfs_clk_disable_limit(clk); + + } else { + PM_ERR("unknown command\n"); + } + return n; +} + diff --git a/arch/arm/plat-rk/rk_pm_tests/freq_limit.h b/arch/arm/plat-rk/rk_pm_tests/freq_limit.h new file mode 100644 index 000000000000..44c7c14ac2cd --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/freq_limit.h @@ -0,0 +1,8 @@ +#ifndef _FREQ_LIMIT_H_ +#define _FREQ_LIMIT_H_ +ssize_t freq_limit_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); +ssize_t freq_limit_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); + +#endif diff --git a/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.c b/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.c new file mode 100644 index 000000000000..9330c450c0e1 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.c @@ -0,0 +1,121 @@ +/* arch/arm/mach-rk30/rk_pm_tests.c + * + * Copyright (C) 2010 ROCKCHIP, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + + +/*************************************************************************/ +/* COPYRIGHT (C) ROCK-CHIPS FUZHOU . ALL RIGHTS RESERVED.*/ +/************************************************************************* +FILE : rk_pm_tests.c +DESC : Power management in dynning state +AUTHOR : chenxing +DATE : 2012-7-2 +NOTES : +$LOG: GPIO.C,V $ +REVISION 0.01 +#include +#include + ***************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include "rk_pm_tests.h" + +#include "clk_rate.h" +#include "clk_volt.h" +#include "freq_limit.h" +#include "cpu_usage.h" +//#include "rk2928_freq.h" +//#include "rk2928_max_freq.h" +//#include "cpu_calc.h" +//#include "ddr_scale_freq.h" +//#include "pmic_delay.h" +//#include "rk30_volt_diff.h" + +static struct kobject *pm_tests_kobj; +struct pm_attribute { + struct attribute attr; + ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, + char *buf); + ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n); +}; + +static struct pm_attribute pm_attrs[] = { + /* node_name permision show_func store_func*/ +#ifdef CONFIG_PM_TEST_CLK_RATE + __ATTR(clk_rate, S_IRUGO | S_IWUSR, clk_rate_show, clk_rate_store), +#endif +#ifdef CONFIG_PM_TEST_CLK_VOLT + __ATTR(clk_volt, S_IRUGO | S_IWUSR, clk_volt_show, clk_volt_store), +#endif +#ifdef CONFIG_PM_TEST_FREQ_LIMIT + __ATTR(freq_limit, S_IRUGO | S_IWUSR, freq_limit_show, freq_limit_store), +#endif +#ifdef CONFIG_PM_TEST_CPU_USAGE + __ATTR(cpu_usage, S_IRUGO | S_IWUSR, cpu_usage_show, cpu_usage_store), +#endif + +#if 0 + __ATTR(ddr_scale_freq, S_IRUGO | S_IWUSR, ddr_scale_freq_show, ddr_scale_freq_store), + __ATTR(pmic_delay, S_IRUGO | S_IWUSR, pmic_delay_show, pmic_delay_store), +#endif +// __ATTR(rk2928_freq, S_IRUGO | S_IWUSR, rk2928_freq_show, rk2928_freq_store), +// __ATTR(rk2928_max_freq, S_IRUGO | S_IWUSR, rk2928_max_freq_show, rk2928_max_freq_store), +// __ATTR(cpu_calc, S_IRUGO | S_IWUSR, cpu_calc_show, cpu_calc_store), +// __ATTR(rk30_volt_diff, S_IRUGO | S_IWUSR, rk30_volt_diff_show, rk30_volt_diff_store), +}; + +static void __exit rk_pm_tests_exit(void) +{ + kobject_put(pm_tests_kobj); +} + +static int __init rk_pm_tests_init(void) +{ + int i, ret = 0; + pm_tests_kobj = kobject_create_and_add("pm_tests", NULL); + + if (!pm_tests_kobj) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(pm_attrs); i++) { + ret = sysfs_create_file(pm_tests_kobj, &pm_attrs[i].attr); + if (ret != 0) { + PM_ERR("create index %d error\n", i); + return ret; + } + } + + return ret; +} + +late_initcall(rk_pm_tests_init); +module_exit(rk_pm_tests_exit); diff --git a/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.h b/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.h new file mode 100644 index 000000000000..d0d7a8d2aa65 --- /dev/null +++ b/arch/arm/plat-rk/rk_pm_tests/rk_pm_tests.h @@ -0,0 +1,5 @@ +#ifndef _RK_PM_TESTS_H_ +#define _RK_PM_TESTS_H_ +#define PM_DBG(fmt, args...) printk(KERN_DEBUG "PM_TESTS_DBG:\t"fmt, ##args) +#define PM_ERR(fmt, args...) printk(KERN_ERR "PM_TESTS_ERR:\t"fmt, ##args) +#endif