Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
^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);