^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * This file was based upon code in Powertweak Linux (http://powertweak.sf.net)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * (C) 2000-2003 Dave Jones, Arjan van de Ven, Janne Pänkälä,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Dominik Brodowski.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/cpufreq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/ioport.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/timex.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <asm/cpu_device_id.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include <asm/msr.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define POWERNOW_IOPORT 0xfff0 /* it doesn't matter where, as long
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) as it is unused */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static unsigned int busfreq; /* FSB, in 10 kHz */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) static unsigned int max_multiplier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static unsigned int param_busfreq = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) static unsigned int param_max_multiplier = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) module_param_named(max_multiplier, param_max_multiplier, uint, S_IRUGO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) MODULE_PARM_DESC(max_multiplier, "Maximum multiplier (allowed values: 20 30 35 40 45 50 55 60)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) module_param_named(bus_frequency, param_busfreq, uint, S_IRUGO);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static struct cpufreq_frequency_table clock_ratio[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) {0, 60, /* 110 -> 6.0x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) {0, 55, /* 011 -> 5.5x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) {0, 50, /* 001 -> 5.0x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) {0, 45, /* 000 -> 4.5x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) {0, 40, /* 010 -> 4.0x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) {0, 35, /* 111 -> 3.5x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {0, 30, /* 101 -> 3.0x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {0, 20, /* 100 -> 2.0x */ 0},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {0, 0, CPUFREQ_TABLE_END}
^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) static const u8 index_to_register[8] = { 6, 3, 1, 0, 2, 7, 5, 4 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) static const u8 register_to_index[8] = { 3, 2, 4, 1, 7, 6, 0, 5 };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static const struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) unsigned freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) unsigned mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) } usual_frequency_table[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) { 350000, 35 }, // 100 * 3.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) { 400000, 40 }, // 100 * 4
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) { 450000, 45 }, // 100 * 4.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) { 475000, 50 }, // 95 * 5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) { 500000, 50 }, // 100 * 5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) { 506250, 45 }, // 112.5 * 4.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) { 533500, 55 }, // 97 * 5.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) { 550000, 55 }, // 100 * 5.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) { 562500, 50 }, // 112.5 * 5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) { 570000, 60 }, // 95 * 6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) { 600000, 60 }, // 100 * 6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) { 618750, 55 }, // 112.5 * 5.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) { 660000, 55 }, // 120 * 5.5
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) { 675000, 60 }, // 112.5 * 6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) { 720000, 60 }, // 120 * 6
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) #define FREQ_RANGE 3000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) * Returns the current setting of the frequency multiplier. Core clock
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) * speed is frequency of the Front-Side Bus multiplied with this value.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) static int powernow_k6_get_cpu_multiplier(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) unsigned long invalue = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) u32 msrval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) msrval = POWERNOW_IOPORT + 0x1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) invalue = inl(POWERNOW_IOPORT + 0x8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) msrval = POWERNOW_IOPORT + 0x0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return clock_ratio[register_to_index[(invalue >> 5)&7]].driver_data;
^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) static void powernow_k6_set_cpu_multiplier(unsigned int best_i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) unsigned long outvalue, invalue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) unsigned long msrval;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) unsigned long cr0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) /* we now need to transform best_i to the BVC format, see AMD#23446 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * The processor doesn't respond to inquiry cycles while changing the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) * frequency, so we must disable cache.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) local_irq_disable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) cr0 = read_cr0();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) write_cr0(cr0 | X86_CR0_CD);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) wbinvd();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) outvalue = (1<<12) | (1<<10) | (1<<9) | (index_to_register[best_i]<<5);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) msrval = POWERNOW_IOPORT + 0x1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) invalue = inl(POWERNOW_IOPORT + 0x8);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) invalue = invalue & 0x1f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) outvalue = outvalue | invalue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) outl(outvalue, (POWERNOW_IOPORT + 0x8));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) msrval = POWERNOW_IOPORT + 0x0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) write_cr0(cr0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) local_irq_enable();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) * powernow_k6_target - set the PowerNow! multiplier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) * @best_i: clock_ratio[best_i] is the target multiplier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) * Tries to change the PowerNow! multiplier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) static int powernow_k6_target(struct cpufreq_policy *policy,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) unsigned int best_i)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (clock_ratio[best_i].driver_data > max_multiplier) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) pr_err("invalid target frequency\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) powernow_k6_set_cpu_multiplier(best_i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) struct cpufreq_frequency_table *pos;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) unsigned int i, f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) unsigned khz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) if (policy->cpu != 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) max_multiplier = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) khz = cpu_khz;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) for (i = 0; i < ARRAY_SIZE(usual_frequency_table); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (khz >= usual_frequency_table[i].freq - FREQ_RANGE &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) khz <= usual_frequency_table[i].freq + FREQ_RANGE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) khz = usual_frequency_table[i].freq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) max_multiplier = usual_frequency_table[i].mult;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) if (param_max_multiplier) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) cpufreq_for_each_entry(pos, clock_ratio)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if (pos->driver_data == param_max_multiplier) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) max_multiplier = param_max_multiplier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) goto have_max_multiplier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) pr_err("invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) if (!max_multiplier) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) pr_warn("unknown frequency %u, cannot determine current multiplier\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) khz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) pr_warn("use module parameters max_multiplier and bus_frequency\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) return -EOPNOTSUPP;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) have_max_multiplier:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) param_max_multiplier = max_multiplier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) if (param_busfreq) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (param_busfreq >= 50000 && param_busfreq <= 150000) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) busfreq = param_busfreq / 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) goto have_busfreq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) pr_err("invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) busfreq = khz / max_multiplier;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) have_busfreq:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) param_busfreq = busfreq * 10;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) /* table init */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) cpufreq_for_each_entry(pos, clock_ratio) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) f = pos->driver_data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) if (f > max_multiplier)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) pos->frequency = CPUFREQ_ENTRY_INVALID;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) pos->frequency = busfreq * f;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) /* cpuinfo and default policy values */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) policy->cpuinfo.transition_latency = 500000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) policy->freq_table = clock_ratio;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) static int powernow_k6_cpu_exit(struct cpufreq_policy *policy)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) unsigned int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (clock_ratio[i].driver_data == max_multiplier) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) struct cpufreq_freqs freqs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) freqs.old = policy->cur;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) freqs.new = clock_ratio[i].frequency;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) freqs.flags = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) cpufreq_freq_transition_begin(policy, &freqs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) powernow_k6_target(policy, i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) cpufreq_freq_transition_end(policy, &freqs, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) static unsigned int powernow_k6_get(unsigned int cpu)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) unsigned int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246) ret = (busfreq * powernow_k6_get_cpu_multiplier());
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) static struct cpufreq_driver powernow_k6_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) .verify = cpufreq_generic_frequency_table_verify,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252) .target_index = powernow_k6_target,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) .init = powernow_k6_cpu_init,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) .exit = powernow_k6_cpu_exit,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) .get = powernow_k6_get,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) .name = "powernow-k6",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) .attr = cpufreq_generic_attr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) static const struct x86_cpu_id powernow_k6_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) X86_MATCH_VENDOR_FAM_MODEL(AMD, 5, 12, NULL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) X86_MATCH_VENDOR_FAM_MODEL(AMD, 5, 13, NULL),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) MODULE_DEVICE_TABLE(x86cpu, powernow_k6_ids);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268) * powernow_k6_init - initializes the k6 PowerNow! CPUFreq driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) * Initializes the K6 PowerNow! support. Returns -ENODEV on unsupported
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) * devices, -EINVAL or -ENOMEM on problems during initiatization, and zero
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) * on success.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) static int __init powernow_k6_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276) if (!x86_match_cpu(powernow_k6_ids))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) if (!request_region(POWERNOW_IOPORT, 16, "PowerNow!")) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) pr_info("PowerNow IOPORT region already used\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281) return -EIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) if (cpufreq_register_driver(&powernow_k6_driver)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) release_region(POWERNOW_IOPORT, 16);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294) * powernow_k6_exit - unregisters AMD K6-2+/3+ PowerNow! support
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) * Unregisters AMD K6-2+ / K6-3+ PowerNow! support.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) static void __exit powernow_k6_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) cpufreq_unregister_driver(&powernow_k6_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) release_region(POWERNOW_IOPORT, 16);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) MODULE_AUTHOR("Arjan van de Ven, Dave Jones, "
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306) "Dominik Brodowski <linux@brodo.de>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) MODULE_DESCRIPTION("PowerNow! driver for AMD K6-2+ / K6-3+ processors.");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) module_init(powernow_k6_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) module_exit(powernow_k6_exit);