brintos

brintos / linux-shallow public Read only

0
0
Text · 10.7 KiB · 6e958b0 Raw
424 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  Based on documentation provided by Dave Jones. Thanks!4 *5 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/init.h>13#include <linux/cpufreq.h>14#include <linux/ioport.h>15#include <linux/slab.h>16#include <linux/timex.h>17#include <linux/io.h>18#include <linux/delay.h>19 20#include <asm/cpu_device_id.h>21#include <asm/msr.h>22#include <asm/tsc.h>23 24#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)25#include <linux/acpi.h>26#include <acpi/processor.h>27#endif28 29#define EPS_BRAND_C7M	030#define EPS_BRAND_C7	131#define EPS_BRAND_EDEN	232#define EPS_BRAND_C3	333#define EPS_BRAND_C7D	434 35struct eps_cpu_data {36	u32 fsb;37#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)38	u32 bios_limit;39#endif40	struct cpufreq_frequency_table freq_table[];41};42 43static struct eps_cpu_data *eps_cpu[NR_CPUS];44 45/* Module parameters */46static int freq_failsafe_off;47static int voltage_failsafe_off;48static int set_max_voltage;49 50#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)51static int ignore_acpi_limit;52 53static struct acpi_processor_performance *eps_acpi_cpu_perf;54 55/* Minimum necessary to get acpi_processor_get_bios_limit() working */56static int eps_acpi_init(void)57{58	eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf),59				      GFP_KERNEL);60	if (!eps_acpi_cpu_perf)61		return -ENOMEM;62 63	if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,64								GFP_KERNEL)) {65		kfree(eps_acpi_cpu_perf);66		eps_acpi_cpu_perf = NULL;67		return -ENOMEM;68	}69 70	if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {71		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);72		kfree(eps_acpi_cpu_perf);73		eps_acpi_cpu_perf = NULL;74		return -EIO;75	}76	return 0;77}78 79static int eps_acpi_exit(struct cpufreq_policy *policy)80{81	if (eps_acpi_cpu_perf) {82		acpi_processor_unregister_performance(0);83		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);84		kfree(eps_acpi_cpu_perf);85		eps_acpi_cpu_perf = NULL;86	}87	return 0;88}89#endif90 91static unsigned int eps_get(unsigned int cpu)92{93	struct eps_cpu_data *centaur;94	u32 lo, hi;95 96	if (cpu)97		return 0;98	centaur = eps_cpu[cpu];99	if (centaur == NULL)100		return 0;101 102	/* Return current frequency */103	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);104	return centaur->fsb * ((lo >> 8) & 0xff);105}106 107static int eps_set_state(struct eps_cpu_data *centaur,108			 struct cpufreq_policy *policy,109			 u32 dest_state)110{111	u32 lo, hi;112	int i;113 114	/* Wait while CPU is busy */115	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);116	i = 0;117	while (lo & ((1 << 16) | (1 << 17))) {118		udelay(16);119		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);120		i++;121		if (unlikely(i > 64)) {122			return -ENODEV;123		}124	}125	/* Set new multiplier and voltage */126	wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);127	/* Wait until transition end */128	i = 0;129	do {130		udelay(16);131		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);132		i++;133		if (unlikely(i > 64)) {134			return -ENODEV;135		}136	} while (lo & ((1 << 16) | (1 << 17)));137 138#ifdef DEBUG139	{140	u8 current_multiplier, current_voltage;141 142	/* Print voltage and multiplier */143	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);144	current_voltage = lo & 0xff;145	pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);146	current_multiplier = (lo >> 8) & 0xff;147	pr_info("Current multiplier = %d\n", current_multiplier);148	}149#endif150	return 0;151}152 153static int eps_target(struct cpufreq_policy *policy, unsigned int index)154{155	struct eps_cpu_data *centaur;156	unsigned int cpu = policy->cpu;157	unsigned int dest_state;158	int ret;159 160	if (unlikely(eps_cpu[cpu] == NULL))161		return -ENODEV;162	centaur = eps_cpu[cpu];163 164	/* Make frequency transition */165	dest_state = centaur->freq_table[index].driver_data & 0xffff;166	ret = eps_set_state(centaur, policy, dest_state);167	if (ret)168		pr_err("Timeout!\n");169	return ret;170}171 172static int eps_cpu_init(struct cpufreq_policy *policy)173{174	unsigned int i;175	u32 lo, hi;176	u64 val;177	u8 current_multiplier, current_voltage;178	u8 max_multiplier, max_voltage;179	u8 min_multiplier, min_voltage;180	u8 brand = 0;181	u32 fsb;182	struct eps_cpu_data *centaur;183	struct cpuinfo_x86 *c = &cpu_data(0);184	struct cpufreq_frequency_table *f_table;185	int k, step, voltage;186	int states;187#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)188	unsigned int limit;189#endif190 191	if (policy->cpu != 0)192		return -ENODEV;193 194	/* Check brand */195	pr_info("Detected VIA ");196 197	switch (c->x86_model) {198	case 10:199		rdmsr(0x1153, lo, hi);200		brand = (((lo >> 2) ^ lo) >> 18) & 3;201		pr_cont("Model A ");202		break;203	case 13:204		rdmsr(0x1154, lo, hi);205		brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;206		pr_cont("Model D ");207		break;208	}209 210	switch (brand) {211	case EPS_BRAND_C7M:212		pr_cont("C7-M\n");213		break;214	case EPS_BRAND_C7:215		pr_cont("C7\n");216		break;217	case EPS_BRAND_EDEN:218		pr_cont("Eden\n");219		break;220	case EPS_BRAND_C7D:221		pr_cont("C7-D\n");222		break;223	case EPS_BRAND_C3:224		pr_cont("C3\n");225		return -ENODEV;226	}227	/* Enable Enhanced PowerSaver */228	rdmsrl(MSR_IA32_MISC_ENABLE, val);229	if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {230		val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;231		wrmsrl(MSR_IA32_MISC_ENABLE, val);232		/* Can be locked at 0 */233		rdmsrl(MSR_IA32_MISC_ENABLE, val);234		if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {235			pr_info("Can't enable Enhanced PowerSaver\n");236			return -ENODEV;237		}238	}239 240	/* Print voltage and multiplier */241	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);242	current_voltage = lo & 0xff;243	pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);244	current_multiplier = (lo >> 8) & 0xff;245	pr_info("Current multiplier = %d\n", current_multiplier);246 247	/* Print limits */248	max_voltage = hi & 0xff;249	pr_info("Highest voltage = %dmV\n", max_voltage * 16 + 700);250	max_multiplier = (hi >> 8) & 0xff;251	pr_info("Highest multiplier = %d\n", max_multiplier);252	min_voltage = (hi >> 16) & 0xff;253	pr_info("Lowest voltage = %dmV\n", min_voltage * 16 + 700);254	min_multiplier = (hi >> 24) & 0xff;255	pr_info("Lowest multiplier = %d\n", min_multiplier);256 257	/* Sanity checks */258	if (current_multiplier == 0 || max_multiplier == 0259	    || min_multiplier == 0)260		return -EINVAL;261	if (current_multiplier > max_multiplier262	    || max_multiplier <= min_multiplier)263		return -EINVAL;264	if (current_voltage > 0x1f || max_voltage > 0x1f)265		return -EINVAL;266	if (max_voltage < min_voltage267	    || current_voltage < min_voltage268	    || current_voltage > max_voltage)269		return -EINVAL;270 271	/* Check for systems using underclocked CPU */272	if (!freq_failsafe_off && max_multiplier != current_multiplier) {273		pr_info("Your processor is running at different frequency then its maximum. Aborting.\n");274		pr_info("You can use freq_failsafe_off option to disable this check.\n");275		return -EINVAL;276	}277	if (!voltage_failsafe_off && max_voltage != current_voltage) {278		pr_info("Your processor is running at different voltage then its maximum. Aborting.\n");279		pr_info("You can use voltage_failsafe_off option to disable this check.\n");280		return -EINVAL;281	}282 283	/* Calc FSB speed */284	fsb = cpu_khz / current_multiplier;285 286#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)287	/* Check for ACPI processor speed limit */288	if (!ignore_acpi_limit && !eps_acpi_init()) {289		if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {290			pr_info("ACPI limit %u.%uGHz\n",291				limit/1000000,292				(limit%1000000)/10000);293			eps_acpi_exit(policy);294			/* Check if max_multiplier is in BIOS limits */295			if (limit && max_multiplier * fsb > limit) {296				pr_info("Aborting\n");297				return -EINVAL;298			}299		}300	}301#endif302 303	/* Allow user to set lower maximum voltage then that reported304	 * by processor */305	if (brand == EPS_BRAND_C7M && set_max_voltage) {306		u32 v;307 308		/* Change mV to something hardware can use */309		v = (set_max_voltage - 700) / 16;310		/* Check if voltage is within limits */311		if (v >= min_voltage && v <= max_voltage) {312			pr_info("Setting %dmV as maximum\n", v * 16 + 700);313			max_voltage = v;314		}315	}316 317	/* Calc number of p-states supported */318	if (brand == EPS_BRAND_C7M)319		states = max_multiplier - min_multiplier + 1;320	else321		states = 2;322 323	/* Allocate private data and frequency table for current cpu */324	centaur = kzalloc(struct_size(centaur, freq_table, states + 1),325			  GFP_KERNEL);326	if (!centaur)327		return -ENOMEM;328	eps_cpu[0] = centaur;329 330	/* Copy basic values */331	centaur->fsb = fsb;332#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)333	centaur->bios_limit = limit;334#endif335 336	/* Fill frequency and MSR value table */337	f_table = &centaur->freq_table[0];338	if (brand != EPS_BRAND_C7M) {339		f_table[0].frequency = fsb * min_multiplier;340		f_table[0].driver_data = (min_multiplier << 8) | min_voltage;341		f_table[1].frequency = fsb * max_multiplier;342		f_table[1].driver_data = (max_multiplier << 8) | max_voltage;343		f_table[2].frequency = CPUFREQ_TABLE_END;344	} else {345		k = 0;346		step = ((max_voltage - min_voltage) * 256)347			/ (max_multiplier - min_multiplier);348		for (i = min_multiplier; i <= max_multiplier; i++) {349			voltage = (k * step) / 256 + min_voltage;350			f_table[k].frequency = fsb * i;351			f_table[k].driver_data = (i << 8) | voltage;352			k++;353		}354		f_table[k].frequency = CPUFREQ_TABLE_END;355	}356 357	policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */358	policy->freq_table = &centaur->freq_table[0];359 360	return 0;361}362 363static void eps_cpu_exit(struct cpufreq_policy *policy)364{365	unsigned int cpu = policy->cpu;366 367	/* Bye */368	kfree(eps_cpu[cpu]);369	eps_cpu[cpu] = NULL;370}371 372static struct cpufreq_driver eps_driver = {373	.verify		= cpufreq_generic_frequency_table_verify,374	.target_index	= eps_target,375	.init		= eps_cpu_init,376	.exit		= eps_cpu_exit,377	.get		= eps_get,378	.name		= "e_powersaver",379	.attr		= cpufreq_generic_attr,380};381 382 383/* This driver will work only on Centaur C7 processors with384 * Enhanced SpeedStep/PowerSaver registers */385static const struct x86_cpu_id eps_cpu_id[] = {386	X86_MATCH_VENDOR_FAM_FEATURE(CENTAUR, 6, X86_FEATURE_EST, NULL),387	{}388};389MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);390 391static int __init eps_init(void)392{393	if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)394		return -ENODEV;395	if (cpufreq_register_driver(&eps_driver))396		return -EINVAL;397	return 0;398}399 400static void __exit eps_exit(void)401{402	cpufreq_unregister_driver(&eps_driver);403}404 405/* Allow user to overclock his machine or to change frequency to higher after406 * unloading module */407module_param(freq_failsafe_off, int, 0644);408MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");409module_param(voltage_failsafe_off, int, 0644);410MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");411#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)412module_param(ignore_acpi_limit, int, 0644);413MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");414#endif415module_param(set_max_voltage, int, 0644);416MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");417 418MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");419MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");420MODULE_LICENSE("GPL");421 422module_init(eps_init);423module_exit(eps_exit);424