Commits

Chunlin Yao committed f56219b

create crusoe longrun kernel module

  • Participants

Comments (0)

Files changed (7)

+# New ports collection makefile for:	longrun
+# Date modified:	1 Jan 2011
+# Whom:			Yao Chunlin <chunlinyao@gmail.com>
+# Date created:		30 Apr 2001
+# Whom:			Tamotsu HATTORI <athlete@kta.att.ne.jp>
+#
+# $FreeBSD$
+#
+
+PORTNAME=	longrun
+PORTVERSION=	2001.6.22
+CATEGORIES=	sysutils
+MASTER_SITES=	# none
+DISTFILES=	# none
+
+MAINTAINER=	chunlinyao@gmail.com
+
+WRKSRC=		${WRKDIR}/files
+
+KMODDIR=	${PREFIX}/modules
+
+STARTUP=	longrun.sh
+
+MAKE_ARGS=	KMODDIR="${KMODDIR}"
+
+SRC_BASE?=	/usr/src
+
+.include <bsd.port.pre.mk>
+
+.if ${OSVERSION} < 400000
+BROKEN=		"Systems prior to 400000 are out of support"
+.endif
+
+.if !exists(${SRC_BASE}/sys/Makefile)
+BROKEN=		"Kernel source files required"
+.endif
+
+do-fetch:
+	@${ECHO} "I don't fetch anything..."
+
+do-extract:
+	@${MKDIR} ${WRKSRC}
+	@${SED}	-e 's,@@PREFIX@@,${PREFIX},' \
+		${FILESDIR}/${STARTUP} > ${WRKDIR}/${STARTUP}
+	@cd ${FILESDIR} && ${CP} Makefile *.[ch] ${WRKSRC}/
+
+pre-install:
+	${MKDIR} ${KMODDIR}
+
+post-install:
+	@${ECHO} "Installing ${PREFIX}/etc/rc.d/${STARTUP} startup file.";
+	${INSTALL_SCRIPT} ${WRKDIR}/${STARTUP} ${PREFIX}/etc/rc.d/
+
+.include <bsd.port.post.mk>

File files/Makefile

+# Copyright (c) 2011 Yao Chunlin <Chunlinyao@gmail.com>.
+# Copyright (c) 2001 Tamotsu Hattori.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification,	are permitted provided 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.
+# 3. The name of the author may not be used to endorse or promote products
+#    derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE	IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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	OR CONTRIBUTORS	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.
+#
+
+KMOD=		longrun
+SRCS=		longrun.c
+
+.include <bsd.kmod.mk>

File files/longrun.c

+/*-
+ * Copyright (c) 2011 Yao Chunlin <Chunlinyao@gmail.com>
+ * Copyright (c) 2001 Tamotsu Hattori.
+ * Copyright (c) 2001 Mitsuru IWASAKI.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/i386/i386/longrun.c,v 1.4.2.1.4.1 2010/06/14 02:09:06 kensmith Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/power.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+/*
+ * Transmeta Crusoe LongRun Support by Tamotsu Hattori.
+ */
+
+#define MSR_TMx86_LONGRUN		0x80868010
+#define MSR_TMx86_LONGRUN_FLAGS		0x80868011
+
+#define LONGRUN_MODE_MASK(x)		((x) & 0x000000007f)
+#define LONGRUN_MODE_RESERVED(x)	((x) & 0xffffff80)
+#define LONGRUN_MODE_WRITE(x, y)	(LONGRUN_MODE_RESERVED(x) | LONGRUN_MODE_MASK(y))
+
+#define LONGRUN_MODE_MINFREQUENCY	0x00
+#define LONGRUN_MODE_ECONOMY		0x01
+#define LONGRUN_MODE_PERFORMANCE	0x02
+#define LONGRUN_MODE_MAXFREQUENCY	0x03
+#define LONGRUN_MODE_UNKNOWN		0x04
+#define LONGRUN_MODE_MAX		0x04
+
+union msrinfo {
+	u_int64_t	msr;
+	u_int32_t	regs[2];
+};
+
+static u_int32_t longrun_modes[LONGRUN_MODE_MAX][3] = {
+	/*  MSR low, MSR high, flags bit0 */
+	{	  0,	  0,		0},	/* LONGRUN_MODE_MINFREQUENCY */
+	{	  0,	100,		0},	/* LONGRUN_MODE_ECONOMY */
+	{	  0,	100,		1},	/* LONGRUN_MODE_PERFORMANCE */
+	{	100,	100,		1},	/* LONGRUN_MODE_MAXFREQUENCY */
+};
+
+static u_int
+tmx86_get_longrun_mode(void)
+{
+	u_long		eflags;
+	union msrinfo	msrinfo;
+	u_int		low, high, flags, mode;
+
+	eflags = read_eflags();
+	disable_intr();
+
+	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
+	low = LONGRUN_MODE_MASK(msrinfo.regs[0]);
+	high = LONGRUN_MODE_MASK(msrinfo.regs[1]);
+	flags = rdmsr(MSR_TMx86_LONGRUN_FLAGS) & 0x01;
+
+	for (mode = 0; mode < LONGRUN_MODE_MAX; mode++) {
+		if (low   == longrun_modes[mode][0] &&
+		    high  == longrun_modes[mode][1] &&
+		    flags == longrun_modes[mode][2]) {
+			goto out;
+		}
+	}
+	mode = LONGRUN_MODE_UNKNOWN;
+out:
+	write_eflags(eflags);
+	return (mode);
+}
+
+static u_int
+tmx86_get_longrun_status(u_int * frequency, u_int * voltage, u_int * percentage)
+{
+	u_long		eflags;
+	u_int		regs[4];
+
+	eflags = read_eflags();
+	disable_intr();
+
+	do_cpuid(0x80860007, regs);
+	*frequency = regs[0];
+	*voltage = regs[1];
+	*percentage = regs[2];
+
+	write_eflags(eflags);
+	return (1);
+}
+
+static u_int
+tmx86_set_longrun_mode(u_int mode)
+{
+	u_long		eflags;
+	union msrinfo	msrinfo;
+
+	if (mode >= LONGRUN_MODE_UNKNOWN) {
+		return (0);
+	}
+
+	eflags = read_eflags();
+	disable_intr();
+
+	/* Write LongRun mode values to Model Specific Register. */
+	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN);
+	msrinfo.regs[0] = LONGRUN_MODE_WRITE(msrinfo.regs[0],
+					     longrun_modes[mode][0]);
+	msrinfo.regs[1] = LONGRUN_MODE_WRITE(msrinfo.regs[1],
+					     longrun_modes[mode][1]);
+	wrmsr(MSR_TMx86_LONGRUN, msrinfo.msr);
+
+	/* Write LongRun mode flags to Model Specific Register. */
+	msrinfo.msr = rdmsr(MSR_TMx86_LONGRUN_FLAGS);
+	msrinfo.regs[0] = (msrinfo.regs[0] & ~0x01) | longrun_modes[mode][2];
+	wrmsr(MSR_TMx86_LONGRUN_FLAGS, msrinfo.msr);
+
+	write_eflags(eflags);
+	return (1);
+}
+
+static u_int			 crusoe_longrun;
+static u_int			 crusoe_frequency;
+static u_int	 		 crusoe_voltage;
+static u_int	 		 crusoe_percentage;
+static u_int	 		 crusoe_performance_longrun = LONGRUN_MODE_PERFORMANCE;
+static u_int	 		 crusoe_economy_longrun = LONGRUN_MODE_ECONOMY;
+static struct sysctl_ctx_list	 crusoe_sysctl_ctx;
+static struct sysctl_oid	*crusoe_sysctl_tree;
+static eventhandler_tag 	 power_profile_change_tag;
+
+static void
+tmx86_longrun_power_profile(void *arg)
+{
+	int	state;
+	u_int	new;
+
+	state = power_profile_get_state();
+	if (state != POWER_PROFILE_PERFORMANCE &&
+	    state != POWER_PROFILE_ECONOMY) {
+		return;
+	}
+
+	switch (state) {
+	case POWER_PROFILE_PERFORMANCE:
+		new =crusoe_performance_longrun;
+		break;
+	case POWER_PROFILE_ECONOMY:
+		new = crusoe_economy_longrun;
+		break;
+	default:
+		new = tmx86_get_longrun_mode();
+		break;
+	}
+
+	if (tmx86_get_longrun_mode() != new) {
+		tmx86_set_longrun_mode(new);
+	}
+}
+
+static int
+tmx86_longrun_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	u_int	mode;
+	int	error;
+
+	crusoe_longrun = tmx86_get_longrun_mode();
+	mode = crusoe_longrun;
+	error = sysctl_handle_int(oidp, &mode, 0, req);
+	if (error || !req->newptr) {
+		return (error);
+	}
+	if (mode >= LONGRUN_MODE_UNKNOWN) {
+		error = EINVAL;
+		return (error);
+	}
+	if (crusoe_longrun != mode) {
+		crusoe_longrun = mode;
+		tmx86_set_longrun_mode(crusoe_longrun);
+	}
+
+	return (error);
+}
+
+static int
+tmx86_status_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	u_int	val;
+	int	error;
+
+	tmx86_get_longrun_status(&crusoe_frequency,
+				 &crusoe_voltage, &crusoe_percentage);
+	val = *(u_int *)oidp->oid_arg1;
+	error = sysctl_handle_int(oidp, &val, 0, req);
+	return (error);
+}
+
+static int
+tmx86_longrun_profile_sysctl(SYSCTL_HANDLER_ARGS)
+{
+	u_int32_t *argp;
+	u_int32_t arg;
+	int	error;
+
+	argp = (u_int32_t *)oidp->oid_arg1;
+	arg = *argp;
+	error = sysctl_handle_int(oidp, &arg, 0, req);
+
+	/* error or no new value */
+	if ((error != 0) || (req->newptr == NULL))
+		return (error);
+
+	/* range check */
+	if (arg >= LONGRUN_MODE_UNKNOWN)
+		return (EINVAL);
+
+	/* set new value and possibly switch */
+	*argp = arg;
+
+	tmx86_longrun_power_profile(NULL);
+
+	return (0);
+
+}
+
+static void
+setup_tmx86_longrun(void *dummy __unused)
+{
+
+	if (cpu_vendor_id != CPU_VENDOR_TRANSMETA)
+		return;
+
+	crusoe_longrun = tmx86_get_longrun_mode();
+	tmx86_get_longrun_status(&crusoe_frequency,
+				 &crusoe_voltage, &crusoe_percentage);
+	printf("Crusoe LongRun support enabled, current mode: %d "
+	       "<%dMHz %dmV %d%%>\n", crusoe_longrun, crusoe_frequency,
+	       crusoe_voltage, crusoe_percentage);
+
+	sysctl_ctx_init(&crusoe_sysctl_ctx);
+	crusoe_sysctl_tree = SYSCTL_ADD_NODE(&crusoe_sysctl_ctx,
+				SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
+				"crusoe", CTLFLAG_RD, 0,
+				"Transmeta Crusoe LongRun support");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "longrun", CTLTYPE_INT | CTLFLAG_RW,
+		&crusoe_longrun, 0, tmx86_longrun_sysctl, "I",
+		"LongRun mode [0-3]");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "frequency", CTLTYPE_INT | CTLFLAG_RD,
+		&crusoe_frequency, 0, tmx86_status_sysctl, "I",
+		"Current frequency (MHz)");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "voltage", CTLTYPE_INT | CTLFLAG_RD,
+		&crusoe_voltage, 0, tmx86_status_sysctl, "I",
+		"Current voltage (mV)");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "percentage", CTLTYPE_INT | CTLFLAG_RD,
+		&crusoe_percentage, 0, tmx86_status_sysctl, "I",
+		"Processing performance (%)");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "performance_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
+		&crusoe_performance_longrun, 0, tmx86_longrun_profile_sysctl, "I", "");
+	SYSCTL_ADD_PROC(&crusoe_sysctl_ctx, SYSCTL_CHILDREN(crusoe_sysctl_tree),
+		OID_AUTO, "economy_longrun", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW,
+		&crusoe_economy_longrun, 0, tmx86_longrun_profile_sysctl, "I", "");
+
+	/* register performance profile change handler */
+	power_profile_change_tag = EVENTHANDLER_REGISTER(power_profile_change, tmx86_longrun_power_profile, NULL, 0);
+}
+/* SYSINIT(setup_tmx86_longrun, SI_SUB_CPU, SI_ORDER_ANY, setup_tmx86_longrun,
+    NULL); */
+
+static void
+unload_tmx86_longrun(void *dummy __unused)
+{
+
+	if (cpu_vendor_id != CPU_VENDOR_TRANSMETA)
+		return;
+
+	if (sysctl_ctx_free(&crusoe_sysctl_ctx) != 0) {
+		printf("sysctl unload failed!\n");
+		sysctl_remove_oid(crusoe_sysctl_tree, 1, 1);
+		printf("sysctl_remove_oid(%p) succeeded\n", crusoe_sysctl_tree);
+	} else 
+		printf("sysctl unload ok\n");
+	
+	EVENTHANDLER_DEREGISTER(power_profile_change, power_profile_change_tag);
+}
+static int 
+longrun_modevent(module_t mod, int type, void *data)
+{
+	switch (type) {
+	case MOD_LOAD:
+		printf("LongRun: module load\n");
+		if(cpu_vendor_id != CPU_VENDOR_TRANSMETA)
+		  return ENXIO;
+		setup_tmx86_longrun(NULL);
+		return 0;
+	case MOD_UNLOAD:
+	case MOD_SHUTDOWN:
+		printf("LongRun: module unload\n");
+		unload_tmx86_longrun(NULL);
+		return 0;
+	default:
+		return EOPNOTSUPP;
+	}
+}
+
+
+DEV_MODULE(longrun, longrun_modevent, 0);

File files/longrun.sh

+#!/bin/sh
+
+kmoddir=@@PREFIX@@/modules
+kmod=longrun.ko
+
+#0=min,1=economy,2=performance,3=max
+defaultlevel=2
+
+case "$1" in
+start)
+	if [ -x $kmoddir/$kmod ]; then
+	    echo -n ' longrun'
+	    kldload $kmoddir/$kmod
+	    sysctl hw.crusoe.longrun=$defaultlevel
+	fi
+	;;
+stop)
+	kldunload $kmod && echo -n ' longrun'
+	;;
+*)
+	echo "Usage: `basename $0` {start|stop}" >&2
+	exit 64
+	;;
+esac
+Kernel module which supports Transmeta Crusoe's LongRun
+This is a kernel module for support longrun.
+Longrun is a power management feature of Transmeta Crusoe CPU.
+
+The code was originally written by Tamotsu Hattori.
+etc/rc.d/longrun.sh
+modules/longrun.ko
+@unexec rmdir %D/modules 2>/dev/null || true