From owner-FreeBSD-users-jp@jp.FreeBSD.org Fri Mar 20 20:34:37 2009
Received: (from daemon@localhost)
	by castle.jp.FreeBSD.org (8.11.6p2+3.4W/8.11.3) id n2KBYbQ46534;
	Fri, 20 Mar 2009 20:34:37 +0900 (JST)
	(envelope-from owner-FreeBSD-users-jp@jp.FreeBSD.org)
Received: from wa-out-1112.google.com (wa-out-1112.google.com [209.85.146.176])
	by castle.jp.FreeBSD.org (8.11.6p2+3.4W/8.11.3) with ESMTP/inet id n2KBYat46529
	for <FreeBSD-users-jp@jp.freebsd.org>; Fri, 20 Mar 2009 20:34:36 +0900 (JST)
	(envelope-from annona2@gmail.com)
Received: by wa-out-1112.google.com with SMTP id m16so563226waf.13
        for <FreeBSD-users-jp@jp.freebsd.org>; Fri, 20 Mar 2009 04:34:35 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=gmail.com; s=gamma;
        h=domainkey-signature:received:received:received:message-id:date:from
         :to:subject:in-reply-to:references:user-agent:mime-version
         :content-type;
        bh=8nD05J6yHhHcF7JyuLWhsYRb8lbg+BNhmbsoucfu+QI=;
        b=C3MJ0hAkZmSnL+McJYu8f83bZTiHt8gbgq5zdncDLTpPZPMX5+Wbef5qGSro8ZckPo
         rh9UB5QzvuxV2F+xZdCH4sNFhWClNkJ41YwdxUu7bqO30E2ATePj6WhP63zZ1ytEaYkS
         U/4jVpO0iHGLC49/OslOeRjKwf6BlOTkEDoxY=
DomainKey-Signature: a=rsa-sha1; c=nofws;
        d=gmail.com; s=gamma;
        h=message-id:date:from:to:subject:in-reply-to:references:user-agent
         :mime-version:content-type;
        b=EtrntBeSHzQC2kkTU1JnYPSE0wQDhGvwezPI3e9WNlAL5/Ba4kKVnpUTHN/6WVpIbo
         fTHLkCSgBZZJnwrJn6SUkxcSDOEv1hudnAUqcID55ZLqSnkcoQiP/zosnfTKs43DnqGj
         FyRqo4Jf2o/AwG9T0XYBrv8EVbYw/9jFoe7pU=
Received: by 10.114.94.12 with SMTP id r12mr2376072wab.229.1237548875286;
        Fri, 20 Mar 2009 04:34:35 -0700 (PDT)
Received: from softbank219001162114.bbtec.net (softbank219001162114.bbtec.net [219.1.162.114])
        by mx.google.com with ESMTPS id l30sm2187126waf.34.2009.03.20.04.34.28
        (version=TLSv1/SSLv3 cipher=RC4-MD5);
        Fri, 20 Mar 2009 04:34:35 -0700 (PDT)
Received: from softbank219001162114.bbtec.net (localhost [127.0.0.1])
	by softbank219001162114.bbtec.net (8.14.3/8.14.3) with ESMTP id n2KBY2Od002699
	for <FreeBSD-users-jp@jp.FreeBSD.org>; Fri, 20 Mar 2009 20:34:02 +0900 (JST)
	(envelope-from annona2@gmail.com)
Message-Id: <200903201134.n2KBY2Od002699@softbank219001162114.bbtec.net>
From: "G .Otsuji" <annona2@gmail.com>
To: FreeBSD-users-jp@jp.FreeBSD.org
In-Reply-To: <200903150204.n2F24B31001706@softbank219001162114.bbtec.net>
References: <200811022251.mA2MpLwh001847@localhost>
	<20090228170231.234ec102.nork@ninth-nine.com>
	<20090228173619.cbaf83a7.nork@ninth-nine.com>
	<200903010909.n21990Qb002967@localhost>
	<20090301184041.596a5acd.nork@ninth-nine.com>
	<20090301185737.296751b0.nork@ninth-nine.com>
	<200903021056.n22AuoPu001805@softbank219001162114.bbtec.net>
	<20090308150503.79193061.nork@ninth-nine.com>
	<20090308155809.75f2efeb.nork@ninth-nine.com>
	<200903110835.n2B8Zeg6015312@localhost>
	<20090313031949.e4a29b67.nork@ninth-nine.com>
	<200903131414.n2DEEdZW002029@softbank219001162114.bbtec.net>
	<200903141604.n2EG4bXd018847@softbank219001162114.bbtec.net>
	<200903150204.n2F24B31001706@softbank219001162114.bbtec.net>
User-Agent: Wanderlust/2.15.6 (Almost Unreal) SEMI/1.14.6 (Maruoka)
 FLIM/1.14.8 (=?ISO-8859-4?Q?Shij=F2?=) APEL/10.7 Emacs/22.3
 (i386-portbld-freebsd7.1) MULE/5.0 (SAKAKI)
MIME-Version: 1.0 (generated by SEMI 1.14.6 - "Maruoka")
Content-Type: multipart/mixed;
 boundary="Multipart_Fri_Mar_20_20:34:02_2009-1"
Reply-To: FreeBSD-users-jp@jp.FreeBSD.org
Precedence: list
Date: Fri, 20 Mar 2009 20:34:02 +0900
X-Sequence: FreeBSD-users-jp 92196
Subject: [FreeBSD-users-jp 92196] Re: Phenom
 =?ISO-2022-JP?B?GyRCJEokSSROGyhC?= cpufreq
Sender: owner-FreeBSD-users-jp@jp.FreeBSD.org
X-Originator: annona2@gmail.com
X-Distribute: distribute version 2.1 (Alpha) patchlevel 24e+060209

--Multipart_Fri_Mar_20_20:34:02_2009-1
Content-Type: text/plain; charset=ISO-2022-JP

$BHxDT$G$9!#(B

cnq.c$B$G$9!#(B
(1) i386 8-CURRENT$B$G$?$a$7$^$7$?$i!"(B
   -#include <machine/specialreg.h>
   +#include <amd64/include/specialreg.h>$B$,I,MW$J$h$&$G$9!#(B
(2) acpi_perf$B$H(Bmsr$B$+$i$N>pJs$rF@$k$N$,$4$A$c$^$<$@$C$?$N$G(B
   $B$o$1$^$7$?!#$9$_$^$;$s!#(B
(3) cnq_goto_pstate$B$,(Berror$B$rJV$9$h$&$K$7$F$_$^$7$?!#(B
(4) amdpm_info$B$H(BAMDPM_HW_PSTATE$B$r8+$D$1$^$7$?!#(B
   $B$=$N$?$a(Bcnq_is_capable$B$,$[$H$s$I2?$b$J$/$J$C$?$N$G(Bidentify$B$K=q$/$h$&$K$7$^$7$?!#(B
(5) if(!AMD_10H_11H_PSTATE_EN(msr))$B$,4V0c$$$G$7$?!#(B
(6) kernel$B$K$/$_$3$s$G$?$a$7$F$_$^$7$?!#F0$-$^$7$?!#(B

$B$b$&$"$i$+$?JQ$($k$3$H$O$J$$$H;W$$$?$$$G$9!#(B

$B0J>e$G$9!#$h$m$7$/$*4j$$CW$7$^$9!#(B

$BHxDT(B<annona2@gmail.com>


--Multipart_Fri_Mar_20_20:34:02_2009-1
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="cnq.c"
Content-Transfer-Encoding: 7bit

/*-
 * Copyright (c) 2008-2009 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:
 * BIOS and Kernel Developer's Guide(BKDG) for AMD Family 10h Processors
 * 31116 Rev 3.20  February 04, 2009
 * BIOS and Kernel Developer's Guide(BKDG) for AMD Family 11h Processors
 * 41256 Rev 3.00 - July 07, 2008
 */

#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/malloc.h>
#include <sys/proc.h>
#include <sys/pcpu.h>
#include <sys/smp.h>
#include <sys/sched.h>

#include <machine/md_var.h>
#include <machine/cputypes.h>
/* We use AMDPM_HW_PSTATE and AMD64_CPU_FAMILY. */
#include <amd64/include/specialreg.h>

#include <contrib/dev/acpica/acpi.h>
#include <dev/acpica/acpivar.h>

#include "acpi_if.h"
#include "cpufreq_if.h"

#define	MSR_AMD_10H_11H_LIMIT	0xc0010061
#define	MSR_AMD_10H_11H_CONTROL	0xc0010062
#define	MSR_AMD_10H_11H_STATUS	0xc0010063
#define	MSR_AMD_10H_11H_CONFIG	0xc0010064

#define	AMD_10H_11H_MAX_STATES	16

/* for MSR_AMD_10H_11H_LIMIT C001_0061 */
#define	AMD_10H_11H_GET_PSTATE_MAX_VAL(msr)	(((msr) >> 4) & 0x7)
#define	AMD_10H_11H_GET_PSTATE_LIMIT(msr)	(((msr)) & 0x7)
/* for MSR_AMD_10H_11H_CONFIG 10h:C001_0064:68 / 11h:C001_0064:6B */
#define	AMD_10H_11H_CUR_VID(msr)		(((msr) >> 9) & 0x7F)
#define	AMD_10H_11H_CUR_DID(msr)		(((msr) >> 6) & 0x07)
#define	AMD_10H_11H_CUR_FID(msr)		((msr) & 0x3F)

#define	CNQ_DEBUG(msg...)			\
	do{					\
		if(cnq_verbose)			\
			device_printf(msg);	\
	}while(0)

struct cnq_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;	/* P-State id */
};

struct cnq_softc {
	device_t		dev;
	struct cnq_setting	cnq_settings[AMD_10H_11H_MAX_STATES];
	int			cfnum;
};

static void	cnq_identify(driver_t *driver, device_t parent);
static int	cnq_probe(device_t dev);
static int	cnq_attach(device_t dev);
static int	cnq_detach(device_t dev);
static int	cnq_set(device_t dev, const struct cf_setting *cf);
static int	cnq_get(device_t dev, struct cf_setting *cf);
static int	cnq_settings(device_t dev, struct cf_setting *sets, int *count);
static int	cnq_type(device_t dev, int *type);
static int	cnq_shutdown(device_t dev);
static int	cnq_features(driver_t *driver, u_int *features);
static int	cnq_get_info_from_acpi_perf(device_t dev, device_t perf_dev);
static int	cnq_get_info_from_msr(device_t dev);
static int	cnq_goto_pstate(device_t dev, int pstate_id);

static int	cnq_verbose = 0;
SYSCTL_INT(_debug, OID_AUTO, cnq_verbose, CTLFLAG_RW, /* CTLFLAG_RDTUN */
       &cnq_verbose, 0, "Debug cnq");

static device_method_t cnq_methods[] = {
	/* Device interface */
	DEVMETHOD(device_identify,	cnq_identify),
	DEVMETHOD(device_probe,		cnq_probe),
	DEVMETHOD(device_attach,	cnq_attach),
	DEVMETHOD(device_detach,	cnq_detach),
	DEVMETHOD(device_shutdown,	cnq_shutdown),

	/* cpufreq interface */
	DEVMETHOD(cpufreq_drv_set,	cnq_set),
	DEVMETHOD(cpufreq_drv_get,	cnq_get),
	DEVMETHOD(cpufreq_drv_settings,	cnq_settings),
	DEVMETHOD(cpufreq_drv_type,	cnq_type),

	/* ACPI interface */
	DEVMETHOD(acpi_get_features,	cnq_features),

	{0, 0}
};

static devclass_t cnq_devclass;
static driver_t cnq_driver = {
	"cnq",
	cnq_methods,
	sizeof(struct cnq_softc),
};

DRIVER_MODULE(cnq, cpu, cnq_driver, cnq_devclass, 0, 0);

/*
 * Go to Px-state on all cpus considering the limit.
 */
static int
cnq_goto_pstate(device_t dev, int pstate)
{
	struct cnq_softc *sc;
	struct pcpu *pc;
	uint64_t msr;
	int i, j;
	int limit;
	int id = pstate;
	int error;
	
	sc = device_get_softc(dev);
	/* get the current pstate limit */
	msr = rdmsr(MSR_AMD_10H_11H_LIMIT);
	limit = AMD_10H_11H_GET_PSTATE_LIMIT(msr);
	if(limit > id)
		id = limit;

	error = 0;
	/*
	 * We are going to the same Px-state on all cpus.
	 */
	for (i = 0; i < mp_ncpus; i++) {
		/* Find each cpu. */
		pc = pcpu_find(i);
		if (pc == NULL)
			return (ENXIO);
		thread_lock(curthread);
		/* Bind to each cpu. */
		sched_bind(curthread, pc->pc_cpuid);
		thread_unlock(curthread);
		CNQ_DEBUG(dev, "setting P%d-state on cpu%d\n",
			id, PCPU_GET(cpuid));
		/* Go To Px-state */
		wrmsr(MSR_AMD_10H_11H_CONTROL, id);
		/* wait loop (100*100 usec is enough ?) */
		for(j = 0; j < 100; j++){
			msr = rdmsr(MSR_AMD_10H_11H_STATUS);
			if(msr == id){
				break;
			}
			DELAY(100);
		}
		/* get the result. not assure msr=id */
		msr = rdmsr(MSR_AMD_10H_11H_STATUS);
		CNQ_DEBUG(dev, "result  P%d-state on cpu%d\n",
		    (int)msr, PCPU_GET(cpuid));
		if (msr != id) {
			CNQ_DEBUG(dev, "error: loop is not enough.\n");
			error = ENXIO;
		}
		thread_lock(curthread);
		sched_unbind(curthread);
		thread_unlock(curthread);
	}
	return (error);
}

static int
cnq_set(device_t dev, const struct cf_setting *cf)
{
	struct cnq_softc *sc;
	struct cnq_setting *set;
	int i;

	if (cf == NULL)
		return (EINVAL);
	sc = device_get_softc(dev);
	set = sc->cnq_settings;
	for (i = 0; i < sc->cfnum; i++)
		if (CPUFREQ_CMP(cf->freq, set[i].freq))
			break;
	if (i == sc->cfnum)
		return (EINVAL);

	return (cnq_goto_pstate(dev, set[i].pstate_id));
}

static int
cnq_get(device_t dev, struct cf_setting *cf)
{
	struct cnq_softc *sc;
	struct cnq_setting set;
	uint64_t msr;

	sc = device_get_softc(dev);
	if (cf == NULL)
		return (EINVAL);
	msr = rdmsr(MSR_AMD_10H_11H_STATUS);
	if(msr >= sc->cfnum)
		return (EINVAL);
	set = sc->cnq_settings[msr];

	cf->freq = set.freq;
	cf->volts = set.volts;
	cf->power = set.power;
	cf->lat = set.lat;
	cf->dev = dev;
	return (0);
}

static int
cnq_settings(device_t dev, struct cf_setting *sets, int *count)
{
	struct cnq_softc *sc;
	struct cnq_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->cnq_settings[i];
		sets->freq = set.freq;
		sets->volts = set.volts;
		sets->power = set.power;
		sets->lat = set.lat;
		sets->dev = dev;
	}
	*count = sc->cfnum;

	return (0);
}

static int
cnq_type(device_t dev, int *type)
{

	if (type == NULL)
		return (EINVAL);

	*type = CPUFREQ_TYPE_ABSOLUTE;
	return (0);
}

static void
cnq_identify(driver_t *driver, device_t parent)
{
	device_t child;

	if (device_find_child(parent, "cnq", -1) != NULL)
		return;

	if (cpu_vendor_id != CPU_VENDOR_AMD || AMD64_CPU_FAMILY(cpu_id) < 0x10)
		return;

	/*
	 * Check if hardware pstate enable bit is set.
	 */
	if ((amd_pminfo & AMDPM_HW_PSTATE) == 0)
		return;

	if (resource_disabled("cnq", 0))
		return;

	if ((child = BUS_ADD_CHILD(parent, 10, "cnq", -1)) == NULL)
		device_printf(parent, "cnq: add child failed\n");
}

static int
cnq_probe(device_t dev)
{
	struct cnq_softc *sc;
	device_t perf_dev;
	uint64_t msr;
	int error, type;

	/*
	 * Only cnq0.
	 * It goes well with acpi_throttle.
	 */
	if (device_get_unit(dev) != 0)
		return (ENXIO);

	sc = device_get_softc(dev);
	sc->dev = dev;

	/*
	 * Check if acpi_perf has INFO only flag.
	 */
	perf_dev = device_find_child(device_get_parent(dev), "acpi_perf", -1);
	error = TRUE;
	if (perf_dev && device_is_attached(perf_dev)) {
		error = CPUFREQ_DRV_TYPE(perf_dev, &type);
		if (error == 0) {
			if ((type & CPUFREQ_FLAG_INFO_ONLY) == 0) {
				/*
				 * If acpi_perf doesn't have INFO_ONLY flag,
				 * it will take care of pstate transitions.
				 */
				return (ENXIO);
			} else {
				/*
				 * If acpi_perf has INFO_ONLY flag,
				 * we can get _PSS info from acpi_perf
				 * without going into ACPI.
				 */
				error = cnq_get_info_from_acpi_perf(dev, perf_dev);
			}
		}
	}

	if (error == 0) {
		/*
		 * Now we get _PSS info from acpi_perf without error.
		 * Let's check it.
		 */
		msr = rdmsr(MSR_AMD_10H_11H_LIMIT);
		if (sc->cfnum != 1 + AMD_10H_11H_GET_PSTATE_MAX_VAL(msr))
			error = TRUE;
	}

	/*
	 * If we cannot get info from acpi_perf,
	 * Let's get info from MSRs.
	 */
	if (error)
		error = cnq_get_info_from_msr(dev);
	if (error)
		return (error);

	device_set_desc(dev, "Cool`n'Quiet 2.0");
	return (0);
}

static int
cnq_attach(device_t dev)
{

	return (cpufreq_register(dev));
}

static int
cnq_get_info_from_msr(device_t dev)
{
	struct cnq_softc *sc;
	struct cnq_setting *cnq_set;
	uint64_t msr;
	int family, i, fid, did;

	family = AMD64_CPU_FAMILY(cpu_id);
	sc = device_get_softc(dev);
	/* Get pstate count */
	msr = rdmsr(MSR_AMD_10H_11H_LIMIT);
	sc->cfnum = 1 + AMD_10H_11H_GET_PSTATE_MAX_VAL(msr);
	if (sc->cfnum == 1)
		return (ENXIO);
	cnq_set = sc->cnq_settings;
	for (i = 0; i < sc->cfnum; i++) {
		msr = rdmsr(MSR_AMD_10H_11H_CONFIG + i);
		if ((msr & ((uint64_t)1 << 63)) != ((uint64_t)1 << 63)) {
			CNQ_DEBUG(dev, "msr mismatch.\n");
			return (ENXIO);
		}
		did = AMD_10H_11H_CUR_DID(msr);
		fid = AMD_10H_11H_CUR_FID(msr);
		switch(family) {
		case 0x11:
			/* fid/did to frequency */
			cnq_set[i].freq = 100 * (fid + 0x08) / (1 << did);
			break;
		case 0x10:
			/* fid/did to frequency */
			cnq_set[i].freq = 100 * (fid + 0x10) / (1 << did);
			break;
		default:
			return (ENXIO);
			break;
		}
		cnq_set[i].pstate_id = i;
		cnq_set[i].volts = CPUFREQ_VAL_UNKNOWN;
		cnq_set[i].power = CPUFREQ_VAL_UNKNOWN;
		cnq_set[i].lat = 16;
	}
	return (0);
}

static int
cnq_get_info_from_acpi_perf(device_t dev, device_t perf_dev)
{
	struct cnq_softc *sc;
	struct cf_setting *perf_set;
	struct cnq_setting *cnq_set;
	int count, error, i;

	perf_set = malloc(MAX_SETTINGS * sizeof(*perf_set), M_TEMP, M_NOWAIT);
	if (perf_set == NULL) {
		CNQ_DEBUG(dev, "nomem\n");
		return (ENOMEM);
	}
	/*
	 * Fetch settings from acpi_perf.
	 * Now it is attached, and has info only flag.
	 */
	count = MAX_SETTINGS;
	error = CPUFREQ_DRV_SETTINGS(perf_dev, perf_set, &count);
	if (error)
		goto out;
	sc = device_get_softc(dev);
	sc->cfnum = count;
	cnq_set = sc->cnq_settings;
	for (i = 0; i < count; i++) {
		if (i == perf_set[i].spec[0]) {
			cnq_set[i].pstate_id = i;
			cnq_set[i].freq = perf_set[i].freq;
			cnq_set[i].volts = perf_set[i].volts;
			cnq_set[i].power = perf_set[i].power;
			cnq_set[i].lat = perf_set[i].lat;
		} else {
			CNQ_DEBUG(dev, "ACPI _PSS object mismatch.\n");
			error = ENXIO;
			goto out;
		}
	}
out:
	if (perf_set)
		free(perf_set, M_TEMP);
	return (error);
}

static int
cnq_detach(device_t dev)
{

	cnq_goto_pstate(dev, 0);
	return (cpufreq_unregister(dev));
}

static int
cnq_shutdown(device_t dev)
{

	return (0);
}

static int
cnq_features(driver_t *driver, u_int *features)
{

	/* Notify the ACPI CPU that we support direct access to MSRs */
	*features = ACPI_CAP_PERF_MSRS;
	return (0);
}

--Multipart_Fri_Mar_20_20:34:02_2009-1--
