はじめましてではないのですが、 はじめまして。尾辻と申します。
Phenom/opteron{1,2,8}3XXなcpufreqを作ってみました。 まだβバージョンですが。 こんな感じです。 マザーボードはBIOSTAR TF8200A2+です。 # dmesgのcpuの部分 CPU: AMD Phenom(tm) 9850 Quad-Core Processor (2500.01-MHz 686-class CPU) # uname -aの抜きだし FreeBSD 7.1-PRERELEASE #0 Thu Oct 2 21:20:49 JST 2008 gen@:/tmp/sys/TF8200S i386 # kldload cpufreq hwpstate0: <Cool`n'Quiet 2.0> 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: <Cool`n'Quiet 2.0> 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: <Cool`n'Quiet 2.0> 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: <Cool`n'Quiet 2.0> 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 (???まだβバージョンです。) # kldload cpufreq で元に戻ります。 current mailing listに投稿しても なんというか、私の英語がひどいのかそれとも、 以前のpstate.cがひどかったのか、それともかなり間があいたからなのか というかんじなものですから、こちらにも投稿させていただきます。 以前にcurrentメーリングリストに投稿したらkldunloadできない とかフリーズするとかでしたが。 ベロニカさんの所ではうまくいったのですが、cghostsさんの所では うまくいかないみたいでした。PHKさんの所ではどうなのかわかりません。 今回のcpufreqはアクロバチックな所がないので大丈夫だと思います。 以前のpstate.cはBIOSがやるべきところをwrmsrをつかって 強引にやってました。ですがwindowsでいう所の crystal cpuidのようなものだからいいかなとは思っていたのですが 考えてみると400MHzまで下げられたのですが遅すぎです。 そこで#debug.cpufreq.lowest=1200というのを/boot/loader.confに いれていたのですが、BIOSが作成したhardware pstateは 私の所では1250MHzと2500MHzです。これで十分だと思い直し 妥協しました。 そいうわけでBIOSが作成した Hardware P-state というP-stateを使って それを遷移させるだけのモジュールです。 ソースを読めば300行くらいで手続き的なところを除くと 半分くらいです。 試してみてください。今度のはkldunload できます。 まだβバージョンなのでdebug message がでますが、 ソース中のhwpstate_verbose=0とすればでなくなります。 またコメントもあります。 currentのamd64でもためしているのでamd64でも大丈夫だと思います。 なにぶんはじめてのカーネルモジュールなのでいたらないところ こうしたほうがいいところなど指摘してくださると うれしいです。 よろしくお願いします。 #人柱をお願いしているようでもしわけありませんが、 #自分の環境ではすくなくとも大丈夫です。 尾辻 <[メールアドレス保護] あの夏!> P.S. 以下ソースです。file名はhwpstate.cなのですが。 /sys/module/cpufreq/MakefileのSRCS=にhwpstate.cを追加して、 /sys/i386/cpufre/に以下のファイルhwpstate.cでおいてください。 kerenelにはcpufreqが標準でついているので それを除いたカーネルをつくらないと試せないと思います。 /sys/module/cpuferqでmake && make installすると使えるようになります。 ===================================================== /** * 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<1000;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); }