All of lore.kernel.org
 help / color / mirror / Atom feed
* [new] event mode on speedstep-smi.
@ 2003-09-04 14:52 Hiroshi Miura
  2003-09-04 15:30 ` Ducrot Bruno
  2003-09-04 23:25 ` Dominik Brodowski
  0 siblings, 2 replies; 14+ messages in thread
From: Hiroshi Miura @ 2003-09-04 14:52 UTC (permalink / raw)
  To: Ducrot Bruno, cpufreq


[-- Attachment #1.1: Type: text/plain, Size: 1446 bytes --]

Hi,

from some BIOS document
	http://support.twinhead.com/CSD/CDROM/driver/p98TF/Bios/P98TF07.doc 
   google cache
	http://www.google.co.jp/search?q=cache:_RdlAmnkI4wJ:support.twinhead.com/CSD/CDROM/driver/p98TF/Bios/P98TF07.doc

	"The modem wouldn't connect most of the time and if it did connect , 
	whenever a power status event happened with the speedstep applet, 
	it would cause the modem to disconnect with the IST applet loaded."

I find from this that IST SMI has some 'power status event' call.

	"Intel says that we need to implement an Event Port option in the 
	BIOS to cut down on the amount of SMI events. Resolution
	1.Upgrade the Registry, The IST polling mode will change from SMI mode 
	to Event Port mode."

This said we can use "Event Port mode" other than "SMI power status event".

we are already has data about Event Port mode,  that is 

from dmesgs,
Panasonic Let's Note.
  IST_SMI: signature:0x47534943 command:0x008200b2 event:0x000f104a perf_level:0x07d00000
SONY VAIO PCG-Z505VR/K
  IST_SMI: signature:0x47534943 command:0x008000b2 event:0x000000b3 perf_level:0x07d00100
IBM ThinkPad T21 TYPE 2647-G2J
  IST_SMI: signature:0x47534943 command:0x008200b2 event:0x00c000b3 perf_level:0x07d00000

I found that  "event"  low 16bit is io-port (in).

perf_level:  high 16bit is 0x07d0. this is 2000 in decimal.
I think this means polling interval is 2000ms.

This code is based on my original one.
Ducrot, How about this?


[-- Attachment #1.2: speedstep-smi.c --]
[-- Type: application/octet-stream, Size: 13736 bytes --]

/*
 *  Intel SpeedStep CPUFreq driver using SMI interface.
 *
 *  Copyright (c) 2003  Hiroshi Miura <miura@da-cha.org>
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 *  Intel SpeedStep Technology is a registered trademark of Intel Corporation.
 */

/*
 *  ChangeLog
 *
 *      1 Sep 2003     add "auto" governor
 *                     rewrite speedstep_get_state()
 *     	               add get_event method
 *                     add kernel thread routine.
 *
 */


/*********************************************************************
 *                        SPEEDSTEP - DEFINITIONS                    *
 *********************************************************************/
#include <linux/kernel.h>
#include <linux/module.h> 
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/pm.h>
#include <linux/ist.h>

/* speedstep system management interface port/command.
 *
 * These parameters are got from IST-SMI BIOS call.
 * If user gives it, these are used.
 * Please see Win-XP's registory values.
 *
 */
static int		smi_port	= 0;   /* SMI call io port number*/
static int		smi_cmd		= 0;   /* SMI call command value */
static int		smi_count	= 0;   /* SMI call retry count   */
static int              auto_mode       = 0;

/* for auto_mode */
static int		speedstep_auto   = 0;
static int		smi_ev_port;
static int		smi_ev_mask;
static int		smi_ev_poll;
static int		kistd_running	= 0;
static int		exit_kistd 	= 0;

static DECLARE_WAIT_QUEUE_HEAD(ist_waitqueue);

#define SPEEDSTEP_HIGH	0x00000000
#define SPEEDSTEP_LOW   0x00000001

/* 
 *   There are only two frequency states for each processor. Values
 * are in kHz for the time being.
 */
static struct cpufreq_frequency_table speedstep_freqs[] = {
	{SPEEDSTEP_HIGH, 	0},
	{SPEEDSTEP_LOW,		0},
	{0,			CPUFREQ_TABLE_END},
};

static struct pm_dev *smi_pm_dev;

/* SMI CMD definitions */
#define GET_SPEEDSTEP_OWNER 0
#define GET_SPEEDSTEP_STATE 1
#define SET_SPEEDSTEP_STATE 2
#define GET_SPEEDSTEP_FREQS 4

/* speedstep_get_state functions */ 
#define IST_STATE_CPU     0
#define IST_STATE_POWER   1
#define IST_STATE_MISC    2

/* DEBUG
 *   Define it if you want verbose debug output, e.g. for bug reporting
 */
#define SPEEDSTEP_DEBUG

#ifdef SPEEDSTEP_DEBUG
#define dprintk(msg...) printk(msg)
#else
#define dprintk(msg...) do { } while(0)
#endif

/*********************************************************************
 *    Low-Level Functions                                            *
 *********************************************************************/
/**
 * speedstep_smi_ownership
 */
static int speedstep_smi_ownership (void)
{
        u32     command, result, magic;
        u32     function = GET_SPEEDSTEP_OWNER;
	char	eist_copyright[] = "Copyright (c) 1999 Intel Corporation";

        command = (ist_info.signature & 0xffffff00) | (smi_cmd & 0xff);
	magic = virt_to_phys(eist_copyright);

        __asm__ __volatile__(
            "out %%al, (%%dx)\n"
            : "=D" (result)
            : "a" (command), "b" (function), "c" (0), "d" (smi_port), "D" (0), "S" (magic)
        );

        return result;
}
/**
 * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.
 *
 */
static int speedstep_smi_get_freqs (unsigned int *low, unsigned int *high)
{
        u32     command, result, edi, high_mhz, low_mhz;
        u32     state=0;
        u32	function = GET_SPEEDSTEP_FREQS;

        command = (ist_info.signature & 0xffffff00) | (smi_cmd & 0xff);

        __asm__ __volatile__("movl $0, %%edi\n"
            "out %%al, (%%dx)\n"
            : "=a" (result), "=b" (high_mhz), "=c" (low_mhz), "=d" (state), "=D" (edi)
            : "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
        );
	*high = high_mhz * 1000;
	*low  = low_mhz  * 1000;

	return result;
} 

/**
 * speedstep_get_state - set the SpeedStep state
 * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
 *
 */
static unsigned int speedstep_get_state (int selecter)
{
	u32	  function=GET_SPEEDSTEP_STATE;
	u32	  result, state, acbatt, command, ret;

        command = (ist_info.signature & 0xffffff00) | (smi_cmd & 0xff);

        __asm__ __volatile__("movl $0, %%edi\n"
            "out %%al, (%%dx)\n"
            : "=a" (result), "=b" (state), "=D" (acbatt)
            : "a" (command), "b" (function), "c" (0), "d" (smi_port), "S" (0)
        );

	switch (selecter) {
	case IST_STATE_CPU:
		ret = state;
		break;
	case IST_STATE_POWER:
		ret = acbatt & 0x0f;
		break;
	case IST_STATE_MISC:
		ret = (acbatt & 0xfffff0) >> 8;
		break;
	default:
		ret = 0;
		break;
	}
	return ret;
}

/**
 * speedstep_set_state - set the SpeedStep state
 * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)
 *
 */
static void speedstep_set_state (unsigned int state, unsigned int notify)
{
	unsigned int		old_state, command, new_state=0;
	int			result=0, i;
	unsigned long		flags;
	struct cpufreq_freqs	freqs;
	unsigned int		function=SET_SPEEDSTEP_STATE;

	if (state > 0x1) {
/*
		command = (ist_info.signature & 0xffffff00) | (smi_cmd & 0xff);
		local_irq_save(flags);
		__asm__ __volatile__(
			"movl $0, %%edi\n"
			"out %%al, (%%dx)\n"
			: "=b" (new_state), "=D" (result)
			: "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
			);
		local_irq_restore(flags);
		dprintk(KERN_INFO "cpufreq: new_state: 0x%.8x, result: 0x%.8x.\n", new_state, result);
*/
		return;
	}

	old_state = speedstep_get_state(IST_STATE_CPU);
	freqs.old = speedstep_freqs[old_state].frequency;
	freqs.new = speedstep_freqs[state].frequency;
	freqs.cpu = 0; /* speedstep.c is UP only driver */
	
	if (old_state == state) {
		dprintk(KERN_DEBUG "cpufreq: new state %d is same as old one.\n", state);
		return;
	}

	if (notify)
		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

        command = (ist_info.signature & 0xffffff00) | (smi_cmd & 0xff);

	local_irq_save(flags);
       	__asm__ __volatile__(
    		"movl $0, %%edi\n"
      		"out %%al, (%%dx)\n"
       		: "=b" (new_state), "=D" (result)
       		: "a" (command), "b" (function), "c" (state), "d" (smi_port), "S" (0)
       	);
	local_irq_restore(flags);

	if (new_state == state) {
		dprintk(KERN_INFO "cpufreq: change to %u MHz succeded\n", (freqs.new / 1000));
	} else {
		printk(KERN_ERR "cpufreq: change failed\n");
	}

	if (notify)
		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

	return;
}


/**
 * speedstep_pm_resume - reconnect SMI interface when resume.
 *
 */
static int speedstep_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data)
{
	int result = 0;

	switch (rqst) {
		case PM_RESUME:
			result = speedstep_smi_ownership();
			if (result)
				dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a SMI interface.\n");
			break;
		case PM_SUSPEND:
			break;
		default:
			/* no need actions */
			break;
	}
	return result;
}

static inline unsigned int get_speedstep_event(void) 
{
	unsigned int result;
	unsigned long flags;
	local_irq_save(flags);
	__asm__ __volatile__(
		"in (%%dx), %%eax\n"
		: "=a" (result)
		: "d" (smi_ev_port)
		);
	local_irq_restore(flags);
	return (result);
}

/**
 * kistd_mainloop - mainloop of kistd.
 */
static void kistd_mainloop(void)
{
	int  result, oldresult, on_batt, new_state;

	DECLARE_WAITQUEUE(wait, current);

	add_wait_queue(&ist_waitqueue, &wait);
	set_current_state(TASK_INTERRUPTIBLE);

	oldresult = get_speedstep_event();
	for (;;) {
		schedule_timeout(smi_ev_poll);
		if (exit_kistd)
			break;
		if (speedstep_auto) {
			result = get_speedstep_event();
			if (result != oldresult) {
				new_state = (speedstep_get_state(IST_STATE_POWER) & 1)?SPEEDSTEP_LOW:SPEEDSTEP_HIGH;
				speedstep_set_state(new_state, 1);
			}
			oldresult = result;
		}
		set_current_state(TASK_INTERRUPTIBLE);
	}
	remove_wait_queue(&ist_waitqueue, &wait);

}

/**
 * kistd - kernel thread to poll SpeedStep Freq change event.
 *
 * 	this thread is needed only when Enhanced Speedstep is supported.
 */
static int kistd(void *unused)
{
	kistd_running = 1;
	daemonize();
	strcpy(current->comm, "kistd");
	sigfillset(&current->blocked);
	kistd_mainloop();
	kistd_running = 0;
	return 0;
}

/*******************************************************************************
 *     High-Level CPUFreq Functions                                            *
 *******************************************************************************/
/**
 * speedstep_target - set a new CPUFreq policy
 * @policy: new policy
 * @target_freq: new freq
 * @relation: 
 *
 * Sets a new CPUFreq policy/freq.
 */
static int speedstep_target (struct cpufreq_policy *policy,
			     unsigned int target_freq,
			     unsigned int relation)
{
	unsigned int	newstate = 0;

	if (policy->policy == CPUFREQ_POLICY_GOVERNOR) {
		if (!strnicmp(policy->governor->name,"auto",CPUFREQ_NAME_LEN)) {
			dprintk(KERN_INFO "cpufreq: governor auto is selected.\n");
			speedstep_auto = 1;
			return 0;
		}
	}

	speedstep_auto = 0; 
	if (cpufreq_frequency_table_target(policy, &speedstep_freqs[0], target_freq, relation, &newstate))
		return -EINVAL;

	speedstep_set_state(newstate, 1);
	return 0;
}


/**
 * speedstep_verify - verifies a new CPUFreq policy
 * @freq: new policy
 *
 * Limit must be within speedstep_low_freq and speedstep_high_freq, with
 * at least one border included.
 */
static int speedstep_verify (struct cpufreq_policy *policy)
{
	return cpufreq_frequency_table_verify(policy, &speedstep_freqs[0]);
}

static int cpufreq_governor_auto(struct cpufreq_policy *policy,
				   unsigned int event)
{

#define SPEEDSTEP_AUTO  0x00000003

	switch (event) {
	case CPUFREQ_GOV_START:
		speedstep_auto = 1;		
/*		speedstep_set_state(SPEEDSTEP_AUTO, 0); */
		break;
	case CPUFREQ_GOV_STOP:
		speedstep_auto = 0;
/*		speedstep_set_state(SPEEDSTEP_LOW, 1); */
		break;
	case CPUFREQ_GOV_LIMITS:
		break;
	}
	return 0;
}

static struct cpufreq_governor cpufreq_gov_auto = {
	.name		= "auto",
	.governor	= cpufreq_governor_auto,
	.owner		= THIS_MODULE,
};

/**
 * speedstep_cpu_init - initialize CPUFreq driver vars.
 * @freq: policy
 */

static int speedstep_cpu_init(struct cpufreq_policy *policy)
{
	int		result;
	unsigned int	speed,state;

	/* capability check */
	if (policy->cpu != 0)
		return -ENODEV;

	result = speedstep_smi_ownership();

	if (result)
		dprintk(KERN_INFO "cpufreq: fails an aquiring ownership of a SMI interface.\n");

	/* detect low and high frequency */
	result = speedstep_smi_get_freqs(&speedstep_freqs[SPEEDSTEP_LOW].frequency,
				         &speedstep_freqs[SPEEDSTEP_HIGH].frequency);
	if (result)
		return result;

	/* get current speed setting */
	state = speedstep_get_state(IST_STATE_CPU);
	speed = speedstep_freqs[state].frequency;

	dprintk(KERN_INFO "cpufreq: currently at %s speed setting - %i MHz\n", 
		(speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? "low" : "high",
		(speed / 1000));

	/* cpuinfo and default policy values */
	policy->policy = (speed == speedstep_freqs[SPEEDSTEP_LOW].frequency) ? 
		CPUFREQ_POLICY_POWERSAVE : CPUFREQ_POLICY_PERFORMANCE;
	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
	policy->cur = speed;
	
	return cpufreq_frequency_table_cpuinfo(policy, &speedstep_freqs[0]);
}

static struct cpufreq_driver speedstep_driver = {
	.name		= "speedstep",
	.verify 	= speedstep_verify,
	.target 	= speedstep_target, 
	.init		= speedstep_cpu_init,
/*	.owner 		= THIS_MODULE, */
};

/*******************************************************************************/
/**
 * speedstep_init - initializes the SpeedStep CPUFreq driver
 *
 *   Initializes the SpeedStep support. Returns -ENODEV on unsupported
 * BIOS, -EINVAL on problems during initiatization, and zero on
 * success.
 */
static int __init speedstep_init(void)
{
	/* Error if no IST-SMI BIOS or no PARM 
		 sig= 'ISGE' aka 'Intel Speedstep Gate E' */
	if ((ist_info.signature !=  0x47534943) && ( 
	    (smi_port == 0) || (smi_cmd == 0)))
		return -ENODEV;

	/* setup smi_port from MODLULE_PARM or BIOS */
	if ((smi_port > 0xff) || (smi_port < 0)) {
		return -EINVAL;
	} else if (smi_port == 0) {
		smi_port = ist_info.command & 0xff;
	}

	if ((smi_cmd > 0xff) || (smi_cmd < 0)) {
		return -EINVAL;
	} else if (smi_cmd == 0) {
		smi_cmd = (ist_info.command >> 16) & 0xff;
	}

	smi_count = ist_info.perf_level & 0xffff;
	if (smi_count == 0)
		smi_count = 1;

	smi_pm_dev = pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, speedstep_pm_resume);

	if (auto_mode && ist_info.event) {
		/* fork event moniter daemon */
		smi_ev_port = ist_info.event & 0xffff;
		smi_ev_mask  = (ist_info.event >> 16) & 0xff;
		smi_ev_poll = ((ist_info.perf_level >> 16) & 0xffff) * HZ / 1000;
		kernel_thread(kistd, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
		cpufreq_register_governor(&cpufreq_gov_auto);
	}

	return cpufreq_register_driver(&speedstep_driver);
}


/**
 * speedstep_exit - unregisters SpeedStep support
 *
 *   Unregisters SpeedStep support.
 */
static void __exit speedstep_exit(void)
{
	unsigned long flags;
 
	if (smi_pm_dev)
		pm_unregister(smi_pm_dev);

	local_irq_save(flags);
	__asm__ __volatile__(
		"out %%al, (%%dx)\n"
		: 
		: "a" (0x0080), "d" (smi_port)
		);
	local_irq_restore(flags);

	if (auto_mode) { /* stops kistd kernel thread */
		exit_kistd = 1;
		while (kistd_running)
			schedule();
		cpufreq_unregister_governor(&cpufreq_gov_auto);
	}
	cpufreq_unregister_driver(&speedstep_driver);
}

MODULE_PARM (smi_port, "i");
MODULE_PARM (smi_cmd, "i");
MODULE_PARM (smi_count, "i"); 
MODULE_PARM (auto_mode, "i");

MODULE_AUTHOR ("Hiroshi Miura");
MODULE_DESCRIPTION ("Speedstep driver for IST applet SMI interface.");
MODULE_LICENSE ("GPL");

module_init(speedstep_init);
module_exit(speedstep_exit);

[-- Attachment #1.3: Type: text/plain, Size: 0 bytes --]



[-- Attachment #1.4: Type: text/plain, Size: 248 bytes --]

Hiroshi Miura  --- http://www.da-cha.org/ 
NTTDATA Corp. Marketing & Business Strategy Planning Dept. --- miurahr@nttdata.co.jp 
Key fingerprint = 9117 9407 5684 FBF1 4063  15B4 401D D077 04AB 8617
-- My hacking life is happy as the day is long




[-- Attachment #2: Type: text/plain, Size: 1 bytes --]



[-- Attachment #3: Type: text/plain, Size: 143 bytes --]

_______________________________________________
Cpufreq mailing list
Cpufreq@www.linux.org.uk
http://www.linux.org.uk/mailman/listinfo/cpufreq

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2003-09-08 12:30 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-09-04 14:52 [new] event mode on speedstep-smi Hiroshi Miura
2003-09-04 15:30 ` Ducrot Bruno
2003-09-04 23:40   ` Hiroshi Miura
2003-09-05  6:56     ` Dominik Brodowski
2003-09-05 13:07       ` Ducrot Bruno
2003-09-04 23:25 ` Dominik Brodowski
2003-09-04 23:53   ` Hiroshi Miura
2003-09-05  6:54     ` speedstep-smi into -ac ? Dominik Brodowski
2003-09-05  8:46       ` Ducrot Bruno
2003-09-07 18:55         ` Dominik Brodowski
2003-09-08  8:32           ` Ducrot Bruno
2003-09-08  9:23             ` Dominik Brodowski
2003-09-08 12:30               ` Ducrot Bruno
2003-09-05 14:51     ` [new] event mode on speedstep-smi Ducrot Bruno

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.