rockchip: add pvtm support and pvtm test
author陈亮 <cl@rock-chips.com>
Wed, 27 Aug 2014 13:10:33 +0000 (06:10 -0700)
committer陈亮 <cl@rock-chips.com>
Wed, 27 Aug 2014 13:11:47 +0000 (06:11 -0700)
Signed-off-by: 陈亮 <cl@rock-chips.com>
arch/arm/mach-rockchip/Makefile
arch/arm/mach-rockchip/pvtm.c [new file with mode: 0644]
arch/arm/mach-rockchip/rk_pm_tests/Makefile
arch/arm/mach-rockchip/rk_pm_tests/pvtm_test.c [new file with mode: 0644]
arch/arm/mach-rockchip/rk_pm_tests/rk_pm_tests.c

index a708831d2f4bb6b57d101852376d1fbcc3d92a62..c76144ed67f7f1fff899d9fb4023820ad4e0d517 100755 (executable)
@@ -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 (file)
index 0000000..8bfbd81
--- /dev/null
@@ -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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/rockchip/iomap.h>
+#include <linux/rockchip/cpu.h>
+
+#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);
+
index 0ebed8a61b387e73081f4d2654c66a3e4b759df2..837c38613f853629aa151099f0f72add5c741961 100644 (file)
@@ -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 (file)
index 0000000..c9ea7d2
--- /dev/null
@@ -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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/regulator/consumer.h>
+#include <asm/cacheflush.h>
+
+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;
+}
index 5e033f2481ba93a19c8814162392cc50a308df3f..1ab12bf0fd5a5a73919dec3f492e1d337beb22c8 100644 (file)
@@ -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),