NetBSD-5.0.2/sys/arch/x86/x86/est.c
/* $NetBSD: est.c,v 1.9 2008/04/28 20:23:40 martin Exp $ */
/*
* Copyright (c) 2003 Michael Eriksson.
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
*/
/*-
* Copyright (c) 2004 The NetBSD Foundation, Inc.
* 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.
*/
/*
* This is a driver for Intel's Enhanced SpeedStep Technology (EST),
* as implemented in Pentium M processors.
*
* Reference documentation:
*
* - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
* System Programming Guide.
* Section 13.14, Enhanced Intel SpeedStep technology.
* Table B-2, MSRs in Pentium M Processors.
* http://www.intel.com/design/pentium4/manuals/253668.htm
*
* - Intel Pentium M Processor Datasheet.
* Table 5, Voltage and Current Specifications.
* http://www.intel.com/design/mobile/datashts/252612.htm
*
* - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
* Table 3-4, 3-5, 3-6, Voltage and Current Specifications.
* http://www.intel.com/design/mobile/datashts/302189.htm
*
* - Linux cpufreq patches, speedstep-centrino.c.
* Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
* http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
*
* ACPI objects: _PCT is MSR location, _PSS is freq/voltage, _PPC is caps.
*/
/* #define EST_DEBUG */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: est.c,v 1.9 2008/04/28 20:23:40 martin Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/once.h>
#include <x86/cpuvar.h>
#include <x86/cputypes.h>
#include <x86/cpu_msr.h>
#include <machine/cpu.h>
#include <machine/specialreg.h>
#include "opt_est.h"
#ifdef EST_FREQ_USERWRITE
#define EST_TARGET_CTLFLAG (CTLFLAG_READWRITE | CTLFLAG_ANYWRITE)
#else
#define EST_TARGET_CTLFLAG CTLFLAG_READWRITE
#endif
/* Convert MHz and mV into IDs for passing to the MSR. */
#define ID16(MHz, mV, bus_clk) \
((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
/* Possible bus speeds (multiplied by 100 for rounding) */
enum { BUS100 = 10000, BUS133 = 13333, BUS166 = 16666, BUS200 = 20000 };
/* Ultra Low Voltage Intel Pentium M processor 900 MHz */
static const uint16_t pm130_900_ulv[] = {
ID16( 900, 1004, BUS100),
ID16( 800, 988, BUS100),
ID16( 600, 844, BUS100),
};
/* Ultra Low Voltage Intel Pentium M processor 1.00 GHz */
static const uint16_t pm130_1000_ulv[] = {
ID16(1000, 1004, BUS100),
ID16( 900, 988, BUS100),
ID16( 800, 972, BUS100),
ID16( 600, 844, BUS100),
};
/* Ultra Low Voltage Intel Pentium M processor 1.10 GHz */
static const uint16_t pm130_1100_ulv[] = {
ID16(1100, 1004, BUS100),
ID16(1000, 988, BUS100),
ID16( 900, 972, BUS100),
ID16( 800, 956, BUS100),
ID16( 600, 844, BUS100),
};
/* Low Voltage Intel Pentium M processor 1.10 GHz */
static const uint16_t pm130_1100_lv[] = {
ID16(1100, 1180, BUS100),
ID16(1000, 1164, BUS100),
ID16( 900, 1100, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 956, BUS100),
};
/* Low Voltage Intel Pentium M processor 1.20 GHz */
static const uint16_t pm130_1200_lv[] = {
ID16(1200, 1180, BUS100),
ID16(1100, 1164, BUS100),
ID16(1000, 1100, BUS100),
ID16( 900, 1020, BUS100),
ID16( 800, 1004, BUS100),
ID16( 600, 956, BUS100),
};
/* Low Voltage Intel Pentium M processor 1.30 GHz */
static const uint16_t pm130_1300_lv[] = {
ID16(1300, 1180, BUS100),
ID16(1200, 1164, BUS100),
ID16(1100, 1100, BUS100),
ID16(1000, 1020, BUS100),
ID16( 900, 1004, BUS100),
ID16( 800, 988, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 1.30 GHz */
static const uint16_t pm130_1300[] = {
ID16(1300, 1388, BUS100),
ID16(1200, 1356, BUS100),
ID16(1000, 1292, BUS100),
ID16( 800, 1260, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 1.40 GHz */
static const uint16_t pm130_1400[] = {
ID16(1400, 1484, BUS100),
ID16(1200, 1436, BUS100),
ID16(1000, 1308, BUS100),
ID16( 800, 1180, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 1.50 GHz */
static const uint16_t pm130_1500[] = {
ID16(1500, 1484, BUS100),
ID16(1400, 1452, BUS100),
ID16(1200, 1356, BUS100),
ID16(1000, 1228, BUS100),
ID16( 800, 1116, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 1.60 GHz */
static const uint16_t pm130_1600[] = {
ID16(1600, 1484, BUS100),
ID16(1400, 1420, BUS100),
ID16(1200, 1276, BUS100),
ID16(1000, 1164, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 1.70 GHz */
static const uint16_t pm130_1700[] = {
ID16(1700, 1484, BUS100),
ID16(1400, 1308, BUS100),
ID16(1200, 1228, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1004, BUS100),
ID16( 600, 956, BUS100),
};
/* Intel Pentium M processor 723 1.0 GHz */
static const uint16_t pm90_n723[] = {
ID16(1000, 940, BUS100),
ID16( 900, 908, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #G */
static const uint16_t pm90_n733g[] = {
ID16(1100, 956, BUS100),
ID16(1000, 940, BUS100),
ID16( 900, 908, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #H */
static const uint16_t pm90_n733h[] = {
ID16(1100, 940, BUS100),
ID16(1000, 924, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 876, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #I */
static const uint16_t pm90_n733i[] = {
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #J */
static const uint16_t pm90_n733j[] = {
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #K */
static const uint16_t pm90_n733k[] = {
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 733 1.1 GHz, VID #L */
static const uint16_t pm90_n733l[] = {
ID16(1100, 876, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #G */
static const uint16_t pm90_n753g[] = {
ID16(1200, 956, BUS100),
ID16(1100, 940, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 892, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #H */
static const uint16_t pm90_n753h[] = {
ID16(1200, 940, BUS100),
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #I */
static const uint16_t pm90_n753i[] = {
ID16(1200, 924, BUS100),
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #J */
static const uint16_t pm90_n753j[] = {
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #K */
static const uint16_t pm90_n753k[] = {
ID16(1200, 892, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 753 1.2 GHz, VID #L */
static const uint16_t pm90_n753l[] = {
ID16(1200, 876, BUS100),
ID16(1100, 876, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #G */
static const uint16_t pm90_n773g[] = {
ID16(1300, 956, BUS100),
ID16(1200, 940, BUS100),
ID16(1100, 924, BUS100),
ID16(1000, 908, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #H */
static const uint16_t pm90_n773h[] = {
ID16(1300, 940, BUS100),
ID16(1200, 924, BUS100),
ID16(1100, 908, BUS100),
ID16(1000, 892, BUS100),
ID16( 900, 876, BUS100),
ID16( 800, 860, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #I */
static const uint16_t pm90_n773i[] = {
ID16(1300, 924, BUS100),
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #J */
static const uint16_t pm90_n773j[] = {
ID16(1300, 908, BUS100),
ID16(1200, 908, BUS100),
ID16(1100, 892, BUS100),
ID16(1000, 876, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #K */
static const uint16_t pm90_n773k[] = {
ID16(1300, 892, BUS100),
ID16(1200, 892, BUS100),
ID16(1100, 876, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 773 1.3 GHz, VID #L */
static const uint16_t pm90_n773l[] = {
ID16(1300, 876, BUS100),
ID16(1200, 876, BUS100),
ID16(1100, 860, BUS100),
ID16(1000, 860, BUS100),
ID16( 900, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 812, BUS100),
};
/* Intel Pentium M processor 738 1.4 GHz */
static const uint16_t pm90_n738[] = {
ID16(1400, 1116, BUS100),
ID16(1300, 1116, BUS100),
ID16(1200, 1100, BUS100),
ID16(1100, 1068, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 758 1.5 GHz */
static const uint16_t pm90_n758[] = {
ID16(1500, 1116, BUS100),
ID16(1400, 1116, BUS100),
ID16(1300, 1100, BUS100),
ID16(1200, 1084, BUS100),
ID16(1100, 1068, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 778 1.6 GHz */
static const uint16_t pm90_n778[] = {
ID16(1600, 1116, BUS100),
ID16(1500, 1116, BUS100),
ID16(1400, 1100, BUS100),
ID16(1300, 1184, BUS100),
ID16(1200, 1068, BUS100),
ID16(1100, 1052, BUS100),
ID16(1000, 1052, BUS100),
ID16( 900, 1036, BUS100),
ID16( 800, 1020, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 710 1.4 GHz, 533 MHz FSB */
static const uint16_t pm90_n710[] = {
ID16(1400, 1340, BUS133),
ID16(1200, 1228, BUS133),
ID16(1000, 1148, BUS133),
ID16( 800, 1068, BUS133),
ID16( 600, 998, BUS133),
};
/* Intel Pentium M processor 715 1.5 GHz, VID #A */
static const uint16_t pm90_n715a[] = {
ID16(1500, 1340, BUS100),
ID16(1200, 1228, BUS100),
ID16(1000, 1148, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 715 1.5 GHz, VID #B */
static const uint16_t pm90_n715b[] = {
ID16(1500, 1324, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1148, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 715 1.5 GHz, VID #C */
static const uint16_t pm90_n715c[] = {
ID16(1500, 1308, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 715 1.5 GHz, VID #D */
static const uint16_t pm90_n715d[] = {
ID16(1500, 1276, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 725 1.6 GHz, VID #A */
static const uint16_t pm90_n725a[] = {
ID16(1600, 1340, BUS100),
ID16(1400, 1276, BUS100),
ID16(1200, 1212, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 725 1.6 GHz, VID #B */
static const uint16_t pm90_n725b[] = {
ID16(1600, 1324, BUS100),
ID16(1400, 1260, BUS100),
ID16(1200, 1196, BUS100),
ID16(1000, 1132, BUS100),
ID16( 800, 1068, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 725 1.6 GHz, VID #C */
static const uint16_t pm90_n725c[] = {
ID16(1600, 1308, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 725 1.6 GHz, VID #D */
static const uint16_t pm90_n725d[] = {
ID16(1600, 1276, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 730 1.6 GHz, 533 MHz FSB */
static const uint16_t pm90_n730[] = {
ID16(1600, 1308, BUS133),
ID16(1333, 1260, BUS133),
ID16(1200, 1212, BUS133),
ID16(1067, 1180, BUS133),
ID16( 800, 988, BUS133),
};
/* Intel Pentium M processor 735 1.7 GHz, VID #A */
static const uint16_t pm90_n735a[] = {
ID16(1700, 1340, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 735 1.7 GHz, VID #B */
static const uint16_t pm90_n735b[] = {
ID16(1700, 1324, BUS100),
ID16(1400, 1244, BUS100),
ID16(1200, 1180, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 735 1.7 GHz, VID #C */
static const uint16_t pm90_n735c[] = {
ID16(1700, 1308, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 735 1.7 GHz, VID #D */
static const uint16_t pm90_n735d[] = {
ID16(1700, 1276, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 740 1.73 GHz, 533 MHz FSB */
static const uint16_t pm90_n740[] = {
ID16(1733, 1356, BUS133),
ID16(1333, 1212, BUS133),
ID16(1067, 1100, BUS133),
ID16( 800, 988, BUS133),
};
/* Intel Pentium M processor 745 1.8 GHz, VID #A */
static const uint16_t pm90_n745a[] = {
ID16(1800, 1340, BUS100),
ID16(1600, 1292, BUS100),
ID16(1400, 1228, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 745 1.8 GHz, VID #B */
static const uint16_t pm90_n745b[] = {
ID16(1800, 1324, BUS100),
ID16(1600, 1276, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1164, BUS100),
ID16(1000, 1116, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 745 1.8 GHz, VID #C */
static const uint16_t pm90_n745c[] = {
ID16(1800, 1308, BUS100),
ID16(1600, 1260, BUS100),
ID16(1400, 1212, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 745 1.8 GHz, VID #D */
static const uint16_t pm90_n745d[] = {
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 750 1.86 GHz, 533 MHz FSB */
/* values extracted from \_PR\NPSS (via _PSS) SDST ACPI table */
static const uint16_t pm90_n750[] = {
ID16(1867, 1308, BUS133),
ID16(1600, 1228, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
/* Intel Pentium M processor 755 2.0 GHz, VID #A */
static const uint16_t pm90_n755a[] = {
ID16(2000, 1340, BUS100),
ID16(1800, 1292, BUS100),
ID16(1600, 1244, BUS100),
ID16(1400, 1196, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 755 2.0 GHz, VID #B */
static const uint16_t pm90_n755b[] = {
ID16(2000, 1324, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 755 2.0 GHz, VID #C */
static const uint16_t pm90_n755c[] = {
ID16(2000, 1308, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 755 2.0 GHz, VID #D */
static const uint16_t pm90_n755d[] = {
ID16(2000, 1276, BUS100),
ID16(1800, 1244, BUS100),
ID16(1600, 1196, BUS100),
ID16(1400, 1164, BUS100),
ID16(1200, 1116, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 760 2.0 GHz, 533 MHz FSB */
static const uint16_t pm90_n760[] = {
ID16(2000, 1356, BUS133),
ID16(1600, 1244, BUS133),
ID16(1333, 1164, BUS133),
ID16(1067, 1084, BUS133),
ID16( 800, 988, BUS133),
};
/* Intel Pentium M processor 765 2.1 GHz, VID #A */
static const uint16_t pm90_n765a[] = {
ID16(2100, 1340, BUS100),
ID16(1800, 1276, BUS100),
ID16(1600, 1228, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 765 2.1 GHz, VID #B */
static const uint16_t pm90_n765b[] = {
ID16(2100, 1324, BUS100),
ID16(1800, 1260, BUS100),
ID16(1600, 1212, BUS100),
ID16(1400, 1180, BUS100),
ID16(1200, 1132, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 765 2.1 GHz, VID #C */
static const uint16_t pm90_n765c[] = {
ID16(2100, 1308, BUS100),
ID16(1800, 1244, BUS100),
ID16(1600, 1212, BUS100),
ID16(1400, 1164, BUS100),
ID16(1200, 1116, BUS100),
ID16(1000, 1084, BUS100),
ID16( 800, 1036, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 765 2.1 GHz, VID #E */
static const uint16_t pm90_n765e[] = {
ID16(2100, 1356, BUS100),
ID16(1800, 1292, BUS100),
ID16(1600, 1244, BUS100),
ID16(1400, 1196, BUS100),
ID16(1200, 1148, BUS100),
ID16(1000, 1100, BUS100),
ID16( 800, 1052, BUS100),
ID16( 600, 988, BUS100),
};
/* Intel Pentium M processor 770 2.13 GHz */
static const uint16_t pm90_n770[] = {
ID16(2133, 1356, BUS133),
ID16(1867, 1292, BUS133),
ID16(1600, 1212, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
/* Intel Pentium M processor 780 2.26 GHz */
static const uint16_t pm90_n780[] = {
ID16(2267, 1388, BUS133),
ID16(1867, 1292, BUS133),
ID16(1600, 1212, BUS133),
ID16(1333, 1148, BUS133),
ID16(1067, 1068, BUS133),
ID16( 800, 988, BUS133),
};
/*
* VIA C7-M 500 MHz FSB, 400 MHz FSB, and ULV variants.
* Data from the "VIA C7-M Processor BIOS Writer's Guide (v2.17)" datasheet.
*/
/* 1.00GHz Centaur C7-M ULV */
static const uint16_t C7M_770_ULV[] = {
ID16(1000, 844, BUS100),
ID16( 800, 796, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
/* 1.00GHz Centaur C7-M ULV */
static const uint16_t C7M_779_ULV[] = {
ID16(1000, 796, BUS100),
ID16( 800, 796, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
/* 1.20GHz Centaur C7-M ULV */
static const uint16_t C7M_772_ULV[] = {
ID16(1200, 844, BUS100),
ID16(1000, 844, BUS100),
ID16( 800, 828, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
/* 1.50GHz Centaur C7-M ULV */
static const uint16_t C7M_775_ULV[] = {
ID16(1500, 956, BUS100),
ID16(1400, 940, BUS100),
ID16(1000, 860, BUS100),
ID16( 800, 828, BUS100),
ID16( 600, 796, BUS100),
ID16( 400, 796, BUS100),
};
/* 1.20GHz Centaur C7-M 400 MHz FSB */
static const uint16_t C7M_771[] = {
ID16(1200, 860, BUS100),
ID16(1000, 860, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
/* 1.50GHz Centaur C7-M 400 MHz FSB */
static const uint16_t C7M_754[] = {
ID16(1500, 1004, BUS100),
ID16(1400, 988, BUS100),
ID16(1000, 940, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
/* 1.60GHz Centaur C7-M 400 MHz FSB */
static const uint16_t C7M_764[] = {
ID16(1600, 1084, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
/* 1.80GHz Centaur C7-M 400 MHz FSB */
static const uint16_t C7M_784[] = {
ID16(1800, 1148, BUS100),
ID16(1600, 1100, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
/* 2.00GHz Centaur C7-M 400 MHz FSB */
static const uint16_t C7M_794[] = {
ID16(2000, 1148, BUS100),
ID16(1800, 1132, BUS100),
ID16(1600, 1100, BUS100),
ID16(1400, 1052, BUS100),
ID16(1000, 1004, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
/* 1.60GHz Centaur C7-M 533 MHz FSB */
static const uint16_t C7M_765[] = {
ID16(1600, 1084, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
/* 2.00GHz Centaur C7-M 533 MHz FSB */
static const uint16_t C7M_785[] = {
ID16(1867, 1148, BUS133),
ID16(1600, 1100, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
/* 2.00GHz Centaur C7-M 533 MHz FSB */
static const uint16_t C7M_795[] = {
ID16(2000, 1148, BUS133),
ID16(1867, 1132, BUS133),
ID16(1600, 1100, BUS133),
ID16(1467, 1052, BUS133),
ID16(1200, 1004, BUS133),
ID16( 800, 844, BUS133),
ID16( 667, 844, BUS133),
ID16( 533, 844, BUS133),
};
/* 1.00GHz VIA Eden 90nm 'Esther' */
static const uint16_t eden90_1000[] = {
ID16(1000, 844, BUS100),
ID16( 800, 844, BUS100),
ID16( 600, 844, BUS100),
ID16( 400, 844, BUS100),
};
struct fqlist {
int vendor;
unsigned bus_clk;
unsigned n;
const uint16_t *table;
};
#define ENTRY(ven, bus_clk, tab) \
{ CPUVENDOR_##ven, bus_clk == BUS133 ? 1 : 0, __arraycount(tab), tab }
#define BUS_CLK(fqp) ((fqp)->bus_clk ? BUS133 : BUS100)
static const struct fqlist est_cpus[] = {
ENTRY(INTEL, BUS100, pm130_900_ulv),
ENTRY(INTEL, BUS100, pm130_1000_ulv),
ENTRY(INTEL, BUS100, pm130_1100_ulv),
ENTRY(INTEL, BUS100, pm130_1100_lv),
ENTRY(INTEL, BUS100, pm130_1200_lv),
ENTRY(INTEL, BUS100, pm130_1300_lv),
ENTRY(INTEL, BUS100, pm130_1300),
ENTRY(INTEL, BUS100, pm130_1400),
ENTRY(INTEL, BUS100, pm130_1500),
ENTRY(INTEL, BUS100, pm130_1600),
ENTRY(INTEL, BUS100, pm130_1700),
ENTRY(INTEL, BUS100, pm90_n723),
ENTRY(INTEL, BUS100, pm90_n733g),
ENTRY(INTEL, BUS100, pm90_n733h),
ENTRY(INTEL, BUS100, pm90_n733i),
ENTRY(INTEL, BUS100, pm90_n733j),
ENTRY(INTEL, BUS100, pm90_n733k),
ENTRY(INTEL, BUS100, pm90_n733l),
ENTRY(INTEL, BUS100, pm90_n753g),
ENTRY(INTEL, BUS100, pm90_n753h),
ENTRY(INTEL, BUS100, pm90_n753i),
ENTRY(INTEL, BUS100, pm90_n753j),
ENTRY(INTEL, BUS100, pm90_n753k),
ENTRY(INTEL, BUS100, pm90_n753l),
ENTRY(INTEL, BUS100, pm90_n773g),
ENTRY(INTEL, BUS100, pm90_n773h),
ENTRY(INTEL, BUS100, pm90_n773i),
ENTRY(INTEL, BUS100, pm90_n773j),
ENTRY(INTEL, BUS100, pm90_n773k),
ENTRY(INTEL, BUS100, pm90_n773l),
ENTRY(INTEL, BUS100, pm90_n738),
ENTRY(INTEL, BUS100, pm90_n758),
ENTRY(INTEL, BUS100, pm90_n778),
ENTRY(INTEL, BUS133, pm90_n710),
ENTRY(INTEL, BUS100, pm90_n715a),
ENTRY(INTEL, BUS100, pm90_n715b),
ENTRY(INTEL, BUS100, pm90_n715c),
ENTRY(INTEL, BUS100, pm90_n715d),
ENTRY(INTEL, BUS100, pm90_n725a),
ENTRY(INTEL, BUS100, pm90_n725b),
ENTRY(INTEL, BUS100, pm90_n725c),
ENTRY(INTEL, BUS100, pm90_n725d),
ENTRY(INTEL, BUS133, pm90_n730),
ENTRY(INTEL, BUS100, pm90_n735a),
ENTRY(INTEL, BUS100, pm90_n735b),
ENTRY(INTEL, BUS100, pm90_n735c),
ENTRY(INTEL, BUS100, pm90_n735d),
ENTRY(INTEL, BUS133, pm90_n740),
ENTRY(INTEL, BUS100, pm90_n745a),
ENTRY(INTEL, BUS100, pm90_n745b),
ENTRY(INTEL, BUS100, pm90_n745c),
ENTRY(INTEL, BUS100, pm90_n745d),
ENTRY(INTEL, BUS133, pm90_n750),
ENTRY(INTEL, BUS100, pm90_n755a),
ENTRY(INTEL, BUS100, pm90_n755b),
ENTRY(INTEL, BUS100, pm90_n755c),
ENTRY(INTEL, BUS100, pm90_n755d),
ENTRY(INTEL, BUS133, pm90_n760),
ENTRY(INTEL, BUS100, pm90_n765a),
ENTRY(INTEL, BUS100, pm90_n765b),
ENTRY(INTEL, BUS100, pm90_n765c),
ENTRY(INTEL, BUS100, pm90_n765e),
ENTRY(INTEL, BUS133, pm90_n770),
ENTRY(INTEL, BUS133, pm90_n780),
ENTRY(IDT, BUS100, C7M_770_ULV),
ENTRY(IDT, BUS100, C7M_779_ULV),
ENTRY(IDT, BUS100, C7M_772_ULV),
ENTRY(IDT, BUS100, C7M_771),
ENTRY(IDT, BUS100, C7M_775_ULV),
ENTRY(IDT, BUS100, C7M_754),
ENTRY(IDT, BUS100, C7M_764),
ENTRY(IDT, BUS133, C7M_765),
ENTRY(IDT, BUS100, C7M_784),
ENTRY(IDT, BUS133, C7M_785),
ENTRY(IDT, BUS100, C7M_794),
ENTRY(IDT, BUS133, C7M_795),
ENTRY(IDT, BUS100, eden90_1000)
};
#define MSR2FREQINC(msr) (((int) (msr) >> 8) & 0xff)
#define MSR2VOLTINC(msr) ((int) (msr) & 0xff)
#define MSR2MHZ(msr, bus) ((MSR2FREQINC((msr)) * (bus) + 50) / 100)
#define MSR2MV(msr) (MSR2VOLTINC(msr) * 16 + 700)
static const struct fqlist *est_fqlist; /* not NULL if functional */
static uint16_t *fake_table; /* guessed est_cpu table */
static struct fqlist fake_fqlist;
static int est_node_target, est_node_current;
static const char est_desc[] = "Enhanced SpeedStep";
static int lvendor, bus_clock;
static int est_sysctl_helper(SYSCTLFN_PROTO);
static int est_init_once(void);
static void est_init_main(int);
static int
est_sysctl_helper(SYSCTLFN_ARGS)
{
struct msr_cpu_broadcast mcb;
struct sysctlnode node;
int fq, oldfq, error;
if (est_fqlist == NULL)
return EOPNOTSUPP;
node = *rnode;
node.sysctl_data = &fq;
oldfq = 0;
if (rnode->sysctl_num == est_node_target)
fq = oldfq = MSR2MHZ(rdmsr(MSR_PERF_CTL), bus_clock);
else if (rnode->sysctl_num == est_node_current)
fq = MSR2MHZ(rdmsr(MSR_PERF_STATUS), bus_clock);
else
return EOPNOTSUPP;
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return error;
/* support writing to ...frequency.target */
if (rnode->sysctl_num == est_node_target && fq != oldfq) {
int i;
for (i = est_fqlist->n - 1; i > 0; i--)
if (MSR2MHZ(est_fqlist->table[i], bus_clock) >= fq)
break;
fq = MSR2MHZ(est_fqlist->table[i], bus_clock);
mcb.msr_read = true;
mcb.msr_type = MSR_PERF_CTL;
mcb.msr_mask = 0xffffULL;
mcb.msr_value = est_fqlist->table[i];
msr_cpu_broadcast(&mcb);
}
return 0;
}
static int
est_init_once(void)
{
est_init_main(lvendor);
return 0;
}
void
est_init(int vendor)
{
int error;
static ONCE_DECL(est_initialized);
lvendor = vendor;
error = RUN_ONCE(&est_initialized, est_init_once);
if (__predict_false(error != 0))
return;
}
static void
est_init_main(int vendor)
{
#ifdef __i386__
const struct fqlist *fql;
#endif
const struct sysctlnode *node, *estnode, *freqnode;
uint64_t msr;
uint16_t cur, idhi, idlo;
uint8_t crhi, crlo, crcur;
int i, mv, rc;
size_t len, freq_len;
char *freq_names;
const char *cpuname;
cpuname = device_xname(curcpu()->ci_dev);
if (CPUID2FAMILY(curcpu()->ci_signature) == 15)
bus_clock = p4_get_bus_clock(curcpu());
else if (CPUID2FAMILY(curcpu()->ci_signature) == 6) {
if (vendor == CPUVENDOR_IDT)
bus_clock = via_get_bus_clock(curcpu());
else
bus_clock = p3_get_bus_clock(curcpu());
}
if (bus_clock == 0) {
aprint_debug("%s: unknown system bus clock\n", __func__);
return;
}
msr = rdmsr(MSR_PERF_STATUS);
idhi = (msr >> 32) & 0xffff;
idlo = (msr >> 48) & 0xffff;
cur = msr & 0xffff;
crhi = (idhi >> 8) & 0xff;
crlo = (idlo >> 8) & 0xff;
crcur = (cur >> 8) & 0xff;
#ifdef __i386__
if (idhi == 0 || idlo == 0 || cur == 0 ||
((cur >> 8) & 0xff) < ((idlo >> 8) & 0xff) ||
((cur >> 8) & 0xff) > ((idhi >> 8) & 0xff)) {
aprint_debug("%s: strange msr value 0x%016llx\n", __func__, msr);
return;
}
#endif
#ifdef __amd64__
if (crlo == 0 || crhi == crlo) {
aprint_debug("%s: crlo == 0 || crhi == crlo\n", __func__);
return;
}
if (crhi == 0 || crcur == 0 || crlo > crhi ||
crcur < crlo || crcur > crhi) {
/*
* Do complain about other weirdness, because we first want to
* know about it, before we decide what to do with it
*/
aprint_debug("%s: strange msr value 0x%" PRIu64 "\n",
__func__, msr);
return;
}
#endif
msr = rdmsr(MSR_PERF_STATUS);
mv = MSR2MV(msr);
#ifdef __i386__
/*
* Find an entry which matches (vendor, bus_clock, idhi, idlo)
*/
est_fqlist = NULL;
for (i = 0; i < __arraycount(est_cpus); i++) {
fql = &est_cpus[i];
if (vendor == fql->vendor && bus_clock == BUS_CLK(fql) &&
idhi == fql->table[0] && idlo == fql->table[fql->n - 1]) {
est_fqlist = fql;
break;
}
}
#endif
if (est_fqlist == NULL) {
int j, tablesize, freq, volt;
int minfreq, minvolt, maxfreq, maxvolt, freqinc, voltinc;
/*
* Some CPUs report the same frequency in idhi and idlo,
* so do not run est on them.
*/
if (idhi == idlo) {
aprint_debug("%s: idhi == idlo\n", __func__);
return;
}
#ifdef EST_DEBUG
printf("%s: bus_clock = %d\n", __func__, bus_clock);
printf("%s: idlo = 0x%x\n", __func__, idlo);
printf("%s: lo %4d mV, %4d MHz\n", __func__,
MSR2MV(idlo), MSR2MHZ(idlo, bus_clock));
printf("%s: raw %4d , %4d \n", __func__,
(idlo & 0xff), ((idlo >> 8) & 0xff));
printf("%s: idhi = 0x%x\n", __func__, idhi);
printf("%s: hi %4d mV, %4d MHz\n", __func__,
MSR2MV(idhi), MSR2MHZ(idhi, bus_clock));
printf("%s: raw %4d , %4d \n", __func__,
(idhi & 0xff), ((idhi >> 8) & 0xff));
printf("%s: cur = 0x%x\n", __func__, cur);
#endif
/*
* Generate a fake table with the power states we know,
* interpolating the voltages and frequencies between the
* high and low values. The (milli)voltages are always
* rounded up when computing the table.
*/
minfreq = MSR2FREQINC(idlo);
maxfreq = MSR2FREQINC(idhi);
minvolt = MSR2VOLTINC(idlo);
maxvolt = MSR2VOLTINC(idhi);
freqinc = maxfreq - minfreq;
voltinc = maxvolt - minvolt;
/* Avoid diving by zero. */
if (freqinc == 0)
return;
if (freqinc < voltinc || voltinc == 0) {
tablesize = maxfreq - minfreq + 1;
if (voltinc != 0)
voltinc = voltinc * 100 / freqinc - 1;
freqinc = 100;
} else {
tablesize = maxvolt - minvolt + 1;
freqinc = freqinc * 100 / voltinc - 1;
voltinc = 100;
}
fake_table = malloc(tablesize * sizeof(uint16_t), M_DEVBUF,
M_WAITOK);
fake_fqlist.n = tablesize;
/* The frequency/voltage table is highest frequency first */
freq = maxfreq * 100;
volt = maxvolt * 100;
for (j = 0; j < tablesize; j++) {
fake_table[j] = (((freq + 99) / 100) << 8) +
(volt + 99) / 100;
#ifdef EST_DEBUG
printf("%s: fake entry %d: %4d mV, %4d MHz "
"MSR*100 mV = %4d freq = %4d\n",
__func__, j, MSR2MV(fake_table[j]),
MSR2MHZ(fake_table[j], bus_clock),
volt, freq);
#endif /* EST_DEBUG */
freq -= freqinc;
volt -= voltinc;
}
fake_fqlist.vendor = vendor;
fake_fqlist.table = fake_table;
est_fqlist = &fake_fqlist;
}
/*
* OK, tell the user the available frequencies.
*/
freq_len = est_fqlist->n * (sizeof("9999 ")-1) + 1;
freq_names = malloc(freq_len, M_SYSCTLDATA, M_WAITOK);
freq_names[0] = '\0';
len = 0;
for (i = 0; i < est_fqlist->n; i++) {
len += snprintf(freq_names + len, freq_len - len, "%d%s",
MSR2MHZ(est_fqlist->table[i], bus_clock),
i < est_fqlist->n - 1 ? " " : "");
}
aprint_normal("%s: %s (%d mV) ", cpuname, est_desc, mv);
aprint_normal("%d MHz\n", MSR2MHZ(msr, bus_clock));
aprint_normal("%s: %s frequencies available (MHz): %s\n",
cpuname, est_desc, freq_names);
/*
* Setup the sysctl sub-tree machdep.est.*
*/
if ((rc = sysctl_createv(NULL, 0, NULL, &node,
CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &node, &estnode,
0, CTLTYPE_NODE, "est", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &estnode, &freqnode,
0, CTLTYPE_NODE, "frequency", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
EST_TARGET_CTLFLAG, CTLTYPE_INT, "target", NULL,
est_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
est_node_target = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_INT, "current", NULL,
est_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
est_node_current = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_STRING, "available", NULL,
NULL, 0, freq_names, freq_len, CTL_CREATE, CTL_EOL)) != 0)
goto err;
return;
err:
free(freq_names, M_SYSCTLDATA);
aprint_error("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
}