From 571af4c24c0ad0e8e54459aa642c52f4143f3d3d Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=99=88=E4=BA=AE?= Date: Wed, 27 Aug 2014 06:10:33 -0700 Subject: [PATCH] rockchip: add pvtm support and pvtm test MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: 陈亮 --- arch/arm/mach-rockchip/Makefile | 1 + arch/arm/mach-rockchip/pvtm.c | 159 +++++++ arch/arm/mach-rockchip/rk_pm_tests/Makefile | 1 + .../arm/mach-rockchip/rk_pm_tests/pvtm_test.c | 409 ++++++++++++++++++ .../mach-rockchip/rk_pm_tests/rk_pm_tests.c | 5 +- 5 files changed, 574 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-rockchip/pvtm.c create mode 100644 arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile index a708831d2f4b..c76144ed67f7 100755 --- a/arch/arm/mach-rockchip/Makefile +++ b/arch/arm/mach-rockchip/Makefile @@ -6,6 +6,7 @@ obj-y += rk3188.o obj-y += rk3288.o obj-y += ddr_freq.o obj-y += efuse.o +obj-y += pvtm.o obj-y += rk_system_status.o obj-$(CONFIG_PM) += rockchip_pm.o sleep.o obj-$(CONFIG_RK_FPGA) += fpga.o diff --git a/arch/arm/mach-rockchip/pvtm.c b/arch/arm/mach-rockchip/pvtm.c new file mode 100644 index 000000000000..8bfbd814e262 --- /dev/null +++ b/arch/arm/mach-rockchip/pvtm.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2013 ROCKCHIP, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3288_PVTM_CON0 (0x368) +#define RK3288_PVTM_CON1 (0x36c) +#define RK3288_PVTM_CON2 (0x370) +#define RK3288_PVTM_STATUS0 (0x374) +#define RK3288_PVTM_STATUS1 (0x378) +#define RK3288_PVTM_STATUS2 (0x37c) + +#define RK312X_PVTM_CON0 (0x200) +#define RK312X_PVTM_CON1 (0x204) +#define RK312X_PVTM_CON2 (0x208) +#define RK312X_PVTM_CON3 (0x20c) +#define RK312X_PVTM_STATUS0 (0x210) +#define RK312X_PVTM_STATUS1 (0x214) +#define RK312X_PVTM_STATUS2 (0x218) +#define RK312X_PVTM_STATUS3 (0x21c) + +#define grf_readl(offset) readl_relaxed(RK_GRF_VIRT+offset) +#define grf_writel(val, offset) writel_relaxed(val, RK_GRF_VIRT+offset) + +#define wr_msk_bit(v, off, msk) ((v)<<(off)|(msk<<(16+(off)))) + +static struct clk *ch_clk[3]; + +static DEFINE_MUTEX(pvtm_mutex); + +/* 0 core, 1 gpu*/ +static u32 rk3288_pvtm_get_value(u32 ch , u32 time_us) +{ + u32 val = 0, clk_cnt, check_cnt, pvtm_done_bit; + + if (ch > 1) + return 0; + + /*24m clk ,24cnt=1us*/ + clk_cnt = time_us*24; + + grf_writel(clk_cnt, RK3288_PVTM_CON0+(ch+1)*4); + grf_writel(wr_msk_bit(3, ch*8, 0x3), RK3288_PVTM_CON0); + + if (time_us >= 1000) + mdelay(time_us / 1000); + udelay(time_us % 1000); + + if (ch == 0) + pvtm_done_bit = 1; + else if (ch == 1) + pvtm_done_bit = 0; + + check_cnt = 100; + while (!(grf_readl(RK3288_PVTM_STATUS0) & (1 << pvtm_done_bit))) { + udelay(4); + check_cnt--; + if (!check_cnt) + break; + } + + if (check_cnt) + val = grf_readl(RK3288_PVTM_STATUS0+(ch+1)*4); + + grf_writel(wr_msk_bit(0, ch*8, 0x3), RK3288_PVTM_CON0); + + return val; +} + +/*0 core, 1 gpu, 2 func*/ +static u32 rk312x_pvtm_get_value(u32 ch , u32 time_us) +{ + u32 val = 0, clk_cnt, check_cnt, pvtm_done_bit; + + if (ch > 2) + return 0; + + /*24m clk ,24cnt=1us*/ + clk_cnt = time_us*24; + + grf_writel(clk_cnt, RK312X_PVTM_CON0+(ch+1)*4); + if ((ch == 0) || (ch == 1)) + grf_writel(wr_msk_bit(3, ch*8, 0x3), RK312X_PVTM_CON0); + else if (ch == 2) + grf_writel(wr_msk_bit(3, 12, 0x3), RK312X_PVTM_CON0); + + if (time_us >= 1000) + mdelay(time_us / 1000); + udelay(time_us % 1000); + + if (ch == 0) + pvtm_done_bit = 1; + else if (ch == 1) + pvtm_done_bit = 0; + else if (ch == 2) + pvtm_done_bit = 2; + + check_cnt = 100; + while (!(grf_readl(RK312X_PVTM_STATUS0) & (1 << pvtm_done_bit))) { + udelay(4); + check_cnt--; + if (!check_cnt) + break; + } + + if (check_cnt) + val = grf_readl(RK312X_PVTM_STATUS0+(ch+1)*4); + if ((ch == 0) || (ch == 1)) + grf_writel(wr_msk_bit(0, ch*8, 0x3), RK312X_PVTM_CON0); + else if (ch == 2) + grf_writel(wr_msk_bit(0, 12, 0x3), RK312X_PVTM_CON0); + + return val; +} + +u32 pvtm_get_value(u32 ch, u32 time_us) +{ + u32 val = 0; + + if (IS_ERR_OR_NULL(ch_clk[ch])) + return 0; + + clk_prepare_enable(ch_clk[ch]); + mutex_lock(&pvtm_mutex); + if (cpu_is_rk3288()) + val = rk3288_pvtm_get_value(ch, time_us); + else if (cpu_is_rk312x()) + val = rk312x_pvtm_get_value(ch, time_us); + mutex_unlock(&pvtm_mutex); + clk_disable_unprepare(ch_clk[ch]); + + return val; +} + +static int __init pvtm_init(void) +{ + ch_clk[0] = clk_get(NULL, "g_clk_pvtm_core"); + ch_clk[1] = clk_get(NULL, "g_clk_pvtm_gpu"); + ch_clk[2] = clk_get(NULL, "g_clk_pvtm_func"); + + return 0; +} + +fs_initcall(pvtm_init); + diff --git a/arch/arm/mach-rockchip/rk_pm_tests/Makefile b/arch/arm/mach-rockchip/rk_pm_tests/Makefile index 0ebed8a61b38..837c38613f85 100644 --- a/arch/arm/mach-rockchip/rk_pm_tests/Makefile +++ b/arch/arm/mach-rockchip/rk_pm_tests/Makefile @@ -3,3 +3,4 @@ obj-y += clk_rate.o obj-y += clk_volt.o obj-y += cpu_usage.o obj-y += dvfs_table_scan.o +obj-y += pvtm_test.o diff --git a/arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c b/arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c new file mode 100644 index 000000000000..c9ea7d21306a --- /dev/null +++ b/arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c @@ -0,0 +1,409 @@ +/* arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c + * + * Copyright (C) 2014 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. + * + */ +#include +#include +#include +#include +#include +#include + +extern int rockchip_tsadc_get_temp(int chn); +extern void rk29_wdt_keepalive(void); +extern u32 pvtm_get_value(u32 ch, u32 time_us); +extern char *pvtm_buf; + +static const char pi_result[] = "3141592653589793238462643383279528841971693993751058209749445923078164062862089986280348253421170679821480865132823664709384469555822317253594081284811174502841270193852115559644622948954930381964428810975665933446128475648233786783165271201991456485669234634861045432664821339360726024914127372458706606315588174881520920962829254917153643678925903611330530548820466521384146951941511609433057273657595919530921861173819326117931051185480744623799627495673518857527248912279381830119491298336733624406566438602139494639522473719070217986943702770539217176293176752384674818467669451320005681271452635608277857713427577896091736371787214684409012249534301465495853710579227968925892354201995611212902196864344181598136297747713099605187072113499999983729780499510597317328160963185"; +int calc_pi(void) +{ + int bit = 0, i = 0; + long a = 10000, b = 0, c = 2800, d = 0, e = 0, g = 0; + int *result; + long *f; + int len = 0; + char *pi_calc, *pi_tmp; + char *pi_just = (char *)&pi_result[0]; + size_t pi_just_size = sizeof(pi_result); + + result = vmalloc(10000*sizeof(int)); + if (result == NULL) + return -ENOMEM; + + f = vmalloc(2801*sizeof(long)); + if (f == NULL) + return -ENOMEM; + + pi_calc = vmalloc(1000*sizeof(char)); + if (pi_calc == NULL) + return -ENOMEM; + + for (; b-c; ) + f[b++] = a/5; + for (; d = 0, g = c*2; c -= 14, result[bit++] = e+d/a, e = d%a) + for (b = c; d += f[b]*a, f[b] = d%--g, d /= g--, --b; d *= b) + ; + + pi_tmp = pi_calc; + for (i = 0; i < bit; i++) + len += sprintf(pi_tmp+len, "%d", result[i]); + + if (strncmp(pi_just, pi_calc, pi_just_size) == 0) { + vfree(result); + vfree(f); + vfree(pi_calc); + return 0; + } else { + vfree(result); + vfree(f); + vfree(pi_calc); + + while (1) + pr_info("calc_pi error\n"); + } +} + +void pvtm_repeat_test(void) +{ + struct regulator *regulator_arm; + struct clk *clk_core; + u32 pvtm, delta, old_pvtm; + u32 min_pvtm = -1, max_pvtm = 0; + u32 average = 0, sum = 0; + int i, n; + + regulator_arm = regulator_get(NULL, "vdd_arm"); + if (IS_ERR_OR_NULL(regulator_arm)) { + pr_err("get regulator err\n"); + return; + } + + clk_core = clk_get(NULL, "clk_core"); + if (IS_ERR_OR_NULL(clk_core)) { + pr_err("get clk err\n"); + return; + } + + n = 1000; + for (i = 0; i < n; i++) { + rk29_wdt_keepalive(); + pvtm = pvtm_get_value(0, 1000); + + sum += pvtm; + if (!old_pvtm) + old_pvtm = pvtm; + + if (pvtm > max_pvtm) + max_pvtm = pvtm; + + if (pvtm < min_pvtm) + min_pvtm = pvtm; + } + + average = sum/n; + delta = max_pvtm - min_pvtm; + pr_info("rate %lu volt %d max %u min %u average %u delta %u\n", + clk_get_rate(clk_core), regulator_get_voltage(regulator_arm), + max_pvtm, min_pvtm, average, delta); +} + +void pvtm_samp_interval_test(void) +{ + struct regulator *regulator_arm; + struct clk *clk_core; + u32 pvtm, old_pvtm = 0, samp_interval, times; + int i, n; + + regulator_arm = regulator_get(NULL, "vdd_arm"); + if (IS_ERR_OR_NULL(regulator_arm)) { + pr_err("get regulator err\n"); + return; + } + + clk_core = clk_get(NULL, "clk_core"); + if (IS_ERR_OR_NULL(clk_core)) { + pr_err("get clk err\n"); + return; + } + + + n = 10; + for (i = 0; i < n; i++) { + rk29_wdt_keepalive(); + samp_interval = 10 * (1 << i); + pvtm = pvtm_get_value(0, samp_interval); + + if (!old_pvtm) + old_pvtm = pvtm; + + times = (1000*pvtm)/old_pvtm; + + old_pvtm = pvtm; + + pr_info("rate %lu volt %d samp_interval %d pvtm %d times %d\n", + clk_get_rate(clk_core), + regulator_get_voltage(regulator_arm), + samp_interval, pvtm, times); + } +} + +void pvtm_temp_test(void) +{ + struct regulator *regulator_arm; + struct clk *clk_core; + int temp, old_temp = 0; + int volt; + u32 rate; + + regulator_arm = regulator_get(NULL, "vdd_arm"); + if (IS_ERR_OR_NULL(regulator_arm)) { + pr_err("get regulator err\n"); + return; + } + + clk_core = clk_get(NULL, "clk_core"); + if (IS_ERR_OR_NULL(clk_core)) { + pr_err("get clk err\n"); + return; + } + + volt = 1100000; + rate = 312000000; + regulator_set_voltage(regulator_arm, volt, volt); + clk_set_rate(clk_core, rate); + + do { + rk29_wdt_keepalive(); + temp = rockchip_tsadc_get_temp(1); + if (!old_temp) + old_temp = temp; + + if (temp-old_temp >= 2) + pr_info("rate %lu volt %d temp %d pvtm %u\n", + clk_get_rate(clk_core), + regulator_get_voltage(regulator_arm), + temp, pvtm_get_value(0, 1000)); + + old_temp = temp; + } while (1); +} + + +#define ALL_DONE_FLAG 'a' +#define ONE_DONE_FLAG 'o' +#define ALL_BUF_SIZE (PAGE_SIZE << 8) +#define ONE_BUF_SIZE PAGE_SIZE +#define VOLT_STEP (12500) /*mv*/ +#define VOLT_START (1300000) /*mv*/ +#define VOLT_END (1000000) /*mv*/ +#define RATE_STEP (48000000) /*hz*/ +#define RATE_START (816000000) /*hz*/ +#define RATE_END (1200000000) /*hz*/ + +void scale_min_pvtm_fix_volt(void) +{ + struct regulator *regulator_arm; + struct clk *clk_core; + unsigned long rate; + u32 pvtm, old_pvtm = 0; + int volt, i, ret = 0; + + regulator_arm = regulator_get(NULL, "vdd_arm"); + if (IS_ERR_OR_NULL(regulator_arm)) { + pr_info("get regulator err\n"); + return; + } + + clk_core = clk_get(NULL, "clk_core"); + if (IS_ERR_OR_NULL(clk_core)) { + pr_info("get clk err\n"); + return; + } + + volt = VOLT_START; + rate = RATE_START; + do { + for (i = 0; i < ALL_BUF_SIZE; i += ONE_BUF_SIZE) { + if (pvtm_buf[i] == ALL_DONE_FLAG) { + pr_info("=============test done!========\n"); + do { + if (i) { + i -= ONE_BUF_SIZE; + pr_info("%s", pvtm_buf+i+1); + } else { + pr_info("no item!!!!\n"); + } + } while (i); + + do { + rk29_wdt_keepalive(); + msleep(1000); + } while (1); + } + if (pvtm_buf[i] != ONE_DONE_FLAG) + break; + volt -= VOLT_STEP; + } + + if (volt < VOLT_END) { + pvtm_buf[i] = ALL_DONE_FLAG; + continue; + } + + pvtm_buf[i] = ONE_DONE_FLAG; + + ret = regulator_set_voltage(regulator_arm, volt, volt); + if (ret) { + pr_err("set volt(%d) err:%d\n", volt, ret); + + do { + rk29_wdt_keepalive(); + msleep(1000); + } while (1); + } + + do { + rk29_wdt_keepalive(); + + flush_cache_all(); + outer_flush_all(); + + clk_set_rate(clk_core, rate); + + calc_pi(); + /*fft_test();*/ + mdelay(500); + rk29_wdt_keepalive(); + + pvtm = pvtm_get_value(0, 1000); + if (!old_pvtm) + old_pvtm = pvtm; + sprintf(pvtm_buf+i+1, "%d %lu %d %d %d\n", + volt, clk_get_rate(clk_core), pvtm, + rockchip_tsadc_get_temp(1), old_pvtm-pvtm); + + pr_info("%s", pvtm_buf+i+1); + + old_pvtm = pvtm; + rate += RATE_STEP; + } while (1); + } while (1); +} + +void scale_min_pvtm_fix_rate(void) +{ + struct regulator *regulator_arm; + struct clk *clk_core; + unsigned long rate; + u32 pvtm, old_pvtm = 0; + int volt, i, ret = 0; + + regulator_arm = regulator_get(NULL, "vdd_arm"); + if (IS_ERR_OR_NULL(regulator_arm)) { + pr_info("get regulator err\n"); + return; + } + + clk_core = clk_get(NULL, "clk_core"); + if (IS_ERR_OR_NULL(clk_core)) { + pr_info("get clk err\n"); + return; + } + + volt = VOLT_START; + rate = RATE_START; + do { + for (i = 0; i < ALL_BUF_SIZE; i += ONE_BUF_SIZE) { + if (pvtm_buf[i] == ALL_DONE_FLAG) { + pr_info("=============test done!========\n"); + do { + if (i) { + i -= ONE_BUF_SIZE; + pr_info("%s", pvtm_buf+i+1); + } else { + pr_info("no item!!!!\n"); + } + } while (i); + + do { + rk29_wdt_keepalive(); + msleep(1000); + } while (1); + } + if (pvtm_buf[i] != ONE_DONE_FLAG) + break; + + rate += RATE_STEP; + } + + if (rate > RATE_END) { + pvtm_buf[i] = ALL_DONE_FLAG; + continue; + } + + pvtm_buf[i] = ONE_DONE_FLAG; + + ret = regulator_set_voltage(regulator_arm, volt, volt); + if (ret) { + pr_err("set volt(%d) err:%d\n", volt, ret); + + do { + rk29_wdt_keepalive(); + msleep(1000); + } while (1); + } + + ret = clk_set_rate(clk_core, rate); + do { + rk29_wdt_keepalive(); + + flush_cache_all(); + outer_flush_all(); + regulator_set_voltage(regulator_arm, volt, volt); + + calc_pi(); + mdelay(500); + rk29_wdt_keepalive(); + + pvtm = pvtm_get_value(0, 1000); + if (!old_pvtm) + old_pvtm = pvtm; + + sprintf(pvtm_buf+i+1, "%d %lu %d %d %d\n", + volt, clk_get_rate(clk_core), pvtm, + rockchip_tsadc_get_temp(1), old_pvtm-pvtm); + + pr_info("%s", pvtm_buf+i+1); + old_pvtm = pvtm; + volt -= VOLT_STEP; + } while (1); + } while (1); +} + +ssize_t pvtm_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *str = buf; + + str += sprintf(str, "core:%d\ngpu:%d\nlogic:%d\n", + pvtm_get_value(0, 1000), + pvtm_get_value(1, 1000), + pvtm_get_value(2, 1000)); + return (str - buf); +} + +ssize_t pvtm_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t n) +{ + return n; +} diff --git a/arch/arm/mach-rockchip/rk_pm_tests/rk_pm_tests.c b/arch/arm/mach-rockchip/rk_pm_tests/rk_pm_tests.c index 5e033f2481ba..1ab12bf0fd5a 100644 --- a/arch/arm/mach-rockchip/rk_pm_tests/rk_pm_tests.c +++ b/arch/arm/mach-rockchip/rk_pm_tests/rk_pm_tests.c @@ -59,13 +59,16 @@ struct pm_attribute { const char *buf, size_t n); }; +extern ssize_t pvtm_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf); +extern ssize_t pvtm_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*/ __ATTR(clk_rate, S_IRUGO | S_IWUSR | S_IWUGO, clk_rate_show, clk_rate_store), __ATTR(clk_volt, S_IRUGO | S_IWUSR | S_IWUGO, clk_volt_show, clk_volt_store), __ATTR(dvfs_table_scan, S_IRUGO | S_IWUSR | S_IWUGO, dvfs_table_scan_show, dvfs_table_scan_store), __ATTR(cpu_usage, S_IRUGO | S_IWUSR, cpu_usage_show, cpu_usage_store), - + __ATTR(pvtm, S_IRUGO | S_IWUSR, pvtm_show, pvtm_store), /* __ATTR(maxfreq_volt, S_IRUGO | S_IWUSR, maxfreq_show, maxfreq_store), __ATTR(freq_limit, S_IRUGO | S_IWUSR, freq_limit_show, freq_limit_store), -- 2.34.1