Subject: pentium 4 clock modulation support
To: None <port-i386@netbsd.org, tech-kern@netbsd.org>
From: matthew green <mrg@eterna.com.au>
List: tech-kern
Date: 08/17/2004 17:41:56
hi folks.


the pentium4 cpu has a method of varying the 'duty cycle' of
the clock which has the effect of slowing down the clock,
reducing power usage slightly.  the patch below adds support
for modifying this value, but it's not complete (no MP or HT
support, plus some errata need to be properly considered)
and even at 12.5% of duty cycle my laptop only used maybe 1W
less power.

i'm sending this patch incase someone else wants this support
or wants to finish the missing bits.  with working old
"speedstep" support for (some) P4's, which in low-power mode
uses 5-6W less power for my laptop, the minor power benefit
of modifying the duty cycle doesn't really outweight the
significant cpu speed reduction for me, i have no plans to
use this feature anymore.


.mrg.


Index: conf/files.i386
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.259
diff -p -r1.259 files.i386
*** conf/files.i386	10 Jul 2004 18:51:01 -0000	1.259
--- conf/files.i386	17 Aug 2004 07:30:48 -0000
*************** file	arch/i386/bios/vesa_text.c	vesatext
*** 474,477 ****
--- 474,481 ----
  file	arch/i386/i386/est.c		enhanced_speedstep
  defflag	opt_est.h	EST_FREQ_USERWRITE
  
+ # Pentium 4 Clock Modulation
+ file	arch/i386/i386/p4clockmod.c		pentium4_clock_modulation
+ defflag	opt_p4clockmod.h	P4_CLOCKMOD_USERWRITE
+ 
  include "arch/i386/conf/majors.i386"
Index: i386/identcpu.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/identcpu.c,v
retrieving revision 1.16
diff -p -r1.16 identcpu.c
*** i386/identcpu.c	8 Aug 2004 05:21:01 -0000	1.16
--- i386/identcpu.c	17 Aug 2004 07:30:48 -0000
*************** identifycpu(struct cpu_info *ci)
*** 1375,1378 ****
--- 1375,1383 ----
  	}
  #endif /* ENHANCED_SPEEDSTEP */
  
+ #ifdef PENTIUM4_CLOCK_MODULATION
+ 	if (cpu_feature & CPUID_ACPI)
+ 		p4_clockmod_init(ci);
+ #endif /* PENTIUM4_CLOCK_MODULATION */
+ 
  }
Index: i386/p4clockmod.c
===================================================================
RCS file: i386/p4clockmod.c
diff -N i386/p4clockmod.c
*** /dev/null	1 Jan 1970 00:00:00 -0000
--- i386/p4clockmod.c	17 Aug 2004 07:30:49 -0000
***************
*** 0 ****
--- 1,157 ----
+ /*	$NetBSD$	*/
+ 
+ /*
+  * Copyright (c) 2004 Matthew R. Green
+  * 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.
+  */
+ 
+ #include <sys/cdefs.h>
+ __KERNEL_RCSID(0, "$NetBSD$");
+ 
+ #include <sys/param.h>
+ #include <sys/systm.h>
+ #include <sys/malloc.h>
+ #include <sys/sysctl.h>
+ 
+ #include <machine/cpu.h>
+ #include <machine/specialreg.h>
+ 
+ #include "opt_p4clockmod.h"
+ #ifdef P4_CLOCKMOD_USERWRITE
+ #define P4_CLOCKMOD_CTLFLAG      (CTLFLAG_READWRITE | CTLFLAG_ANYWRITE)
+ #else 
+ #define P4_CLOCKMOD_CTLFLAG      CTLFLAG_READWRITE
+ #endif
+ 
+ u_int	p4_clockmod_dc_to_mask[] = {
+ 	0x00,		/* reserved (same as 100%) */
+ 	0x12,		/* 12.5% */
+ 	0x14,		/* 25.0% */
+ 	0x16,		/* 37.5% */
+ 	0x18,		/* 50.0% */
+ 	0x1A,		/* 63.5% */
+ 	0x1C,		/* 75.0% */
+ 	0x1E,		/* 87.5% */
+ };
+ 
+ #define	CLOCK_MOD_MASK		(~0x1e)
+ #define	CLOCK_MOD_ENABLE	0x10
+ 
+ /*
+  * returns 0 if clockmod isn't enabled, otherwise the value of
+  * the 3 DC bits.  (0 is invalid if CLOCK_MOD_ENABLE is set.)
+  */
+ static int
+ p4_clockmod_read_dc(void)
+ {
+ 	u_int64_t	msr;
+ 
+ 	msr = rdmsr(MSR_CLOCK_MODULATION) & ~CLOCK_MOD_MASK;
+ 
+ 	if ((msr & CLOCK_MOD_ENABLE) == 0)
+ 		return 0;
+ 	return (msr & ~CLOCK_MOD_ENABLE) >> 1;
+ }
+ 
+ static int
+ p4_clockmod_sysctl_helper(SYSCTLFN_ARGS)
+ {
+ 	struct sysctlnode	node;
+ 	int			dc, olddc, error;
+ 
+ 	dc = olddc = p4_clockmod_read_dc();
+ 	node = *rnode;
+ 	node.sysctl_data = &dc;
+ 
+ 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
+ 	if (error || newp == NULL)
+ 		return (error);
+ 	
+ 	if (dc > 7 || dc < 0)
+ 		return (EINVAL);
+ 
+ 	/*
+ 	 * XXX see below
+ 	 */
+ 	if (dc == 1 || dc == 2)
+ 		dc = 3;
+ 
+ 	if (dc != olddc) {
+ 		u_int64_t	msr;
+ 
+ 		/*
+ 		 * XXX HyperThreading really wants this set the same
+ 		 * per physical cpu... but it doesn't really break it
+ 		 * just uses the highest setting...
+ 		 */
+ 		msr = rdmsr(MSR_CLOCK_MODULATION) & CLOCK_MOD_MASK;
+ 		msr |= p4_clockmod_dc_to_mask[dc];
+ 		wrmsr(MSR_CLOCK_MODULATION, msr);
+ 	}
+ 
+ 	return (0);
+ }
+ 
+ void
+ p4_clockmod_init(struct cpu_info *ci)
+ {
+ 	struct sysctlnode	*node, *modnode;
+ 	int			rv;
+ 
+ 	/* Don't bother setting up if we have EST? */
+ 	if (cpu_feature2 & CPUID2_EST)
+ 		return;
+ 
+ 	if ((cpu_feature & CPUID_ACPI) == 0)
+ 		return;
+ 
+ 	aprint_normal("%s: Pentium Clock Modulation enabled\n",
+ 	    ci->ci_dev->dv_xname);
+ 
+ 	/*
+ 	 * XXX there are several errata about setting modulation
+ 	 * to 12.5 or 25% hanging the system;  For now just disable
+ 	 * those modes (pretend they'are 37.5.)
+ 	 */
+ 	p4_clockmod_dc_to_mask[1] = p4_clockmod_dc_to_mask[3];
+ 	p4_clockmod_dc_to_mask[2] = p4_clockmod_dc_to_mask[3];
+ 
+ 	/*
+ 	 * Setup the machdep.p4clockmod_duty_cycle sysctl
+ 	 */
+ 	if ((rv = sysctl_createv(NULL, 0, NULL, &node,
+ 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
+ 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
+ 		goto err;
+ 	if ((rv = sysctl_createv(NULL, 0, &node, &modnode,
+ 	    P4_CLOCKMOD_CTLFLAG, CTLTYPE_INT, "p4clockmod_duty_cycle", NULL,
+ 	    p4_clockmod_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
+ 		goto err;
+ 
+ 	return;
+ err:
+ 	aprint_normal("%s: sysctl_createv failed (rv = %d)\n", __func__, rv);
+ }
Index: include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/include/cpu.h,v
retrieving revision 1.115
diff -p -r1.115 cpu.h
*** include/cpu.h	16 May 2004 12:32:53 -0000	1.115
--- include/cpu.h	17 Aug 2004 07:30:49 -0000
*************** void x86_bus_space_mallocok(void);
*** 436,441 ****
--- 436,444 ----
  /* est.c */
  void	est_init(struct cpu_info *);
  
+ /* p4clockmod.c */
+ void	p4_clockmod_init(struct cpu_info *);
+ 
  #endif /* _KERNEL */
  
  /*