From owner-freebsd-bugs@FreeBSD.ORG Tue Nov 4 02:40:01 2008 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C90DF1065676 for ; Tue, 4 Nov 2008 02:40:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id A51A68FC1B for ; Tue, 4 Nov 2008 02:40:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id mA42e1mV011315 for ; Tue, 4 Nov 2008 02:40:01 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id mA42e1NJ011314; Tue, 4 Nov 2008 02:40:01 GMT (envelope-from gnats) Resent-Date: Tue, 4 Nov 2008 02:40:01 GMT Resent-Message-Id: <200811040240.mA42e1NJ011314@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Gen Otsuji Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 2369E106567A for ; Tue, 4 Nov 2008 02:37:34 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (www.freebsd.org [IPv6:2001:4f8:fff6::21]) by mx1.freebsd.org (Postfix) with ESMTP id 07AA78FC21 for ; Tue, 4 Nov 2008 02:37:34 +0000 (UTC) (envelope-from nobody@FreeBSD.org) Received: from www.freebsd.org (localhost [127.0.0.1]) by www.freebsd.org (8.14.3/8.14.3) with ESMTP id mA42bXR1060331 for ; Tue, 4 Nov 2008 02:37:33 GMT (envelope-from nobody@www.freebsd.org) Received: (from nobody@localhost) by www.freebsd.org (8.14.3/8.14.3/Submit) id mA42bXGH060330; Tue, 4 Nov 2008 02:37:33 GMT (envelope-from nobody) Message-Id: <200811040237.mA42bXGH060330@www.freebsd.org> Date: Tue, 4 Nov 2008 02:37:33 GMT From: Gen Otsuji To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Cc: Subject: kern/128575: [cpufreq] add support for Phenom etc..(beta version) X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 04 Nov 2008 02:40:01 -0000 >Number: 128575 >Category: kern >Synopsis: [cpufreq] add support for Phenom etc..(beta version) >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Tue Nov 04 02:40:01 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Gen Otsuji >Release: FreeBSD 7.1-PRERELEASE >Organization: >Environment: FreeBSD gain.felix 7.1-PRERELEASE FreeBSD 7.1-PRERELEASE #BSD/Linux Lite: Thu Oct 2 21:20:49 JST 2008 gen@:/tmp/sys/TF8200S i386 >Description: cpufreq has no support for AMD Phenom (Family 10h) now. I created pstate.c cpufreq module and posted that previous version to the "current mailing list" in september. ( though very much thanks to veronica for her kindness, I was deadly wrong in aspect of quick response. ) aside from it, that previous pstate.c was an acrobatic module, and has many bugs. and also I noticed that OS has not to have a feature like the "Crystal CPUID" for Windows. So I changed the pstate.c to hwpstate.c and to use the BIOS setting of cpufreq for Phenom. so this is "hard-ware" pstate. I posted to the current mailing list also. Now the code became small. and can kldunload. I tested also in the 8-current and in amd64 architecture. >How-To-Repeat: # dmesg | grep CPU: CPU: AMD Phenom(tm) 9850 Quad-Core Processor (2500.01-MHz 686-class CPU) # kldload cpufreq hwpstate0: on cpu0 hwpstate0: SVI mode hwpstate0: you have 2 P-state. hwpstate0: freq=2500MHz volts=1300mV hwpstate0: freq=1250MHz volts=1050mV hwpstate0: Now P0-state. hwpstate1: on cpu1 hwpstate1: SVI mode hwpstate1: you have 2 P-state. hwpstate1: freq=2500MHz volts=1300mV hwpstate1: freq=1250MHz volts=1050mV hwpstate1: Now P0-state. hwpstate2: on cpu2 hwpstate2: SVI mode hwpstate2: you have 2 P-state. hwpstate2: freq=2500MHz volts=1300mV hwpstate2: freq=1250MHz volts=1050mV hwpstate2: Now P0-state. hwpstate3: on cpu3 hwpstate3: SVI mode hwpstate3: you have 2 P-state. hwpstate3: freq=2500MHz volts=1300mV hwpstate3: freq=1250MHz volts=1050mV hwpstate3: Now P0-state. # sysctl dev.cpu.0.freq_levels dev.cpu.0.freq_levels: 2500/-1 1250/-1 # sysctl dev.cpu.0.freq dev.cpu.0.freq: 2500 # sysctl dev.cpu.0.freq=1250 dev.cpu.0.freq: 2500 -> 1250 # dmesg hwpstate0: goto P1-state hwpstate0: Now P1-state. hwpstate1: goto P1-state hwpstate1: Now P1-state. hwpstate2: goto P1-state hwpstate2: Now P1-state. hwpstate3: goto P1-state hwpstate3: Now P1-state. # kldunload cpufreq hwpstate0: Now P0-state. hwpstate0: detached hwpstate1: Now P0-state. hwpstate1: detached hwpstate2: Now P0-state. hwpstate2: detached hwpstate3: Now P0-state. hwpstate3: detached # sysctl dev.cpu.0.freq dev.cpu.0.freq: 1250 <-------- this is not I expected (beta version.) # kldload cpufreq # sysctl dev.cpu.0.freq dev.cpu.0.freq: 2500 >Fix: 1. shell# /etc/rc.d/powerd stop 2. shell# cp hwpstate.c /sys/i386/cpufreq/ 3. edit /sys/modules/cpufreq/Makefile and change, -SRCS+= est.c p4tcc.c powernow.c +SRCS+= est.c p4tcc.c powernow.c hwpstate.c 4. delete the line "device cpufreq" from KERNCONF file. and make kernel without cpufreq. 5. shell# cd /sys/module/cpufreq/ && make && make install 6. shell# kldload cpufreq 7, shell# sysctl dev.cpu.0.freq_levels 8, shell# sysctl dev.cpu.0.freq=XXXX 9, shell# kldunlpad cpufreq Patch attached with submission follows: --- /dev/null 2008-11-04 11:33:00.000000000 +0900 +++ /sys/i386/cpufreq/hwpstate.c 2008-11-04 10:29:26.000000000 +0900 @@ -0,0 +1,378 @@ +/*- + * Copyright (c) 2008 Gen Otsuji + * 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. + */ + +/** + * let me say very much thanks to Veronica(fluffles.net) + * for her kindness , and to others. + */ + +/** + * Reference: + * Rev 3.06 March 26, 2008 - BIOS and Kernel Developer's Guide(BKDG) + * for AMD Family 10h Processors + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "acpi_if.h" +#include "cpufreq_if.h" + +#define MSR_AMD10H_LIMIT 0xc0010061 +#define MSR_AMD10H_CONTROL 0xc0010062 +#define MSR_AMD10H_STATUS 0xc0010063 +#define MSR_AMD10H_CONFIG 0xc0010064 +#define AMD10H_PVI_MODE 1 +#define AMD10H_SVI_MODE 0 +#define AMD10H_MAX_STATES 16 + +/* for MSR_AMD10H_LIMIT C001_0061 */ +#define AMD10H_GET_PSTATE_MAX_VAL(msr) (((msr) >> 4) & 0xF) +/* for MSR_AMD10H_CONFIG C001_0064:68 */ +#define AMD10H_CUR_VID(msr) (((msr) >> 9) & 0x3F) +#define AMD10H_CUR_DID(msr) (((msr) >> 6) & 0x07) +#define AMD10H_CUR_FID(msr) ((msr) & 0x3F) + +/** + * setting this to 0 can hush up verbose messages. + */ +static int hwpstate_verbose = 1; + +struct hwpstate_setting { + int freq; /* CPU clock in Mhz or 100ths of a percent. */ + int volts; /* Voltage in mV. */ + int power; /* Power consumed in mW. */ + int lat; /* Transition latency in us. */ + int pstate_id; + device_t dev; /* Driver providing this setting. */ +}; + +struct hwpstate_softc { + device_t dev; + struct hwpstate_setting hwpstate_settings[AMD10H_MAX_STATES]; + int cfnum; + int voltage_mode; /* for AMD10H_PVI_MODE / AMD10H_SVI_MODE */ + int curpstate; +}; + +static void hwpstate_identify(driver_t * driver, device_t parent); +static int hwpstate_probe(device_t dev); +static int hwpstate_attach(device_t dev); +static int hwpstate_detach(device_t dev); +static int hwpstate_set(device_t dev, const struct cf_setting *cf); +static int hwpstate_get(device_t dev, struct cf_setting *cf); +static int hwpstate_settings(device_t dev, struct cf_setting *sets, int *count); +static int hwpstate_type(device_t dev, int *type); +static int hwpstate_shutdown(device_t dev); +static int hwpstate_features(driver_t * driver, u_int * features); + +static device_method_t hwpstate_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, hwpstate_identify), + DEVMETHOD(device_probe, hwpstate_probe), + DEVMETHOD(device_attach, hwpstate_attach), + DEVMETHOD(device_detach, hwpstate_detach), + DEVMETHOD(device_shutdown, hwpstate_shutdown), + + /* cpufreq interface */ + DEVMETHOD(cpufreq_drv_set, hwpstate_set), + DEVMETHOD(cpufreq_drv_get, hwpstate_get), + DEVMETHOD(cpufreq_drv_settings, hwpstate_settings), + DEVMETHOD(cpufreq_drv_type, hwpstate_type), + + /* ACPI interface */ + DEVMETHOD(acpi_get_features, hwpstate_features), + + {0, 0} +}; + +static devclass_t hwpstate_devclass; +static driver_t hwpstate_driver = { + "hwpstate", + hwpstate_methods, + sizeof(struct hwpstate_softc), +}; +DRIVER_MODULE(hwpstate, cpu, hwpstate_driver, hwpstate_devclass, 0, 0); + +static void +hwpstate_goto_pstate(device_t dev,int pstate) +{ + struct hwpstate_softc *sc; + uint64_t msr; + int i; + sc = device_get_softc(dev); + sc->curpstate = pstate; + wrmsr(MSR_AMD10H_CONTROL, pstate); + for(i=0;i<100;i++){ + msr=rdmsr(MSR_AMD10H_STATUS); + if(msr==pstate){ + break; + } + DELAY(100); + } + msr=rdmsr(MSR_AMD10H_STATUS); + if(hwpstate_verbose) + device_printf(dev,"Now P%d-state.\n",(int)msr); + return; +} + +static int +hwpstate_set(device_t dev, const struct cf_setting *cf) +{ + struct hwpstate_softc *sc; + struct hwpstate_setting *set; + int i; + if (cf == NULL) + return (EINVAL); + sc = device_get_softc(dev); + set = sc->hwpstate_settings; + for (i = 0; i < sc->cfnum; i++) + if (cf->freq == set[i].freq) + break; + if (i == sc->cfnum) + return EINVAL; + if(hwpstate_verbose) + device_printf(dev,"goto P%d-state\n",set[i].pstate_id); + sc->curpstate = set[i].pstate_id; + hwpstate_goto_pstate(dev,set[i].pstate_id); + return (0); +} + +static int +hwpstate_get(device_t dev, struct cf_setting *cf) +{ + struct hwpstate_softc *sc; + struct hwpstate_setting set; + sc = device_get_softc(dev); + if (cf == NULL) + return (EINVAL); + set = sc->hwpstate_settings[sc->curpstate]; + cf->freq = set.freq; + cf->volts = set.volts; + cf->power = CPUFREQ_VAL_UNKNOWN; + cf->lat = 16; + cf->dev = dev; + return (0); +} + +static int +hwpstate_settings(device_t dev, struct cf_setting *sets, int *count) +{ + struct hwpstate_softc *sc; + struct hwpstate_setting set; + int i; + if (sets == NULL || count == NULL) + return (EINVAL); + sc = device_get_softc(dev); + if (*count < sc->cfnum) + return (E2BIG); + for (i = 0; i < sc->cfnum; i++, sets++) { + set = sc->hwpstate_settings[i]; + sets->freq = set.freq; + sets->volts = set.volts; + sets->power = set.power; + sets->lat = set.lat; + sets->dev = set.dev; + } + *count = sc->cfnum; + return (0); +} + +static int +hwpstate_type(device_t dev, int *type) +{ + + if (type == NULL) + return (EINVAL); + *type = CPUFREQ_TYPE_ABSOLUTE; + return (0); +} + +static int +hwpstate_is_capable(void) +{ + u_int regs[4]; + if (strcmp(cpu_vendor, "AuthenticAMD") != 0 || + cpu_exthigh < 0x80000007) + return (FALSE); + do_cpuid(0x80000007, regs); + if (regs[3] & 0x80) { /* HwPstate Enable bit */ + return (TRUE); + } + return (FALSE); +} + +static void +hwpstate_identify(driver_t * driver, device_t parent) +{ + device_t child; + if (device_find_child(parent, "hwpstate", -1) != NULL) { + return; + } + if ((child = BUS_ADD_CHILD(parent, 10, "hwpstate", -1)) == NULL) + device_printf(parent, "hwpstate: add child failed\n"); +} + +static int +hwpstate_probe(device_t dev) +{ + struct hwpstate_softc *sc; + device_t perf_dev; + uint64_t msr; + int error, type; + if (resource_disabled("hwpstate", 0)) + return (ENXIO); + + /* this had not to be in hwpstate_identify() */ + if (hwpstate_is_capable() == FALSE) { + return (ENXIO); + } + perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1); + if (perf_dev && device_is_attached(perf_dev)) { + error = CPUFREQ_DRV_TYPE(perf_dev, &type); + if (error == 0 && (type & CPUFREQ_FLAG_INFO_ONLY) == 0) + return (ENXIO); + } + sc = device_get_softc(dev); + switch (cpu_id) { + case 0x100f2A: /* family 10h rev.DR-BA */ + case 0x100f22: /* family 10h rev.DR-B2 */ + case 0x100f23: /* family 10h rev.DR-B3 */ + break; + default: + return (ENXIO); + } + msr = rdmsr(MSR_AMD10H_LIMIT); + sc->cfnum = AMD10H_GET_PSTATE_MAX_VAL(msr); + if (sc->cfnum == 0) { + device_printf(dev, "hardware-pstate is not supported by the bios.\n"); + return ENXIO; + } + device_set_desc(dev, "Cool`n'Quiet 2.0"); + return (0); +} + +static int +hwpstate_attach(device_t dev) +{ + struct hwpstate_softc *sc; + struct hwpstate_setting *set; + device_t F3; + uint64_t msr; + uint32_t cfg; + int i, vid, did, fid; + sc = device_get_softc(dev); + + /** + * following 24 means the 1st cpu. 25-31 instead of 24 is MP system. + * I don't have MP system . But only for reading from 1st cpu. + * so if the same 2*cpu , 4*cpu ,or 8*cpu , this can work , I think. + */ + F3 = pci_find_bsf(0, 24, 3); + cfg = pci_read_config(F3, 0xA0, 4); + if (cfg & 0x10) { /* PVI mode */ + if (hwpstate_verbose) + device_printf(dev, "PVI mode\n"); + sc->voltage_mode = AMD10H_PVI_MODE; + } else { /* SVI mode */ + if (hwpstate_verbose) + device_printf(dev, "SVI mode\n"); + sc->voltage_mode = AMD10H_SVI_MODE; + } + msr = rdmsr(MSR_AMD10H_LIMIT); + sc->cfnum = 1 + AMD10H_GET_PSTATE_MAX_VAL(msr); + if (hwpstate_verbose) + device_printf(dev, "you have %d P-state.\n", sc->cfnum); + set = sc->hwpstate_settings; + for (i = 0; i < sc->cfnum; i++, set++) { + msr = rdmsr(MSR_AMD10H_CONFIG + i); + if ((msr & 0x8000000000000000)) { + vid = AMD10H_CUR_VID(msr); + did = AMD10H_CUR_DID(msr); + fid = AMD10H_CUR_FID(msr); + set->freq = 100 * (fid + 0x10) / (1 << did); + if (sc->voltage_mode == AMD10H_PVI_MODE) { + /* 2.4.1.6.2 Parallel VID Encodings */ + if (vid >= 0x20) + set->volts = (7625 - 125 * (vid - 0x20)) / 10; + else + set->volts = 1550 - 25 * vid; + } else { + /* 2.4.1.6.3 Serial VID Encodings */ + if (vid >= 0x7F) + set->volts = 0; + else + set->volts = (15500 - 125 * vid) / 10; + } + if (hwpstate_verbose) + device_printf(dev, "freq=%dMHz volts=%dmV\n", set->freq, set->volts); + set->pstate_id = i; + set->power = CPUFREQ_VAL_UNKNOWN; + set->lat = 16; + set->dev = dev; + } + } + cpufreq_register(dev); + hwpstate_goto_pstate(dev,0); + return (0); +} + +static int +hwpstate_detach(device_t dev) +{ + + hwpstate_goto_pstate(dev,0); + return (cpufreq_unregister(dev)); +} + +static int +hwpstate_shutdown(device_t dev) +{ + + hwpstate_goto_pstate(dev,0); + return (0); +} + +static int +hwpstate_features(driver_t * driver, u_int * features) +{ + + *features = ACPI_CAP_PERF_MSRS; + return (0); +} + >Release-Note: >Audit-Trail: >Unformatted: