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