0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0013
0014 #include <linux/kernel.h>
0015 #include <linux/module.h>
0016 #include <linux/init.h>
0017
0018 #include <linux/delay.h>
0019 #include <linux/cpufreq.h>
0020 #include <linux/timex.h>
0021 #include <linux/io.h>
0022
0023 #include <asm/cpu_device_id.h>
0024 #include <asm/msr.h>
0025
0026 #define MMCR_BASE 0xfffef000
0027 #define OFFS_CPUCTL 0x2
0028
0029 static __u8 __iomem *cpuctl;
0030
0031 static struct cpufreq_frequency_table sc520_freq_table[] = {
0032 {0, 0x01, 100000},
0033 {0, 0x02, 133000},
0034 {0, 0, CPUFREQ_TABLE_END},
0035 };
0036
0037 static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)
0038 {
0039 u8 clockspeed_reg = *cpuctl;
0040
0041 switch (clockspeed_reg & 0x03) {
0042 default:
0043 pr_err("error: cpuctl register has unexpected value %02x\n",
0044 clockspeed_reg);
0045 fallthrough;
0046 case 0x01:
0047 return 100000;
0048 case 0x02:
0049 return 133000;
0050 }
0051 }
0052
0053 static int sc520_freq_target(struct cpufreq_policy *policy, unsigned int state)
0054 {
0055
0056 u8 clockspeed_reg;
0057
0058 local_irq_disable();
0059
0060 clockspeed_reg = *cpuctl & ~0x03;
0061 *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data;
0062
0063 local_irq_enable();
0064
0065 return 0;
0066 }
0067
0068
0069
0070
0071
0072 static int sc520_freq_cpu_init(struct cpufreq_policy *policy)
0073 {
0074 struct cpuinfo_x86 *c = &cpu_data(0);
0075
0076
0077 if (c->x86_vendor != X86_VENDOR_AMD ||
0078 c->x86 != 4 || c->x86_model != 9)
0079 return -ENODEV;
0080
0081
0082 policy->cpuinfo.transition_latency = 1000000;
0083 policy->freq_table = sc520_freq_table;
0084
0085 return 0;
0086 }
0087
0088
0089 static struct cpufreq_driver sc520_freq_driver = {
0090 .get = sc520_freq_get_cpu_frequency,
0091 .verify = cpufreq_generic_frequency_table_verify,
0092 .target_index = sc520_freq_target,
0093 .init = sc520_freq_cpu_init,
0094 .name = "sc520_freq",
0095 .attr = cpufreq_generic_attr,
0096 };
0097
0098 static const struct x86_cpu_id sc520_ids[] = {
0099 X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 9, NULL),
0100 {}
0101 };
0102 MODULE_DEVICE_TABLE(x86cpu, sc520_ids);
0103
0104 static int __init sc520_freq_init(void)
0105 {
0106 int err;
0107
0108 if (!x86_match_cpu(sc520_ids))
0109 return -ENODEV;
0110
0111 cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);
0112 if (!cpuctl) {
0113 pr_err("sc520_freq: error: failed to remap memory\n");
0114 return -ENOMEM;
0115 }
0116
0117 err = cpufreq_register_driver(&sc520_freq_driver);
0118 if (err)
0119 iounmap(cpuctl);
0120
0121 return err;
0122 }
0123
0124
0125 static void __exit sc520_freq_exit(void)
0126 {
0127 cpufreq_unregister_driver(&sc520_freq_driver);
0128 iounmap(cpuctl);
0129 }
0130
0131
0132 MODULE_LICENSE("GPL");
0133 MODULE_AUTHOR("Sean Young <sean@mess.org>");
0134 MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");
0135
0136 module_init(sc520_freq_init);
0137 module_exit(sc520_freq_exit);
0138