Module Name: src Committed By: jruoho Date: Thu Feb 24 10:56:03 UTC 2011
Modified Files: src/sys/arch/amd64/conf: GENERIC XEN3_DOM0 src/sys/arch/i386/conf: ALL GENERIC XEN3_DOM0 files.i386 src/sys/arch/x86/conf: files.x86 src/sys/arch/x86/include: powernow.h src/sys/arch/x86/x86: cpu.c identcpu.c src/sys/arch/xen/conf: files.xen src/sys/arch/xen/x86: cpu.c Added Files: src/sys/arch/x86/x86: powernow.c Removed Files: src/sys/arch/i386/i386: powernow_k7.c src/sys/arch/x86/x86: powernow_common.c powernow_k8.c Log Message: Move PowerNow! to the cpufeaturebus. To generate a diff of this commit: cvs rdiff -u -r1.313 -r1.314 src/sys/arch/amd64/conf/GENERIC cvs rdiff -u -r1.63 -r1.64 src/sys/arch/amd64/conf/XEN3_DOM0 cvs rdiff -u -r1.294 -r1.295 src/sys/arch/i386/conf/ALL cvs rdiff -u -r1.1021 -r1.1022 src/sys/arch/i386/conf/GENERIC cvs rdiff -u -r1.45 -r1.46 src/sys/arch/i386/conf/XEN3_DOM0 cvs rdiff -u -r1.355 -r1.356 src/sys/arch/i386/conf/files.i386 cvs rdiff -u -r1.33 -r0 src/sys/arch/i386/i386/powernow_k7.c cvs rdiff -u -r1.60 -r1.61 src/sys/arch/x86/conf/files.x86 cvs rdiff -u -r1.12 -r1.13 src/sys/arch/x86/include/powernow.h cvs rdiff -u -r1.83 -r1.84 src/sys/arch/x86/x86/cpu.c cvs rdiff -u -r1.25 -r1.26 src/sys/arch/x86/x86/identcpu.c cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/x86/powernow.c cvs rdiff -u -r1.11 -r0 src/sys/arch/x86/x86/powernow_common.c cvs rdiff -u -r1.28 -r0 src/sys/arch/x86/x86/powernow_k8.c cvs rdiff -u -r1.112 -r1.113 src/sys/arch/xen/conf/files.xen cvs rdiff -u -r1.53 -r1.54 src/sys/arch/xen/x86/cpu.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/amd64/conf/GENERIC diff -u src/sys/arch/amd64/conf/GENERIC:1.313 src/sys/arch/amd64/conf/GENERIC:1.314 --- src/sys/arch/amd64/conf/GENERIC:1.313 Wed Feb 23 11:43:21 2011 +++ src/sys/arch/amd64/conf/GENERIC Thu Feb 24 10:56:00 2011 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.313 2011/02/23 11:43:21 jruoho Exp $ +# $NetBSD: GENERIC,v 1.314 2011/02/24 10:56:00 jruoho Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.313 $" +#ident "GENERIC-$Revision: 1.314 $" maxusers 64 # estimated number of users @@ -78,12 +78,10 @@ #options PIPE_SOCKETPAIR # smaller, but slower pipe(2) options SYSCTL_INCLUDE_DESCR # Include sysctl descriptions in kernel -# AMD PowerNow! and Cool`n'Quiet technology -options POWERNOW_K8 - # CPU features coretemp* at cpu? # Intel on-die thermal sensor est0 at cpu0 # Intel Enhanced SpeedStep (non-ACPI) +powernow0 at cpu0 # AMD PowerNow! and Cool'n'Quiet (non-ACPI) # Intel(R) On Demand Clock Modulation (aka ODCM) # options INTEL_ONDEMAND_CLOCKMOD Index: src/sys/arch/amd64/conf/XEN3_DOM0 diff -u src/sys/arch/amd64/conf/XEN3_DOM0:1.63 src/sys/arch/amd64/conf/XEN3_DOM0:1.64 --- src/sys/arch/amd64/conf/XEN3_DOM0:1.63 Thu Feb 24 04:42:54 2011 +++ src/sys/arch/amd64/conf/XEN3_DOM0 Thu Feb 24 10:56:00 2011 @@ -1,4 +1,4 @@ -# $NetBSD: XEN3_DOM0,v 1.63 2011/02/24 04:42:54 jruoho Exp $ +# $NetBSD: XEN3_DOM0,v 1.64 2011/02/24 10:56:00 jruoho Exp $ include "arch/amd64/conf/std.xen" @@ -20,11 +20,9 @@ #options VM86 # virtual 8086 emulation #options USER_LDT # user-settable LDT; used by WINE -# AMD PowerNow! and Cool`n'Quiet technology -options POWERNOW_K8 - # CPU features est0 at cpu0 # Intel Enhanced SpeedStep (non-ACPI) +powernow0 at cpu0 # AMD PowerNow! and Cool'n'Quiet (non-ACPI) #options MTRR # memory-type range register syscall support Index: src/sys/arch/i386/conf/ALL diff -u src/sys/arch/i386/conf/ALL:1.294 src/sys/arch/i386/conf/ALL:1.295 --- src/sys/arch/i386/conf/ALL:1.294 Wed Feb 23 11:43:22 2011 +++ src/sys/arch/i386/conf/ALL Thu Feb 24 10:56:01 2011 @@ -1,4 +1,4 @@ -# $NetBSD: ALL,v 1.294 2011/02/23 11:43:22 jruoho Exp $ +# $NetBSD: ALL,v 1.295 2011/02/24 10:56:01 jruoho Exp $ # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp # # ALL machine description file @@ -17,7 +17,7 @@ options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "ALL-$Revision: 1.294 $" +#ident "ALL-$Revision: 1.295 $" maxusers 64 # estimated number of users @@ -29,16 +29,11 @@ options X86EMU # 386 Real Mode emulator options PAE # PAE mode (36 bits physical addressing) -# AMD PowerNow! K7 -options POWERNOW_K7 - -# AMD PowerNow! and Cool`n'Quiet technology -options POWERNOW_K8 - # CPU features coretemp* at cpu? # Intel on-die thermal sensor est0 at cpu0 # Intel Enhanced SpeedStep (non-ACPI) padlock0 at cpu0 # VIA PadLock +powernow0 at cpu0 # AMD PowerNow! and Cool'n'Quiet (non-ACPI) # Intel(R) On Demand Clock Modulation (aka ODCM) options INTEL_ONDEMAND_CLOCKMOD Index: src/sys/arch/i386/conf/GENERIC diff -u src/sys/arch/i386/conf/GENERIC:1.1021 src/sys/arch/i386/conf/GENERIC:1.1022 --- src/sys/arch/i386/conf/GENERIC:1.1021 Wed Feb 23 11:43:22 2011 +++ src/sys/arch/i386/conf/GENERIC Thu Feb 24 10:56:01 2011 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.1021 2011/02/23 11:43:22 jruoho Exp $ +# $NetBSD: GENERIC,v 1.1022 2011/02/24 10:56:01 jruoho Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.1021 $" +#ident "GENERIC-$Revision: 1.1022 $" maxusers 64 # estimated number of users @@ -37,16 +37,11 @@ options USER_LDT # user-settable LDT; used by WINE #options PAE # PAE mode (36 bits physical addressing) -# AMD PowerNow! K7 -options POWERNOW_K7 - -# AMD PowerNow! and Cool`n'Quiet technology -options POWERNOW_K8 - # CPU features coretemp* at cpu? # Intel on-die thermal sensor est0 at cpu0 # Intel Enhanced SpeedStep (non-ACPI) #padlock0 at cpu0 # VIA PadLock +powernow0 at cpu0 # AMD PowerNow! and Cool'n'Quiet (non-ACPI) # Intel(R) On Demand Clock Modulation (aka ODCM) #options INTEL_ONDEMAND_CLOCKMOD Index: src/sys/arch/i386/conf/XEN3_DOM0 diff -u src/sys/arch/i386/conf/XEN3_DOM0:1.45 src/sys/arch/i386/conf/XEN3_DOM0:1.46 --- src/sys/arch/i386/conf/XEN3_DOM0:1.45 Thu Feb 24 07:51:16 2011 +++ src/sys/arch/i386/conf/XEN3_DOM0 Thu Feb 24 10:56:01 2011 @@ -1,4 +1,4 @@ -# $NetBSD: XEN3_DOM0,v 1.45 2011/02/24 07:51:16 jruoho Exp $ +# $NetBSD: XEN3_DOM0,v 1.46 2011/02/24 10:56:01 jruoho Exp $ # # XEN3_0: Xen 3.0 domain0 kernel @@ -30,14 +30,9 @@ #options VM86 # virtual 8086 emulation #options USER_LDT # user-settable LDT; used by WINE -# AMD PowerNow! K7 -options POWERNOW_K7 - -# AMD PowerNow! and Cool`n'Quiet technology -options POWERNOW_K8 - # CPU features est0 at cpu0 # Intel Enhanced SpeedStep (non-ACPI) +powernow0 at cpu0 # AMD PowerNow! and Cool'n'Quiet (non-ACPI) #options MTRR # memory-type range register syscall support Index: src/sys/arch/i386/conf/files.i386 diff -u src/sys/arch/i386/conf/files.i386:1.355 src/sys/arch/i386/conf/files.i386:1.356 --- src/sys/arch/i386/conf/files.i386:1.355 Thu Jul 8 11:24:59 2010 +++ src/sys/arch/i386/conf/files.i386 Thu Feb 24 10:56:01 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.i386,v 1.355 2010/07/08 11:24:59 rmind Exp $ +# $NetBSD: files.i386,v 1.356 2011/02/24 10:56:01 jruoho Exp $ # # new style config file for i386 architecture # @@ -59,9 +59,6 @@ obsolete defparam MULTIBOOT_SYMTAB_SPACE file arch/i386/i386/multiboot.c multiboot -# PowerNow K7 -defflag POWERNOW_K7 - file arch/i386/i386/autoconf.c file arch/i386/i386/aout_machdep.c exec_aout file arch/i386/i386/busfunc.S @@ -489,9 +486,6 @@ obsolete defparam opt_vesafb.h VESAFB_WIDTH VESAFB_HEIGHT VESAFB_DEPTH obsolete defflag opt_vesafb.h VESAFB_PM -# AMD PowerNow K7 -file arch/i386/i386/powernow_k7.c powernow_k7 - # AMD Geode LX Security Block device glxsb: opencrypto attach glxsb at pci Index: src/sys/arch/x86/conf/files.x86 diff -u src/sys/arch/x86/conf/files.x86:1.60 src/sys/arch/x86/conf/files.x86:1.61 --- src/sys/arch/x86/conf/files.x86:1.60 Wed Feb 23 11:43:22 2011 +++ src/sys/arch/x86/conf/files.x86 Thu Feb 24 10:56:02 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.x86,v 1.60 2011/02/23 11:43:22 jruoho Exp $ +# $NetBSD: files.x86,v 1.61 2011/02/24 10:56:02 jruoho Exp $ # options for MP configuration through the MP spec defflag opt_mpbios.h MPBIOS MPVERBOSE MPDEBUG MPBIOS_SCANPCI @@ -16,9 +16,6 @@ # Pentium 4+ Thermal Monitor ODCM (aka On Demand Clock Modulation) defflag opt_intel_odcm.h INTEL_ONDEMAND_CLOCKMOD -# AMD Powernow/Cool`n'Quiet Technology -defflag opt_powernow_k8.h POWERNOW_K8 - # VIA C7 Temperature sensor defflag opt_via_c7temp.h VIA_C7TEMP: sysmon_envsys @@ -37,19 +34,23 @@ attach cpu at cpubus file arch/x86/x86/cpu.c cpu +device coretemp: sysmon_envsys +attach coretemp at cpufeaturebus +file arch/x86/x86/coretemp.c coretemp + device est attach est at cpufeaturebus file arch/x86/x86/est.c est file arch/x86/x86/intel_busclock.c est -device coretemp: sysmon_envsys -attach coretemp at cpufeaturebus -file arch/x86/x86/coretemp.c coretemp - device padlock: opencrypto attach padlock at cpufeaturebus file arch/x86/x86/via_padlock.c padlock +device powernow +attach powernow at cpufeaturebus +file arch/x86/x86/powernow.c powernow + file arch/x86/x86/apic.c ioapic | lapic file arch/x86/x86/bus_dma.c file arch/x86/x86/bus_space.c @@ -98,10 +99,6 @@ file arch/x86/isa/isa_machdep.c isa -# Powernow common functions -file arch/x86/x86/powernow_k8.c powernow_k8 -file arch/x86/x86/powernow_common.c powernow_k8 | powernow_k7 - # Intel On Demand Clock Modulation file arch/x86/x86/iclockmod.c intel_ondemand_clockmod Index: src/sys/arch/x86/include/powernow.h diff -u src/sys/arch/x86/include/powernow.h:1.12 src/sys/arch/x86/include/powernow.h:1.13 --- src/sys/arch/x86/include/powernow.h:1.12 Tue Oct 26 07:54:12 2010 +++ src/sys/arch/x86/include/powernow.h Thu Feb 24 10:56:02 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: powernow.h,v 1.12 2010/10/26 07:54:12 jruoho Exp $ */ +/* $NetBSD: powernow.h,v 1.13 2011/02/24 10:56:02 jruoho Exp $ */ /*- * Copyright (c) 2004 Martin Végiard. @@ -52,8 +52,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef X86_POWERNOW_H -#define X86_POWERNOW_H +#ifndef _X86_POWERNOW_H +#define _X86_POWERNOW_H #ifdef POWERNOW_DEBUG #define DPRINTF(x) do { printf x; } while (0) @@ -126,7 +126,7 @@ }; struct powernow_cpu_state { - struct powernow_state state_table[POWERNOW_MAX_STATES]; + struct powernow_state state_table[POWERNOW_MAX_STATES]; unsigned int fsb; unsigned int n_states; unsigned int sgtc; @@ -155,14 +155,4 @@ uint8_t n_states; }; -int powernow_probe(struct cpu_info *); - -/* i386/i386/powernow_k7.c */ -void k7_powernow_init(void); -void k7_powernow_destroy(void); - -/* x86/x86/powernow_k8.c */ -void k8_powernow_init(void); -void k8_powernow_destroy(void); - -#endif +#endif /* !_X86_POWERNOW_H */ Index: src/sys/arch/x86/x86/cpu.c diff -u src/sys/arch/x86/x86/cpu.c:1.83 src/sys/arch/x86/x86/cpu.c:1.84 --- src/sys/arch/x86/x86/cpu.c:1.83 Wed Feb 23 11:43:23 2011 +++ src/sys/arch/x86/x86/cpu.c Thu Feb 24 10:56:02 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: cpu.c,v 1.83 2011/02/23 11:43:23 jruoho Exp $ */ +/* $NetBSD: cpu.c,v 1.84 2011/02/24 10:56:02 jruoho Exp $ */ /*- * Copyright (c) 2000, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -62,7 +62,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.83 2011/02/23 11:43:23 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.84 2011/02/24 10:56:02 jruoho Exp $"); #include "opt_ddb.h" #include "opt_mpbios.h" /* for MPDEBUG */ @@ -470,6 +470,12 @@ "cpufeaturebus", &cfaa, NULL); } + if (ci->ci_frequency == NULL) { + cfaa.name = "powernow"; + ci->ci_frequency = config_found_ia(self, + "cpufeaturebus", &cfaa, NULL); + } + if (ci->ci_padlock == NULL) { cfaa.name = "padlock"; ci->ci_padlock = config_found_ia(self, Index: src/sys/arch/x86/x86/identcpu.c diff -u src/sys/arch/x86/x86/identcpu.c:1.25 src/sys/arch/x86/x86/identcpu.c:1.26 --- src/sys/arch/x86/x86/identcpu.c:1.25 Wed Feb 23 11:43:23 2011 +++ src/sys/arch/x86/x86/identcpu.c Thu Feb 24 10:56:02 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: identcpu.c,v 1.25 2011/02/23 11:43:23 jruoho Exp $ */ +/* $NetBSD: identcpu.c,v 1.26 2011/02/24 10:56:02 jruoho Exp $ */ /*- * Copyright (c) 1999, 2000, 2001, 2006, 2007, 2008 The NetBSD Foundation, Inc. @@ -30,15 +30,11 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.25 2011/02/23 11:43:23 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.26 2011/02/24 10:56:02 jruoho Exp $"); #include "opt_intel_odcm.h" #include "opt_via_c7temp.h" -#include "opt_powernow_k8.h" #include "opt_xen.h" -#ifdef i386 /* XXX */ -#include "opt_powernow_k7.h" -#endif #include <sys/param.h> #include <sys/systm.h> @@ -54,7 +50,6 @@ #include <x86/cacheinfo.h> #include <x86/cpuvar.h> #include <x86/cpu_msr.h> -#include <x86/powernow.h> static const struct x86_cache_info intel_cpuid_cache_info[] = INTEL_CACHE_INFO; @@ -813,25 +808,6 @@ } #endif -#if defined(POWERNOW_K7) || defined(POWERNOW_K8) - if (cpu_vendor == CPUVENDOR_AMD && powernow_probe(ci)) { - switch (CPUID2FAMILY(ci->ci_signature)) { -#ifdef POWERNOW_K7 - case 6: - k7_powernow_init(); - break; -#endif -#ifdef POWERNOW_K8 - case 15: - k8_powernow_init(); - break; -#endif - default: - break; - } - } -#endif /* POWERNOW_K7 || POWERNOW_K8 */ - #ifdef INTEL_ONDEMAND_CLOCKMOD if (cpuid_level >= 1) { clockmod_init(); Index: src/sys/arch/xen/conf/files.xen diff -u src/sys/arch/xen/conf/files.xen:1.112 src/sys/arch/xen/conf/files.xen:1.113 --- src/sys/arch/xen/conf/files.xen:1.112 Thu Feb 24 04:42:54 2011 +++ src/sys/arch/xen/conf/files.xen Thu Feb 24 10:56:03 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.xen,v 1.112 2011/02/24 04:42:54 jruoho Exp $ +# $NetBSD: files.xen,v 1.113 2011/02/24 10:56:03 jruoho Exp $ # NetBSD: files.x86,v 1.10 2003/10/08 17:30:00 bouyer Exp # NetBSD: files.i386,v 1.254 2004/03/25 23:32:10 jmc Exp @@ -33,12 +33,6 @@ defparam PCI_CONF_MODE -# AMD Powernow/Cool`n'Quiet Technology -defflag opt_powernow_k8.h POWERNOW_K8 -# Powernow common functions -file arch/x86/x86/powernow_k8.c powernow_k8 -file arch/x86/x86/powernow_common.c powernow_k8 | powernow_k7 - file arch/xen/x86/autoconf.c ifdef i386 file arch/i386/i386/aout_machdep.c exec_aout @@ -73,10 +67,6 @@ file crypto/blowfish/arch/i386/bf_enc.S blowfish file crypto/blowfish/arch/i386/bf_cbc.S blowfish -# AMD PowerNow K7 -defflag POWERNOW_K7 -file arch/i386/i386/powernow_k7.c powernow_k7 - elifdef amd64 file arch/amd64/amd64/busfunc.S file arch/amd64/amd64/cpufunc.S @@ -302,6 +292,10 @@ file arch/x86/x86/est.c est file arch/x86/x86/intel_busclock.c est +device powernow +attach powernow at cpufeaturebus +file arch/x86/x86/powernow.c powernow + # # Compatibility modules # Index: src/sys/arch/xen/x86/cpu.c diff -u src/sys/arch/xen/x86/cpu.c:1.53 src/sys/arch/xen/x86/cpu.c:1.54 --- src/sys/arch/xen/x86/cpu.c:1.53 Thu Feb 24 04:42:55 2011 +++ src/sys/arch/xen/x86/cpu.c Thu Feb 24 10:56:03 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: cpu.c,v 1.53 2011/02/24 04:42:55 jruoho Exp $ */ +/* $NetBSD: cpu.c,v 1.54 2011/02/24 10:56:03 jruoho Exp $ */ /* NetBSD: cpu.c,v 1.18 2004/02/20 17:35:01 yamt Exp */ /*- @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.53 2011/02/24 04:42:55 jruoho Exp $"); +__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.54 2011/02/24 10:56:03 jruoho Exp $"); #include "opt_ddb.h" #include "opt_multiprocessor.h" @@ -290,6 +290,12 @@ ci->ci_frequency = config_found_ia(self, "cpufeaturebus", &cfaa, NULL); } + + if (ci->ci_frequency == NULL) { + cfaa.name = "powernow"; + ci->ci_frequency = config_found_ia(self, + "cpufeaturebus", &cfaa, NULL); + } } return 0; Added files: Index: src/sys/arch/x86/x86/powernow.c diff -u /dev/null src/sys/arch/x86/x86/powernow.c:1.1 --- /dev/null Thu Feb 24 10:56:04 2011 +++ src/sys/arch/x86/x86/powernow.c Thu Feb 24 10:56:02 2011 @@ -0,0 +1,1000 @@ +/* $NetBSD: powernow.c,v 1.1 2011/02/24 10:56:02 jruoho Exp $ */ +/* $OpenBSD: powernow-k8.c,v 1.8 2006/06/16 05:58:50 gwk Exp $ */ + +/*- + * Copyright (c) 2004, 2006, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Juan Romero Pardines and Martin Vegiard. + * + * 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. + */ + +/* + * Copyright (c) 2004-2005 Bruno Ducrot + * Copyright (c) 2004 FUKUDA Nobuhiko <nfuk...@spa.is.uec.ac.jp> + * Copyright (c) 2004 Martin Vegiard + * + * 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 <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: powernow.c,v 1.1 2011/02/24 10:56:02 jruoho Exp $"); + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/xcall.h> + +#include <dev/isa/isareg.h> + +#include <x86/cpuvar.h> +#include <x86/cputypes.h> +#include <x86/cpu_msr.h> +#include <x86/isa_machdep.h> +#include <x86/powernow.h> +#include <x86/specialreg.h> + +#ifdef __i386__ +/* + * Divide each value by 10 to get the processor multiplier. + * Taken from powernow-k7.c/Linux by Dave Jones. + */ +static int powernow_k7_fidtomult[32] = { + 110, 115, 120, 125, 50, 55, 60, 65, + 70, 75, 80, 85, 90, 95, 100, 105, + 30, 190, 40, 200, 130, 135, 140, 210, + 150, 225, 160, 165, 170, 180, -1, -1 +}; + +/* + * The following CPUs do not have same CPUID signature + * in the PST tables, but they have correct FID/VID values. + */ +static struct powernow_k7_quirk { + uint32_t rcpusig; /* Correct cpu signature */ + uint32_t pcpusig; /* PST cpu signature */ +} powernow_k7_quirk[] = { + { 0x680, 0x780 }, /* Reported by Olaf 'Rhialto' Seibert */ + { 0x6a0, 0x781 }, /* Reported by Eric Schnoebelen */ + { 0x6a0, 0x7a0 }, /* Reported by Nino Dehne */ + { 0, 0 } +}; + +#endif /* __i386__ */ + +struct powernow_softc { + device_t sc_dev; + struct cpu_info *sc_ci; + struct sysctllog *sc_log; + struct powernow_cpu_state *sc_state; + const char *sc_tech; + char *sc_freqs; + size_t sc_freqs_len; + unsigned int sc_freqs_cur; + int sc_node_target; + int sc_node_current; + int sc_flags; +}; + +static int powernow_match(device_t, cfdata_t, void *); +static void powernow_attach(device_t, device_t, void *); +static int powernow_detach(device_t, int); +static int powernow_sysctl(device_t); +static int powernow_sysctl_helper(SYSCTLFN_PROTO); + +#ifdef __i386__ +static int powernow_k7_init(device_t); +static int powernow_k7_states(device_t, unsigned int, unsigned int); +static int powernow_k7_setperf(device_t, unsigned int); +static bool powernow_k7_check(uint32_t, uint32_t); +static int powernow_k7_decode_pst(struct powernow_softc *, uint8_t *,int); +#endif + +static int powernow_k8_init(device_t); +static int powernow_k8_states(device_t, unsigned int, unsigned int); +static int powernow_k8_setperf(device_t, unsigned int); +static uint64_t powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl); +static int powernow_k8_decode_pst(struct powernow_cpu_state *, uint8_t *); + +CFATTACH_DECL_NEW(powernow, sizeof(struct powernow_softc), + powernow_match, powernow_attach, powernow_detach, NULL); + +static int +powernow_match(device_t parent, cfdata_t cf, void *aux) +{ + struct cpufeature_attach_args *cfaa = aux; + struct cpu_info *ci = cfaa->ci; + uint32_t family, regs[4]; + + if (strcmp(cfaa->name, "powernow") != 0) + return 0; + + if (cpu_vendor != CPUVENDOR_AMD) + return 0; + + family = CPUID2FAMILY(ci->ci_signature); + + if (family != 0x06 && family != 0x0f) + return 0; + else { + x86_cpuid(0x80000000, regs); + + if (regs[0] < 0x80000007) + return 0; + + x86_cpuid(0x80000007, regs); + } + + return (regs[3] & AMD_PN_FID_VID) == AMD_PN_FID_VID; +} + +static void +powernow_attach(device_t parent, device_t self, void *aux) +{ + struct powernow_softc *sc = device_private(self); + struct cpufeature_attach_args *cfaa = aux; + struct cpu_info *ci = cfaa->ci; + uint32_t family; + int rv; + + sc->sc_ci = ci; + sc->sc_dev = self; + + sc->sc_flags = 0; + sc->sc_log = NULL; + sc->sc_tech = NULL; + sc->sc_state = NULL; + sc->sc_freqs = NULL; + + family = CPUID2FAMILY(ci->ci_signature); + + switch (family) { + +#ifdef __i386__ + case 0x06: + rv = powernow_k7_init(self); + break; +#endif + case 0x0f: + rv = powernow_k8_init(self); + break; + + default: + rv = ENODEV; + } + + if (rv != 0) { + aprint_normal(": failed to initialize (err %d)\n", rv); + return; + } + + rv = powernow_sysctl(self); + + if (rv != 0) { + aprint_normal(": failed to initialize sysctl (err %d)\n", rv); + return; + } + + KASSERT(sc->sc_tech != NULL); + + aprint_naive("\n"); + aprint_normal(": AMD %s\n", sc->sc_tech); + + (void)pmf_device_register(self, NULL, NULL); +} + +static int +powernow_detach(device_t self, int flags) +{ + struct powernow_softc *sc = device_private(self); + + if (sc->sc_log != NULL) + sysctl_teardown(&sc->sc_log); + + if (sc->sc_state != NULL) + kmem_free(sc->sc_state, sizeof(*sc->sc_state)); + + if (sc->sc_freqs != NULL) + kmem_free(sc->sc_freqs, sc->sc_freqs_len); + + pmf_device_deregister(self); + + return 0; +} + +static int +powernow_sysctl(device_t self) +{ + const struct sysctlnode *freqnode, *node, *pnownode; + struct powernow_softc *sc = device_private(self); + int rv; + + /* + * Setup the sysctl sub-tree machdep.powernow. + */ + rv = sysctl_createv(&sc->sc_log, 0, NULL, &node, + CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL, + NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL); + + if (rv != 0) + goto fail; + + rv = sysctl_createv(&sc->sc_log, 0, &node, &pnownode, + 0, CTLTYPE_NODE, "powernow", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + + if (rv != 0) + goto fail; + + rv = sysctl_createv(&sc->sc_log, 0, &pnownode, &freqnode, + 0, CTLTYPE_NODE, "frequency", NULL, + NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); + + if (rv != 0) + goto fail; + + rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL, + powernow_sysctl_helper, 0, sc, 0, CTL_CREATE, CTL_EOL); + + if (rv != 0) + goto fail; + + sc->sc_node_target = node->sysctl_num; + + rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + 0, CTLTYPE_INT, "current", NULL, + powernow_sysctl_helper, 0, sc, 0, CTL_CREATE, CTL_EOL); + + if (rv != 0) + goto fail; + + sc->sc_node_current = node->sysctl_num; + + rv = sysctl_createv(&sc->sc_log, 0, &freqnode, &node, + 0, CTLTYPE_STRING, "available", NULL, + NULL, 0, sc->sc_freqs, sc->sc_freqs_len, CTL_CREATE, CTL_EOL); + + if (rv != 0) + goto fail; + + return 0; + +fail: + sysctl_teardown(&sc->sc_log); + sc->sc_log = NULL; + + return rv; +} + +static int +powernow_sysctl_helper(SYSCTLFN_ARGS) +{ + struct powernow_softc *sc; + struct sysctlnode node; + int fq, oldfq, error; + uint32_t family; + int rv; + + fq = oldfq = 0; + + node = *rnode; + sc = node.sysctl_data; + + if (sc->sc_state == NULL) + return EOPNOTSUPP; + + node.sysctl_data = &fq; + + if (rnode->sysctl_num == sc->sc_node_target) + fq = oldfq = sc->sc_freqs_cur; + else if (rnode->sysctl_num == sc->sc_node_current) + fq = sc->sc_freqs_cur; + else + return EOPNOTSUPP; + + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + + if (error || newp == NULL) + return error; + + family = CPUID2FAMILY(sc->sc_ci->ci_signature); + + if (rnode->sysctl_num == sc->sc_node_target && fq != oldfq) { + + switch (family) { + +#ifdef __i386__ + case 0x06: + rv = powernow_k7_setperf(sc->sc_dev, fq); + break; +#endif + case 0x0f: + rv = powernow_k8_setperf(sc->sc_dev, fq); + break; + + default: + return ENODEV; + } + + if (rv != 0) + return EINVAL; + + sc->sc_freqs_cur = fq; + } + + return 0; +} + +#ifdef __i386__ + +static int +powernow_k7_init(device_t self) +{ + struct powernow_softc *sc = device_private(self); + uint32_t currentfid, maxfid, mhz, startvid; + uint64_t status; + int i, rv, len; + char tmp[6]; + + sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP); + + if (sc->sc_state == NULL) + return ENOMEM; + + if (sc->sc_ci->ci_signature == AMD_ERRATA_A0_CPUSIG) + sc->sc_flags |= PN7_FLAG_ERRATA_A0; + + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + maxfid = PN7_STA_MFID(status); + startvid = PN7_STA_SVID(status); + currentfid = PN7_STA_CFID(status); + + mhz = sc->sc_ci->ci_data.cpu_cc_freq / 1000000; + sc->sc_state->fsb = mhz / (powernow_k7_fidtomult[currentfid] / 10); + + if (powernow_k7_states(self, maxfid, startvid) == 0) { + rv = ENXIO; + goto fail; + } + + if (sc->sc_state->n_states == 0) { + rv = ENODEV; + goto fail; + } + + sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1; + sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP); + + if (sc->sc_freqs == NULL) { + rv = ENOMEM; + goto fail; + } + + for (i = 0; i < sc->sc_state->n_states; i++) { + + /* Skip duplicated matches. */ + if (sc->sc_state->state_table[i].freq == + sc->sc_state->state_table[i - 1].freq) + continue; + + DPRINTF(("%s: cstate->state_table.freq=%d\n", + __func__, sc->sc_state->state_table[i].freq)); + DPRINTF(("%s: fid=%d vid=%d\n", __func__, + sc->sc_state->state_table[i].fid, + sc->sc_state->state_table[i].vid)); + + len += snprintf(tmp, sizeof(tmp), "%d%s", + sc->sc_state->state_table[i].freq, + i < sc->sc_state->n_states - 1 ? " " : ""); + + DPRINTF(("%s: tmp=%s\n", __func__, tmp)); + + (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len); + } + + sc->sc_tech = ((sc->sc_flags & PN7_FLAG_DESKTOP_VRM) != 0) ? + "Cool`n'Quiet K7" : "Powernow! K7"; + + return 0; + +fail: + kmem_free(sc->sc_state, sizeof(*sc->sc_state)); + sc->sc_state = NULL; + + return rv; +} + +static int +powernow_k7_states(device_t self, unsigned int fid, unsigned int vid) +{ + struct powernow_softc *sc = device_private(self); + struct powernow_cpu_state *cstate; + struct powernow_psb_s *psb; + struct powernow_pst_s *pst; + uint32_t cpusig, maxpst; + bool cpusig_ok; + uint8_t *p; + + cstate = sc->sc_state; + cpusig = sc->sc_ci->ci_signature; + + /* + * Look in the 0xe0000 - 0x100000 physical address + * range for the pst tables; 16 byte blocks + */ + for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START); + p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); + p+= BIOS_STEP) { + + if (memcmp(p, "AMDK7PNOW!", 10) == 0) { + + psb = (struct powernow_psb_s *)p; + + if (psb->version != PN7_PSB_VERSION) + return 0; + + cstate->sgtc = psb->ttime * cstate->fsb; + + if (cstate->sgtc < 100 * cstate->fsb) + cstate->sgtc = 100 * cstate->fsb; + + if (psb->flags & 1) + sc->sc_flags |= PN7_FLAG_DESKTOP_VRM; + + p += sizeof(struct powernow_psb_s); + + for (maxpst = 0; maxpst < psb->n_pst; maxpst++) { + pst = (struct powernow_pst_s*) p; + + /* + * Some models do not have same CPUID + * signature in the PST table. Accept to + * report frequencies via a quirk table + * and fid/vid match. + */ + DPRINTF(("%s: cpusig=0x%x pst->sig:0x%x\n", + __func__, cpusig, pst->signature)); + + cpusig_ok = powernow_k7_check(cpusig, + pst->signature); + + if ((cpusig_ok != false && + (fid == pst->fid && vid == pst->vid))) { + DPRINTF(("%s: pst->signature ok\n", + __func__)); + if (abs(cstate->fsb - pst->pll) > 5) + continue; + cstate->n_states = pst->n_states; + return (powernow_k7_decode_pst(sc, + p + sizeof(struct powernow_pst_s), + cstate->n_states)); + + } + + p += sizeof(struct powernow_pst_s) + + (2 * pst->n_states); + } + } + } + + return 0; +} + +static int +powernow_k7_setperf(device_t self, unsigned int freq) +{ + struct powernow_softc *sc = device_private(self); + int cvid, cfid, vid = 0, fid = 0; + uint64_t status, ctl; + unsigned int i; + + DPRINTF(("%s: cstate->n_states=%d\n", + __func__, sc->sc_state->n_states)); + + for (i = 0; i < sc->sc_state->n_states; i++) { + + if (sc->sc_state->state_table[i].freq >= freq) { + DPRINTF(("%s: freq=%d\n", __func__, freq)); + fid = sc->sc_state->state_table[i].fid; + vid = sc->sc_state->state_table[i].vid; + DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid)); + break; + } + } + + if (fid == 0 || vid == 0) + return 0; + + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + cfid = PN7_STA_CFID(status); + cvid = PN7_STA_CVID(status); + + /* + * We're already at the requested level. + */ + if (fid == cfid && vid == cvid) + return 0; + + ctl = rdmsr(MSR_AMDK7_FIDVID_CTL) & PN7_CTR_FIDCHRATIO; + + ctl |= PN7_CTR_FID(fid); + ctl |= PN7_CTR_VID(vid); + ctl |= PN7_CTR_SGTC(sc->sc_state->sgtc); + + if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0) + x86_disable_intr(); + + if (powernow_k7_fidtomult[fid] < powernow_k7_fidtomult[cfid]) { + + wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); + + if (vid != cvid) + wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); + + } else { + + wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_VIDC); + + if (fid != cfid) + wrmsr(MSR_AMDK7_FIDVID_CTL, ctl | PN7_CTR_FIDC); + } + + if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0) + x86_enable_intr(); + + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + + cfid = PN7_STA_CFID(status); + cvid = PN7_STA_CVID(status); + + if (cfid == fid || cvid == vid) + freq = sc->sc_state->state_table[i].freq; + + return 0; +} + +static bool +powernow_k7_check(uint32_t real_cpusig, uint32_t pst_cpusig) +{ + int j; + + if (real_cpusig == pst_cpusig) + return true; + + for (j = 0; powernow_k7_quirk[j].rcpusig != 0; j++) { + + if ((real_cpusig == powernow_k7_quirk[j].rcpusig) && + (pst_cpusig == powernow_k7_quirk[j].pcpusig)) + return true; + } + + return false; +} + +static int +powernow_k7_decode_pst(struct powernow_softc *sc, uint8_t *p, int npst) +{ + struct powernow_cpu_state *cstate = sc->sc_state; + struct powernow_state state; + int i, j, n; + + for (n = 0, i = 0; i < npst; ++i) { + + state.fid = *p++; + state.vid = *p++; + state.freq = powernow_k7_fidtomult[state.fid]/10 * cstate->fsb; + + if ((sc->sc_flags & PN7_FLAG_ERRATA_A0) != 0 && + (powernow_k7_fidtomult[state.fid] % 10) == 5) + continue; + + j = n; + + while (j > 0 && cstate->state_table[j - 1].freq > state.freq) { + + (void)memcpy(&cstate->state_table[j], + &cstate->state_table[j - 1], + sizeof(struct powernow_state)); + + --j; + } + + (void)memcpy(&cstate->state_table[j], &state, + sizeof(struct powernow_state)); + + ++n; + } + + /* + * Fix powernow_max_states, if errata_a0 gave + * us less states than expected. + */ + cstate->n_states = n; + + return 1; +} + +#endif /* __i386__ */ + +static int +powernow_k8_init(device_t self) +{ + struct powernow_softc *sc = device_private(self); + uint32_t i, maxfid, maxvid; + uint64_t status; + int len, rv; + char tmp[6]; + + sc->sc_state = kmem_alloc(sizeof(*sc->sc_state), KM_SLEEP); + + if (sc->sc_state == NULL) + return ENOMEM; + + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + maxfid = PN8_STA_MFID(status); + maxvid = PN8_STA_MVID(status); + + if (powernow_k8_states(self, maxfid, maxvid) == 0) { + rv = ENXIO; + goto fail; + } + + if (sc->sc_state->n_states == 0) { + rv = ENODEV; + goto fail; + } + + sc->sc_freqs_len = sc->sc_state->n_states * (sizeof("9999 ") - 1) + 1; + sc->sc_freqs = kmem_zalloc(sc->sc_freqs_len, KM_SLEEP); + + if (sc->sc_freqs == NULL) { + rv = ENOMEM; + goto fail; + } + + for (i = 0; i < sc->sc_state->n_states; i++) { + + DPRINTF(("%s: cstate->state_table.freq=%d\n", + __func__, sc->sc_state->state_table[i].freq)); + + DPRINTF(("%s: fid=%d vid=%d\n", __func__, + sc->sc_state->state_table[i].fid, + sc->sc_state->state_table[i].vid)); + + len += snprintf(tmp, sizeof(tmp), "%d%s", + sc->sc_state->state_table[i].freq, + i < sc->sc_state->n_states - 1 ? " " : ""); + + DPRINTF(("%s: tmp=%s\n", __func__, tmp)); + + (void)strlcat(sc->sc_freqs, tmp, sc->sc_freqs_len); + } + + /* + * If start FID is different to max FID, then it is a mobile + * processor. If not, it is a low powered desktop processor. + */ + sc->sc_tech = (PN8_STA_SFID(status) != PN8_STA_MFID(status)) ? + "PowerNow!" : "Cool`n'Quiet"; + + return 0; + +fail: + kmem_free(sc->sc_state, sizeof(*sc->sc_state)); + sc->sc_state = NULL; + + return rv; +} + +static int +powernow_k8_states(device_t self, unsigned int fid, unsigned int vid) +{ + struct powernow_softc *sc = device_private(self); + struct powernow_cpu_state *cstate; + struct powernow_psb_s *psb; + struct powernow_pst_s *pst; + uint32_t cpusig; + uint8_t *p; + int i; + + cstate = sc->sc_state; + cpusig = sc->sc_ci->ci_signature; + + DPRINTF(("%s: before the for loop\n", __func__)); + + for (p = (uint8_t *)ISA_HOLE_VADDR(BIOS_START); + p < (uint8_t *)ISA_HOLE_VADDR(BIOS_START + BIOS_LEN); p += 16) { + + if (memcmp(p, "AMDK7PNOW!", 10) != 0) + continue; + + DPRINTF(("%s: inside the for loop\n", __func__)); + + psb = (struct powernow_psb_s *)p; + + if (psb->version != 0x14) { + DPRINTF(("%s: psb->version != 0x14\n", __func__)); + return 0; + } + + cstate->vst = psb->ttime; + cstate->rvo = PN8_PSB_TO_RVO(psb->reserved); + cstate->irt = PN8_PSB_TO_IRT(psb->reserved); + cstate->mvs = PN8_PSB_TO_MVS(psb->reserved); + cstate->low = PN8_PSB_TO_BATT(psb->reserved); + + p += sizeof(struct powernow_psb_s); + + for(i = 0; i < psb->n_pst; ++i) { + + pst = (struct powernow_pst_s *)p; + + cstate->pll = pst->pll; + cstate->n_states = pst->n_states; + + if (cpusig == pst->signature && + pst->fid == fid && pst->vid == vid) { + + DPRINTF(("%s: cpusig = sig\n", __func__)); + + return (powernow_k8_decode_pst(cstate, + p+= sizeof(struct powernow_pst_s))); + } + + p += sizeof(struct powernow_pst_s) + + 2 * cstate->n_states; + } + } + + DPRINTF(("%s: returns 0!\n", __func__)); + + return 0; +} + +static int +powernow_k8_setperf(device_t self, unsigned int freq) +{ + struct powernow_softc *sc = device_private(self); + int cfid, cvid, fid = 0, vid = 0; + uint64_t status; + unsigned int i; + uint32_t val; + int rvo; + + /* + * We dont do a "pending wait" here, given + * that we need to ensure that the change + * pending bit is not stuck. + */ + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + + if (PN8_STA_PENDING(status)) + return 1; + + cfid = PN8_STA_CFID(status); + cvid = PN8_STA_CVID(status); + + DPRINTF(("%s: sc->sc_state->n_states=%d\n", + __func__, sc->sc_state->n_states)); + + for (i = 0; i < sc->sc_state->n_states; i++) { + + if (sc->sc_state->state_table[i].freq >= freq) { + DPRINTF(("%s: freq=%d\n", __func__, freq)); + fid = sc->sc_state->state_table[i].fid; + vid = sc->sc_state->state_table[i].vid; + DPRINTF(("%s: fid=%d vid=%d\n", __func__, fid, vid)); + break; + } + + } + + if (fid == cfid && vid == cvid) + return 0; + + /* + * Phase 1: raise the core voltage to the + * requested VID if frequency is going up. + */ + while (cvid > vid) { + val = cvid - (1 << sc->sc_state->mvs); + status = powernow_k8_fidvid(cfid, (val > 0) ? val : 0, 1ULL); + cvid = PN8_STA_CVID(status); + COUNT_OFF_VST(sc->sc_state->vst); + } + + /* + * Then raise to voltage + RVO (if required). + */ + for (rvo = sc->sc_state->rvo; rvo > 0 && cvid > 0; --rvo) { + /* + * XXX: It is not clear from the specification if we have + * to do that in 0.25 step or in MVS. Therefore do it + * as it is done under Linux. + */ + status = powernow_k8_fidvid(cfid, cvid - 1, 1ULL); + cvid = PN8_STA_CVID(status); + COUNT_OFF_VST(sc->sc_state->vst); + } + + /* + * Phase 2: change to requested core frequency. + */ + if (cfid != fid) { + uint32_t vco_fid, vco_cfid; + + vco_fid = FID_TO_VCO_FID(fid); + vco_cfid = FID_TO_VCO_FID(cfid); + + while (abs(vco_fid - vco_cfid) > 2) { + + if (fid > cfid) { + if (cfid > 6) + val = cfid + 2; + else + val = FID_TO_VCO_FID(cfid) + 2; + } else + val = cfid - 2; + + status = powernow_k8_fidvid(val, cvid, + (uint64_t)sc->sc_state->pll * 1000 / 5); + + cfid = PN8_STA_CFID(status); + COUNT_OFF_IRT(sc->sc_state->irt); + vco_cfid = FID_TO_VCO_FID(cfid); + } + + + status = powernow_k8_fidvid(fid, cvid, + (uint64_t)sc->sc_state->pll * 1000 / 5); + + cfid = PN8_STA_CFID(status); + COUNT_OFF_IRT(sc->sc_state->irt); + } + + /* + * Phase 3: change to requested voltage. + */ + if (cvid != vid) { + status = powernow_k8_fidvid(cfid, vid, 1ULL); + cvid = PN8_STA_CVID(status); + COUNT_OFF_VST(sc->sc_state->vst); + } + + if (cfid == fid || cvid == vid) + freq = sc->sc_state->state_table[i].freq; + + return 0; +} + +static uint64_t +powernow_k8_fidvid(u_int fid, uint64_t vid, uint64_t ctrl) +{ + struct msr_rw_info msr; + uint64_t status, xc; + + msr.msr_read = false; + msr.msr_value = (ctrl << 32) | (1ULL << 16) | (vid << 8) | fid; + msr.msr_type = MSR_AMDK7_FIDVID_CTL; + + xc = xc_broadcast(0, (xcfunc_t)x86_msr_xcall, &msr, NULL); + xc_wait(xc); + + do { + status = rdmsr(MSR_AMDK7_FIDVID_STATUS); + + } while (PN8_STA_PENDING(status)); + + return status; +} + +/* + * Given a set of pair of FID/VID, and a number of performance + * states, compute a state table via an insertion sort. + */ +static int +powernow_k8_decode_pst(struct powernow_cpu_state *cstate, uint8_t *p) +{ + struct powernow_state state; + int i, j, n; + + for (n = 0, i = 0; i < cstate->n_states; i++) { + + state.fid = *p++; + state.vid = *p++; + + /* + * The minimum supported frequency per the data sheet is + * 800 MHz. The maximum supported frequency is 5000 MHz. + */ + state.freq = 800 + state.fid * 100; + j = n; + + while (j > 0 && cstate->state_table[j - 1].freq > state.freq) { + + (void)memcpy(&cstate->state_table[j], + &cstate->state_table[j - 1], + sizeof(struct powernow_state)); + + --j; + } + + (void)memcpy(&cstate->state_table[j], &state, + sizeof(struct powernow_state)); + + n++; + } + + return 1; +} + +MODULE(MODULE_CLASS_DRIVER, powernow, NULL); + +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +powernow_modcmd(modcmd_t cmd, void *aux) +{ + int error = 0; + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + error = config_init_component(cfdriver_ioconf_powernow, + cfattach_ioconf_powernow, cfdata_ioconf_powernow); +#endif + return error; + case MODULE_CMD_FINI: +#ifdef _MODULE + error = config_fini_component(cfdriver_ioconf_powernow, + cfattach_ioconf_powernow, cfdata_ioconf_powernow); +#endif + return error; + default: + return ENOTTY; + } +}