Module Name:    src
Committed By:   jmcneill
Date:           Wed May 13 11:06:13 UTC 2015

Modified Files:
        src/sys/arch/arm/nvidia: files.tegra soc_tegra124.c tegra_car.c
            tegra_carreg.h tegra_soc.c tegra_var.h
Added Files:
        src/sys/arch/arm/nvidia: tegra_cpufreq.c

Log Message:
Tegra K1 CPU frequency scaling support.

jetsontk1# sysctl machdep.cpu
machdep.cpu.frequency.target = 2292
machdep.cpu.frequency.current = 2292
machdep.cpu.frequency.available = 2292 2100 1896 1692 1500 1296 1092 900 696


To generate a diff of this commit:
cvs rdiff -u -r1.9 -r1.10 src/sys/arch/arm/nvidia/files.tegra
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/nvidia/soc_tegra124.c
cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/nvidia/tegra_car.c
cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/nvidia/tegra_carreg.h
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/nvidia/tegra_cpufreq.c
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/nvidia/tegra_soc.c
cvs rdiff -u -r1.14 -r1.15 src/sys/arch/arm/nvidia/tegra_var.h

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/nvidia/files.tegra
diff -u src/sys/arch/arm/nvidia/files.tegra:1.9 src/sys/arch/arm/nvidia/files.tegra:1.10
--- src/sys/arch/arm/nvidia/files.tegra:1.9	Sun May 10 23:50:21 2015
+++ src/sys/arch/arm/nvidia/files.tegra	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: files.tegra,v 1.9 2015/05/10 23:50:21 jmcneill Exp $
+#	$NetBSD: files.tegra,v 1.10 2015/05/13 11:06:13 jmcneill Exp $
 #
 # Configuration info for NVIDIA Tegra ARM Peripherals
 #
@@ -14,6 +14,7 @@ file	arch/arm/arm32/armv7_generic_space.
 file	arch/arm/arm/bus_space_a4x.S
 
 file	arch/arm/nvidia/tegra_soc.c
+file	arch/arm/nvidia/tegra_cpufreq.c
 file	arch/arm/nvidia/soc_tegra124.c		soc_tegra124
 
 # On-board I/O
@@ -89,6 +90,9 @@ defparam opt_tegra.h			CONMODE
 # Memory parameters
 defparam opt_tegra.h			MEMSIZE
 
+# CPU parameters
+defparam opt_tegra.h			CPUFREQ_BOOT
+
 # SOC parameters
 defflag	opt_tegra.h			SOC_TEGRAK1
 defflag	opt_tegra.h			SOC_TEGRA124: SOC_TEGRAK1

Index: src/sys/arch/arm/nvidia/soc_tegra124.c
diff -u src/sys/arch/arm/nvidia/soc_tegra124.c:1.2 src/sys/arch/arm/nvidia/soc_tegra124.c:1.3
--- src/sys/arch/arm/nvidia/soc_tegra124.c:1.2	Sun Apr 26 22:04:28 2015
+++ src/sys/arch/arm/nvidia/soc_tegra124.c	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: soc_tegra124.c,v 1.2 2015/04/26 22:04:28 jmcneill Exp $ */
+/* $NetBSD: soc_tegra124.c,v 1.3 2015/05/13 11:06:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -30,7 +30,7 @@
 #include "opt_multiprocessor.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: soc_tegra124.c,v 1.2 2015/04/26 22:04:28 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: soc_tegra124.c,v 1.3 2015/05/13 11:06:13 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -47,6 +47,80 @@ __KERNEL_RCSID(0, "$NetBSD: soc_tegra124
 
 #define EVP_RESET_VECTOR_0_REG	0x100
 
+static u_int	tegra124_cpufreq_set_rate(u_int);
+static u_int	tegra124_cpufreq_get_rate(void);
+static size_t	tegra124_cpufreq_get_available(u_int *, size_t);
+
+static const struct tegra_cpufreq_func tegra124_cpufreq_func = {
+	.set_rate = tegra124_cpufreq_set_rate,
+	.get_rate = tegra124_cpufreq_get_rate,
+	.get_available = tegra124_cpufreq_get_available,
+};
+
+static struct tegra124_cpufreq_rate {
+	u_int rate;
+	u_int divm;
+	u_int divn;
+	u_int divp;
+} tegra124_cpufreq_rates[] = {
+	{ 2292, 1, 191, 0 },
+	{ 2100, 1, 175, 0 },
+	{ 1896, 1, 158, 0 },
+	{ 1692, 1, 141, 0 },
+	{ 1500, 1, 125, 0 },
+	{ 1296, 1, 108, 0 },
+	{ 1092, 1, 91, 0 },
+	{ 900, 1, 75, 0 },
+	{ 696, 1, 58, 0 }
+};
+
+void
+tegra124_cpuinit(void)
+{
+	tegra_cpufreq_register(&tegra124_cpufreq_func);
+}
+
+static u_int
+tegra124_cpufreq_set_rate(u_int rate)
+{
+	const u_int nrates = __arraycount(tegra124_cpufreq_rates);
+	const struct tegra124_cpufreq_rate *r = NULL;
+
+	for (int i = 0; i < nrates; i++) {
+		if (tegra124_cpufreq_rates[i].rate == rate) {
+			r = &tegra124_cpufreq_rates[i];
+			break;
+		}
+	}
+	if (r == NULL)
+		return EINVAL;
+
+	tegra_car_pllx_set_rate(r->divm, r->divn, r->divp);
+
+	return 0;
+}
+
+static u_int
+tegra124_cpufreq_get_rate(void)
+{
+	return tegra_car_pllx_rate() / 1000000;
+}
+
+static size_t
+tegra124_cpufreq_get_available(u_int *pavail, size_t maxavail)
+{
+	const u_int nrates = __arraycount(tegra124_cpufreq_rates);
+	u_int n;
+
+	KASSERT(nrates <= maxavail);
+
+	for (n = 0; n < nrates; n++) {
+		pavail[n] = tegra124_cpufreq_rates[n].rate;
+	}
+
+	return nrates;
+}
+
 void
 tegra124_mpinit(void)
 {

Index: src/sys/arch/arm/nvidia/tegra_car.c
diff -u src/sys/arch/arm/nvidia/tegra_car.c:1.10 src/sys/arch/arm/nvidia/tegra_car.c:1.11
--- src/sys/arch/arm/nvidia/tegra_car.c:1.10	Sun May 10 23:56:21 2015
+++ src/sys/arch/arm/nvidia/tegra_car.c	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_car.c,v 1.10 2015/05/10 23:56:21 jmcneill Exp $ */
+/* $NetBSD: tegra_car.c,v 1.11 2015/05/13 11:06:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -29,7 +29,7 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.10 2015/05/10 23:56:21 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.11 2015/05/13 11:06:13 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -117,7 +117,6 @@ tegra_car_pll_rate(u_int base_reg, u_int
 
 	tegra_car_get_bs(&bst, &bsh);
 
-	rate = tegra_car_osc_rate();	
 	const uint32_t base = bus_space_read_4(bst, bsh, base_reg);
 	const u_int divm = __SHIFTOUT(base, divm_mask);
 	const u_int divn = __SHIFTOUT(base, divn_mask);
@@ -128,6 +127,50 @@ tegra_car_pll_rate(u_int base_reg, u_int
 	return rate / (divm << divp);
 }
 
+void
+tegra_car_pllx_set_rate(u_int divm, u_int divn, u_int divp)
+{
+	bus_space_tag_t bst;
+	bus_space_handle_t bsh;
+	uint32_t base, bp;
+
+	tegra_car_get_bs(&bst, &bsh);
+
+	bp = bus_space_read_4(bst, bsh, CAR_CCLKG_BURST_POLICY_REG);
+	bp &= ~CAR_CCLKG_BURST_POLICY_CPU_STATE;
+	bp |= __SHIFTIN(CAR_CCLKG_BURST_POLICY_CPU_STATE_IDLE,
+			CAR_CCLKG_BURST_POLICY_CPU_STATE);
+	bp &= ~CAR_CCLKG_BURST_POLICY_CWAKEUP_IDLE_SOURCE;
+	bp |= __SHIFTIN(CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_CLKM,
+			CAR_CCLKG_BURST_POLICY_CWAKEUP_IDLE_SOURCE);
+	bus_space_write_4(bst, bsh, CAR_CCLKG_BURST_POLICY_REG, bp);
+
+	base = bus_space_read_4(bst, bsh, CAR_PLLX_BASE_REG);
+	base &= ~CAR_PLLX_BASE_DIVM;
+	base &= ~CAR_PLLX_BASE_DIVN;
+	base &= ~CAR_PLLX_BASE_DIVP;
+	base |= __SHIFTIN(divm, CAR_PLLX_BASE_DIVM);
+	base |= __SHIFTIN(divn, CAR_PLLX_BASE_DIVN);
+	base |= __SHIFTIN(divp, CAR_PLLX_BASE_DIVP);
+	bus_space_write_4(bst, bsh, CAR_PLLX_BASE_REG, base);
+
+	tegra_reg_set_clear(bst, bsh, CAR_PLLX_MISC_REG,
+	    CAR_PLLX_MISC_LOCK_ENABLE, 0);
+	do {
+		delay(2);
+		base = bus_space_read_4(bst, bsh, CAR_PLLX_BASE_REG);
+	} while ((base & CAR_PLLX_BASE_LOCK) == 0);
+	delay(100);
+
+	bp &= ~CAR_CCLKG_BURST_POLICY_CPU_STATE;
+	bp |= __SHIFTIN(CAR_CCLKG_BURST_POLICY_CPU_STATE_RUN,
+			CAR_CCLKG_BURST_POLICY_CPU_STATE);
+	bp &= ~CAR_CCLKG_BURST_POLICY_CWAKEUP_IDLE_SOURCE;
+	bp |= __SHIFTIN(CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_PLLX_OUT0_LJ,
+			CAR_CCLKG_BURST_POLICY_CWAKEUP_IDLE_SOURCE);
+	bus_space_write_4(bst, bsh, CAR_CCLKG_BURST_POLICY_REG, bp);
+}
+
 u_int
 tegra_car_pllx_rate(void)
 {

Index: src/sys/arch/arm/nvidia/tegra_carreg.h
diff -u src/sys/arch/arm/nvidia/tegra_carreg.h:1.11 src/sys/arch/arm/nvidia/tegra_carreg.h:1.12
--- src/sys/arch/arm/nvidia/tegra_carreg.h:1.11	Sun May 10 23:50:21 2015
+++ src/sys/arch/arm/nvidia/tegra_carreg.h	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_carreg.h,v 1.11 2015/05/10 23:50:21 jmcneill Exp $ */
+/* $NetBSD: tegra_carreg.h,v 1.12 2015/05/13 11:06:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -88,6 +88,10 @@
 #define CAR_PLLX_BASE_DIVM		__BITS(7,0)
 
 #define CAR_PLLX_MISC_REG	0xe4
+#define CAR_PLLX_MISC_FO_LP_DISABLE	__BIT(29)
+#define CAR_PLLX_MISC_FO_G_DISABLE	__BIT(28)
+#define CAR_PLLX_MISC_PTS		__BITS(23,22)
+#define CAR_PLLX_MISC_LOCK_ENABLE	__BIT(18)
 
 #define CAR_PLLE_BASE_REG	0xe8
 #define CAR_PLLE_BASE_ENABLE		__BIT(30)
@@ -296,6 +300,14 @@
 #define CAR_DEV_X_CAM_MCLK		__BIT(4)
 #define CAR_DEV_X_SPARE			__BIT(0)
 
+#define CAR_CCLKG_BURST_POLICY_REG	0x368
+#define CAR_CCLKG_BURST_POLICY_CPU_STATE	__BITS(31,28)
+#define CAR_CCLKG_BURST_POLICY_CPU_STATE_IDLE			1
+#define CAR_CCLKG_BURST_POLICY_CPU_STATE_RUN			2
+#define CAR_CCLKG_BURST_POLICY_CWAKEUP_IDLE_SOURCE __BITS(3,0)
+#define CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_CLKM		0
+#define CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_PLLX_OUT0_LJ	8
+
 #define CAR_UTMIP_PLL_CFG0_REG		0x480
 
 #define CAR_UTMIP_PLL_CFG1_REG		0x484

Index: src/sys/arch/arm/nvidia/tegra_soc.c
diff -u src/sys/arch/arm/nvidia/tegra_soc.c:1.5 src/sys/arch/arm/nvidia/tegra_soc.c:1.6
--- src/sys/arch/arm/nvidia/tegra_soc.c:1.5	Sun May  3 01:07:44 2015
+++ src/sys/arch/arm/nvidia/tegra_soc.c	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_soc.c,v 1.5 2015/05/03 01:07:44 jmcneill Exp $ */
+/* $NetBSD: tegra_soc.c,v 1.6 2015/05/13 11:06:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -30,7 +30,7 @@
 #include "opt_multiprocessor.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_soc.c,v 1.5 2015/05/03 01:07:44 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_soc.c,v 1.6 2015/05/13 11:06:13 jmcneill Exp $");
 
 #define	_ARM32_BUS_DMA_PRIVATE
 #include <sys/param.h>
@@ -108,6 +108,20 @@ tegra_dma_bootstrap(psize_t psize)
 	tegra_coherent_dma_ranges[0].dr_len = psize;
 }
 
+void
+tegra_cpuinit(void)
+{
+	switch (tegra_chip_id()) {
+#ifdef SOC_TEGRA124
+	case CHIP_ID_TEGRA124:
+		tegra124_cpuinit();
+		break;
+#endif
+	}
+
+	tegra_cpufreq_init();
+}
+
 static void
 tegra_mpinit(void)
 {

Index: src/sys/arch/arm/nvidia/tegra_var.h
diff -u src/sys/arch/arm/nvidia/tegra_var.h:1.14 src/sys/arch/arm/nvidia/tegra_var.h:1.15
--- src/sys/arch/arm/nvidia/tegra_var.h:1.14	Sun May 10 23:50:21 2015
+++ src/sys/arch/arm/nvidia/tegra_var.h	Wed May 13 11:06:13 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_var.h,v 1.14 2015/05/10 23:50:21 jmcneill Exp $ */
+/* $NetBSD: tegra_var.h,v 1.15 2015/05/13 11:06:13 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -72,11 +72,13 @@ u_int	tegra_chip_id(void);
 const char *tegra_chip_name(void);
 void	tegra_bootstrap(void);
 void	tegra_dma_bootstrap(psize_t);
+void	tegra_cpuinit(void);
 
 u_int	tegra_car_osc_rate(void);
 u_int	tegra_car_pllc_rate(void);
 u_int	tegra_car_plle_rate(void);
 u_int	tegra_car_pllx_rate(void);
+void	tegra_car_pllx_set_rate(u_int, u_int, u_int);
 u_int	tegra_car_pllu_rate(void);
 u_int	tegra_car_pllp0_rate(void);
 u_int	tegra_car_uart_rate(u_int);
@@ -121,7 +123,17 @@ void	tegra_pmc_power(u_int, bool);
 
 psize_t	tegra_mc_memsize(void);
 
+#define TEGRA_CPUFREQ_MAX	16
+struct tegra_cpufreq_func {
+	u_int (*set_rate)(u_int);
+	u_int (*get_rate)(void);
+	size_t (*get_available)(u_int *, size_t);
+};
+void	tegra_cpufreq_register(const struct tegra_cpufreq_func *);
+void	tegra_cpufreq_init(void);
+
 #if defined(SOC_TEGRA124)
+void	tegra124_cpuinit(void);
 void	tegra124_mpinit(void);
 #endif
 

Added files:

Index: src/sys/arch/arm/nvidia/tegra_cpufreq.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_cpufreq.c:1.1
--- /dev/null	Wed May 13 11:06:13 2015
+++ src/sys/arch/arm/nvidia/tegra_cpufreq.c	Wed May 13 11:06:13 2015
@@ -0,0 +1,185 @@
+/* $NetBSD: tegra_cpufreq.c,v 1.1 2015/05/13 11:06:13 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "locators.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_cpufreq.c,v 1.1 2015/05/13 11:06:13 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/atomic.h>
+#include <sys/kmem.h>
+#include <sys/xcall.h>
+#include <sys/sysctl.h>
+
+#include <arm/nvidia/tegra_var.h>
+
+static u_int cpufreq_busy;
+static struct sysctllog *cpufreq_log;
+static int cpufreq_node_target, cpufreq_node_current, cpufreq_node_available;
+
+static const struct tegra_cpufreq_func *cpufreq_func = NULL;
+
+static void	tegra_cpufreq_post(void *, void *);
+static int	tegra_cpufreq_freq_helper(SYSCTLFN_PROTO);
+static char 	tegra_cpufreq_available[TEGRA_CPUFREQ_MAX * 5];
+
+#define cpufreq_set_rate	cpufreq_func->set_rate
+#define cpufreq_get_rate	cpufreq_func->get_rate
+#define cpufreq_get_available	cpufreq_func->get_available
+
+void
+tegra_cpufreq_register(const struct tegra_cpufreq_func *cf)
+{
+	KASSERT(cpufreq_func == NULL);
+	cpufreq_func = cf;
+}
+
+void
+tegra_cpufreq_init(void)
+{
+	const struct sysctlnode *node, *cpunode, *freqnode;
+	u_int availfreq[TEGRA_CPUFREQ_MAX];
+	size_t nfreq;
+	int error;
+
+	if (cpufreq_func == NULL)
+		return;
+
+	nfreq = cpufreq_get_available(availfreq, TEGRA_CPUFREQ_MAX);
+	if (nfreq == 0)
+		return;
+
+	KASSERT(nfreq <= TEGRA_CPUFREQ_MAX);
+
+	for (int i = 0; i < nfreq; i++) {
+		char buf[6];
+		snprintf(buf, sizeof(buf), i ? " %u" : "%u", availfreq[i]);
+		strcat(tegra_cpufreq_available, buf);
+	}
+
+	error = sysctl_createv(&cpufreq_log, 0, NULL, &node,
+	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+	error = sysctl_createv(&cpufreq_log, 0, &node, &cpunode,
+	    0, CTLTYPE_NODE, "cpu", NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+	error = sysctl_createv(&cpufreq_log, 0, &cpunode, &freqnode,
+	    0, CTLTYPE_NODE, "frequency", NULL,
+	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+
+	error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
+	    tegra_cpufreq_freq_helper, 0, NULL, 0,
+	    CTL_CREATE, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+	cpufreq_node_target = node->sysctl_num;
+
+	error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node,
+	    CTLFLAG_READWRITE, CTLTYPE_INT, "current", NULL,
+	    tegra_cpufreq_freq_helper, 0, NULL, 0,
+	    CTL_CREATE, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+	cpufreq_node_current = node->sysctl_num;
+
+	error = sysctl_createv(&cpufreq_log, 0, &freqnode, &node,
+	    0, CTLTYPE_STRING, "available", NULL,
+	    NULL, 0, tegra_cpufreq_available, 0,
+	    CTL_CREATE, CTL_EOL);
+	if (error)
+		goto sysctl_failed;
+	cpufreq_node_available = node->sysctl_num;
+
+#ifdef CPUFREQ_BOOT
+	cpufreq_set_rate(CPUFREQ_BOOT);
+	tegra_cpufreq_post(NULL, NULL);
+#endif
+
+	return;
+
+sysctl_failed:
+	aprint_error("cpufreq: couldn't create sysctl nodes (%d)\n", error);
+	sysctl_teardown(&cpufreq_log);
+}
+
+static void
+tegra_cpufreq_post(void *arg1, void *arg2)
+{
+	struct cpu_info *ci = curcpu();
+
+	ci->ci_data.cpu_cc_freq = cpufreq_get_rate() * 1000000;
+}
+
+static int
+tegra_cpufreq_freq_helper(SYSCTLFN_ARGS)
+{
+	struct sysctlnode node;
+	int fq, oldfq = 0, error;
+	uint64_t xc;
+
+	node = *rnode;
+	node.sysctl_data = &fq;
+
+	fq = cpufreq_get_rate();
+	if (rnode->sysctl_num == cpufreq_node_target)
+		oldfq = fq;
+
+	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+	if (error || newp == NULL)
+		return error;
+
+	if (fq == oldfq || rnode->sysctl_num != cpufreq_node_target)
+		return 0;
+
+	if (atomic_cas_uint(&cpufreq_busy, 0, 1) != 0)
+		return EBUSY;
+
+	error = cpufreq_set_rate(fq);
+	if (error == 0) {
+		xc = xc_broadcast(0, tegra_cpufreq_post, NULL, NULL);
+		xc_wait(xc);
+		pmf_event_inject(NULL, PMFE_SPEED_CHANGED);
+	}
+
+	atomic_dec_uint(&cpufreq_busy);
+
+	return error;
+}

Reply via email to