Date: Sat, 01 Nov 2008 22:27:20 +0900 From: "G .Otsuji" <annona2@gmail.com> To: "fluffles.net" <bsd@fluffles.net> Cc: "G .Otsuji" <annona2@gmail.com>, Poul-Henning Kamp <phk@phk.freebsd.dk>, FreeBSD Current <freebsd-current@freebsd.org> Subject: Re: AMD Family 10h cpufreq driver Message-ID: <200811011327.mA1DRKrk002761@localhost> In-Reply-To: <48D98E42.5040801@fluffles.net> References: <200809232121.m8NLLnig001691@softbank219001162114.bbtec.net> <26111.1222205043@critter.freebsd.dk> <200809232307.m8NN79th001888@softbank219001162114.bbtec.net> <48D98E42.5040801@fluffles.net>
next in thread | previous in thread | raw e-mail | index | archive | help
Thank you very much for your kindness, and I came back again after a long time.sorry. I've spent quite time with cpufreq and acpidump , and renewed. Prelude: This is the first time of my kernel driver developping, so, i'm very happy with this, but some mistake would be there! Hardware P-state is a new power state of AMD Family 10h. powernow.c has also p-state. So i renamed pstate.c to hwpstate.c. From "acpidump -d" , my mother board has only two hw-pstate. It is very few for me. But my old kernel driver was dangerlous. So I decided to use for bios pstate. only 2 state though. but I think it is a compromise. and 1250 MHz is supported by the bios .so that's enough. Changelog: 1). with many verbose message without boot -v. you can hush up with hwpstate_verbose=0 in the source. (not sysctl, I don't know how to make it :-) ). 2). renamed to hwpstate.c. because powernow.c has also a pstate. 3). looking at the bios supported hardware P-state. So it is not dangerous ( I hope.) How to install and more: ( 0. shell> /etc/rc.d/powerd stop ) 1. shell> patch < "[this mail]" 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. "umount -a" or "mount -u -o ro /somewhere" as possible, and sync;sync;sync; ) 7. shell> kldload cpufreq 8. the verbose messages will be there. 9, shell> sysctl dev.cpu.0.freq_levels 10, shell> sysctl dev.cpu.0.freq=XXXX 11, shell> /etc/rc.d/powerd start Please try this renewed cpufreq module if there is Phenom or Opteron [128]3XX 's. Thank you in advance. Best Regards, G. Otsuji<anonna2@gmail.com> --- /dev/null 2008-11-01 22:22:00.000000000 +0900 +++ ./hwpstate.c 2008-11-01 22:23:15.000000000 +0900 @@ -0,0 +1,372 @@ +/*- + * 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. + */ + +/** + * Reference: + * Rev 3.06 March 26, 2008 - BIOS and Kernel Developer's Guide(BKDG) + * for AMD Family 10h Processors + */ + +#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/proc.h> +#include <dev/pci/pcivar.h> +#include <machine/md_var.h> + +#include <contrib/dev/acpica/acpi.h> +#include <dev/acpica/acpivar.h> + +#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 MSR_AMD10H_CUR_VID(msr) (((msr) >> 9) & 0x3F) +#define MSR_AMD10H_CUR_DID(msr) (((msr) >> 6) & 0x07) +#define MSR_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 = MSR_AMD10H_CUR_VID(msr); + did = MSR_AMD10H_CUR_DID(msr); + fid = MSR_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); +}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200811011327.mA1DRKrk002761>