Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  Based on documentation provided by Dave Jones. Thanks!
0004  *
0005  *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/init.h>
0013 #include <linux/cpufreq.h>
0014 #include <linux/ioport.h>
0015 #include <linux/slab.h>
0016 #include <linux/timex.h>
0017 #include <linux/io.h>
0018 #include <linux/delay.h>
0019 
0020 #include <asm/cpu_device_id.h>
0021 #include <asm/msr.h>
0022 #include <asm/tsc.h>
0023 
0024 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0025 #include <linux/acpi.h>
0026 #include <acpi/processor.h>
0027 #endif
0028 
0029 #define EPS_BRAND_C7M   0
0030 #define EPS_BRAND_C7    1
0031 #define EPS_BRAND_EDEN  2
0032 #define EPS_BRAND_C3    3
0033 #define EPS_BRAND_C7D   4
0034 
0035 struct eps_cpu_data {
0036     u32 fsb;
0037 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0038     u32 bios_limit;
0039 #endif
0040     struct cpufreq_frequency_table freq_table[];
0041 };
0042 
0043 static struct eps_cpu_data *eps_cpu[NR_CPUS];
0044 
0045 /* Module parameters */
0046 static int freq_failsafe_off;
0047 static int voltage_failsafe_off;
0048 static int set_max_voltage;
0049 
0050 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0051 static int ignore_acpi_limit;
0052 
0053 static struct acpi_processor_performance *eps_acpi_cpu_perf;
0054 
0055 /* Minimum necessary to get acpi_processor_get_bios_limit() working */
0056 static int eps_acpi_init(void)
0057 {
0058     eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf),
0059                       GFP_KERNEL);
0060     if (!eps_acpi_cpu_perf)
0061         return -ENOMEM;
0062 
0063     if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
0064                                 GFP_KERNEL)) {
0065         kfree(eps_acpi_cpu_perf);
0066         eps_acpi_cpu_perf = NULL;
0067         return -ENOMEM;
0068     }
0069 
0070     if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
0071         free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
0072         kfree(eps_acpi_cpu_perf);
0073         eps_acpi_cpu_perf = NULL;
0074         return -EIO;
0075     }
0076     return 0;
0077 }
0078 
0079 static int eps_acpi_exit(struct cpufreq_policy *policy)
0080 {
0081     if (eps_acpi_cpu_perf) {
0082         acpi_processor_unregister_performance(0);
0083         free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
0084         kfree(eps_acpi_cpu_perf);
0085         eps_acpi_cpu_perf = NULL;
0086     }
0087     return 0;
0088 }
0089 #endif
0090 
0091 static unsigned int eps_get(unsigned int cpu)
0092 {
0093     struct eps_cpu_data *centaur;
0094     u32 lo, hi;
0095 
0096     if (cpu)
0097         return 0;
0098     centaur = eps_cpu[cpu];
0099     if (centaur == NULL)
0100         return 0;
0101 
0102     /* Return current frequency */
0103     rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0104     return centaur->fsb * ((lo >> 8) & 0xff);
0105 }
0106 
0107 static int eps_set_state(struct eps_cpu_data *centaur,
0108              struct cpufreq_policy *policy,
0109              u32 dest_state)
0110 {
0111     u32 lo, hi;
0112     int i;
0113 
0114     /* Wait while CPU is busy */
0115     rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0116     i = 0;
0117     while (lo & ((1 << 16) | (1 << 17))) {
0118         udelay(16);
0119         rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0120         i++;
0121         if (unlikely(i > 64)) {
0122             return -ENODEV;
0123         }
0124     }
0125     /* Set new multiplier and voltage */
0126     wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
0127     /* Wait until transition end */
0128     i = 0;
0129     do {
0130         udelay(16);
0131         rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0132         i++;
0133         if (unlikely(i > 64)) {
0134             return -ENODEV;
0135         }
0136     } while (lo & ((1 << 16) | (1 << 17)));
0137 
0138 #ifdef DEBUG
0139     {
0140     u8 current_multiplier, current_voltage;
0141 
0142     /* Print voltage and multiplier */
0143     rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0144     current_voltage = lo & 0xff;
0145     pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
0146     current_multiplier = (lo >> 8) & 0xff;
0147     pr_info("Current multiplier = %d\n", current_multiplier);
0148     }
0149 #endif
0150     return 0;
0151 }
0152 
0153 static int eps_target(struct cpufreq_policy *policy, unsigned int index)
0154 {
0155     struct eps_cpu_data *centaur;
0156     unsigned int cpu = policy->cpu;
0157     unsigned int dest_state;
0158     int ret;
0159 
0160     if (unlikely(eps_cpu[cpu] == NULL))
0161         return -ENODEV;
0162     centaur = eps_cpu[cpu];
0163 
0164     /* Make frequency transition */
0165     dest_state = centaur->freq_table[index].driver_data & 0xffff;
0166     ret = eps_set_state(centaur, policy, dest_state);
0167     if (ret)
0168         pr_err("Timeout!\n");
0169     return ret;
0170 }
0171 
0172 static int eps_cpu_init(struct cpufreq_policy *policy)
0173 {
0174     unsigned int i;
0175     u32 lo, hi;
0176     u64 val;
0177     u8 current_multiplier, current_voltage;
0178     u8 max_multiplier, max_voltage;
0179     u8 min_multiplier, min_voltage;
0180     u8 brand = 0;
0181     u32 fsb;
0182     struct eps_cpu_data *centaur;
0183     struct cpuinfo_x86 *c = &cpu_data(0);
0184     struct cpufreq_frequency_table *f_table;
0185     int k, step, voltage;
0186     int states;
0187 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0188     unsigned int limit;
0189 #endif
0190 
0191     if (policy->cpu != 0)
0192         return -ENODEV;
0193 
0194     /* Check brand */
0195     pr_info("Detected VIA ");
0196 
0197     switch (c->x86_model) {
0198     case 10:
0199         rdmsr(0x1153, lo, hi);
0200         brand = (((lo >> 2) ^ lo) >> 18) & 3;
0201         pr_cont("Model A ");
0202         break;
0203     case 13:
0204         rdmsr(0x1154, lo, hi);
0205         brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
0206         pr_cont("Model D ");
0207         break;
0208     }
0209 
0210     switch (brand) {
0211     case EPS_BRAND_C7M:
0212         pr_cont("C7-M\n");
0213         break;
0214     case EPS_BRAND_C7:
0215         pr_cont("C7\n");
0216         break;
0217     case EPS_BRAND_EDEN:
0218         pr_cont("Eden\n");
0219         break;
0220     case EPS_BRAND_C7D:
0221         pr_cont("C7-D\n");
0222         break;
0223     case EPS_BRAND_C3:
0224         pr_cont("C3\n");
0225         return -ENODEV;
0226     }
0227     /* Enable Enhanced PowerSaver */
0228     rdmsrl(MSR_IA32_MISC_ENABLE, val);
0229     if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
0230         val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
0231         wrmsrl(MSR_IA32_MISC_ENABLE, val);
0232         /* Can be locked at 0 */
0233         rdmsrl(MSR_IA32_MISC_ENABLE, val);
0234         if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
0235             pr_info("Can't enable Enhanced PowerSaver\n");
0236             return -ENODEV;
0237         }
0238     }
0239 
0240     /* Print voltage and multiplier */
0241     rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
0242     current_voltage = lo & 0xff;
0243     pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
0244     current_multiplier = (lo >> 8) & 0xff;
0245     pr_info("Current multiplier = %d\n", current_multiplier);
0246 
0247     /* Print limits */
0248     max_voltage = hi & 0xff;
0249     pr_info("Highest voltage = %dmV\n", max_voltage * 16 + 700);
0250     max_multiplier = (hi >> 8) & 0xff;
0251     pr_info("Highest multiplier = %d\n", max_multiplier);
0252     min_voltage = (hi >> 16) & 0xff;
0253     pr_info("Lowest voltage = %dmV\n", min_voltage * 16 + 700);
0254     min_multiplier = (hi >> 24) & 0xff;
0255     pr_info("Lowest multiplier = %d\n", min_multiplier);
0256 
0257     /* Sanity checks */
0258     if (current_multiplier == 0 || max_multiplier == 0
0259         || min_multiplier == 0)
0260         return -EINVAL;
0261     if (current_multiplier > max_multiplier
0262         || max_multiplier <= min_multiplier)
0263         return -EINVAL;
0264     if (current_voltage > 0x1f || max_voltage > 0x1f)
0265         return -EINVAL;
0266     if (max_voltage < min_voltage
0267         || current_voltage < min_voltage
0268         || current_voltage > max_voltage)
0269         return -EINVAL;
0270 
0271     /* Check for systems using underclocked CPU */
0272     if (!freq_failsafe_off && max_multiplier != current_multiplier) {
0273         pr_info("Your processor is running at different frequency then its maximum. Aborting.\n");
0274         pr_info("You can use freq_failsafe_off option to disable this check.\n");
0275         return -EINVAL;
0276     }
0277     if (!voltage_failsafe_off && max_voltage != current_voltage) {
0278         pr_info("Your processor is running at different voltage then its maximum. Aborting.\n");
0279         pr_info("You can use voltage_failsafe_off option to disable this check.\n");
0280         return -EINVAL;
0281     }
0282 
0283     /* Calc FSB speed */
0284     fsb = cpu_khz / current_multiplier;
0285 
0286 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0287     /* Check for ACPI processor speed limit */
0288     if (!ignore_acpi_limit && !eps_acpi_init()) {
0289         if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
0290             pr_info("ACPI limit %u.%uGHz\n",
0291                 limit/1000000,
0292                 (limit%1000000)/10000);
0293             eps_acpi_exit(policy);
0294             /* Check if max_multiplier is in BIOS limits */
0295             if (limit && max_multiplier * fsb > limit) {
0296                 pr_info("Aborting\n");
0297                 return -EINVAL;
0298             }
0299         }
0300     }
0301 #endif
0302 
0303     /* Allow user to set lower maximum voltage then that reported
0304      * by processor */
0305     if (brand == EPS_BRAND_C7M && set_max_voltage) {
0306         u32 v;
0307 
0308         /* Change mV to something hardware can use */
0309         v = (set_max_voltage - 700) / 16;
0310         /* Check if voltage is within limits */
0311         if (v >= min_voltage && v <= max_voltage) {
0312             pr_info("Setting %dmV as maximum\n", v * 16 + 700);
0313             max_voltage = v;
0314         }
0315     }
0316 
0317     /* Calc number of p-states supported */
0318     if (brand == EPS_BRAND_C7M)
0319         states = max_multiplier - min_multiplier + 1;
0320     else
0321         states = 2;
0322 
0323     /* Allocate private data and frequency table for current cpu */
0324     centaur = kzalloc(struct_size(centaur, freq_table, states + 1),
0325               GFP_KERNEL);
0326     if (!centaur)
0327         return -ENOMEM;
0328     eps_cpu[0] = centaur;
0329 
0330     /* Copy basic values */
0331     centaur->fsb = fsb;
0332 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0333     centaur->bios_limit = limit;
0334 #endif
0335 
0336     /* Fill frequency and MSR value table */
0337     f_table = &centaur->freq_table[0];
0338     if (brand != EPS_BRAND_C7M) {
0339         f_table[0].frequency = fsb * min_multiplier;
0340         f_table[0].driver_data = (min_multiplier << 8) | min_voltage;
0341         f_table[1].frequency = fsb * max_multiplier;
0342         f_table[1].driver_data = (max_multiplier << 8) | max_voltage;
0343         f_table[2].frequency = CPUFREQ_TABLE_END;
0344     } else {
0345         k = 0;
0346         step = ((max_voltage - min_voltage) * 256)
0347             / (max_multiplier - min_multiplier);
0348         for (i = min_multiplier; i <= max_multiplier; i++) {
0349             voltage = (k * step) / 256 + min_voltage;
0350             f_table[k].frequency = fsb * i;
0351             f_table[k].driver_data = (i << 8) | voltage;
0352             k++;
0353         }
0354         f_table[k].frequency = CPUFREQ_TABLE_END;
0355     }
0356 
0357     policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
0358     policy->freq_table = &centaur->freq_table[0];
0359 
0360     return 0;
0361 }
0362 
0363 static int eps_cpu_exit(struct cpufreq_policy *policy)
0364 {
0365     unsigned int cpu = policy->cpu;
0366 
0367     /* Bye */
0368     kfree(eps_cpu[cpu]);
0369     eps_cpu[cpu] = NULL;
0370     return 0;
0371 }
0372 
0373 static struct cpufreq_driver eps_driver = {
0374     .verify     = cpufreq_generic_frequency_table_verify,
0375     .target_index   = eps_target,
0376     .init       = eps_cpu_init,
0377     .exit       = eps_cpu_exit,
0378     .get        = eps_get,
0379     .name       = "e_powersaver",
0380     .attr       = cpufreq_generic_attr,
0381 };
0382 
0383 
0384 /* This driver will work only on Centaur C7 processors with
0385  * Enhanced SpeedStep/PowerSaver registers */
0386 static const struct x86_cpu_id eps_cpu_id[] = {
0387     X86_MATCH_VENDOR_FAM_FEATURE(CENTAUR, 6, X86_FEATURE_EST, NULL),
0388     {}
0389 };
0390 MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
0391 
0392 static int __init eps_init(void)
0393 {
0394     if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
0395         return -ENODEV;
0396     if (cpufreq_register_driver(&eps_driver))
0397         return -EINVAL;
0398     return 0;
0399 }
0400 
0401 static void __exit eps_exit(void)
0402 {
0403     cpufreq_unregister_driver(&eps_driver);
0404 }
0405 
0406 /* Allow user to overclock his machine or to change frequency to higher after
0407  * unloading module */
0408 module_param(freq_failsafe_off, int, 0644);
0409 MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
0410 module_param(voltage_failsafe_off, int, 0644);
0411 MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
0412 #if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
0413 module_param(ignore_acpi_limit, int, 0644);
0414 MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
0415 #endif
0416 module_param(set_max_voltage, int, 0644);
0417 MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
0418 
0419 MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
0420 MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
0421 MODULE_LICENSE("GPL");
0422 
0423 module_init(eps_init);
0424 module_exit(eps_exit);