diff options
Diffstat (limited to 'drivers/idle/intel_idle.c')
| -rw-r--r-- | drivers/idle/intel_idle.c | 120 | 
1 files changed, 66 insertions, 54 deletions
| diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 54ab97bae042..d0f59c3f87ef 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -62,6 +62,7 @@  #include <linux/notifier.h>  #include <linux/cpu.h>  #include <linux/module.h> +#include <asm/cpu_device_id.h>  #include <asm/mwait.h>  #include <asm/msr.h> @@ -81,6 +82,17 @@ static unsigned int mwait_substates;  /* Reliable LAPIC Timer States, bit 1 for C1 etc.  */  static unsigned int lapic_timer_reliable_states = (1 << 1);	 /* Default to only C1 */ +struct idle_cpu { +	struct cpuidle_state *state_table; + +	/* +	 * Hardware C-state auto-demotion may not always be optimal. +	 * Indicate which enable bits to clear here. +	 */ +	unsigned long auto_demotion_disable_flags; +}; + +static const struct idle_cpu *icpu;  static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;  static int intel_idle(struct cpuidle_device *dev,  			struct cpuidle_driver *drv, int index); @@ -88,12 +100,6 @@ static int intel_idle(struct cpuidle_device *dev,  static struct cpuidle_state *cpuidle_state_table;  /* - * Hardware C-state auto-demotion may not always be optimal. - * Indicate which enable bits to clear here. - */ -static unsigned long long auto_demotion_disable_flags; - -/*   * Set this flag for states where the HW flushes the TLB for us   * and so we don't need cross-calls to keep it consistent.   * If this flag is set, SW flushes the TLB, so even if the @@ -319,27 +325,68 @@ static void auto_demotion_disable(void *dummy)  	unsigned long long msr_bits;  	rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); -	msr_bits &= ~auto_demotion_disable_flags; +	msr_bits &= ~(icpu->auto_demotion_disable_flags);  	wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits);  } +static const struct idle_cpu idle_cpu_nehalem = { +	.state_table = nehalem_cstates, +	.auto_demotion_disable_flags = NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE, +}; + +static const struct idle_cpu idle_cpu_atom = { +	.state_table = atom_cstates, +}; + +static const struct idle_cpu idle_cpu_lincroft = { +	.state_table = atom_cstates, +	.auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE, +}; + +static const struct idle_cpu idle_cpu_snb = { +	.state_table = snb_cstates, +}; + +#define ICPU(model, cpu) \ +	{ X86_VENDOR_INTEL, 6, model, X86_FEATURE_MWAIT, (unsigned long)&cpu } + +static const struct x86_cpu_id intel_idle_ids[] = { +	ICPU(0x1a, idle_cpu_nehalem), +	ICPU(0x1e, idle_cpu_nehalem), +	ICPU(0x1f, idle_cpu_nehalem), +	ICPU(0x25, idle_cpu_nehalem), +	ICPU(0x2c, idle_cpu_nehalem), +	ICPU(0x2e, idle_cpu_nehalem), +	ICPU(0x1c, idle_cpu_atom), +	ICPU(0x26, idle_cpu_lincroft), +	ICPU(0x2f, idle_cpu_nehalem), +	ICPU(0x2a, idle_cpu_snb), +	ICPU(0x2d, idle_cpu_snb), +	{} +}; +MODULE_DEVICE_TABLE(x86cpu, intel_idle_ids); +  /*   * intel_idle_probe()   */  static int intel_idle_probe(void)  {  	unsigned int eax, ebx, ecx; +	const struct x86_cpu_id *id;  	if (max_cstate == 0) {  		pr_debug(PREFIX "disabled\n");  		return -EPERM;  	} -	if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) -		return -ENODEV; - -	if (!boot_cpu_has(X86_FEATURE_MWAIT)) +	id = x86_match_cpu(intel_idle_ids); +	if (!id) { +		if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && +		    boot_cpu_data.x86 == 6) +			pr_debug(PREFIX "does not run on family %d model %d\n", +				boot_cpu_data.x86, boot_cpu_data.x86_model);  		return -ENODEV; +	}  	if (boot_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)  		return -ENODEV; @@ -353,43 +400,8 @@ static int intel_idle_probe(void)  	pr_debug(PREFIX "MWAIT substates: 0x%x\n", mwait_substates); - -	if (boot_cpu_data.x86 != 6)	/* family 6 */ -		return -ENODEV; - -	switch (boot_cpu_data.x86_model) { - -	case 0x1A:	/* Core i7, Xeon 5500 series */ -	case 0x1E:	/* Core i7 and i5 Processor - Lynnfield Jasper Forest */ -	case 0x1F:	/* Core i7 and i5 Processor - Nehalem */ -	case 0x2E:	/* Nehalem-EX Xeon */ -	case 0x2F:	/* Westmere-EX Xeon */ -	case 0x25:	/* Westmere */ -	case 0x2C:	/* Westmere */ -		cpuidle_state_table = nehalem_cstates; -		auto_demotion_disable_flags = -			(NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE); -		break; - -	case 0x1C:	/* 28 - Atom Processor */ -		cpuidle_state_table = atom_cstates; -		break; - -	case 0x26:	/* 38 - Lincroft Atom Processor */ -		cpuidle_state_table = atom_cstates; -		auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE; -		break; - -	case 0x2A:	/* SNB */ -	case 0x2D:	/* SNB Xeon */ -		cpuidle_state_table = snb_cstates; -		break; - -	default: -		pr_debug(PREFIX "does not run on family %d model %d\n", -			boot_cpu_data.x86, boot_cpu_data.x86_model); -		return -ENODEV; -	} +	icpu = (const struct idle_cpu *)id->driver_data; +	cpuidle_state_table = icpu->state_table;  	if (boot_cpu_has(X86_FEATURE_ARAT))	/* Always Reliable APIC Timer */  		lapic_timer_reliable_states = LAPIC_TIMER_ALWAYS_RELIABLE; @@ -470,7 +482,7 @@ static int intel_idle_cpuidle_driver_init(void)  		drv->state_count += 1;  	} -	if (auto_demotion_disable_flags) +	if (icpu->auto_demotion_disable_flags)  		on_each_cpu(auto_demotion_disable, NULL, 1);  	return 0; @@ -495,8 +507,7 @@ int intel_idle_cpu_init(int cpu)  		int num_substates;  		if (cstate > max_cstate) { -			printk(PREFIX "max_cstate %d reached\n", -			       max_cstate); +			printk(PREFIX "max_cstate %d reached\n", max_cstate);  			break;  		} @@ -512,8 +523,9 @@ int intel_idle_cpu_init(int cpu)  		dev->states_usage[dev->state_count].driver_data =  			(void *)get_driver_data(cstate); -			dev->state_count += 1; -		} +		dev->state_count += 1; +	} +  	dev->cpu = cpu;  	if (cpuidle_register_device(dev)) { @@ -522,7 +534,7 @@ int intel_idle_cpu_init(int cpu)  		return -EIO;  	} -	if (auto_demotion_disable_flags) +	if (icpu->auto_demotion_disable_flags)  		smp_call_function_single(cpu, auto_demotion_disable, NULL, 1);  	return 0; | 
