Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 16 Feb 2005 17:16:22 -0800
From:      Nate Lawson <nate@root.org>
To:        current@freebsd.org
Cc:        acpi@freebsd.org
Subject:   patch: p4tcc and speedstep cpufreq drivers
Message-ID:  <4213F066.2050708@root.org>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------060506010507060809010505
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Attached is a patch that I'd like to get tested.  After applying it, 
rebuild and load the cpufreq.ko module.  Be sure you do _not_ have 
"options CPU_ENABLE_TCC" in your kernel config or the new driver will 
conflict with the old.

I do not have either hardware so I am not certain the drivers work.  For 
starters, I'd just like to get results from people saying whether or not 
the driver attaches and they show new settings and second, whether it 
does anything useful when changing settings.  Try a command like "dd 
if=/dev/zero bs=1m count=100 | md5" and see how the bytes/sec vary with 
settings.  If it appears to work, please run through the settings being 
sure each one lowers or raises the performance accordingly, and also 
making sure each setting has been used twice.  That way I know if there 
is a problem going from 50% back up to 100% again, for instance.  Also, 
if your system supports both p4tcc and acpi_throttle, I'm interested in 
if these settings are truly independent for all systems.  The way to see 
if they are independent is to compare the performance after setting each 
value and make sure it changes appropriately.  If there are duplicates, 
then I'm wrong.

The patch should either work or crash, but please be careful to shut 
down if your cpu gets too hot, etc.

Thanks,
-- 
Nate

--------------060506010507060809010505
Content-Type: text/plain;
 name="cpufreq_est_p4tcc.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="cpufreq_est_p4tcc.diff"

Index: conf/files.i386
===================================================================
RCS file: /home/ncvs/src/sys/conf/files.i386,v
retrieving revision 1.516
diff -u -r1.516 files.i386
--- conf/files.i386	9 Feb 2005 20:03:39 -0000	1.516
+++ conf/files.i386	16 Feb 2005 16:05:02 -0000
@@ -218,6 +218,8 @@
 i386/bios/smapi_bios.S		optional smapi
 i386/bios/smbios.c		optional smbios
 i386/bios/vpd.c			optional vpd
+i386/cpufreq/est.c		optional cpufreq
+i386/cpufreq/p4tcc.c		optional cpufreq
 #i386/i386/apic_vector.s		optional apic
 i386/i386/atomic.c		standard				\
 	compile-with	"${CC} -c ${CFLAGS} ${DEFINED_PROF:S/^$/-fomit-frame-pointer/} ${.IMPSRC}"
Index: modules/cpufreq/Makefile
===================================================================
RCS file: /home/ncvs/src/sys/modules/cpufreq/Makefile,v
retrieving revision 1.1
diff -u -r1.1 Makefile
--- modules/cpufreq/Makefile	4 Feb 2005 05:49:36 -0000	1.1
+++ modules/cpufreq/Makefile	17 Feb 2005 01:03:30 -0000
@@ -1,10 +1,15 @@
 # $FreeBSD$
 
-.PATH: ${.CURDIR}/../../dev/cpufreq
+.PATH:	${.CURDIR}/../../dev/cpufreq		\
+	${.CURDIR}/../../${MACHINE_ARCH}/cpufreq
 
 KMOD=	cpufreq
 WARNS?=	2
 SRCS=	ichss.c
 SRCS+=	bus_if.h cpufreq_if.h device_if.h pci_if.h
 
+.if ${MACHINE} == "i386"
+SRCS+=	est.c p4tcc.c
+.endif
+
 .include <bsd.kmod.mk>
Index: i386/cpufreq/est.c
===================================================================
RCS file: i386/cpufreq/est.c
diff -N i386/cpufreq/est.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ i386/cpufreq/est.c	17 Feb 2005 00:57:02 -0000
@@ -0,0 +1,779 @@
+/*-
+ * Copyright (c) 2004 Colin Percival
+ * Copyright (c) 2005 Nate Lawson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing 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>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/smp.h>
+#include <sys/systm.h>
+
+#include "cpufreq_if.h"
+#include <machine/md_var.h>
+
+/* Status/control registers (from the IA-32 System Programming Guide). */
+#define MSR_PERF_STATUS		0x198
+#define MSR_PERF_CTL		0x199
+
+/* Register and bit for enabling SpeedStep. */
+#define MSR_MISC_ENABLE		0x1a0
+#define MSR_SS_ENABLE		(1<<16)
+
+/* Frequency and MSR control values. */
+typedef struct {
+	uint16_t	freq;
+	uint16_t	volts;
+	uint16_t	id16;
+} freq_info;
+
+/* Identifying characteristics of a processor and supported frequencies. */
+typedef struct {
+	const char	*vendor;
+	uint32_t	id32;
+	uint32_t	bus_clk;
+	const freq_info	*freqtab;
+} cpu_info;
+
+struct est_softc {
+	device_t dev;
+	const freq_info *freq_list;
+};
+
+/* Convert MHz and mV into IDs for passing to the MSR. */
+#define ID16(MHz, mV, bus_clk)				\
+	(((MHz / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
+#define ID32(MHz_hi, mV_hi, MHz_lo, mV_lo, bus_clk)	\
+	((ID16(MHz_lo, mV_lo, bus_clk) << 16) | (ID16(MHz_hi, mV_hi, bus_clk)))
+
+/* Format for storing IDs in our table. */
+#define FREQ_INFO(MHz, mV, bus_clk)			\
+	{ MHz, mV, ID16(MHz, mV, bus_clk) }
+#define INTEL(tab, zhi, vhi, zlo, vlo, bus_clk)		\
+	{ GenuineIntel, ID32(zhi, vhi, zlo, vlo, bus_clk), bus_clk, tab }
+
+const char GenuineIntel[] = "GenuineIntel";
+
+/* Default bus clock value for Centrino processors. */
+#define INTEL_BUS_CLK		100
+
+/* XXX Update this if new CPUs have more settings. */
+#define EST_MAX_SETTINGS	10
+CTASSERT(EST_MAX_SETTINGS <= MAX_SETTINGS);
+
+/* Estimate in microseconds of latency for performing a transition. */
+#define EST_TRANS_LAT		10
+
+/*
+ * Frequency (MHz) and voltage (mV) settings.  Data from the
+ * Intel Pentium M Processor Datasheet (Order Number 252612), Table 5.
+ *
+ * XXX New Dothan processors have multiple VID# with different
+ * settings for each VID#.  Since we can't uniquely identify this info
+ * without undisclosed methods from Intel, we can't support newer
+ * processors with this table method.  If ACPI Px states are supported,
+ * we can get info from them.
+ */
+const freq_info PM17_130[] = {
+	/* 130nm 1.70GHz Pentium M */
+	FREQ_INFO(1700, 1484, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1004, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM16_130[] = {
+	/* 130nm 1.60GHz Pentium M */
+	FREQ_INFO(1600, 1484, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1420, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1164, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM15_130[] = {
+	/* 130nm 1.50GHz Pentium M */
+	FREQ_INFO(1500, 1484, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1452, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1356, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1228, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM14_130[] = {
+	/* 130nm 1.40GHz Pentium M */
+	FREQ_INFO(1400, 1484, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1436, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1308, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1180, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM13_130[] = {
+	/* 130nm 1.30GHz Pentium M */
+	FREQ_INFO(1300, 1388, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1356, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1292, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1260, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM13_LV_130[] = {
+	/* 130nm 1.30GHz Low Voltage Pentium M */
+	FREQ_INFO(1300, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1100, 1100, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1020, INTEL_BUS_CLK),
+	FREQ_INFO( 900, 1004, INTEL_BUS_CLK),
+	FREQ_INFO( 800,  988, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM12_LV_130[] = {
+	/* 130 nm 1.20GHz Low Voltage Pentium M */
+	FREQ_INFO(1200, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1100, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 900, 1020, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1004, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM11_LV_130[] = {
+	/* 130 nm 1.10GHz Low Voltage Pentium M */
+	FREQ_INFO(1100, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1164, INTEL_BUS_CLK),
+	FREQ_INFO( 900, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1020, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  956, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM11_ULV_130[] = {
+	/* 130 nm 1.10GHz Ultra Low Voltage Pentium M */
+	FREQ_INFO(1100, 1004, INTEL_BUS_CLK),
+	FREQ_INFO(1000,  988, INTEL_BUS_CLK),
+	FREQ_INFO( 900,  972, INTEL_BUS_CLK),
+	FREQ_INFO( 800,  956, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  844, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM10_ULV_130[] = {
+	/* 130 nm 1.00GHz Ultra Low Voltage Pentium M */
+	FREQ_INFO(1000, 1004, INTEL_BUS_CLK),
+	FREQ_INFO( 900,  988, INTEL_BUS_CLK),
+	FREQ_INFO( 800,  972, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  844, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+
+/*
+ * Data from "Intel Pentium M Processor on 90nm Process with
+ * 2-MB L2 Cache Datasheet", Order Number 302189, Table 5.
+ */
+const freq_info PM_765A_90[] = {
+	/* 90 nm 2.10GHz Pentium M, VID #A */
+	FREQ_INFO(2100, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1132, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_765B_90[] = {
+	/* 90 nm 2.10GHz Pentium M, VID #B */
+	FREQ_INFO(2100, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1260, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1132, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_765C_90[] = {
+	/* 90 nm 2.10GHz Pentium M, VID #C */
+	FREQ_INFO(2100, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1116, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_765E_90[] = {
+	/* 90 nm 2.10GHz Pentium M, VID #E */
+	FREQ_INFO(2100, 1356, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1292, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1196, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1148, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_755A_90[] = {
+	/* 90 nm 2.00GHz Pentium M, VID #A */
+	FREQ_INFO(2000, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1292, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1196, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1148, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_755B_90[] = {
+	/* 90 nm 2.00GHz Pentium M, VID #B */
+	FREQ_INFO(2000, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1132, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_755C_90[] = {
+	/* 90 nm 2.00GHz Pentium M, VID #C */
+	FREQ_INFO(2000, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1132, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_755D_90[] = {
+	/* 90 nm 2.00GHz Pentium M, VID #D */
+	FREQ_INFO(2000, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1800, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1196, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1116, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_745A_90[] = {
+	/* 90 nm 1.80GHz Pentium M, VID #A */
+	FREQ_INFO(1800, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1292, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_745B_90[] = {
+	/* 90 nm 1.80GHz Pentium M, VID #B */
+	FREQ_INFO(1800, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_745C_90[] = {
+	/* 90 nm 1.80GHz Pentium M, VID #C */
+	FREQ_INFO(1800, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1260, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1148, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_745D_90[] = {
+	/* 90 nm 1.80GHz Pentium M, VID #D */
+	FREQ_INFO(1800, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1600, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1132, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1084, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_735A_90[] = {
+	/* 90 nm 1.70GHz Pentium M, VID #A */
+	FREQ_INFO(1700, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_735B_90[] = {
+	/* 90 nm 1.70GHz Pentium M, VID #B */
+	FREQ_INFO(1700, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_735C_90[] = {
+	/* 90 nm 1.70GHz Pentium M, VID #C */
+	FREQ_INFO(1700, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_735D_90[] = {
+	/* 90 nm 1.70GHz Pentium M, VID #D */
+	FREQ_INFO(1700, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1148, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1100, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_725A_90[] = {
+	/* 90 nm 1.60GHz Pentium M, VID #A */
+	FREQ_INFO(1600, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1132, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1068, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_725B_90[] = {
+	/* 90 nm 1.60GHz Pentium M, VID #B */
+	FREQ_INFO(1600, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1260, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1196, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1132, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1068, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_725C_90[] = {
+	/* 90 nm 1.60GHz Pentium M, VID #C */
+	FREQ_INFO(1600, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1244, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_725D_90[] = {
+	/* 90 nm 1.60GHz Pentium M, VID #D */
+	FREQ_INFO(1600, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1400, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1164, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_715A_90[] = {
+	/* 90 nm 1.50GHz Pentium M, VID #A */
+	FREQ_INFO(1500, 1340, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1228, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1148, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1068, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_715B_90[] = {
+	/* 90 nm 1.50GHz Pentium M, VID #B */
+	FREQ_INFO(1500, 1324, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1148, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1068, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_715C_90[] = {
+	/* 90 nm 1.50GHz Pentium M, VID #C */
+	FREQ_INFO(1500, 1308, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1212, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1132, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1068, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_715D_90[] = {
+	/* 90 nm 1.50GHz Pentium M, VID #D */
+	FREQ_INFO(1500, 1276, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1180, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1116, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_738_90[] = {
+	/* 90 nm 1.40GHz Low Voltage Pentium M */
+	FREQ_INFO(1400, 1116, INTEL_BUS_CLK),
+	FREQ_INFO(1300, 1116, INTEL_BUS_CLK),
+	FREQ_INFO(1200, 1100, INTEL_BUS_CLK),
+	FREQ_INFO(1100, 1068, INTEL_BUS_CLK),
+	FREQ_INFO(1000, 1052, INTEL_BUS_CLK),
+	FREQ_INFO( 900, 1036, INTEL_BUS_CLK),
+	FREQ_INFO( 800, 1020, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  988, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_733_90[] = {
+	/* 90 nm 1.10GHz Ultra Low Voltage Pentium M */
+	FREQ_INFO(1100,  940, INTEL_BUS_CLK),
+	FREQ_INFO(1000,  924, INTEL_BUS_CLK),
+	FREQ_INFO( 900,  892, INTEL_BUS_CLK),
+	FREQ_INFO( 800,  876, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  812, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+const freq_info PM_723_90[] = {
+	/* 90 nm 1.00GHz Ultra Low Voltage Pentium M */
+	FREQ_INFO(1000,  940, INTEL_BUS_CLK),
+	FREQ_INFO( 900,  908, INTEL_BUS_CLK),
+	FREQ_INFO( 800,  876, INTEL_BUS_CLK),
+	FREQ_INFO( 600,  812, INTEL_BUS_CLK),
+	FREQ_INFO(   0,    0, 1),
+};
+
+const cpu_info ESTprocs[] = {
+	INTEL(PM17_130,		1700, 1484, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM16_130,		1600, 1484, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM15_130,		1500, 1484, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM14_130,		1400, 1484, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM13_130,		1300, 1388, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM13_LV_130,	1300, 1180, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM12_LV_130,	1200, 1180, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM11_LV_130,	1100, 1180, 600, 956, INTEL_BUS_CLK),
+	INTEL(PM11_ULV_130,	1100, 1004, 600, 844, INTEL_BUS_CLK),
+	INTEL(PM10_ULV_130,	1000, 1004, 600, 844, INTEL_BUS_CLK),
+	INTEL(PM_765A_90,	2100, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_765B_90,	2100, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_765C_90,	2100, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_765E_90,	2100, 1356, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_755A_90,	2000, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_755B_90,	2000, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_755C_90,	2000, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_755D_90,	2000, 1276, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_745A_90,	1800, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_745B_90,	1800, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_745C_90,	1800, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_745D_90,	1800, 1276, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_735A_90,	1700, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_735B_90,	1700, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_735C_90,	1700, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_735D_90,	1700, 1276, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_725A_90,	1600, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_725B_90,	1600, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_725C_90,	1600, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_725D_90,	1600, 1276, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_715A_90,	1500, 1340, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_715B_90,	1500, 1324, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_715C_90,	1500, 1308, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_715D_90,	1500, 1276, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_738_90,	1400, 1116, 600, 988, INTEL_BUS_CLK),
+	INTEL(PM_733_90,	1100,  940, 600, 812, INTEL_BUS_CLK),
+	INTEL(PM_723_90,	1000,  940, 600, 812, INTEL_BUS_CLK),
+	{ NULL, 0, 0, NULL },
+};
+
+static void	est_identify(driver_t *driver, device_t parent);
+static int	est_probe(device_t parent);
+static int	est_attach(device_t parent);
+static int	est_detach(device_t parent);
+static int	est_find_cpu(const char *vendor, uint64_t msr, uint32_t bus_clk,
+		    const freq_info **freqs);
+static const freq_info *est_get_current(const freq_info *freq_list);
+static int	est_settings(device_t dev, struct cf_setting *sets, int *count,
+		    int *type);
+static int	est_set(device_t dev, const struct cf_setting *set);
+static int	est_get(device_t dev, struct cf_setting *set);
+
+static device_method_t est_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	est_identify),
+	DEVMETHOD(device_probe,		est_probe),
+	DEVMETHOD(device_attach,	est_attach),
+	DEVMETHOD(device_detach,	est_detach),
+
+	/* cpufreq interface */
+	DEVMETHOD(cpufreq_drv_set,	est_set),
+	DEVMETHOD(cpufreq_drv_get,	est_get),
+	DEVMETHOD(cpufreq_drv_settings,	est_settings),
+	{0, 0}
+};
+
+static driver_t est_driver = {
+	"est",
+	est_methods,
+	sizeof(struct est_softc),
+};
+
+static devclass_t est_devclass;
+DRIVER_MODULE(est, cpu, est_driver, est_devclass, 0, 0);
+
+static void
+est_identify(driver_t *driver, device_t parent)
+{
+	u_int p[4];
+
+	/*
+	 * XXX If we're not on cpu 0, don't add a child.  We should add
+	 * MP support instead.
+	 */
+	if (device_get_unit(parent) != 0)
+		return;
+
+	/* Make sure we're not being doubly invoked. */
+	if (device_find_child(parent, "est", -1) != NULL)
+		return;
+
+	/* Check that CPUID is supported and the vendor is Intel.*/
+	if (cpu_high == 0 || strcmp(cpu_vendor, GenuineIntel) != 0)
+		return;
+
+	/* Read capability bits and check if the CPU supports EST. */
+	do_cpuid(1, p);
+	if ((p[2] & 0x80) == 0)
+		return;
+
+	if (BUS_ADD_CHILD(parent, 0, "est", -1) == NULL)
+		device_printf(parent, "add est child failed\n");
+}
+
+static int
+est_probe(device_t dev)
+{
+	struct cf_setting set;
+	const freq_info *f;
+	device_t perf_dev;
+	uint64_t msr;
+	int count, type;
+
+	/*
+	 * If the ACPI perf driver has attached and is not just offering
+	 * info, let it manage things.
+	 */
+	perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1);
+	if (perf_dev && device_is_attached(perf_dev)) {
+		type = 0;
+		count = 1;
+		CPUFREQ_DRV_SETTINGS(perf_dev, &set, &count, &type);
+		if ((type & CPUFREQ_FLAG_INFO_ONLY) == 0)
+			return (ENXIO);
+	}
+
+	/* Attempt to enable SpeedStep if not currently enabled. */
+	msr = rdmsr(MSR_MISC_ENABLE);
+	if ((msr & MSR_SS_ENABLE) == 0) {
+		msr |= MSR_SS_ENABLE;
+		wrmsr(MSR_MISC_ENABLE, msr);
+
+		/* Check if the enable failed. */
+		msr = rdmsr(MSR_MISC_ENABLE);
+		if ((msr & MSR_SS_ENABLE) == 0) {
+			device_printf(dev, "failed to enable SpeedStep\n");
+			return (ENXIO);
+		}
+	}
+
+	/* Identify the exact CPU model */
+	msr = rdmsr(MSR_PERF_STATUS);
+	if (est_find_cpu(cpu_vendor, msr, INTEL_BUS_CLK, &f) != 0) {
+		printf(
+	"CPU claims to support Enhanced Speedstep, but is not recognized.\n"
+	"Please update driver or contact the maintainer.\n"
+	"cpu_vendor = %s msr = %0jx, bus_clk = %x\n",
+		    cpu_vendor, msr, INTEL_BUS_CLK);
+		return (ENXIO);
+	}
+
+	device_set_desc(dev, "Enhanced SpeedStep Frequency Control");
+	return (0);
+}
+
+static int
+est_attach(device_t dev)
+{
+	struct est_softc *sc;
+	uint64_t msr;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	msr = rdmsr(MSR_PERF_STATUS);
+	est_find_cpu(cpu_vendor, msr, INTEL_BUS_CLK, &sc->freq_list);
+
+	return (0);
+}
+
+static int
+est_detach(device_t dev)
+{
+	return (ENXIO);
+}
+
+static int
+est_find_cpu(const char *vendor, uint64_t msr, uint32_t bus_clk,
+    const freq_info **freqs)
+{
+	const cpu_info *p;
+	uint32_t id;
+
+	/* Find a table which matches (vendor, id, bus_clk). */
+	id = msr >> 32;
+	for (p = ESTprocs; p->id32 != 0; p++) {
+		if (strcmp(p->vendor, vendor) == 0 && p->id32 == id &&
+		    p->bus_clk == bus_clk)
+			break;
+	}
+	if (p->id32 == 0)
+		return (EOPNOTSUPP);
+
+	/* Make sure the current setpoint is valid. */
+	if (est_get_current(p->freqtab) == NULL)
+		return (EOPNOTSUPP);
+
+	*freqs = p->freqtab;
+	return (0);
+}
+
+static const freq_info *
+est_get_current(const freq_info *freq_list)
+{
+	const freq_info *f;
+	int i;
+	uint16_t id16;
+
+	/*
+	 * Try a few times to get a valid value.  Sometimes, if the CPU
+	 * is in the middle of an asynchronous transition (i.e., P4TCC),
+	 * we get a temporary invalid result.
+	 */
+	for (i = 0; i < 5; i++) {
+		id16 = rdmsr(MSR_PERF_STATUS) & 0xffff;
+		for (f = freq_list; f->id16 != 0; f++) {
+			if (f->id16 == id16)
+				return (f);
+		}
+	}
+	return (NULL);
+}
+
+static int
+est_settings(device_t dev, struct cf_setting *sets, int *count, int *type)
+{
+	struct est_softc *sc;
+	const freq_info *f;
+	int i;
+
+	if (*count < EST_MAX_SETTINGS)
+		return (E2BIG);
+
+	i = 0;
+	for (f = sc->freq_list; f->freq != 0; f++) {
+		sets[i].freq = f->freq;
+		sets[i].volts = f->volts;
+		sets[i].power = CPUFREQ_VAL_UNKNOWN;
+		sets[i].lat = EST_TRANS_LAT;
+		sets[i].dev = dev;
+		i++;
+	}
+	*count = i + 1;
+	*type = CPUFREQ_TYPE_ABSOLUTE;
+
+	return (0);
+}
+
+static int
+est_set(device_t dev, const struct cf_setting *set)
+{
+	struct est_softc *sc;
+	const freq_info *f;
+	uint64_t msr;
+
+	/* Find the setting matching the requested one. */
+	sc = device_get_softc(dev);
+	for (f = sc->freq_list; f->freq != 0; f++) {
+		if (f->freq == set->freq)
+			break;
+	}
+	if (f->freq == 0)
+		return (EINVAL);
+
+	/* Read the current register, mask out the old, set the new id. */
+	msr = rdmsr(MSR_PERF_CTL);
+	msr = (msr & ~0xffffLL) | f->id16;
+	wrmsr(MSR_PERF_CTL, msr);
+
+	return (0);
+}
+
+static int
+est_get(device_t dev, struct cf_setting *set)
+{
+	struct est_softc *sc;
+	const freq_info *f;
+
+	sc = device_get_softc(dev);
+	f = est_get_current(sc->freq_list);
+	if (f == NULL)
+		return (ENXIO);
+
+	set->freq = f->freq;
+	set->volts = f->volts;
+	set->power = CPUFREQ_VAL_UNKNOWN;
+	set->lat = EST_TRANS_LAT;
+	set->dev = dev;
+	return (0);
+}
Index: i386/cpufreq/p4tcc.c
===================================================================
RCS file: i386/cpufreq/p4tcc.c
diff -N i386/cpufreq/p4tcc.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ i386/cpufreq/p4tcc.c	17 Feb 2005 01:04:41 -0000
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2003 Ted Unangst
+ * Copyright (c) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * Copyright (c) 2005 Nate Lawson
+ * 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 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.
+ */
+
+/*
+ * Throttle clock frequency by using the thermal control circuit.  This
+ * operates independently of SpeedStep and ACPI throttling and is supported
+ * on Pentium 4 and later models (feature TM).
+ *
+ * Reference:  Intel Developer's manual v.3 #245472-012
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+#include "cpufreq_if.h"
+
+struct p4tcc_softc {
+	device_t	dev;
+	int		set_count;
+};
+
+#define TCC_NUM_SETTINGS	8
+
+#define TCC_ENABLE_BIT		(1<<4)
+#define TCC_REG_OFFSET		1
+#define TCC_SPEED_PERCENT(x)	((10000 * (x)) / TCC_NUM_SETTINGS)
+
+static void	p4tcc_identify(driver_t *driver, device_t parent);
+static int	p4tcc_probe(device_t dev);
+static int	p4tcc_attach(device_t dev);
+static int	p4tcc_settings(device_t dev, struct cf_setting *sets,
+		    int *count, int *type);
+static int	p4tcc_set(device_t dev, const struct cf_setting *set);
+static int	p4tcc_get(device_t dev, struct cf_setting *set);
+
+static device_method_t p4tcc_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_identify,	p4tcc_identify),
+	DEVMETHOD(device_probe,		p4tcc_probe),
+	DEVMETHOD(device_attach,	p4tcc_attach),
+
+	/* cpufreq interface */
+	DEVMETHOD(cpufreq_drv_set,	p4tcc_set),
+	DEVMETHOD(cpufreq_drv_get,	p4tcc_get),
+	DEVMETHOD(cpufreq_drv_settings,	p4tcc_settings),
+	{0, 0}
+};
+
+static driver_t p4tcc_driver = {
+	"p4tcc",
+	p4tcc_methods,
+	sizeof(struct p4tcc_softc),
+};
+
+static devclass_t p4tcc_devclass;
+DRIVER_MODULE(p4tcc, cpu, p4tcc_driver, p4tcc_devclass, 0, 0);
+
+static void
+p4tcc_identify(driver_t *driver, device_t parent)
+{
+
+	if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != (CPUID_ACPI | CPUID_TM))
+		return;
+	if (BUS_ADD_CHILD(parent, 0, "p4tcc", -1) == NULL)
+		device_printf(parent, "add p4tcc child failed\n");
+}
+
+static int
+p4tcc_probe(device_t dev)
+{
+
+	device_set_desc(dev, "CPU Frequency Thermal Control");
+	return (0);
+}
+
+static int
+p4tcc_attach(device_t dev)
+{
+	struct p4tcc_softc *sc;
+	struct cf_setting set;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+	sc->set_count = TCC_NUM_SETTINGS;
+
+	switch (cpu_id & 0xf) {
+	case 0x22:
+	case 0x24:
+	case 0x25:
+	case 0x27:
+	case 0x29:
+		/*
+		 * These CPU models hang when set to 12.5%.
+		 * See Errata O50, P44, and Z21.
+		 */
+		sc->set_count -= 1;
+		break;
+	case 0x07:	/* errata N44 and P18 */
+	case 0x0a:
+	case 0x12:
+	case 0x13:
+		/*
+		 * These CPU models hang when set to 12.5% or 25%.
+		 * See Errata N44 and P18l.
+		 */
+		sc->set_count -= 2;
+		break;
+	}
+
+	/*
+	 * On boot, the TCC is usually in Automatic mode where reading the
+	 * current performance level is likely to produce bogus results.
+	 * We switch it to On-Demand mode and set a known performance level
+	 * to avoid ambiguity.  Unfortunately, there is no reliable way to
+	 * check that TCC is in the Automatic mode.  Reading bit 4 of ACPI
+	 * Thermal Monitor Control Register produces 0 no matter what the
+	 * current mode.
+	 */
+	set.freq = 10000;
+	set.dev = dev;
+	CPUFREQ_DRV_SET(dev, &set);
+
+	return (0);
+}
+
+static int
+p4tcc_settings(device_t dev, struct cf_setting *sets, int *count, int *type)
+{
+	struct p4tcc_softc *sc;
+	int i, val;
+
+	sc = device_get_softc(dev);
+	if (sets == NULL || count == NULL)
+		return (EINVAL);
+	if (*count < sc->set_count)
+		return (E2BIG);
+
+	/* Return a list of valid settings for this driver. */
+	memset(sets, CPUFREQ_VAL_UNKNOWN, sizeof(*sets) * sc->set_count);
+	for (i = 0, val = sc->set_count; val != 0; i++, val--) {
+		sets[i].freq = TCC_SPEED_PERCENT(val);
+		sets[i].dev = dev;
+	}
+	*count = sc->set_count;
+	*type = CPUFREQ_TYPE_RELATIVE;
+
+	return (0);
+}
+
+static int
+p4tcc_set(device_t dev, const struct cf_setting *set)
+{
+	struct p4tcc_softc *sc;
+	uint64_t mask, msr;
+	int val;
+
+	if (set == NULL)
+		return (EINVAL);
+	sc = device_get_softc(dev);
+
+	/*
+	 * Validate requested state converts to a setting that is an
+	 * integer from [1 .. sc->set_count].
+	 */
+	val = set->freq * sc->set_count / 10000;
+	if (val * 10000 != set->freq * sc->set_count ||
+	    val < 1 || val > sc->set_count)
+		return (EINVAL);
+
+	/* Convert a setting of TCC_NUM_SETTINGS to 0. */
+	if (val == TCC_NUM_SETTINGS)
+		val = 0;
+
+	/*
+	 * Read the current register and mask off the old setting/enable bit.
+	 * If the new val is nonzero, set it and the enable bit.  Finally,
+	 * write the new register.
+	 */
+	msr = rdmsr(MSR_THERM_CONTROL);
+	mask = (TCC_NUM_SETTINGS - 1) << TCC_REG_OFFSET;
+	msr &= ~(mask | TCC_ENABLE_BIT);
+	if (val)
+		msr |= (val << TCC_REG_OFFSET) | TCC_ENABLE_BIT;
+	wrmsr(MSR_THERM_CONTROL, msr);
+
+	return (0);
+}
+
+static int
+p4tcc_get(device_t dev, struct cf_setting *set)
+{
+	uint64_t msr;
+	int val;
+
+	if (set == NULL)
+		return (EINVAL);
+
+	/* Read the current register and extract the current setting. */
+	msr = rdmsr(MSR_THERM_CONTROL);
+	val = (msr >> TCC_REG_OFFSET) & (TCC_NUM_SETTINGS - 1);
+
+	/* Convert a setting of 0 to TCC_NUM_SETTINGS. */
+	if (val == 0)
+		val = TCC_NUM_SETTINGS;
+
+	memset(set, CPUFREQ_VAL_UNKNOWN, sizeof(*set));
+	set->freq = TCC_SPEED_PERCENT(val);
+	set->dev = dev;
+
+	return (0);
+}

--------------060506010507060809010505--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4213F066.2050708>