Module Name:    src
Committed By:   jruoho
Date:           Wed Sep 28 10:55:49 UTC 2011

Modified Files:
        src/distrib/sets/lists/comp: mi
        src/share/man/man9: Makefile
        src/sys/conf: files
Added Files:
        src/share/man/man9: cpufreq.9
        src/sys/kern: subr_cpufreq.c
        src/sys/sys: cpufreq.h

Log Message:
Add a simple cpufreq(9).

This is slightly improved version presented on tech-kern@ with proper
locking and few additional functions, mainly required for ioctl(9)/cpuctl(8).


To generate a diff of this commit:
cvs rdiff -u -r1.1680 -r1.1681 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.350 -r1.351 src/share/man/man9/Makefile
cvs rdiff -u -r0 -r1.1 src/share/man/man9/cpufreq.9
cvs rdiff -u -r1.1027 -r1.1028 src/sys/conf/files
cvs rdiff -u -r0 -r1.1 src/sys/kern/subr_cpufreq.c
cvs rdiff -u -r0 -r1.1 src/sys/sys/cpufreq.h

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

Modified files:

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.1680 src/distrib/sets/lists/comp/mi:1.1681
--- src/distrib/sets/lists/comp/mi:1.1680	Tue Sep 27 00:36:49 2011
+++ src/distrib/sets/lists/comp/mi	Wed Sep 28 10:55:46 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.1680 2011/09/27 00:36:49 jym Exp $
+#	$NetBSD: mi,v 1.1681 2011/09/28 10:55:46 jruoho Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2354,6 +2354,7 @@
 ./usr/include/sys/conf.h			comp-c-include
 ./usr/include/sys/core.h			comp-c-include
 ./usr/include/sys/cpu_data.h			comp-obsolete		obsolete
+./usr/include/sys/cpufreq.h			comp-c-include
 ./usr/include/sys/cpuio.h			comp-c-include
 ./usr/include/sys/ctype_bits.h			comp-c-include
 ./usr/include/sys/ctype_inline.h		comp-c-include
@@ -9768,6 +9769,17 @@
 ./usr/share/man/cat9/cpu_switchto.0		comp-sys-catman		.cat
 ./usr/share/man/cat9/cpu_sysctl.0		comp-obsolete		obsolete
 ./usr/share/man/cat9/cpu_wait.0			comp-obsolete		obsolete
+./usr/share/man/cat9/cpufreq.0			comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_register.0		comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_deregister.0	comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_suspend.0		comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_resume.0		comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_get.0		comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_get_backend.0	comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_get_state.0	comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_get_state_index.0	comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_set.0		comp-sys-catman		.cat
+./usr/share/man/cat9/cpufreq_set_all.0		comp-sys-catman		.cat
 ./usr/share/man/cat9/crcopy.0			comp-obsolete		obsolete
 ./usr/share/man/cat9/crcvt.0			comp-obsolete		obsolete
 ./usr/share/man/cat9/crdup.0			comp-obsolete		obsolete
@@ -15840,6 +15852,17 @@
 ./usr/share/man/html9/cpu_swapin.html		comp-obsolete		obsolete
 ./usr/share/man/html9/cpu_swapout.html		comp-obsolete		obsolete
 ./usr/share/man/html9/cpu_switchto.html		comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq.html		comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_register.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_deregister.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_suspend.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_resume.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_get.html		comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_get_backend.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_get_state.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_get_state_index.html	comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_set.html		comp-sys-htmlman	html
+./usr/share/man/html9/cpufreq_set_all.html	comp-sys-htmlman	html
 ./usr/share/man/html9/crypto_dispatch.html	comp-sys-htmlman	html
 ./usr/share/man/html9/crypto_done.html		comp-sys-htmlman	html
 ./usr/share/man/html9/crypto_freereq.html	comp-sys-htmlman	html
@@ -22041,6 +22064,17 @@
 ./usr/share/man/man9/cpu_switchto.9		comp-sys-man		.man
 ./usr/share/man/man9/cpu_sysctl.9		comp-obsolete		obsolete
 ./usr/share/man/man9/cpu_wait.9			comp-obsolete		obsolete
+./usr/share/man/man9/cpufreq.9			comp-sys-man		.man
+./usr/share/man/man9/cpufreq_register.9		comp-sys-man		.man
+./usr/share/man/man9/cpufreq_deregister.9	comp-sys-man		.man
+./usr/share/man/man9/cpufreq_suspend.9		comp-sys-man		.man
+./usr/share/man/man9/cpufreq_resume.9		comp-sys-man		.man
+./usr/share/man/man9/cpufreq_get.9		comp-sys-man		.man
+./usr/share/man/man9/cpufreq_get_backend.9	comp-sys-man		.man
+./usr/share/man/man9/cpufreq_get_state.9	comp-sys-man		.man
+./usr/share/man/man9/cpufreq_get_state_index.9	comp-sys-man		.man
+./usr/share/man/man9/cpufreq_set.9		comp-sys-man		.man
+./usr/share/man/man9/cpufreq_set_all.9		comp-sys-man		.man
 ./usr/share/man/man9/crcopy.9			comp-obsolete		obsolete
 ./usr/share/man/man9/crcvt.9			comp-obsolete		obsolete
 ./usr/share/man/man9/crdup.9			comp-obsolete		obsolete

Index: src/share/man/man9/Makefile
diff -u src/share/man/man9/Makefile:1.350 src/share/man/man9/Makefile:1.351
--- src/share/man/man9/Makefile:1.350	Tue Sep 27 00:36:51 2011
+++ src/share/man/man9/Makefile	Wed Sep 28 10:55:48 2011
@@ -1,4 +1,4 @@
-#       $NetBSD: Makefile,v 1.350 2011/09/27 00:36:51 jym Exp $
+#       $NetBSD: Makefile,v 1.351 2011/09/28 10:55:48 jruoho Exp $
 
 #	Makefile for section 9 (kernel function and variable) manual pages.
 
@@ -12,7 +12,7 @@ MAN=	accept_filter.9 accf_data.9 accf_ht
 	cpu_lwp_fork.9 \
 	cpu_idle.9 cpu_initclocks.9 cpu_need_resched.9 \
 	cpu_number.9 cpu_reboot.9 cpu_rootconf.9 \
-	cpu_startup.9 cpu_switchto.9 \
+	cpu_startup.9 cpu_switchto.9 cpufreq.9 \
 	csf.9 ctod.9 \
 	curproc.9 \
 	delay.9 disk.9 ddc.9 disklabel.9 dofileread.9 \
@@ -234,6 +234,16 @@ MLINKS+=cpu_dumpconf.9 cpu_dump.9 cpu_du
 	cpu_dumpconf.9 dumpsys.9
 MLINKS+=cpu_lwp_fork.9 child_return.9 cpu_lwp_fork.9 proc_trampoline.9
 MLINKS+=cpu_rootconf.9 setroot.9
+MLINKS+=cpufreq.9 cpufreq_register.9 \
+	cpufreq.9 cpufreq_deregister.9 \
+	cpufreq.9 cpufreq_suspend.9 \
+	cpufreq.9 cpufreq_resume.9 \
+	cpufreq.9 cpufreq_get.9 \
+	cpufreq.9 cpufreq_get_backend.9 \
+	cpufreq.9 cpufreq_get_state.9 \
+	cpufreq.9 cpufreq_get_state_index.9 \
+	cpufreq.9 cpufreq_set.9 \
+	cpufreq.9 cpufreq_set_all.9
 MLINKS+=ctod.9 dtoc.9 \
 	ctod.9 ctob.9 \
 	ctod.9 btoc.9 \

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1027 src/sys/conf/files:1.1028
--- src/sys/conf/files:1.1027	Mon Sep 19 08:53:30 2011
+++ src/sys/conf/files	Wed Sep 28 10:55:46 2011
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1027 2011/09/19 08:53:30 jnemeth Exp $
+#	$NetBSD: files,v 1.1028 2011/09/28 10:55:46 jruoho Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20100430
@@ -1525,6 +1525,7 @@ file	kern/subr_autoconf.c
 file	kern/subr_blist.c		vmswap
 file	kern/subr_bufq.c
 file	kern/subr_callback.c
+file	kern/subr_cpufreq.c
 file	kern/subr_copy.c
 file	kern/subr_debug.c		debug
 file	kern/subr_device.c

Added files:

Index: src/share/man/man9/cpufreq.9
diff -u /dev/null src/share/man/man9/cpufreq.9:1.1
--- /dev/null	Wed Sep 28 10:55:49 2011
+++ src/share/man/man9/cpufreq.9	Wed Sep 28 10:55:48 2011
@@ -0,0 +1,303 @@
+.\" $NetBSD: cpufreq.9,v 1.1 2011/09/28 10:55:48 jruoho Exp $ */
+.\"
+.\" Copyright (c) 2011 Jukka Ruohonen <jruohonen.iki.fi>
+.\" 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``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 FOUNDATION OR CONTRIBUTORS
+.\" 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.
+.\"
+.Dd September 23, 2011
+.Dt CPUFREQ 9
+.Os
+.Sh NAME
+.Nm cpufreq ,
+.Nm cpufreq_register ,
+.Nm cpufreq_deregister ,
+.Nm cpufreq_suspend ,
+.Nm cpufreq_resume ,
+.Nm cpufreq_get ,
+.Nm cpufreq_get_backend ,
+.Nm cpufreq_get_state ,
+.Nm cpufreq_get_state_index ,
+.Nm cpufreq_set ,
+.Nm cpufreq_set_all
+.Nd interface for CPU frequency scaling
+.Sh SYNOPSIS
+.In sys/cpufreq.h
+.Ft int
+.Fn cpufreq_register "struct cpufreq *cf"
+.Ft void
+.Fn cpufreq_deregister "void"
+.Ft void
+.Fn cpufreq_suspend "struct cpu_info *ci"
+.Ft void
+.Fn cpufreq_resume "struct cpu_info *ci"
+.Ft uint32_t
+.Fn cpufreq_get "struct cpu_info *ci"
+.Ft int
+.Fn cpufreq_get_backend "struct cpufreq *cf"
+.Ft int
+.Fn cpufreq_get_state "uint32_t freq" "struct cpufreq_state *cfs"
+.Ft int
+.Fn cpufreq_get_state_index "uint32_t index" "struct cpufreq_state *cfs"
+.Ft void
+.Fn cpufreq_set "struct cpu_info *ci" "uint32_t freq"
+.Ft void
+.Fn cpufreq_set_all "uint32_t freq"
+.\" .Ft void
+.\" .Fn cpufreq_set_higher "struct cpu_info *ci"
+.\" .Ft void
+.\" .Fn cpufreq_set_lower "struct cpu_info *ci"
+.Sh DESCRIPTION
+The machine-independent
+.Nm
+interface provides a framework for
+.Tn CPU
+frequency scaling done by a machine-dependent backend implementation.
+User space control is available via
+.Xr cpuctl 8 .
+.Pp
+The
+.Nm
+interface is a per-CPU framework.
+It is implicitly assumed that the frequency can be set
+independently for all processors in the system.
+However,
+.Nm
+does not imply any restrictions upon whether this information
+is utilized by the actual machine-dependent implementation.
+It is possible to use
+.Nm
+with frequency scaling implemented via
+.Xr pci 4 .
+In addition, it assumed that the available frequency levels are
+shared uniformly by all processors in the system,
+even when it is possible to control the frequency of individual processors.
+.Pp
+It should be noted that the
+.Nm
+interface is generally stateless.
+This implies for instance that possible caching should
+be done in the machine-dependent backend.
+The
+.Fn cpufreq_suspend
+and
+.Fn cpufreq_resume
+functions are exceptions.
+These can be integrated with
+.Xr pmf 9 .
+.Sh FUNCTIONS
+.Bl -tag -width compact
+.It Fn cpufreq_register "cf"
+The
+.Fn cpufreq_register
+function initializes the interface by associating
+a machine-dependent backend with the framework.
+Only one backend can be registered.
+Upon successful completion,
+.Fn cpufreq_register
+returns 0 and sets the frequency to the maximum available level.
+.Pp
+The following elements in
+.Vt struct cpufreq
+should be filled prior to the call:
+.Bd -literal -offset indent
+char			 cf_name[CPUFREQ_NAME_MAX];
+struct cpufreq_state     cf_state[CPUFREQ_STATE_MAX];
+uint32_t                 cf_state_count;
+bool			 cf_mp;
+void                    *cf_cookie;
+xcfunc_t                 cf_get_freq;
+xcfunc_t                 cf_set_freq;
+.Ed
+.Pp
+.Bl -bullet
+.It
+The name of the backend should be given in
+.Vt cf_name .
+.It
+The
+.Vt cpufreq_state
+structure conveys descriptive information about the frequency states.
+The following fields can be used for the registration:
+.Bd -literal -offset 2n
+uint32_t		 cfs_freq;
+uint32_t		 cfs_power;
+.Ed
+.Pp
+From these
+.Vt cfs_freq
+(the clock frequency in MHz) is mandatory, whereas the optional
+.Vt cfs_power
+can be filled to describe the power consumption (in mW) of each state.
+.Pp
+If the backend operates with a simple boolean switch
+without knowing the clock frequencies, the
+.Fa cfs_freq
+field should be set to
+.Dv CPUFREQ_STATE_ENABLED
+or
+.Dv CPUFREQ_STATE_DISABLED .
+.It
+The
+.Vt cf_state_count
+field defines the number of states that the backend has filled in the
+.Vt cf_state
+array.
+.It
+The
+.Vt cf_mp
+boolean should be set to false if it is known that the backend
+can not handle per-CPU frequency states;
+changes should always be propagated
+to all processors in the system.
+.It
+The
+.Vt cf_cookie
+field is an opaque pointer passed to the backend when
+.Fn cpufreq_get
+.Fn cpufreq_set ,
+or
+.Fn cpufreq_set_all
+is called.
+.It
+The
+.Vt cf_get_freq
+and
+.Vt cf_set_freq
+are function pointers that should be associated with the
+machine-dependent functions to get and set a frequency, respectively.
+The
+.Vt xcfunc_t
+type is part of
+.Xr xcall 9 .
+When the function pointers are invoked by
+.Nm ,
+the first parameter is always the
+.Vt cf_cookie
+and the second parameter is the frequency, defined as
+.Vt uint32_t * .
+.El
+.It Fn cpufreq_deregister
+Deregisters any possible backend in use.
+.It Fn cpufreq_suspend "ci"
+The
+.Fn cpufreq_suspend
+can be called when the processor suspends.
+The function saves the current frequency of
+.Fa ci
+and sets the minimum available frequency.
+.It Fn cpufreq_resume "ci"
+Resumes the frequency of
+.Fa ci
+that was used before suspend.
+.It Fn cpufreq_get "ci"
+Returns the current frequency of the processor
+.Fa ci .
+A value zero is returned upon failure.
+.It Fn cpufreq_get_backend "cf"
+Upon successful completion,
+.Fn cpufreq_get_backend
+returns 0 and fills
+.Fa cf
+with the data related to the currently used backend.
+.It Fn cpufreq_get_state "freq" "cfs"
+The
+.Fn cpufreq_get_state
+function looks for the given frequency
+from the array of known frequency states.
+If
+.Fa freq
+is not found, the closest match is returned.
+Upon successful completion,
+the function returns zero and stores the state information to
+.Fa cfs .
+.It Fn cpufreq_get_state_index "index" "cfs"
+Stores the frequency state with the given
+.Fa index
+to
+.Fa cfs ,
+returning zero upon successful completion.
+.It Fn cpufreq_set "ci" "freq"
+The
+.Fn cpufreq_set
+function sets the frequency of
+.Fa ci
+to
+.Fa freq .
+.It Fn cpufreq_set_all "freq"
+Sets
+.Fa freq
+for all processors in the system.
+.\" .It Fn cpufreq_set_higher "ci"
+.\" Decrements the current frequency level of
+.\" .Fa ci
+.\" by one state.
+.\" .It Fn cpufreq_set_lower "ci"
+.\" Increases the current frequency state of
+.\" .Fa ci
+.\" by one state.
+.El
+.Pp
+The three functions
+.Fn cpufreq_get ,
+.Fn cpufreq_set ,
+and
+.Fn cpufreq_set_all
+guarantee that the call will be made in
+.Xr curcpu 9 .
+The interface holds a
+.Xr mutex 9
+while calling the functions.
+This, and the use of
+.Xr xcall 9 ,
+implies that no memory can be allocated in the backend during the calls.
+.Sh CODE REFERENCES
+The
+.Nm
+interface is implemented within
+.Pa sys/kern/subr_cpufreq.c .
+.Sh SEE ALSO
+.Xr cpuctl 8 ,
+.Xr pmf 9 ,
+.Xr xcall 9
+.Rs
+.%A Venkatesh Pallipadi
+.%A Alexey Starikovskiy
+.%T The Ondemand Governor. Past, Present, and Future
+.%I Intel Open Source Technology Center
+.%O Proceedings of the Linux Symposium
+.%D July, 2006
+.%U http://www.kernel.org/doc/ols/2006/ols2006v2-pages-223-238.pdf
+.Re
+.Sh HISTORY
+The
+.Nm
+interface first appeared in
+.Nx 6.0 .
+.Sh AUTHORS
+.An Jukka Ruohonen
+.Aq jruoho...@iki.fi
+.Sh BUGS
+The interface does not support different
+.Dq governors
+and policies.

Index: src/sys/kern/subr_cpufreq.c
diff -u /dev/null src/sys/kern/subr_cpufreq.c:1.1
--- /dev/null	Wed Sep 28 10:55:49 2011
+++ src/sys/kern/subr_cpufreq.c	Wed Sep 28 10:55:48 2011
@@ -0,0 +1,526 @@
+/*	$NetBSD: subr_cpufreq.c,v 1.1 2011/09/28 10:55:48 jruoho Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jukka Ruohonen.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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 <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: subr_cpufreq.c,v 1.1 2011/09/28 10:55:48 jruoho Exp $");
+
+#include <sys/param.h>
+#include <sys/cpu.h>
+#include <sys/cpufreq.h>
+#include <sys/kmem.h>
+#include <sys/mutex.h>
+#include <sys/once.h>
+#include <sys/time.h>
+#include <sys/xcall.h>
+
+static int	 cpufreq_init(void);
+static int	 cpufreq_latency(void);
+static uint32_t	 cpufreq_get_max(void);
+static uint32_t	 cpufreq_get_min(void);
+static uint32_t	 cpufreq_get_raw(struct cpu_info *);
+static void	 cpufreq_get_state_raw(uint32_t, struct cpufreq_state *);
+static void	 cpufreq_set_raw(struct cpu_info *, uint32_t);
+static void	 cpufreq_set_all_raw(uint32_t);
+
+static kmutex_t cpufreq_lock __cacheline_aligned;
+static struct cpufreq *cf_backend __read_mostly = NULL;
+
+static int
+cpufreq_init(void)
+{
+
+	mutex_init(&cpufreq_lock, MUTEX_DEFAULT, IPL_NONE);
+
+	return 0;
+}
+
+int
+cpufreq_register(struct cpufreq *cf)
+{
+	static ONCE_DECL(cpufreq_once);
+	uint32_t count, i, j, k, m;
+	int rv;
+
+	rv = RUN_ONCE(&cpufreq_once, cpufreq_init);
+
+	KASSERT(rv == 0);
+	KASSERT(cf != NULL);
+	KASSERT(cf->cf_get_freq != NULL);
+	KASSERT(cf->cf_set_freq != NULL);
+	KASSERT(cf->cf_state_count > 0);
+	KASSERT(cf->cf_state_count < CPUFREQ_STATE_MAX);
+
+	mutex_enter(&cpufreq_lock);
+
+	if (cf_backend != NULL) {
+		mutex_exit(&cpufreq_lock);
+		return EALREADY;
+	}
+
+	mutex_exit(&cpufreq_lock);
+	cf_backend = kmem_zalloc(sizeof(*cf), KM_SLEEP);
+
+	if (cf_backend == NULL)
+		return ENOMEM;
+
+	mutex_enter(&cpufreq_lock);
+
+	cf_backend->cf_mp = cf->cf_mp;
+	cf_backend->cf_cookie = cf->cf_cookie;
+	cf_backend->cf_get_freq = cf->cf_get_freq;
+	cf_backend->cf_set_freq = cf->cf_set_freq;
+
+	(void)strlcpy(cf_backend->cf_name, cf->cf_name, sizeof(cf->cf_name));
+
+	/*
+	 * Sanity check the values and verify descending order.
+	 */
+	for (count = i = 0; i < cf->cf_state_count; i++) {
+
+		CTASSERT(CPUFREQ_STATE_ENABLED != 0);
+		CTASSERT(CPUFREQ_STATE_DISABLED != 0);
+
+		if (cf->cf_state[i].cfs_freq == 0)
+			continue;
+
+		for (j = k = 0; j < i; j++) {
+
+			if (cf->cf_state[i].cfs_freq >=
+			    cf->cf_state[j].cfs_freq) {
+				k = 1;
+				break;
+			}
+		}
+
+		if (k != 0)
+			continue;
+
+		cf_backend->cf_state[i].cfs_index = count;
+		cf_backend->cf_state[i].cfs_freq = cf->cf_state[i].cfs_freq;
+		cf_backend->cf_state[i].cfs_power = cf->cf_state[i].cfs_power;
+
+		count++;
+	}
+
+	cf_backend->cf_state_count = count;
+
+	if (cf_backend->cf_state_count == 0) {
+		mutex_exit(&cpufreq_lock);
+		cpufreq_deregister();
+		return EINVAL;
+	}
+
+	rv = cpufreq_latency();
+
+	if (rv != 0) {
+		mutex_exit(&cpufreq_lock);
+		cpufreq_deregister();
+		return rv;
+	}
+
+	m = cpufreq_get_max();
+	cpufreq_set_all_raw(m);
+	mutex_exit(&cpufreq_lock);
+
+	return 0;
+}
+
+void
+cpufreq_deregister(void)
+{
+
+	mutex_enter(&cpufreq_lock);
+
+	if (cf_backend == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	mutex_exit(&cpufreq_lock);
+	kmem_free(cf_backend, sizeof(*cf_backend));
+	cf_backend = NULL;
+}
+
+static int
+cpufreq_latency(void)
+{
+	struct cpufreq *cf = cf_backend;
+	struct timespec nta, ntb;
+	const uint32_t n = 10;
+	uint32_t i, j, l, m;
+	uint64_t s;
+
+	l = cpufreq_get_min();
+	m = cpufreq_get_max();
+
+	/*
+	 * For each state, sample the average transition
+	 * latency required to set the state for all CPUs.
+	 */
+	for (i = 0; i < cf->cf_state_count; i++) {
+
+		for (s = 0, j = 0; j < n; j++) {
+
+			/*
+			 * Attempt to exclude possible
+			 * caching done by the backend.
+			 */
+			if (i == 0)
+				cpufreq_set_all_raw(l);
+			else {
+				cpufreq_set_all_raw(m);
+			}
+
+			nta.tv_sec = nta.tv_nsec = 0;
+			ntb.tv_sec = ntb.tv_nsec = 0;
+
+			nanotime(&nta);
+			cpufreq_set_all_raw(cf->cf_state[i].cfs_freq);
+			nanotime(&ntb);
+			timespecsub(&ntb, &nta, &ntb);
+
+			if (ntb.tv_sec != 0 ||
+			    ntb.tv_nsec > CPUFREQ_LATENCY_MAX)
+				continue;
+
+			if (s >= UINT64_MAX - CPUFREQ_LATENCY_MAX)
+				break;
+
+			s += ntb.tv_nsec;
+		}
+
+		/*
+		 * Consider the backend unsuitable if
+		 * the transition latency was too high.
+		 */
+		if (s == 0)
+			return EMSGSIZE;
+
+		cf->cf_state[i].cfs_latency = s / n;
+	}
+
+	return 0;
+}
+
+void
+cpufreq_suspend(struct cpu_info *ci)
+{
+	struct cpufreq *cf;
+	uint32_t l, s;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (cf == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	l = cpufreq_get_min();
+	s = cpufreq_get_raw(ci);
+
+	cpufreq_set_raw(ci, l);
+	cf->cf_state_saved = s;
+
+	mutex_exit(&cpufreq_lock);
+}
+
+void
+cpufreq_resume(struct cpu_info *ci)
+{
+	struct cpufreq *cf;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (cf == NULL || cf->cf_state_saved == 0) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	cpufreq_set_raw(ci, cf->cf_state_saved);
+	mutex_exit(&cpufreq_lock);
+}
+
+uint32_t
+cpufreq_get(struct cpu_info *ci)
+{
+	struct cpufreq *cf;
+	uint32_t freq;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (cf == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return 0;
+	}
+
+	freq = cpufreq_get_raw(ci);
+	mutex_exit(&cpufreq_lock);
+
+	return freq;
+}
+
+static uint32_t
+cpufreq_get_max(void)
+{
+	struct cpufreq *cf = cf_backend;
+
+	KASSERT(cf != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	return cf->cf_state[0].cfs_freq;
+}
+
+static uint32_t
+cpufreq_get_min(void)
+{
+	struct cpufreq *cf = cf_backend;
+
+	KASSERT(cf != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	return cf->cf_state[cf->cf_state_count - 1].cfs_freq;
+}
+
+static uint32_t
+cpufreq_get_raw(struct cpu_info *ci)
+{
+	struct cpufreq *cf = cf_backend;
+	uint32_t freq = 0;
+	uint64_t xc;
+
+	KASSERT(cf != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	xc = xc_unicast(0, (*cf->cf_get_freq), cf->cf_cookie, &freq, ci);
+	xc_wait(xc);
+
+	return freq;
+}
+
+int
+cpufreq_get_backend(struct cpufreq *cf)
+{
+
+	mutex_enter(&cpufreq_lock);
+
+	if (cf_backend == NULL || cf == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return ENODEV;
+	}
+
+	(void)memcpy(cf, cf_backend, sizeof(*cf));
+	mutex_exit(&cpufreq_lock);
+
+	return 0;
+}
+
+int
+cpufreq_get_state(uint32_t freq, struct cpufreq_state *cfs)
+{
+	struct cpufreq *cf;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (cf == NULL || cfs == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return ENODEV;
+	}
+
+	cpufreq_get_state_raw(freq, cfs);
+	mutex_exit(&cpufreq_lock);
+
+	return 0;
+}
+
+int
+cpufreq_get_state_index(uint32_t index, struct cpufreq_state *cfs)
+{
+	struct cpufreq *cf;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (cf == NULL || cfs == NULL) {
+		mutex_exit(&cpufreq_lock);
+		return ENODEV;
+	}
+
+	if (index >= cf->cf_state_count) {
+		mutex_exit(&cpu_lock);
+		return EINVAL;
+	}
+
+	(void)memcpy(cfs, &cf->cf_state[index], sizeof(*cfs));
+	mutex_exit(&cpufreq_lock);
+
+	return 0;
+}
+
+static void
+cpufreq_get_state_raw(uint32_t freq, struct cpufreq_state *cfs)
+{
+	struct cpufreq *cf = cf_backend;
+	uint32_t f, hi, i = 0, lo = 0;
+
+	KASSERT(cf != NULL && cfs != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	hi = cf->cf_state_count;
+
+	while (lo < hi) {
+
+		i = (lo + hi) >> 1;
+		f = cf->cf_state[i].cfs_freq;
+
+		if (freq == f)
+			break;
+		else if (freq > f)
+			hi = i;
+		else {
+			lo = i + 1;
+		}
+	}
+
+	(void)memcpy(cfs, &cf->cf_state[i], sizeof(*cfs));
+}
+
+void
+cpufreq_set(struct cpu_info *ci, uint32_t freq)
+{
+	struct cpufreq *cf;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (__predict_false(cf == NULL)) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	cpufreq_set_raw(ci, freq);
+	mutex_exit(&cpufreq_lock);
+}
+
+static void
+cpufreq_set_raw(struct cpu_info *ci, uint32_t freq)
+{
+	struct cpufreq *cf = cf_backend;
+	uint64_t xc;
+
+	KASSERT(cf != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	xc = xc_unicast(0, (*cf->cf_set_freq), cf->cf_cookie, &freq, ci);
+	xc_wait(xc);
+}
+
+void
+cpufreq_set_all(uint32_t freq)
+{
+	struct cpufreq *cf;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (__predict_false(cf == NULL)) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	cpufreq_set_all_raw(freq);
+	mutex_exit(&cpufreq_lock);
+}
+
+static void
+cpufreq_set_all_raw(uint32_t freq)
+{
+	struct cpufreq *cf = cf_backend;
+	uint64_t xc;
+
+	KASSERT(cf != NULL);
+	KASSERT(mutex_owned(&cpufreq_lock) != 0);
+
+	xc = xc_broadcast(0, (*cf->cf_set_freq), cf->cf_cookie, &freq);
+	xc_wait(xc);
+}
+
+#ifdef notyet
+void
+cpufreq_set_higher(struct cpu_info *ci)
+{
+	cpufreq_set_step(ci, -1);
+}
+
+void
+cpufreq_set_lower(struct cpu_info *ci)
+{
+	cpufreq_set_step(ci, 1);
+}
+
+static void
+cpufreq_set_step(struct cpu_info *ci, int32_t step)
+{
+	struct cpufreq_state cfs;
+	struct cpufreq *cf;
+	uint32_t freq;
+	int32_t index;
+
+	mutex_enter(&cpufreq_lock);
+	cf = cf_backend;
+
+	if (__predict_false(cf == NULL)) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	freq = cpufreq_get_raw(ci);
+
+	if (__predict_false(freq == 0)) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	cpufreq_get_state_raw(freq, &cfs);
+	index = cfs.cfs_index + step;
+
+	if (index < 0 || index >= (int32_t)cf->cf_state_count) {
+		mutex_exit(&cpufreq_lock);
+		return;
+	}
+
+	cpufreq_set_raw(ci, cf->cf_state[index].cfs_freq);
+	mutex_exit(&cpufreq_lock);
+}
+#endif

Index: src/sys/sys/cpufreq.h
diff -u /dev/null src/sys/sys/cpufreq.h:1.1
--- /dev/null	Wed Sep 28 10:55:49 2011
+++ src/sys/sys/cpufreq.h	Wed Sep 28 10:55:48 2011
@@ -0,0 +1,91 @@
+/*	$NetBSD: cpufreq.h,v 1.1 2011/09/28 10:55:48 jruoho Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jukka Ruohonen.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+#ifndef	_SYS_CPUFREQ_H_
+#define	_SYS_CPUFREQ_H_
+
+#ifndef _KERNEL
+#include <stdbool.h>
+#endif
+
+#ifdef _KERNEL
+#ifndef _SYS_XCALL_H_
+#include <sys/xcall.h>
+#endif
+#endif
+
+#define CPUFREQ_NAME_MAX	 16
+#define CPUFREQ_STATE_MAX	 64
+#define CPUFREQ_LATENCY_MAX	 UINT16_MAX	  /* Maximum per-CPU latency */
+
+#define CPUFREQ_STATE_ENABLED	UINT32_MAX
+#define CPUFREQ_STATE_DISABLED	UINT32_MAX - 1
+
+struct cpufreq_state {
+	uint32_t		 cfs_freq;	  /* MHz  */
+	uint32_t		 cfs_power;	  /* mW   */
+	uint32_t		 cfs_latency;	  /* nsec */
+	uint32_t		 cfs_index;
+	uint32_t		 cfs_reserved[5];
+};
+
+struct cpufreq {
+	char			 cf_name[CPUFREQ_NAME_MAX];
+	uint32_t		 cf_state_count;
+	uint32_t		 cf_state_target;
+	uint32_t		 cf_state_current;
+	uint32_t		 cf_reserved[5];
+	u_int			 cf_index;
+
+#ifdef _KERNEL
+	bool			 cf_mp;
+	void			*cf_cookie;
+	xcfunc_t		 cf_get_freq;
+	xcfunc_t		 cf_set_freq;
+	uint32_t		 cf_state_saved;
+	struct cpufreq_state	 cf_state[CPUFREQ_STATE_MAX];
+#endif	/* _KERNEL */
+};
+
+#ifdef _KERNEL
+int		cpufreq_register(struct cpufreq *);
+void		cpufreq_deregister(void);
+void		cpufreq_suspend(struct cpu_info *);
+void		cpufreq_resume(struct cpu_info *);
+uint32_t	cpufreq_get(struct cpu_info *);
+int		cpufreq_get_backend(struct cpufreq *);
+int		cpufreq_get_state(uint32_t, struct cpufreq_state *);
+int		cpufreq_get_state_index(uint32_t, struct cpufreq_state *);
+void		cpufreq_set(struct cpu_info *, uint32_t);
+void		cpufreq_set_all(uint32_t);
+#endif	/* _KERNEL */
+
+#endif /* _SYS_CPUFREQ_H_ */

Reply via email to