Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch Implement CPU speed control for Exynos4 and Exynos5...
details: https://anonhg.NetBSD.org/src/rev/198d90522aad
branches: trunk
changeset: 331882:198d90522aad
user: reinoud <reinoud%NetBSD.org@localhost>
date: Thu Aug 28 18:02:36 2014 +0000
description:
Implement CPU speed control for Exynos4 and Exynos5 CPUs using APLL frequency
adjustment.
diffstat:
sys/arch/arm/samsung/exynos_io.c | 6 +-
sys/arch/arm/samsung/exynos_soc.c | 259 +++++++++++++++++++++++++++++++-
sys/arch/arm/samsung/exynos_var.h | 6 +-
sys/arch/evbarm/odroid/odroid_machdep.c | 12 +-
4 files changed, 274 insertions(+), 9 deletions(-)
diffs (truncated from 363 to 300 lines):
diff -r bda6805362f7 -r 198d90522aad sys/arch/arm/samsung/exynos_io.c
--- a/sys/arch/arm/samsung/exynos_io.c Thu Aug 28 17:59:46 2014 +0000
+++ b/sys/arch/arm/samsung/exynos_io.c Thu Aug 28 18:02:36 2014 +0000
@@ -34,7 +34,7 @@
#include "opt_exynos.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: exynos_io.c,v 1.6 2014/05/14 09:03:09 reinoud Exp $");
+__KERNEL_RCSID(1, "$NetBSD: exynos_io.c,v 1.7 2014/08/28 18:02:36 reinoud Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -139,6 +139,10 @@
aprint_naive(": Exynos %x\n", product_id);
aprint_normal(": Exynos %x\n", product_id);
+ /* add sysctl nodes */
+ exynos_sysctl_cpufreq_init();
+
+ /* add all children */
#if defined(EXYNOS4)
if (IS_EXYNOS4_P()) {
l = exynos4_locinfo.locators;
diff -r bda6805362f7 -r 198d90522aad sys/arch/arm/samsung/exynos_soc.c
--- a/sys/arch/arm/samsung/exynos_soc.c Thu Aug 28 17:59:46 2014 +0000
+++ b/sys/arch/arm/samsung/exynos_soc.c Thu Aug 28 18:02:36 2014 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: exynos_soc.c,v 1.15 2014/08/26 11:55:54 reinoud Exp $ */
+/* $NetBSD: exynos_soc.c,v 1.16 2014/08/28 18:02:36 reinoud Exp $ */
/*-
* Copyright (c) 2014 The NetBSD Foundation, Inc.
* All rights reserved.
@@ -33,7 +33,7 @@
#define _ARM32_BUS_DMA_PRIVATE
#include <sys/cdefs.h>
-__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.15 2014/08/26 11:55:54 reinoud Exp $");
+__KERNEL_RCSID(1, "$NetBSD: exynos_soc.c,v 1.16 2014/08/28 18:02:36 reinoud Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -69,6 +69,63 @@
uint32_t exynos_pop_id = 0;
+/* cpu frequencies */
+struct cpu_freq {
+ uint64_t freq;
+ int P;
+ int M;
+ int S;
+};
+
+
+#ifdef EXYNOS4
+const struct cpu_freq cpu_freq_settings_exynos4[] = {
+ { 200, 3, 100, 2},
+ { 300, 4, 200, 2},
+ { 400, 3, 100, 1},
+ { 500, 3, 125, 1},
+ { 600, 4, 200, 1},
+ { 700, 3, 175, 1},
+ { 800, 3, 100, 0},
+ { 900, 4, 150, 0},
+ {1000, 3, 125, 0},
+ {1100, 6, 275, 0},
+ {1200, 4, 200, 0},
+ {1300, 6, 325, 0},
+ {1400, 3, 175, 0},
+ {1600, 3, 200, 0},
+};
+#endif
+
+
+#ifdef EXYNOS5
+const struct cpu_freq cpu_freq_settings_exynos5[] = {
+ { 200, 3, 100, 2},
+ { 333, 4, 222, 2},
+ { 400, 3, 100, 1},
+ { 533, 12, 533, 1},
+ { 600, 4, 200, 1},
+ { 667, 7, 389, 1},
+ { 800, 3, 100, 0},
+ {1000, 3, 125, 0},
+ {1066, 12, 533, 0},
+ {1200, 3, 150, 0},
+ {1400, 3, 175, 0},
+ {1600, 3, 200, 0},
+};
+#endif
+
+static struct cpu_freq const *cpu_freq_settings = NULL;
+static int ncpu_freq_settings = 0;
+
+static int cpu_freq_target = 0;
+#define NFRQS 15
+static char sysctl_cpu_freqs_txt[NFRQS*5];
+
+static int sysctl_cpufreq_target(SYSCTLFN_ARGS);
+static int sysctl_cpufreq_current(SYSCTLFN_ARGS);
+
+
/*
* the early serial console
*/
@@ -208,6 +265,204 @@
void
+exynos_sysctl_cpufreq_init(void)
+{
+ const struct sysctlnode *node, *cpunode, *freqnode;
+ char *cpos;
+ int i, val;
+ int error;
+
+ memset(sysctl_cpu_freqs_txt, (int) ' ', sizeof(sysctl_cpu_freqs_txt));
+ cpos = sysctl_cpu_freqs_txt;
+ for (i = 0; i < ncpu_freq_settings; i++) {
+ val = cpu_freq_settings[i].freq;
+ snprintf(cpos, 6, "%d ", val);
+ cpos += (val < 1000) ? 4 : 5;
+ }
+ *cpos = 0;
+
+ error = sysctl_createv(NULL, 0, NULL, &node,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+ NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
+ if (error)
+ printf("couldn't create `machdep' node\n");
+
+ error = sysctl_createv(NULL, 0, &node, &cpunode,
+ 0, CTLTYPE_NODE, "cpu", NULL,
+ NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+ if (error)
+ printf("couldn't create `cpu' node\n");
+
+ error = sysctl_createv(NULL, 0, &cpunode, &freqnode,
+ 0, CTLTYPE_NODE, "frequency", NULL,
+ NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+ if (error)
+ printf("couldn't create `frequency' node\n");
+
+ error = sysctl_createv(NULL, 0, &freqnode, &node,
+ CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
+ sysctl_cpufreq_target, 0, &cpu_freq_target, 0,
+ CTL_CREATE, CTL_EOL);
+ if (error)
+ printf("couldn't create `target' node\n");
+
+ error = sysctl_createv(NULL, 0, &freqnode, &node,
+ 0, CTLTYPE_INT, "current", NULL,
+ sysctl_cpufreq_current, 0, NULL, 0,
+ CTL_CREATE, CTL_EOL);
+ if (error)
+ printf("couldn't create `current' node\n");
+
+ error = sysctl_createv(NULL, 0, &freqnode, &node,
+ CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
+ NULL, 0, sysctl_cpu_freqs_txt, 0,
+ CTL_CREATE, CTL_EOL);
+ if (error)
+ printf("couldn't create `available' node\b");
+}
+
+
+uint64_t
+exynos_get_cpufreq(void)
+{
+ uint32_t reg = 0;
+ uint32_t regval;
+ uint32_t freq;
+
+#ifdef EXYNOS4
+ if (IS_EXYNOS4_P())
+ reg = EXYNOS4_CMU_APLL + PLL_CON0_OFFSET;
+#endif
+#ifdef EXYNOS5
+ if (IS_EXYNOS5_P())
+ reg = EXYNOS5_CMU_APLL + PLL_CON0_OFFSET;
+#endif
+ KASSERT(reg);
+
+ regval = bus_space_read_4(&exynos_bs_tag, exynos_core_bsh, reg);
+ freq = PLL_FREQ(EXYNOS_F_IN_FREQ, regval);
+
+ return freq;
+}
+
+
+static void
+exynos_set_cpufreq(const struct cpu_freq *freqreq)
+{
+ uint32_t reg = 0;
+ uint32_t regval;
+ int M, P, S;
+
+ M = freqreq->M;
+ P = freqreq->P;
+ S = freqreq->S;
+
+ regval = __SHIFTIN(M, PLL_CON0_M) |
+ __SHIFTIN(P, PLL_CON0_P) |
+ __SHIFTIN(S, PLL_CON0_S);
+
+#ifdef EXYNOS4
+ if (IS_EXYNOS4_P())
+ reg = EXYNOS4_CMU_APLL + PLL_CON0_OFFSET;
+#endif
+#ifdef EXYNOS5
+ if (IS_EXYNOS5_P())
+ reg = EXYNOS5_CMU_APLL + PLL_CON0_OFFSET;
+#endif
+ KASSERT(reg);
+
+ /* enable PPL and write config */
+ regval |= PLL_CON0_ENABLE;
+ bus_space_write_4(&exynos_bs_tag, exynos_core_bsh, reg, regval);
+}
+
+
+static int
+sysctl_cpufreq_target(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node;
+ uint32_t t, curfreq, minfreq, maxfreq;
+ int i, best_i, diff;
+ int error;
+
+ curfreq = exynos_get_cpufreq() / (1000*1000);
+ t = *(int *)rnode->sysctl_data;
+ if (t == 0)
+ t = curfreq;
+
+ node = *rnode;
+ node.sysctl_data = &t;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ if (error || newp == NULL)
+ return error;
+
+ minfreq = cpu_freq_settings[0].freq;
+ maxfreq = cpu_freq_settings[ncpu_freq_settings-1].freq;
+
+ if ((t < minfreq) || (t > maxfreq))
+ return EINVAL;
+
+ if (t == curfreq) {
+ *(int *)rnode->sysctl_data = t;
+ return 0;
+ }
+
+ diff = maxfreq;
+ best_i = -1;
+ for (i = 0; i < ncpu_freq_settings; i++) {
+ if (abs(t - cpu_freq_settings[i].freq) <= diff) {
+ diff = labs(t - cpu_freq_settings[i].freq);
+ best_i = i;
+ }
+ }
+ if (best_i < 0)
+ return EINVAL;
+
+ exynos_set_cpufreq(&cpu_freq_settings[best_i]);
+
+ *(int *)rnode->sysctl_data = t;
+ return 0;
+}
+
+
+static int
+sysctl_cpufreq_current(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node = *rnode;
+ uint32_t freq;
+
+ freq = exynos_get_cpufreq() / (1000*1000);
+ node.sysctl_data = &freq;
+
+ return sysctl_lookup(SYSCTLFN_CALL(&node));
+}
+
+
+void
+exynos_clocks_bootstrap(void)
+{
+#ifdef EXYNOS4
+ if (IS_EXYNOS4_P()) {
+ cpu_freq_settings = cpu_freq_settings_exynos4;
+ ncpu_freq_settings = __arraycount(cpu_freq_settings_exynos4);
+ }
+#endif
+#ifdef EXYNOS5
+ if (IS_EXYNOS5_P()) {
+ cpu_freq_settings = cpu_freq_settings_exynos5;
+ ncpu_freq_settings = __arraycount(cpu_freq_settings_exynos5);
+ }
+#endif
+ KASSERT(ncpu_freq_settings != 0);
+ KASSERT(ncpu_freq_settings < NFRQS);
+
Home |
Main Index |
Thread Index |
Old Index