[PATCH 3.0,3.2,3.4,3.10-stable] powernow-k6: correctly initialize default parameters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



upstream commit d82b922a4acc1781d368aceac2f9da43b038cab2
Author: Mikulas Patocka <mpatocka@xxxxxxxxxx>
Date:   Wed Dec 11 19:38:53 2013 -0500

    powernow-k6: correctly initialize default parameters
    
    The powernow-k6 driver used to read the initial multiplier from the
    powernow register. However, there is a problem with this:
    
    * If there was a frequency transition before, the multiplier read from the
      register corresponds to the current multiplier.
    * If there was no frequency transition since reset, the field in the
      register always reads as zero, regardless of the current multiplier that
      is set using switches on the mainboard and that the CPU is running at.
    
    The zero value corresponds to multiplier 4.5, so as a consequence, the
    powernow-k6 driver always assumes multiplier 4.5.
    
    For example, if we have 550MHz CPU with bus frequency 100MHz and
    multiplier 5.5, the powernow-k6 driver thinks that the multiplier is 4.5
    and bus frequency is 122MHz. The powernow-k6 driver then sets the
    multiplier to 4.5, underclocking the CPU to 450MHz, but reports the
    current frequency as 550MHz.
    
    There is no reliable way how to read the initial multiplier. I modified
    the driver so that it contains a table of known frequencies (based on
    parameters of existing CPUs and some common overclocking schemes) and sets
    the multiplier according to the frequency. If the frequency is unknown
    (because of unusual overclocking or underclocking), the user must supply
    the bus speed and maximum multiplier as module parameters.
    
    This patch should be backported to all stable kernels. If it doesn't
    apply cleanly, change it, or ask me to change it.
    
    Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx>
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>

---
 drivers/cpufreq/powernow-k6.c |   76 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 72 insertions(+), 4 deletions(-)

Index: linux-stable/drivers/cpufreq/powernow-k6.c
===================================================================
--- linux-stable.orig/drivers/cpufreq/powernow-k6.c	2014-04-08 22:39:01.360170059 +0200
+++ linux-stable/drivers/cpufreq/powernow-k6.c	2014-04-08 22:39:31.892433481 +0200
@@ -26,6 +26,14 @@
 static unsigned int                     busfreq;   /* FSB, in 10 kHz */
 static unsigned int                     max_multiplier;
 
+static unsigned int			param_busfreq = 0;
+static unsigned int			param_max_multiplier = 0;
+
+module_param_named(max_multiplier, param_max_multiplier, uint, S_IRUGO);
+MODULE_PARM_DESC(max_multiplier, "Maximum multiplier (allowed values: 20 30 35 40 45 50 55 60)");
+
+module_param_named(bus_frequency, param_busfreq, uint, S_IRUGO);
+MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz");
 
 /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
 static struct cpufreq_frequency_table clock_ratio[] = {
@@ -40,6 +48,27 @@ static struct cpufreq_frequency_table cl
 	{0, CPUFREQ_TABLE_END}
 };
 
+static const struct {
+	unsigned freq;
+	unsigned mult;
+} usual_frequency_table[] = {
+	{ 400000, 40 },	// 100   * 4
+	{ 450000, 45 }, // 100   * 4.5
+	{ 475000, 50 }, //  95   * 5
+	{ 500000, 50 }, // 100   * 5
+	{ 506250, 45 }, // 112.5 * 4.5
+	{ 533500, 55 }, //  97   * 5.5
+	{ 550000, 55 }, // 100   * 5.5
+	{ 562500, 50 }, // 112.5 * 5
+	{ 570000, 60 }, //  95   * 6
+	{ 600000, 60 }, // 100   * 6
+	{ 618750, 55 }, // 112.5 * 5.5
+	{ 660000, 55 }, // 120   * 5.5
+	{ 675000, 60 }, // 112.5 * 6
+	{ 720000, 60 }, // 120   * 6
+};
+
+#define FREQ_RANGE		3000
 
 /**
  * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
@@ -163,18 +192,57 @@ static int powernow_k6_target(struct cpu
 	return 0;
 }
 
-
 static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
 {
 	unsigned int i, f;
 	int result;
+	unsigned khz;
 
 	if (policy->cpu != 0)
 		return -ENODEV;
 
-	/* get frequencies */
-	max_multiplier = powernow_k6_get_cpu_multiplier();
-	busfreq = cpu_khz / max_multiplier;
+	max_multiplier = 0;
+	khz = cpu_khz;
+	for (i = 0; i < ARRAY_SIZE(usual_frequency_table); i++) {
+		if (khz >= usual_frequency_table[i].freq - FREQ_RANGE &&
+		    khz <= usual_frequency_table[i].freq + FREQ_RANGE) {
+			khz = usual_frequency_table[i].freq;
+			max_multiplier = usual_frequency_table[i].mult;
+			break;
+		}
+	}
+	if (param_max_multiplier) {
+		for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
+			if (clock_ratio[i].index == param_max_multiplier) {
+				max_multiplier = param_max_multiplier;
+				goto have_max_multiplier;
+			}
+		}
+		printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
+		return -EINVAL;
+	}
+
+	if (!max_multiplier) {
+		printk(KERN_WARNING "powernow-k6: unknown frequency %u, cannot determine current multiplier\n", khz);
+		printk(KERN_WARNING "powernow-k6: use module parameters max_multiplier and bus_frequency\n");
+		return -EOPNOTSUPP;
+	}
+
+have_max_multiplier:
+	param_max_multiplier = max_multiplier;
+
+	if (param_busfreq) {
+		if (param_busfreq >= 50000 && param_busfreq <= 150000) {
+			busfreq = param_busfreq / 10;
+			goto have_busfreq;
+		}
+		printk(KERN_ERR "powernow-k6: invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n");
+		return -EINVAL;
+	}
+
+	busfreq = khz / max_multiplier;
+have_busfreq:
+	param_busfreq = busfreq * 10;
 
 	/* table init */
 	for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {

--
To unsubscribe from this list: send the line "unsubscribe stable" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]