Module Name:    src
Committed By:   reinoud
Date:           Thu Aug 28 18:02:37 UTC 2014

Modified Files:
        src/sys/arch/arm/samsung: exynos_io.c exynos_soc.c exynos_var.h
        src/sys/arch/evbarm/odroid: odroid_machdep.c

Log Message:
Implement CPU speed control for Exynos4 and Exynos5 CPUs using APLL frequency
adjustment.


To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/arm/samsung/exynos_io.c
cvs rdiff -u -r1.15 -r1.16 src/sys/arch/arm/samsung/exynos_soc.c
cvs rdiff -u -r1.12 -r1.13 src/sys/arch/arm/samsung/exynos_var.h
cvs rdiff -u -r1.30 -r1.31 src/sys/arch/evbarm/odroid/odroid_machdep.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/samsung/exynos_io.c
diff -u src/sys/arch/arm/samsung/exynos_io.c:1.6 src/sys/arch/arm/samsung/exynos_io.c:1.7
--- src/sys/arch/arm/samsung/exynos_io.c:1.6	Wed May 14 09:03:09 2014
+++ src/sys/arch/arm/samsung/exynos_io.c	Thu Aug 28 18:02:36 2014
@@ -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 @@ exyo_attach(device_t parent, device_t se
 	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;

Index: src/sys/arch/arm/samsung/exynos_soc.c
diff -u src/sys/arch/arm/samsung/exynos_soc.c:1.15 src/sys/arch/arm/samsung/exynos_soc.c:1.16
--- src/sys/arch/arm/samsung/exynos_soc.c:1.15	Tue Aug 26 11:55:54 2014
+++ src/sys/arch/arm/samsung/exynos_soc.c	Thu Aug 28 18:02:36 2014
@@ -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_soc_id = 0;
 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 @@ exynos_l2cc_init(void)
 
 
 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);
+
+	/* set max cpufreq */
+	exynos_set_cpufreq(&cpu_freq_settings[ncpu_freq_settings-1]);
+	curcpu()->ci_data.cpu_cc_freq = exynos_get_cpufreq();
+}
+
+
+void
 exynos_bootstrap(vaddr_t iobase, vaddr_t uartbase)
 {
 	int error;

Index: src/sys/arch/arm/samsung/exynos_var.h
diff -u src/sys/arch/arm/samsung/exynos_var.h:1.12 src/sys/arch/arm/samsung/exynos_var.h:1.13
--- src/sys/arch/arm/samsung/exynos_var.h:1.12	Wed Jun 11 14:54:32 2014
+++ src/sys/arch/arm/samsung/exynos_var.h	Thu Aug 28 18:02:36 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: exynos_var.h,v 1.12 2014/06/11 14:54:32 reinoud Exp $ */
+/* $NetBSD: exynos_var.h,v 1.13 2014/08/28 18:02:36 reinoud Exp $ */
 /*-
  * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -118,6 +118,10 @@ extern void exynos_bootstrap(vaddr_t, va
 extern void exynos_dma_bootstrap(psize_t memsize);
 extern void exynos_gpio_bootstrap(void);
 
+extern void exynos_clocks_bootstrap(void);
+extern void exynos_sysctl_cpufreq_init(void);
+extern uint64_t exynos_get_cpufreq(void);
+
 extern void exynos_device_register(device_t self, void *aux);
 extern void exynos_device_register_post_config(device_t self, void *aux);
 extern void exyo_device_register(device_t self, void *aux);

Index: src/sys/arch/evbarm/odroid/odroid_machdep.c
diff -u src/sys/arch/evbarm/odroid/odroid_machdep.c:1.30 src/sys/arch/evbarm/odroid/odroid_machdep.c:1.31
--- src/sys/arch/evbarm/odroid/odroid_machdep.c:1.30	Tue Aug 26 20:33:35 2014
+++ src/sys/arch/evbarm/odroid/odroid_machdep.c	Thu Aug 28 18:02:37 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: odroid_machdep.c,v 1.30 2014/08/26 20:33:35 reinoud Exp $ */
+/*	$NetBSD: odroid_machdep.c,v 1.31 2014/08/28 18:02:37 reinoud Exp $ */
 
 /*
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: odroid_machdep.c,v 1.30 2014/08/26 20:33:35 reinoud Exp $");
+__KERNEL_RCSID(0, "$NetBSD: odroid_machdep.c,v 1.31 2014/08/28 18:02:37 reinoud Exp $");
 
 #include "opt_evbarm_boardtype.h"
 #include "opt_exynos.h"
@@ -332,9 +332,11 @@ initarm(void *arg)
 	printf("initarm: cbar=%#x\n", armreg_cbar_read());
 #endif
 
-	/* init clocks */
-	/* determine cpu clock source */
-curcpu()->ci_data.cpu_cc_freq = 1*1000*1000*1000;	/* XXX hack XXX */
+	/* determine cpu0 clock rate */
+	exynos_clocks_bootstrap();
+#ifdef VERBOSE_INIT_ARM
+	printf("CPU0 now running on %"PRIu64" Mhz\n", exynos_get_cpufreq()/(1000*1000));
+#endif
 
 #if NARML2CC > 0
 	if (CPU_ID_CORTEX_A9_P(curcpu()->ci_arm_cpuid)) {

Reply via email to