brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · 0ce9d4b Raw
394 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Intel SpeedStep SMI driver.4 *5 * (C) 2003  Hiroshi Miura <miura@da-cha.org>6 */7 8 9/*********************************************************************10 *                        SPEEDSTEP - DEFINITIONS                    *11 *********************************************************************/12 13#define pr_fmt(fmt) "cpufreq: " fmt14 15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <linux/init.h>19#include <linux/cpufreq.h>20#include <linux/delay.h>21#include <linux/io.h>22#include <asm/ist.h>23#include <asm/cpu_device_id.h>24 25#include "speedstep-lib.h"26 27/* speedstep system management interface port/command.28 *29 * These parameters are got from IST-SMI BIOS call.30 * If user gives it, these are used.31 *32 */33static int smi_port;34static int smi_cmd;35static unsigned int smi_sig;36 37/* info about the processor */38static enum speedstep_processor speedstep_processor;39 40/*41 * There are only two frequency states for each processor. Values42 * are in kHz for the time being.43 */44static struct cpufreq_frequency_table speedstep_freqs[] = {45	{0, SPEEDSTEP_HIGH,	0},46	{0, SPEEDSTEP_LOW,	0},47	{0, 0,			CPUFREQ_TABLE_END},48};49 50#define GET_SPEEDSTEP_OWNER 051#define GET_SPEEDSTEP_STATE 152#define SET_SPEEDSTEP_STATE 253#define GET_SPEEDSTEP_FREQS 454 55/* how often shall the SMI call be tried if it failed, e.g. because56 * of DMA activity going on? */57#define SMI_TRIES 558 59/**60 * speedstep_smi_ownership61 */62static int speedstep_smi_ownership(void)63{64	u32 command, result, magic, dummy;65	u32 function = GET_SPEEDSTEP_OWNER;66	unsigned char magic_data[] = "Copyright (c) 1999 Intel Corporation";67 68	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);69	magic = virt_to_phys(magic_data);70 71	pr_debug("trying to obtain ownership with command %x at port %x\n",72			command, smi_port);73 74	__asm__ __volatile__(75		"push %%ebp\n"76		"out %%al, (%%dx)\n"77		"pop %%ebp\n"78		: "=D" (result),79		  "=a" (dummy), "=b" (dummy), "=c" (dummy), "=d" (dummy),80		  "=S" (dummy)81		: "a" (command), "b" (function), "c" (0), "d" (smi_port),82		  "D" (0), "S" (magic)83		: "memory"84	);85 86	pr_debug("result is %x\n", result);87 88	return result;89}90 91/**92 * speedstep_smi_get_freqs - get SpeedStep preferred & current freq.93 * @low: the low frequency value is placed here94 * @high: the high frequency value is placed here95 *96 * Only available on later SpeedStep-enabled systems, returns false results or97 * even hangs [cf. bugme.osdl.org # 1422] on earlier systems. Empirical testing98 * shows that the latter occurs if !(ist_info.event & 0xFFFF).99 */100static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high)101{102	u32 command, result = 0, edi, high_mhz, low_mhz, dummy;103	u32 state = 0;104	u32 function = GET_SPEEDSTEP_FREQS;105 106	if (!(ist_info.event & 0xFFFF)) {107		pr_debug("bug #1422 -- can't read freqs from BIOS\n");108		return -ENODEV;109	}110 111	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);112 113	pr_debug("trying to determine frequencies with command %x at port %x\n",114			command, smi_port);115 116	__asm__ __volatile__(117		"push %%ebp\n"118		"out %%al, (%%dx)\n"119		"pop %%ebp"120		: "=a" (result),121		  "=b" (high_mhz),122		  "=c" (low_mhz),123		  "=d" (state), "=D" (edi), "=S" (dummy)124		: "a" (command),125		  "b" (function),126		  "c" (state),127		  "d" (smi_port), "S" (0), "D" (0)128	);129 130	pr_debug("result %x, low_freq %u, high_freq %u\n",131			result, low_mhz, high_mhz);132 133	/* abort if results are obviously incorrect... */134	if ((high_mhz + low_mhz) < 600)135		return -EINVAL;136 137	*high = high_mhz * 1000;138	*low  = low_mhz  * 1000;139 140	return result;141}142 143/**144 * speedstep_set_state - set the SpeedStep state145 * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH)146 *147 */148static void speedstep_set_state(unsigned int state)149{150	unsigned int result = 0, command, new_state, dummy;151	unsigned long flags;152	unsigned int function = SET_SPEEDSTEP_STATE;153	unsigned int retry = 0;154 155	if (state > 0x1)156		return;157 158	/* Disable IRQs */159	preempt_disable();160	local_irq_save(flags);161 162	command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff);163 164	pr_debug("trying to set frequency to state %u "165		"with command %x at port %x\n",166		state, command, smi_port);167 168	do {169		if (retry) {170			/*171			 * We need to enable interrupts, otherwise the blockage172			 * won't resolve.173			 *174			 * We disable preemption so that other processes don't175			 * run. If other processes were running, they could176			 * submit more DMA requests, making the blockage worse.177			 */178			pr_debug("retry %u, previous result %u, waiting...\n",179					retry, result);180			local_irq_enable();181			mdelay(retry * 50);182			local_irq_disable();183		}184		retry++;185		__asm__ __volatile__(186			"push %%ebp\n"187			"out %%al, (%%dx)\n"188			"pop %%ebp"189			: "=b" (new_state), "=D" (result),190			  "=c" (dummy), "=a" (dummy),191			  "=d" (dummy), "=S" (dummy)192			: "a" (command), "b" (function), "c" (state),193			  "d" (smi_port), "S" (0), "D" (0)194			);195	} while ((new_state != state) && (retry <= SMI_TRIES));196 197	/* enable IRQs */198	local_irq_restore(flags);199	preempt_enable();200 201	if (new_state == state)202		pr_debug("change to %u MHz succeeded after %u tries "203			"with result %u\n",204			(speedstep_freqs[new_state].frequency / 1000),205			retry, result);206	else207		pr_err("change to state %u failed with new_state %u and result %u\n",208		       state, new_state, result);209 210	return;211}212 213 214/**215 * speedstep_target - set a new CPUFreq policy216 * @policy: new policy217 * @index: index of new freq218 *219 * Sets a new CPUFreq policy/freq.220 */221static int speedstep_target(struct cpufreq_policy *policy, unsigned int index)222{223	speedstep_set_state(index);224 225	return 0;226}227 228 229static int speedstep_cpu_init(struct cpufreq_policy *policy)230{231	int result;232	unsigned int *low, *high;233 234	/* capability check */235	if (policy->cpu != 0)236		return -ENODEV;237 238	result = speedstep_smi_ownership();239	if (result) {240		pr_debug("fails in acquiring ownership of a SMI interface.\n");241		return -EINVAL;242	}243 244	/* detect low and high frequency */245	low = &speedstep_freqs[SPEEDSTEP_LOW].frequency;246	high = &speedstep_freqs[SPEEDSTEP_HIGH].frequency;247 248	result = speedstep_smi_get_freqs(low, high);249	if (result) {250		/* fall back to speedstep_lib.c dection mechanism:251		 * try both states out */252		pr_debug("could not detect low and high frequencies "253				"by SMI call.\n");254		result = speedstep_get_freqs(speedstep_processor,255				low, high,256				NULL,257				&speedstep_set_state);258 259		if (result) {260			pr_debug("could not detect two different speeds"261					" -- aborting.\n");262			return result;263		} else264			pr_debug("workaround worked.\n");265	}266 267	policy->freq_table = speedstep_freqs;268 269	return 0;270}271 272static unsigned int speedstep_get(unsigned int cpu)273{274	if (cpu)275		return -ENODEV;276	return speedstep_get_frequency(speedstep_processor);277}278 279 280static int speedstep_resume(struct cpufreq_policy *policy)281{282	int result = speedstep_smi_ownership();283 284	if (result)285		pr_debug("fails in re-acquiring ownership of a SMI interface.\n");286 287	return result;288}289 290static struct cpufreq_driver speedstep_driver = {291	.name		= "speedstep-smi",292	.flags		= CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,293	.verify		= cpufreq_generic_frequency_table_verify,294	.target_index	= speedstep_target,295	.init		= speedstep_cpu_init,296	.get		= speedstep_get,297	.resume		= speedstep_resume,298	.attr		= cpufreq_generic_attr,299};300 301static const struct x86_cpu_id ss_smi_ids[] = {302	X86_MATCH_VENDOR_FAM_MODEL(INTEL,  6, 0x8, 0),303	X86_MATCH_VENDOR_FAM_MODEL(INTEL,  6, 0xb, 0),304	X86_MATCH_VENDOR_FAM_MODEL(INTEL, 15, 0x2, 0),305	{}306};307 308/**309 * speedstep_init - initializes the SpeedStep CPUFreq driver310 *311 *   Initializes the SpeedStep support. Returns -ENODEV on unsupported312 * BIOS, -EINVAL on problems during initiatization, and zero on313 * success.314 */315static int __init speedstep_init(void)316{317	if (!x86_match_cpu(ss_smi_ids))318		return -ENODEV;319 320	speedstep_processor = speedstep_detect_processor();321 322	switch (speedstep_processor) {323	case SPEEDSTEP_CPU_PIII_T:324	case SPEEDSTEP_CPU_PIII_C:325	case SPEEDSTEP_CPU_PIII_C_EARLY:326		break;327	default:328		speedstep_processor = 0;329	}330 331	if (!speedstep_processor) {332		pr_debug("No supported Intel CPU detected.\n");333		return -ENODEV;334	}335 336	pr_debug("signature:0x%.8x, command:0x%.8x, "337		"event:0x%.8x, perf_level:0x%.8x.\n",338		ist_info.signature, ist_info.command,339		ist_info.event, ist_info.perf_level);340 341	/* Error if no IST-SMI BIOS or no PARM342		 sig= 'ISGE' aka 'Intel Speedstep Gate E' */343	if ((ist_info.signature !=  0x47534943) && (344	    (smi_port == 0) || (smi_cmd == 0)))345		return -ENODEV;346 347	if (smi_sig == 1)348		smi_sig = 0x47534943;349	else350		smi_sig = ist_info.signature;351 352	/* setup smi_port from MODLULE_PARM or BIOS */353	if ((smi_port > 0xff) || (smi_port < 0))354		return -EINVAL;355	else if (smi_port == 0)356		smi_port = ist_info.command & 0xff;357 358	if ((smi_cmd > 0xff) || (smi_cmd < 0))359		return -EINVAL;360	else if (smi_cmd == 0)361		smi_cmd = (ist_info.command >> 16) & 0xff;362 363	return cpufreq_register_driver(&speedstep_driver);364}365 366 367/**368 * speedstep_exit - unregisters SpeedStep support369 *370 *   Unregisters SpeedStep support.371 */372static void __exit speedstep_exit(void)373{374	cpufreq_unregister_driver(&speedstep_driver);375}376 377module_param_hw(smi_port, int, ioport, 0444);378module_param(smi_cmd,  int, 0444);379module_param(smi_sig, uint, 0444);380 381MODULE_PARM_DESC(smi_port, "Override the BIOS-given IST port with this value "382		"-- Intel's default setting is 0xb2");383MODULE_PARM_DESC(smi_cmd, "Override the BIOS-given IST command with this value "384		"-- Intel's default setting is 0x82");385MODULE_PARM_DESC(smi_sig, "Set to 1 to fake the IST signature when using the "386		"SMI interface.");387 388MODULE_AUTHOR("Hiroshi Miura");389MODULE_DESCRIPTION("Speedstep driver for IST applet SMI interface.");390MODULE_LICENSE("GPL");391 392module_init(speedstep_init);393module_exit(speedstep_exit);394