Skip site navigation (1)Skip section navigation (2)
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>