1 /* $OpenBSD: powernow.c,v 1.6 2014/09/14 14:17:23 jsg Exp $ */ 2 /* 3 * Copyright (c) 2004 Ted Unangst 4 * All rights reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * AMD K6 PowerNow driver, see AMD tech doc #23525 21 * Numerous hints from Linux cpufreq driver 22 */ 23 24 #include <sys/param.h> 25 #include <sys/systm.h> 26 #include <sys/sysctl.h> 27 28 #include <machine/cpu.h> 29 #include <machine/cpufunc.h> 30 #include <machine/specialreg.h> 31 #include <machine/pio.h> 32 33 /* table 27 in amd 23535. entries after 45 taken from linux cpufreq */ 34 struct { 35 int mult; /* not actually used, maybe for cpuspeed */ 36 int magic; 37 } k6_multipliers[] = { 38 { 20, 4 }, 39 { 30, 5 }, 40 { 35, 7 }, 41 { 40, 2 }, 42 { 45, 0 }, 43 { 55, 3 }, 44 { 50, 1 }, 45 { 60, 6 }, 46 }; 47 #define NUM_K6_ENTRIES (sizeof k6_multipliers / sizeof k6_multipliers[0]) 48 49 int k6_maxindex; /* no setting higher than this */ 50 51 /* linux says: "it doesn't matter where, as long as it is unused */ 52 #define K6PORT 0xfff0 53 54 void 55 k6_powernow_init(void) 56 { 57 uint64_t msrval; 58 uint32_t portval; 59 int i; 60 61 /* on */ 62 msrval = K6PORT | 0x1; 63 wrmsr(MSR_K6_EPMR, msrval); 64 /* read */ 65 portval = inl(K6PORT + 8); 66 /* off */ 67 wrmsr(MSR_K6_EPMR, 0LL); 68 69 portval = (portval >> 5) & 7; 70 71 for (i = 0; i < NUM_K6_ENTRIES; i++) 72 if (portval == k6_multipliers[i].magic) { 73 k6_maxindex = i; 74 break; 75 } 76 if (i == NUM_K6_ENTRIES) { 77 printf("bad value for current multiplier\n"); 78 return; 79 } 80 81 cpu_setperf = k6_powernow_setperf; 82 } 83 84 void 85 k6_powernow_setperf(int level) 86 { 87 uint64_t msrval; 88 uint32_t portval; 89 int index; 90 91 index = level * k6_maxindex / 100; 92 93 /* on */ 94 msrval = K6PORT | 0x1; 95 wrmsr(MSR_K6_EPMR, msrval); 96 /* read and update */ 97 portval = inl(K6PORT + 8); 98 portval &= 0xf; 99 portval |= 1 << 12 | 1 << 10 | 1 << 9 | 100 k6_multipliers[index].magic << 5; 101 outl(K6PORT + 8, portval); 102 /* off */ 103 wrmsr(MSR_K6_EPMR, 0LL); 104 } 105