Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *  elanfreq:   cpufreq driver for the AMD ELAN family
0004  *
0005  *  (c) Copyright 2002 Robert Schwebel <r.schwebel@pengutronix.de>
0006  *
0007  *  Parts of this code are (c) Sven Geggus <sven@geggus.net>
0008  *
0009  *      All Rights Reserved.
0010  *
0011  *  2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel
0012  */
0013 
0014 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0015 
0016 #include <linux/kernel.h>
0017 #include <linux/module.h>
0018 #include <linux/init.h>
0019 
0020 #include <linux/delay.h>
0021 #include <linux/cpufreq.h>
0022 
0023 #include <asm/cpu_device_id.h>
0024 #include <asm/msr.h>
0025 #include <linux/timex.h>
0026 #include <linux/io.h>
0027 
0028 #define REG_CSCIR 0x22      /* Chip Setup and Control Index Register    */
0029 #define REG_CSCDR 0x23      /* Chip Setup and Control Data  Register    */
0030 
0031 /* Module parameter */
0032 static int max_freq;
0033 
0034 struct s_elan_multiplier {
0035     int clock;      /* frequency in kHz                         */
0036     int val40h;     /* PMU Force Mode register                  */
0037     int val80h;     /* CPU Clock Speed Register                 */
0038 };
0039 
0040 /*
0041  * It is important that the frequencies
0042  * are listed in ascending order here!
0043  */
0044 static struct s_elan_multiplier elan_multiplier[] = {
0045     {1000,  0x02,   0x18},
0046     {2000,  0x02,   0x10},
0047     {4000,  0x02,   0x08},
0048     {8000,  0x00,   0x00},
0049     {16000, 0x00,   0x02},
0050     {33000, 0x00,   0x04},
0051     {66000, 0x01,   0x04},
0052     {99000, 0x01,   0x05}
0053 };
0054 
0055 static struct cpufreq_frequency_table elanfreq_table[] = {
0056     {0, 0,  1000},
0057     {0, 1,  2000},
0058     {0, 2,  4000},
0059     {0, 3,  8000},
0060     {0, 4,  16000},
0061     {0, 5,  33000},
0062     {0, 6,  66000},
0063     {0, 7,  99000},
0064     {0, 0,  CPUFREQ_TABLE_END},
0065 };
0066 
0067 
0068 /**
0069  *  elanfreq_get_cpu_frequency: determine current cpu speed
0070  *
0071  *  Finds out at which frequency the CPU of the Elan SOC runs
0072  *  at the moment. Frequencies from 1 to 33 MHz are generated
0073  *  the normal way, 66 and 99 MHz are called "Hyperspeed Mode"
0074  *  and have the rest of the chip running with 33 MHz.
0075  */
0076 
0077 static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu)
0078 {
0079     u8 clockspeed_reg;    /* Clock Speed Register */
0080 
0081     local_irq_disable();
0082     outb_p(0x80, REG_CSCIR);
0083     clockspeed_reg = inb_p(REG_CSCDR);
0084     local_irq_enable();
0085 
0086     if ((clockspeed_reg & 0xE0) == 0xE0)
0087         return 0;
0088 
0089     /* Are we in CPU clock multiplied mode (66/99 MHz)? */
0090     if ((clockspeed_reg & 0xE0) == 0xC0) {
0091         if ((clockspeed_reg & 0x01) == 0)
0092             return 66000;
0093         else
0094             return 99000;
0095     }
0096 
0097     /* 33 MHz is not 32 MHz... */
0098     if ((clockspeed_reg & 0xE0) == 0xA0)
0099         return 33000;
0100 
0101     return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000;
0102 }
0103 
0104 
0105 static int elanfreq_target(struct cpufreq_policy *policy,
0106                 unsigned int state)
0107 {
0108     /*
0109      * Access to the Elan's internal registers is indexed via
0110      * 0x22: Chip Setup & Control Register Index Register (CSCI)
0111      * 0x23: Chip Setup & Control Register Data  Register (CSCD)
0112      *
0113      */
0114 
0115     /*
0116      * 0x40 is the Power Management Unit's Force Mode Register.
0117      * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency)
0118      */
0119 
0120     local_irq_disable();
0121     outb_p(0x40, REG_CSCIR);        /* Disable hyperspeed mode */
0122     outb_p(0x00, REG_CSCDR);
0123     local_irq_enable();     /* wait till internal pipelines and */
0124     udelay(1000);           /* buffers have cleaned up          */
0125 
0126     local_irq_disable();
0127 
0128     /* now, set the CPU clock speed register (0x80) */
0129     outb_p(0x80, REG_CSCIR);
0130     outb_p(elan_multiplier[state].val80h, REG_CSCDR);
0131 
0132     /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */
0133     outb_p(0x40, REG_CSCIR);
0134     outb_p(elan_multiplier[state].val40h, REG_CSCDR);
0135     udelay(10000);
0136     local_irq_enable();
0137 
0138     return 0;
0139 }
0140 /*
0141  *  Module init and exit code
0142  */
0143 
0144 static int elanfreq_cpu_init(struct cpufreq_policy *policy)
0145 {
0146     struct cpuinfo_x86 *c = &cpu_data(0);
0147     struct cpufreq_frequency_table *pos;
0148 
0149     /* capability check */
0150     if ((c->x86_vendor != X86_VENDOR_AMD) ||
0151         (c->x86 != 4) || (c->x86_model != 10))
0152         return -ENODEV;
0153 
0154     /* max freq */
0155     if (!max_freq)
0156         max_freq = elanfreq_get_cpu_frequency(0);
0157 
0158     /* table init */
0159     cpufreq_for_each_entry(pos, elanfreq_table)
0160         if (pos->frequency > max_freq)
0161             pos->frequency = CPUFREQ_ENTRY_INVALID;
0162 
0163     policy->freq_table = elanfreq_table;
0164     return 0;
0165 }
0166 
0167 
0168 #ifndef MODULE
0169 /**
0170  * elanfreq_setup - elanfreq command line parameter parsing
0171  *
0172  * elanfreq command line parameter.  Use:
0173  *  elanfreq=66000
0174  * to set the maximum CPU frequency to 66 MHz. Note that in
0175  * case you do not give this boot parameter, the maximum
0176  * frequency will fall back to _current_ CPU frequency which
0177  * might be lower. If you build this as a module, use the
0178  * max_freq module parameter instead.
0179  */
0180 static int __init elanfreq_setup(char *str)
0181 {
0182     max_freq = simple_strtoul(str, &str, 0);
0183     pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n");
0184     return 1;
0185 }
0186 __setup("elanfreq=", elanfreq_setup);
0187 #endif
0188 
0189 
0190 static struct cpufreq_driver elanfreq_driver = {
0191     .get        = elanfreq_get_cpu_frequency,
0192     .flags      = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
0193     .verify     = cpufreq_generic_frequency_table_verify,
0194     .target_index   = elanfreq_target,
0195     .init       = elanfreq_cpu_init,
0196     .name       = "elanfreq",
0197     .attr       = cpufreq_generic_attr,
0198 };
0199 
0200 static const struct x86_cpu_id elan_id[] = {
0201     X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 10, NULL),
0202     {}
0203 };
0204 MODULE_DEVICE_TABLE(x86cpu, elan_id);
0205 
0206 static int __init elanfreq_init(void)
0207 {
0208     if (!x86_match_cpu(elan_id))
0209         return -ENODEV;
0210     return cpufreq_register_driver(&elanfreq_driver);
0211 }
0212 
0213 
0214 static void __exit elanfreq_exit(void)
0215 {
0216     cpufreq_unregister_driver(&elanfreq_driver);
0217 }
0218 
0219 
0220 module_param(max_freq, int, 0444);
0221 
0222 MODULE_LICENSE("GPL");
0223 MODULE_AUTHOR("Robert Schwebel <r.schwebel@pengutronix.de>, "
0224         "Sven Geggus <sven@geggus.net>");
0225 MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs");
0226 
0227 module_init(elanfreq_init);
0228 module_exit(elanfreq_exit);