Date: Sun, 07 Sep 2008 09:36:03 +0900 From: "G. Otsuji" <annona2@gmail.com> To: FreeBSD Current <freebsd-current@freebsd.org> Subject: AMD Family 10h cpufreq driver Message-ID: <200809070036.m870a3NC001532@softbank219001162114.bbtec.net>
index | next in thread | raw e-mail
[-- Attachment #1 --]
Hello ,
I have AMD Phenom x4 9850 BE cpu ,but
there was no sysctl oid dev.cpu.0.freq_levels.
so I read the BKDG (bios and kernel developer guide for AMD Family 10h)
and finally I reached some result.
I wrote pstate.c cpufreq driver module. attached in this mail.
I have tested this module on i386 current/stable, amd64 current/stable.
and make -j20 buildworld can be done with running powerd.
This module has capability to down clock to 400MHz.
but It's slow, so echo "debug.cpufreq.lowest=1200" >>/boot/loader.conf.
and the reuslt is as follows.
$ sysctl dev.cpu.0.freq_levels
dev.cpu.0.freq_levels: 2500/-1 2400/-1 2300/-1 2200/-1 2100/-1 2000/-1
1900/-1 1800/-1 1700/-1 1600/-1 1500/-1 1400/-1 1300/-1 1200/-1
I have enjoying this module in a few days but It's OK.
AMD Family 10h cpu_id's are 100f23,100f2a,100f22.
I don't have Opteron 13XX 23XX 83XX,Phenom X3, and other Phenom X4,And MP system.
if you have these cpu's , i would like you to try this module.
feel free to contact me.
Sincerely,
G. Otsuji <annona2@gmail.com>
[-- Attachment #2 --]
/*-
* 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 AMD Family 10h Processor BKDG
*/
#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 <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/types.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_PSTATE_LIMIT 0xc0010061
#define MSR_PSTATE_CONTROL 0xc0010062
#define MSR_PSTATE_STATUS 0xc0010063
#define MSR_PSTATE_CONFIG 0xc0010064
#define MSR_PSTATE_COFVID 0xc0010071
#define MSR_PSTATE_MOF(msr) (((uint64_t)(msr)>>49)&0x3F)
#define MSR_PSTATE_CUR_VID(msr) (((msr) >> 9) & 0x3F)
#define MSR_PSTATE_CUR_DID(msr) (((msr) >> 6) & 0x07)
#define MSR_PSTATE_CUR_FID(msr) ((msr) & 0x3F)
#define PSTATE_LISTVID_TO_VID(listvid,mult) ((listvid) * (mult))
#define PSTATE_VID_TO_LISTVID(vid,mult) ((vid) / (mult))
#define PSTATE_LISTVID_TO_VOLTS(listvid) (1550 - 25 * (listvid))
#define PSTATE_VID_TO_VOLTS(vid,mult) (1550 - 250 * (vid) / (mult) /10)
#define PSTATE_MK_PSTATE(msr,listvid,mult) \
(((msr) & 0xFFFFFFFFFFFF0000) | \
(((PSTATE_LISTVID_TO_VID(listvid,mult)) & 0x7F) << 9) | \
((pstate_did_list[id] & 0x07) << 6) | \
((pstate_fid_list[id] & 0x3F)))
static const int pstate_vid_list[33] = {
26, 26, 26, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 18, 18, 17, 16,
15, 14, 14, 13, 12, 11, 10, 10, 9, 8, 7, 6, 6, 5, 4, 3
};
static const int pstate_fid_list[33] = {
0, 0, 0, 0, 4, 8, 12, 0, 2, 4, 6, 8, 10, 12, 14, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17
};
static const int pstate_did_list[33] = {
2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const int pstate_did_to_div[] = {
1, 2, 4, 8, 16, 16, 16, 16
};
#define PSTATE_MAX_STATES 64
struct pstate_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. */
device_t dev; /* Driver providing this setting. */
};
struct pstate_softc {
device_t dev;
struct pstate_setting pstate_settings[PSTATE_MAX_STATES];
int cfnum;
int mof_id; /* Maximum Operating Frequency / 100 */
int mult; /* 2(in svi mode) 1(in pvi mode) */
uint64_t backup [5];
device_t F3;
};
static void pstate_identify(driver_t * driver, device_t parent);
static int pstate_probe(device_t dev);
static int pstate_attach(device_t dev);
static int pstate_detach(device_t dev);
static int pstate_set(device_t dev, const struct cf_setting *cf);
static int pstate_get(device_t dev, struct cf_setting *cf);
static int pstate_settings(device_t dev, struct cf_setting *sets, int *count);
static int pstate_type(device_t dev, int *type);
static int pstate_shutdown(device_t dev);
static int pstate_features(driver_t * driver, u_int * features);
static device_method_t pstate_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, pstate_identify),
DEVMETHOD(device_probe, pstate_probe),
DEVMETHOD(device_attach, pstate_attach),
DEVMETHOD(device_detach, pstate_detach),
DEVMETHOD(device_shutdown, pstate_shutdown),
/* cpufreq interface */
DEVMETHOD(cpufreq_drv_set, pstate_set),
DEVMETHOD(cpufreq_drv_get, pstate_get),
DEVMETHOD(cpufreq_drv_settings, pstate_settings),
DEVMETHOD(cpufreq_drv_type, pstate_type),
/* ACPI interface */
DEVMETHOD(acpi_get_features, pstate_features),
{0, 0}
};
static devclass_t pstate_devclass;
static driver_t pstate_driver = {
"pstate",
pstate_methods,
sizeof(struct pstate_softc),
};
DRIVER_MODULE(pstate, cpu, pstate_driver, pstate_devclass, 0, 0);
static int
pstate_cur_cpu_freq(void)
{
uint64_t msr;
int did , fid;
msr = rdmsr(MSR_PSTATE_COFVID);
did = MSR_PSTATE_CUR_DID(msr);
fid = MSR_PSTATE_CUR_FID(msr);
if (bootverbose)
printf("pstate: DID=%d,FID=%d\n", did, fid);
return (100 * (fid + 16) / pstate_did_to_div[did]);
}
static int
pstate_cur_cpu_volts(int mult)
{
uint64_t msr;
int vid;
msr = rdmsr(MSR_PSTATE_COFVID);
vid = MSR_PSTATE_CUR_VID(msr);
if (bootverbose)
printf("pstate: VID=%d\n", vid);
return (PSTATE_VID_TO_VOLTS(vid, mult));
}
static int
pstate_set(device_t dev, const struct cf_setting *cf)
{
struct pstate_softc *sc;
struct pstate_setting *ps;
uint64_t msr;
int i , id, setfreq, curfreq, curvolts;
if (cf == NULL)
return (EINVAL);
msr = rdmsr(MSR_PSTATE_CONFIG + 1);
if (!(msr & 0x8000000000000000)) {
if (bootverbose)
device_printf(dev, "P1 not supported by hardware.\n");
return (ENODEV);
}
sc = device_get_softc(dev);
ps = sc->pstate_settings;
for (i = 0; i < sc->cfnum; i++, ps++)
if (cf->freq == ps->freq) {
break;
}
setfreq = ps->freq;
if (i == sc->cfnum) {
if (bootverbose)
device_printf(dev, "%d MHz is not supported.\n",
cf->freq);
return (EINVAL);
}
/* go to P0 */
wrmsr(MSR_PSTATE_CONTROL, 0);
DELAY(3000);
if (setfreq / 100 == sc->mof_id) {
if (bootverbose)
device_printf(dev, "going back to default setting.\n");
for (i = 1; i < 5; i++)
wrmsr(MSR_PSTATE_CONFIG + i, sc->backup[i]);
return (0);
}
/* copy config val from P0 to P1 */
msr = rdmsr(MSR_PSTATE_CONFIG);
wrmsr(MSR_PSTATE_CONFIG + 1, msr);
/* make pstate */
id = sc->mof_id - i - 1;
msr = PSTATE_MK_PSTATE(msr, pstate_vid_list[id], sc->mult);
wrmsr(MSR_PSTATE_CONFIG + 1, msr);
if (bootverbose)
device_printf(dev, "going to %dMHz\n", setfreq);
/* go to P1 */
wrmsr(MSR_PSTATE_CONTROL, 1);
for (i = 0; i < 1000; i++) {
DELAY(3000);
curfreq = pstate_cur_cpu_freq();
curvolts = pstate_cur_cpu_volts(sc->mult);
if (setfreq == curfreq)
break;
}
if (setfreq != curfreq && bootverbose) {
device_printf(dev, "current %dMHz and set %dMHz differ.\n",
curfreq, setfreq);
return (0);
}
if (bootverbose)
device_printf(dev, "Now: %d MHz %d mV\n", curfreq, curvolts);
msr = rdmsr(MSR_PSTATE_STATUS);
if (msr != 1 && bootverbose)
device_printf(dev, "P1 is not enabled.\n");
return (0);
}
static int
pstate_get(device_t dev, struct cf_setting *cf)
{
struct pstate_softc *sc;
sc = device_get_softc(dev);
if (cf == NULL)
return (EINVAL);
cf->freq = pstate_cur_cpu_freq();
cf->volts = pstate_cur_cpu_volts(sc->mult);
cf->power = CPUFREQ_VAL_UNKNOWN;
cf->lat = 16;
cf->dev = dev;
return (0);
}
static int
pstate_settings(device_t dev, struct cf_setting *sets, int *count)
{
struct pstate_softc *sc;
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++) {
sets->freq = sc->pstate_settings[i].freq;
sets->volts = sc->pstate_settings[i].volts;
sets->power = sc->pstate_settings[i].power;
sets->lat = sc->pstate_settings[i].lat;
sets->dev = sc->pstate_settings[i].dev;
}
*count = sc->cfnum;
return (0);
}
static int
pstate_type(device_t dev, int *type)
{
if (type == NULL)
return (EINVAL);
*type = CPUFREQ_TYPE_ABSOLUTE;
return (0);
}
static int
pstate_is_capable(void)
{
u_int regs [4];
if (strcmp(cpu_vendor, "AuthenticAMD") != 0 ||
cpu_exthigh < 0x80000007)
return (FALSE);
switch (cpu_id) {
case 0x100f2A:
case 0x100f22:
case 0x100f23:
break;
default:
return (FALSE);
}
do_cpuid(0x80000007, regs);
if (regs[3] & 0x80) {
return (TRUE);
}
return (FALSE);
}
static void
pstate_identify(driver_t * driver, device_t parent)
{
device_t child;
if (device_find_child(parent, "pstate", -1) != NULL)
return;
if (pstate_is_capable() == FALSE)
return;
if ((child = BUS_ADD_CHILD(parent, 10, "pstate", -1)) == NULL)
device_printf(parent, "pstate: add child failed\n");
}
static int
pstate_probe(device_t dev)
{
device_t perf_dev;
int error , type;
if (resource_disabled("pstate", 0))
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);
}
device_set_desc(dev, "Cool`n'Quiet 2.0");
return (0);
}
static int
pstate_attach(device_t dev)
{
struct pstate_softc *sc;
uint64_t msr;
uint32_t cfg;
int i , j, listvid;
u_int regs [4], reg;
char cpu_model [48], *p = cpu_model;
sc = device_get_softc(dev);
for (i = 0; i < 5; i++)
sc->backup[i] = rdmsr(MSR_PSTATE_CONFIG + i);
msr = rdmsr(MSR_PSTATE_COFVID);
sc->mof_id = MSR_PSTATE_MOF(msr) / 100;
if (sc->mof_id == 0) {
for (i = 0; i < 3; i++) {
do_cpuid(0x80000002 + i, regs);
for (j = 0; j < 4; j++) {
reg = regs[j];
*p++ = (char)(reg & 0xff);
*p++ = (char)((reg >> 8) & 0xff);
*p++ = (char)((reg >> 16) & 0xff);
*p++ = (char)((reg >> 24) & 0xff);
}
}
if (strstr(cpu_model, "Phenom")) {
if (strstr(cpu_model, "9600")) {
sc->mof_id = 23; /* 2.3 GHz */
} else if (strstr(cpu_model, "9850")) {
sc->mof_id = 25; /* 2.5 GHz */
} else if (strstr(cpu_model, "9950")) {
sc->mof_id = 26; /* 2.6 GHz */
}
}
if (sc->mof_id == 0) {
return (ENODEV);
}
}
/* if 2500,..600,500,400 MHz => sc->mof_id=25; sc->cfnum=22; */
sc->cfnum = sc->mof_id - 3;
/**
* following 24 means the 1st cpu. 25-31 instead of 24 is MP system.
* I don't have MP system :-< .
*/
sc->F3 = pci_find_bsf(0, 24, 3);
cfg = pci_read_config(sc->F3, 0xA0, 4);
if (cfg & 0x10) /* PVI mode */
sc->mult = 1;
else /* SVI mode */
sc->mult = 2;
for (i = 0; i < sc->cfnum; i++) {
sc->pstate_settings[i].freq = 100 * (sc->mof_id - i);
listvid = pstate_vid_list[sc->mof_id - i];
sc->pstate_settings[i].volts = PSTATE_LISTVID_TO_VOLTS(listvid);
sc->pstate_settings[i].power = CPUFREQ_VAL_UNKNOWN;
sc->pstate_settings[i].lat = 16;
sc->pstate_settings[i].dev = dev;
}
cpufreq_register(dev);
return (0);
}
static int
pstate_detach(device_t dev)
{
struct pstate_softc *sc;
int new;
sc = device_get_softc(dev);
new = sc->mof_id * 100;
if (new != 0)
kernel_sysctlbyname(&thread0, "dev.cpu.0.freq",
0, 0, &new, sizeof(new), NULL, 0);
return (cpufreq_unregister(dev));
}
static int
pstate_shutdown(device_t dev)
{
struct pstate_softc *sc;
int new;
sc = device_get_softc(dev);
new = sc->mof_id * 100;
if (new != 0)
kernel_sysctlbyname(&thread0, "dev.cpu.0.freq",
0, 0, &new, sizeof(new), NULL, 0);
return (0);
}
static int
pstate_features(driver_t * driver, u_int * features)
{
*features = ACPI_CAP_PERF_MSRS;
return (0);
}
[-- Attachment #3 --]
help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200809070036.m870a3NC001532>
