139 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * sc520_freq.c: cpufreq driver for the AMD Elan sc5204 *5 * Copyright (C) 2005 Sean Young <sean@mess.org>6 *7 * Based on elanfreq.c8 *9 * 2005-03-30: - initial revision10 */11 12#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt13 14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/init.h>17 18#include <linux/delay.h>19#include <linux/cpufreq.h>20#include <linux/timex.h>21#include <linux/io.h>22 23#include <asm/cpu_device_id.h>24#include <asm/msr.h>25 26#define MMCR_BASE 0xfffef000 /* The default base address */27#define OFFS_CPUCTL 0x2 /* CPU Control Register */28 29static __u8 __iomem *cpuctl;30 31static struct cpufreq_frequency_table sc520_freq_table[] = {32 {0, 0x01, 100000},33 {0, 0x02, 133000},34 {0, 0, CPUFREQ_TABLE_END},35};36 37static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu)38{39 u8 clockspeed_reg = *cpuctl;40 41 switch (clockspeed_reg & 0x03) {42 default:43 pr_err("error: cpuctl register has unexpected value %02x\n",44 clockspeed_reg);45 fallthrough;46 case 0x01:47 return 100000;48 case 0x02:49 return 133000;50 }51}52 53static int sc520_freq_target(struct cpufreq_policy *policy, unsigned int state)54{55 56 u8 clockspeed_reg;57 58 local_irq_disable();59 60 clockspeed_reg = *cpuctl & ~0x03;61 *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data;62 63 local_irq_enable();64 65 return 0;66}67 68/*69 * Module init and exit code70 */71 72static int sc520_freq_cpu_init(struct cpufreq_policy *policy)73{74 struct cpuinfo_x86 *c = &cpu_data(0);75 76 /* capability check */77 if (c->x86_vendor != X86_VENDOR_AMD ||78 c->x86 != 4 || c->x86_model != 9)79 return -ENODEV;80 81 /* cpuinfo and default policy values */82 policy->cpuinfo.transition_latency = 1000000; /* 1ms */83 policy->freq_table = sc520_freq_table;84 85 return 0;86}87 88 89static struct cpufreq_driver sc520_freq_driver = {90 .get = sc520_freq_get_cpu_frequency,91 .verify = cpufreq_generic_frequency_table_verify,92 .target_index = sc520_freq_target,93 .init = sc520_freq_cpu_init,94 .name = "sc520_freq",95 .attr = cpufreq_generic_attr,96};97 98static const struct x86_cpu_id sc520_ids[] = {99 X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 9, NULL),100 {}101};102MODULE_DEVICE_TABLE(x86cpu, sc520_ids);103 104static int __init sc520_freq_init(void)105{106 int err;107 108 if (!x86_match_cpu(sc520_ids))109 return -ENODEV;110 111 cpuctl = ioremap((unsigned long)(MMCR_BASE + OFFS_CPUCTL), 1);112 if (!cpuctl) {113 pr_err("sc520_freq: error: failed to remap memory\n");114 return -ENOMEM;115 }116 117 err = cpufreq_register_driver(&sc520_freq_driver);118 if (err)119 iounmap(cpuctl);120 121 return err;122}123 124 125static void __exit sc520_freq_exit(void)126{127 cpufreq_unregister_driver(&sc520_freq_driver);128 iounmap(cpuctl);129}130 131 132MODULE_LICENSE("GPL");133MODULE_AUTHOR("Sean Young <sean@mess.org>");134MODULE_DESCRIPTION("cpufreq driver for AMD's Elan sc520 CPU");135 136module_init(sc520_freq_init);137module_exit(sc520_freq_exit);138 139