Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * (C) 2002 - 2003  Dominik Brodowski <linux@brodo.de>
0004  *
0005  *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/module.h>
0010 #include <linux/init.h>
0011 #include <linux/cpufreq.h>
0012 #include <linux/timex.h>
0013 
0014 #include <asm/msr.h>
0015 #include <asm/processor.h>
0016 #include <asm/cpu_device_id.h>
0017 
0018 static struct cpufreq_driver    longrun_driver;
0019 
0020 /**
0021  * longrun_{low,high}_freq is needed for the conversion of cpufreq kHz
0022  * values into per cent values. In TMTA microcode, the following is valid:
0023  * performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
0024  */
0025 static unsigned int longrun_low_freq, longrun_high_freq;
0026 
0027 
0028 /**
0029  * longrun_get_policy - get the current LongRun policy
0030  * @policy: struct cpufreq_policy where current policy is written into
0031  *
0032  * Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS
0033  * and MSR_TMTA_LONGRUN_CTRL
0034  */
0035 static void longrun_get_policy(struct cpufreq_policy *policy)
0036 {
0037     u32 msr_lo, msr_hi;
0038 
0039     rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
0040     pr_debug("longrun flags are %x - %x\n", msr_lo, msr_hi);
0041     if (msr_lo & 0x01)
0042         policy->policy = CPUFREQ_POLICY_PERFORMANCE;
0043     else
0044         policy->policy = CPUFREQ_POLICY_POWERSAVE;
0045 
0046     rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
0047     pr_debug("longrun ctrl is %x - %x\n", msr_lo, msr_hi);
0048     msr_lo &= 0x0000007F;
0049     msr_hi &= 0x0000007F;
0050 
0051     if (longrun_high_freq <= longrun_low_freq) {
0052         /* Assume degenerate Longrun table */
0053         policy->min = policy->max = longrun_high_freq;
0054     } else {
0055         policy->min = longrun_low_freq + msr_lo *
0056             ((longrun_high_freq - longrun_low_freq) / 100);
0057         policy->max = longrun_low_freq + msr_hi *
0058             ((longrun_high_freq - longrun_low_freq) / 100);
0059     }
0060     policy->cpu = 0;
0061 }
0062 
0063 
0064 /**
0065  * longrun_set_policy - sets a new CPUFreq policy
0066  * @policy: new policy
0067  *
0068  * Sets a new CPUFreq policy on LongRun-capable processors. This function
0069  * has to be called with cpufreq_driver locked.
0070  */
0071 static int longrun_set_policy(struct cpufreq_policy *policy)
0072 {
0073     u32 msr_lo, msr_hi;
0074     u32 pctg_lo, pctg_hi;
0075 
0076     if (!policy)
0077         return -EINVAL;
0078 
0079     if (longrun_high_freq <= longrun_low_freq) {
0080         /* Assume degenerate Longrun table */
0081         pctg_lo = pctg_hi = 100;
0082     } else {
0083         pctg_lo = (policy->min - longrun_low_freq) /
0084             ((longrun_high_freq - longrun_low_freq) / 100);
0085         pctg_hi = (policy->max - longrun_low_freq) /
0086             ((longrun_high_freq - longrun_low_freq) / 100);
0087     }
0088 
0089     if (pctg_hi > 100)
0090         pctg_hi = 100;
0091     if (pctg_lo > pctg_hi)
0092         pctg_lo = pctg_hi;
0093 
0094     /* performance or economy mode */
0095     rdmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
0096     msr_lo &= 0xFFFFFFFE;
0097     switch (policy->policy) {
0098     case CPUFREQ_POLICY_PERFORMANCE:
0099         msr_lo |= 0x00000001;
0100         break;
0101     case CPUFREQ_POLICY_POWERSAVE:
0102         break;
0103     }
0104     wrmsr(MSR_TMTA_LONGRUN_FLAGS, msr_lo, msr_hi);
0105 
0106     /* lower and upper boundary */
0107     rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
0108     msr_lo &= 0xFFFFFF80;
0109     msr_hi &= 0xFFFFFF80;
0110     msr_lo |= pctg_lo;
0111     msr_hi |= pctg_hi;
0112     wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
0113 
0114     return 0;
0115 }
0116 
0117 
0118 /**
0119  * longrun_verify_poliy - verifies a new CPUFreq policy
0120  * @policy: the policy to verify
0121  *
0122  * Validates a new CPUFreq policy. This function has to be called with
0123  * cpufreq_driver locked.
0124  */
0125 static int longrun_verify_policy(struct cpufreq_policy_data *policy)
0126 {
0127     if (!policy)
0128         return -EINVAL;
0129 
0130     policy->cpu = 0;
0131     cpufreq_verify_within_cpu_limits(policy);
0132 
0133     return 0;
0134 }
0135 
0136 static unsigned int longrun_get(unsigned int cpu)
0137 {
0138     u32 eax, ebx, ecx, edx;
0139 
0140     if (cpu)
0141         return 0;
0142 
0143     cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
0144     pr_debug("cpuid eax is %u\n", eax);
0145 
0146     return eax * 1000;
0147 }
0148 
0149 /**
0150  * longrun_determine_freqs - determines the lowest and highest possible core frequency
0151  * @low_freq: an int to put the lowest frequency into
0152  * @high_freq: an int to put the highest frequency into
0153  *
0154  * Determines the lowest and highest possible core frequencies on this CPU.
0155  * This is necessary to calculate the performance percentage according to
0156  * TMTA rules:
0157  * performance_pctg = (target_freq - low_freq)/(high_freq - low_freq)
0158  */
0159 static int longrun_determine_freqs(unsigned int *low_freq,
0160                               unsigned int *high_freq)
0161 {
0162     u32 msr_lo, msr_hi;
0163     u32 save_lo, save_hi;
0164     u32 eax, ebx, ecx, edx;
0165     u32 try_hi;
0166     struct cpuinfo_x86 *c = &cpu_data(0);
0167 
0168     if (!low_freq || !high_freq)
0169         return -EINVAL;
0170 
0171     if (cpu_has(c, X86_FEATURE_LRTI)) {
0172         /* if the LongRun Table Interface is present, the
0173          * detection is a bit easier:
0174          * For minimum frequency, read out the maximum
0175          * level (msr_hi), write that into "currently
0176          * selected level", and read out the frequency.
0177          * For maximum frequency, read out level zero.
0178          */
0179         /* minimum */
0180         rdmsr(MSR_TMTA_LRTI_READOUT, msr_lo, msr_hi);
0181         wrmsr(MSR_TMTA_LRTI_READOUT, msr_hi, msr_hi);
0182         rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
0183         *low_freq = msr_lo * 1000; /* to kHz */
0184 
0185         /* maximum */
0186         wrmsr(MSR_TMTA_LRTI_READOUT, 0, msr_hi);
0187         rdmsr(MSR_TMTA_LRTI_VOLT_MHZ, msr_lo, msr_hi);
0188         *high_freq = msr_lo * 1000; /* to kHz */
0189 
0190         pr_debug("longrun table interface told %u - %u kHz\n",
0191                 *low_freq, *high_freq);
0192 
0193         if (*low_freq > *high_freq)
0194             *low_freq = *high_freq;
0195         return 0;
0196     }
0197 
0198     /* set the upper border to the value determined during TSC init */
0199     *high_freq = (cpu_khz / 1000);
0200     *high_freq = *high_freq * 1000;
0201     pr_debug("high frequency is %u kHz\n", *high_freq);
0202 
0203     /* get current borders */
0204     rdmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
0205     save_lo = msr_lo & 0x0000007F;
0206     save_hi = msr_hi & 0x0000007F;
0207 
0208     /* if current perf_pctg is larger than 90%, we need to decrease the
0209      * upper limit to make the calculation more accurate.
0210      */
0211     cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
0212     /* try decreasing in 10% steps, some processors react only
0213      * on some barrier values */
0214     for (try_hi = 80; try_hi > 0 && ecx > 90; try_hi -= 10) {
0215         /* set to 0 to try_hi perf_pctg */
0216         msr_lo &= 0xFFFFFF80;
0217         msr_hi &= 0xFFFFFF80;
0218         msr_hi |= try_hi;
0219         wrmsr(MSR_TMTA_LONGRUN_CTRL, msr_lo, msr_hi);
0220 
0221         /* read out current core MHz and current perf_pctg */
0222         cpuid(0x80860007, &eax, &ebx, &ecx, &edx);
0223 
0224         /* restore values */
0225         wrmsr(MSR_TMTA_LONGRUN_CTRL, save_lo, save_hi);
0226     }
0227     pr_debug("percentage is %u %%, freq is %u MHz\n", ecx, eax);
0228 
0229     /* performance_pctg = (current_freq - low_freq)/(high_freq - low_freq)
0230      * eqals
0231      * low_freq * (1 - perf_pctg) = (cur_freq - high_freq * perf_pctg)
0232      *
0233      * high_freq * perf_pctg is stored tempoarily into "ebx".
0234      */
0235     ebx = (((cpu_khz / 1000) * ecx) / 100); /* to MHz */
0236 
0237     if ((ecx > 95) || (ecx == 0) || (eax < ebx))
0238         return -EIO;
0239 
0240     edx = ((eax - ebx) * 100) / (100 - ecx);
0241     *low_freq = edx * 1000; /* back to kHz */
0242 
0243     pr_debug("low frequency is %u kHz\n", *low_freq);
0244 
0245     if (*low_freq > *high_freq)
0246         *low_freq = *high_freq;
0247 
0248     return 0;
0249 }
0250 
0251 
0252 static int longrun_cpu_init(struct cpufreq_policy *policy)
0253 {
0254     int result = 0;
0255 
0256     /* capability check */
0257     if (policy->cpu != 0)
0258         return -ENODEV;
0259 
0260     /* detect low and high frequency */
0261     result = longrun_determine_freqs(&longrun_low_freq, &longrun_high_freq);
0262     if (result)
0263         return result;
0264 
0265     /* cpuinfo and default policy values */
0266     policy->cpuinfo.min_freq = longrun_low_freq;
0267     policy->cpuinfo.max_freq = longrun_high_freq;
0268     longrun_get_policy(policy);
0269 
0270     return 0;
0271 }
0272 
0273 
0274 static struct cpufreq_driver longrun_driver = {
0275     .flags      = CPUFREQ_CONST_LOOPS,
0276     .verify     = longrun_verify_policy,
0277     .setpolicy  = longrun_set_policy,
0278     .get        = longrun_get,
0279     .init       = longrun_cpu_init,
0280     .name       = "longrun",
0281 };
0282 
0283 static const struct x86_cpu_id longrun_ids[] = {
0284     X86_MATCH_VENDOR_FEATURE(TRANSMETA, X86_FEATURE_LONGRUN, NULL),
0285     {}
0286 };
0287 MODULE_DEVICE_TABLE(x86cpu, longrun_ids);
0288 
0289 /**
0290  * longrun_init - initializes the Transmeta Crusoe LongRun CPUFreq driver
0291  *
0292  * Initializes the LongRun support.
0293  */
0294 static int __init longrun_init(void)
0295 {
0296     if (!x86_match_cpu(longrun_ids))
0297         return -ENODEV;
0298     return cpufreq_register_driver(&longrun_driver);
0299 }
0300 
0301 
0302 /**
0303  * longrun_exit - unregisters LongRun support
0304  */
0305 static void __exit longrun_exit(void)
0306 {
0307     cpufreq_unregister_driver(&longrun_driver);
0308 }
0309 
0310 
0311 MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
0312 MODULE_DESCRIPTION("LongRun driver for Transmeta Crusoe and "
0313         "Efficeon processors.");
0314 MODULE_LICENSE("GPL");
0315 
0316 module_init(longrun_init);
0317 module_exit(longrun_exit);