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_ */