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 [email protected]
+.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_ */