blob: 5d515fc348364c36f24b15ef02630730480e0fca [file] [log] [blame]
Thomas Gleixner4f190482019-05-27 08:55:14 +02001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * AMD K7 Powernow driver.
Dave Jonesf4432c52008-10-20 13:31:45 -04004 * (C) 2003 Dave Jones on behalf of SuSE Labs.
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Based upon datasheets & sample CPUs kindly provided by AMD.
7 *
Dave Jonesb9e76382009-01-18 00:32:26 -05008 * Errata 5:
9 * CPU may fail to execute a FID/VID change in presence of interrupt.
10 * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
11 * Errata 15:
12 * CPU with half frequency multipliers may hang upon wakeup from disconnect.
13 * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
Linus Torvalds1da177e2005-04-16 15:20:36 -070014 */
15
Joe Perches1c5864e2016-04-05 13:28:25 -070016#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/moduleparam.h>
21#include <linux/init.h>
22#include <linux/cpufreq.h>
23#include <linux/slab.h>
24#include <linux/string.h>
25#include <linux/dmi.h>
Dave Jonesb9e76382009-01-18 00:32:26 -050026#include <linux/timex.h>
27#include <linux/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Dave Jonesb9e76382009-01-18 00:32:26 -050029#include <asm/timer.h> /* Needed for recalibrate_cpu_khz() */
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <asm/msr.h>
Andi Kleenfa8031a2012-01-26 00:09:12 +010031#include <asm/cpu_device_id.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#ifdef CONFIG_X86_POWERNOW_K7_ACPI
34#include <linux/acpi.h>
35#include <acpi/processor.h>
36#endif
37
38#include "powernow-k7.h"
39
Linus Torvalds1da177e2005-04-16 15:20:36 -070040struct psb_s {
41 u8 signature[10];
42 u8 tableversion;
43 u8 flags;
44 u16 settlingtime;
45 u8 reserved1;
46 u8 numpst;
47};
48
49struct pst_s {
50 u32 cpuid;
51 u8 fsbspeed;
52 u8 maxfid;
53 u8 startvid;
54 u8 numpstates;
55};
56
57#ifdef CONFIG_X86_POWERNOW_K7_ACPI
58union powernow_acpi_control_t {
59 struct {
60 unsigned long fid:5,
Dave Jonesb9e76382009-01-18 00:32:26 -050061 vid:5,
62 sgtc:20,
63 res1:2;
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 } bits;
65 unsigned long val;
66};
67#endif
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/* divide by 1000 to get VCore voltage in V. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050070static const int mobile_vid_table[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
72 1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
73 1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
74 1075, 1050, 1025, 1000, 975, 950, 925, 0,
75};
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77/* divide by 10 to get FID. */
Dave Jonesbd5ab262007-02-22 19:11:16 -050078static const int fid_codes[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 110, 115, 120, 125, 50, 55, 60, 65,
80 70, 75, 80, 85, 90, 95, 100, 105,
81 30, 190, 40, 200, 130, 135, 140, 210,
82 150, 225, 160, 165, 170, 180, -1, -1,
83};
84
85/* This parameter is used in order to force ACPI instead of legacy method for
86 * configuration purpose.
87 */
88
89static int acpi_force;
90
91static struct cpufreq_frequency_table *powernow_table;
92
93static unsigned int can_scale_bus;
94static unsigned int can_scale_vid;
Dave Jonesfff78ad2009-01-17 22:28:42 -050095static unsigned int minimum_speed = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096static unsigned int maximum_speed;
97static unsigned int number_scales;
98static unsigned int fsb;
99static unsigned int latency;
100static char have_a0;
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102static int check_fsb(unsigned int fsbspeed)
103{
104 int delta;
105 unsigned int f = fsb / 1000;
106
107 delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
Dave Jonesb9e76382009-01-18 00:32:26 -0500108 return delta < 5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109}
110
Andi Kleenfa8031a2012-01-26 00:09:12 +0100111static const struct x86_cpu_id powernow_k7_cpuids[] = {
Thomas Gleixnerb11d77f2020-03-24 14:51:51 +0100112 X86_MATCH_VENDOR_FAM(AMD, 6, NULL),
Andi Kleenfa8031a2012-01-26 00:09:12 +0100113 {}
114};
115MODULE_DEVICE_TABLE(x86cpu, powernow_k7_cpuids);
116
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117static int check_powernow(void)
118{
Mike Travis92cb7612007-10-19 20:35:04 +0200119 struct cpuinfo_x86 *c = &cpu_data(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 unsigned int maxei, eax, ebx, ecx, edx;
121
Andi Kleenfa8031a2012-01-26 00:09:12 +0100122 if (!x86_match_cpu(powernow_k7_cpuids))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124
125 /* Get maximum capabilities */
Dave Jonesb9e76382009-01-18 00:32:26 -0500126 maxei = cpuid_eax(0x80000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 if (maxei < 0x80000007) { /* Any powernow info ? */
128#ifdef MODULE
Joe Perches1c5864e2016-04-05 13:28:25 -0700129 pr_info("No powernow capabilities detected\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130#endif
131 return 0;
132 }
133
Jia Zhangb3991512018-01-01 09:52:10 +0800134 if ((c->x86_model == 6) && (c->x86_stepping == 0)) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700135 pr_info("K7 660[A0] core detected, enabling errata workarounds\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 have_a0 = 1;
137 }
138
139 cpuid(0x80000007, &eax, &ebx, &ecx, &edx);
140
141 /* Check we can actually do something before we say anything.*/
142 if (!(edx & (1 << 1 | 1 << 2)))
143 return 0;
144
Joe Perches1c5864e2016-04-05 13:28:25 -0700145 pr_info("PowerNOW! Technology present. Can scale: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
147 if (edx & 1 << 1) {
Joe Perchesb49c22a2016-04-05 13:28:24 -0700148 pr_cont("frequency");
Dave Jonesb9e76382009-01-18 00:32:26 -0500149 can_scale_bus = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 }
151
152 if ((edx & (1 << 1 | 1 << 2)) == 0x6)
Joe Perchesb49c22a2016-04-05 13:28:24 -0700153 pr_cont(" and ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 if (edx & 1 << 2) {
Joe Perchesb49c22a2016-04-05 13:28:24 -0700156 pr_cont("voltage");
Dave Jonesb9e76382009-01-18 00:32:26 -0500157 can_scale_vid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 }
159
Joe Perchesb49c22a2016-04-05 13:28:24 -0700160 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 return 1;
162}
163
Dave Jonesd38e73e2009-04-23 13:36:12 -0400164#ifdef CONFIG_X86_POWERNOW_K7_ACPI
Dave Jonesb9e76382009-01-18 00:32:26 -0500165static void invalidate_entry(unsigned int entry)
166{
167 powernow_table[entry].frequency = CPUFREQ_ENTRY_INVALID;
168}
Dave Jonesd38e73e2009-04-23 13:36:12 -0400169#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Dave Jonesb9e76382009-01-18 00:32:26 -0500171static int get_ranges(unsigned char *pst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172{
173 unsigned int j;
174 unsigned int speed;
175 u8 fid, vid;
176
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530177 powernow_table = kzalloc((sizeof(*powernow_table) *
Dave Jonesb9e76382009-01-18 00:32:26 -0500178 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 if (!powernow_table)
180 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
Dave Jonesb9e76382009-01-18 00:32:26 -0500182 for (j = 0 ; j < number_scales; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 fid = *pst++;
184
185 powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
Viresh Kumar50701582013-03-30 16:25:15 +0530186 powernow_table[j].driver_data = fid; /* lower 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 speed = powernow_table[j].frequency;
189
Dave Jonesb9e76382009-01-18 00:32:26 -0500190 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191#ifdef CONFIG_X86_POWERNOW_K7_ACPI
192 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500193 invalidate_entry(j);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194#endif
195 }
196
197 if (speed < minimum_speed)
198 minimum_speed = speed;
199 if (speed > maximum_speed)
200 maximum_speed = speed;
201
202 vid = *pst++;
Viresh Kumar50701582013-03-30 16:25:15 +0530203 powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200205 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500206 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
207 fid_codes[fid] % 10, speed/1000, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 mobile_vid_table[vid]/1000,
209 mobile_vid_table[vid]%1000);
210 }
211 powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
Viresh Kumar50701582013-03-30 16:25:15 +0530212 powernow_table[number_scales].driver_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
214 return 0;
215}
216
217
218static void change_FID(int fid)
219{
220 union msr_fidvidctl fidvidctl;
221
Dave Jonesb9e76382009-01-18 00:32:26 -0500222 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 if (fidvidctl.bits.FID != fid) {
224 fidvidctl.bits.SGTC = latency;
225 fidvidctl.bits.FID = fid;
226 fidvidctl.bits.VIDC = 0;
227 fidvidctl.bits.FIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500228 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 }
230}
231
232
233static void change_VID(int vid)
234{
235 union msr_fidvidctl fidvidctl;
236
Dave Jonesb9e76382009-01-18 00:32:26 -0500237 rdmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 if (fidvidctl.bits.VID != vid) {
239 fidvidctl.bits.SGTC = latency;
240 fidvidctl.bits.VID = vid;
241 fidvidctl.bits.FIDC = 0;
242 fidvidctl.bits.VIDC = 1;
Dave Jonesb9e76382009-01-18 00:32:26 -0500243 wrmsrl(MSR_K7_FID_VID_CTL, fidvidctl.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 }
245}
246
247
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +0530248static int powernow_target(struct cpufreq_policy *policy, unsigned int index)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
250 u8 fid, vid;
251 struct cpufreq_freqs freqs;
252 union msr_fidvidstatus fidvidstatus;
253 int cfid;
254
255 /* fid are the lower 8 bits of the index we stored into
256 * the cpufreq frequency table in powernow_decode_bios,
257 * vid are the upper 8 bits.
258 */
259
Viresh Kumar50701582013-03-30 16:25:15 +0530260 fid = powernow_table[index].driver_data & 0xFF;
261 vid = (powernow_table[index].driver_data & 0xFF00) >> 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Dave Jonesb9e76382009-01-18 00:32:26 -0500263 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 cfid = fidvidstatus.bits.CFID;
265 freqs.old = fsb * fid_codes[cfid] / 10;
266
267 freqs.new = powernow_table[index].frequency;
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 /* Now do the magic poking into the MSRs. */
270
271 if (have_a0 == 1) /* A0 errata 5 */
272 local_irq_disable();
273
274 if (freqs.old > freqs.new) {
275 /* Going down, so change FID first */
276 change_FID(fid);
277 change_VID(vid);
278 } else {
279 /* Going up, so change VID first */
280 change_VID(vid);
281 change_FID(fid);
282 }
283
284
285 if (have_a0 == 1)
286 local_irq_enable();
287
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +0530288 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289}
290
291
292#ifdef CONFIG_X86_POWERNOW_K7_ACPI
293
294static struct acpi_processor_performance *acpi_processor_perf;
295
296static int powernow_acpi_init(void)
297{
298 int i;
299 int retval = 0;
300 union powernow_acpi_control_t pc;
301
302 if (acpi_processor_perf != NULL && powernow_table != NULL) {
303 retval = -EINVAL;
304 goto err0;
305 }
306
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530307 acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 if (!acpi_processor_perf) {
309 retval = -ENOMEM;
310 goto err0;
311 }
312
Yinghai Lueaa95842009-06-06 14:51:36 -0700313 if (!zalloc_cpumask_var(&acpi_processor_perf->shared_cpu_map,
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800314 GFP_KERNEL)) {
315 retval = -ENOMEM;
316 goto err05;
317 }
318
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
320 retval = -EIO;
321 goto err1;
322 }
323
Dave Jonesb9e76382009-01-18 00:32:26 -0500324 if (acpi_processor_perf->control_register.space_id !=
325 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 retval = -ENODEV;
327 goto err2;
328 }
329
Dave Jonesb9e76382009-01-18 00:32:26 -0500330 if (acpi_processor_perf->status_register.space_id !=
331 ACPI_ADR_SPACE_FIXED_HARDWARE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 retval = -ENODEV;
333 goto err2;
334 }
335
336 number_scales = acpi_processor_perf->state_count;
337
338 if (number_scales < 2) {
339 retval = -ENODEV;
340 goto err2;
341 }
342
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530343 powernow_table = kzalloc((sizeof(*powernow_table) *
Dave Jonesb9e76382009-01-18 00:32:26 -0500344 (number_scales + 1)), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 if (!powernow_table) {
346 retval = -ENOMEM;
347 goto err2;
348 }
349
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 pc.val = (unsigned long) acpi_processor_perf->states[0].control;
351 for (i = 0; i < number_scales; i++) {
352 u8 fid, vid;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100353 struct acpi_processor_px *state =
354 &acpi_processor_perf->states[i];
355 unsigned int speed, speed_mhz;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Daniel Drakedc2585e2007-05-02 23:19:05 +0100357 pc.val = (unsigned long) state->control;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200358 pr_debug("acpi: P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 i,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100360 (u32) state->core_frequency,
361 (u32) state->power,
362 (u32) state->transition_latency,
363 (u32) state->control,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 pc.bits.sgtc);
365
366 vid = pc.bits.vid;
367 fid = pc.bits.fid;
368
369 powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
Viresh Kumar50701582013-03-30 16:25:15 +0530370 powernow_table[i].driver_data = fid; /* lower 8 bits */
371 powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373 speed = powernow_table[i].frequency;
Daniel Drakedc2585e2007-05-02 23:19:05 +0100374 speed_mhz = speed / 1000;
375
376 /* processor_perflib will multiply the MHz value by 1000 to
377 * get a KHz value (e.g. 1266000). However, powernow-k7 works
378 * with true KHz values (e.g. 1266768). To ensure that all
379 * powernow frequencies are available, we must ensure that
380 * ACPI doesn't restrict them, so we round up the MHz value
381 * to ensure that perflib's computed KHz value is greater than
382 * or equal to powernow's KHz value.
383 */
384 if (speed % 1000 > 0)
385 speed_mhz++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Dave Jonesb9e76382009-01-18 00:32:26 -0500387 if ((fid_codes[fid] % 10) == 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 if (have_a0 == 1)
Dave Jonesb9e76382009-01-18 00:32:26 -0500389 invalidate_entry(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 }
391
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200392 pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
Dave Jones32ee8c32006-02-28 00:43:23 -0500393 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
Daniel Drakedc2585e2007-05-02 23:19:05 +0100394 fid_codes[fid] % 10, speed_mhz, vid,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 mobile_vid_table[vid]/1000,
396 mobile_vid_table[vid]%1000);
397
Daniel Drakedc2585e2007-05-02 23:19:05 +0100398 if (state->core_frequency != speed_mhz) {
399 state->core_frequency = speed_mhz;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200400 pr_debug(" Corrected ACPI frequency to %d\n",
Daniel Drakedc2585e2007-05-02 23:19:05 +0100401 speed_mhz);
402 }
403
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 if (latency < pc.bits.sgtc)
405 latency = pc.bits.sgtc;
406
407 if (speed < minimum_speed)
408 minimum_speed = speed;
409 if (speed > maximum_speed)
410 maximum_speed = speed;
411 }
412
413 powernow_table[i].frequency = CPUFREQ_TABLE_END;
Viresh Kumar50701582013-03-30 16:25:15 +0530414 powernow_table[i].driver_data = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415
416 /* notify BIOS that we exist */
417 acpi_processor_notify_smm(THIS_MODULE);
418
419 return 0;
420
421err2:
Rafael J. Wysockib2f8dc42015-07-22 22:11:16 +0200422 acpi_processor_unregister_performance(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423err1:
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800424 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
425err05:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426 kfree(acpi_processor_perf);
427err0:
Joe Perches1c5864e2016-04-05 13:28:25 -0700428 pr_warn("ACPI perflib can not be used on this platform\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 acpi_processor_perf = NULL;
430 return retval;
431}
432#else
433static int powernow_acpi_init(void)
434{
Joe Perches1c5864e2016-04-05 13:28:25 -0700435 pr_info("no support for ACPI processor found - please recompile your kernel with ACPI processor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 return -EINVAL;
437}
438#endif
439
Dave Jonesb9e76382009-01-18 00:32:26 -0500440static void print_pst_entry(struct pst_s *pst, unsigned int j)
441{
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200442 pr_debug("PST:%d (@%p)\n", j, pst);
443 pr_debug(" cpuid: 0x%x fsb: %d maxFID: 0x%x startvid: 0x%x\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500444 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);
445}
446
447static int powernow_decode_bios(int maxfid, int startvid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
449 struct psb_s *psb;
450 struct pst_s *pst;
451 unsigned int i, j;
452 unsigned char *p;
453 unsigned int etuple;
454 unsigned int ret;
455
456 etuple = cpuid_eax(0x80000001);
457
Dave Jonesb9e76382009-01-18 00:32:26 -0500458 for (i = 0xC0000; i < 0xffff0 ; i += 16) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
460 p = phys_to_virt(i);
461
Dave Jonesb9e76382009-01-18 00:32:26 -0500462 if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200463 pr_debug("Found PSB header at %p\n", p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 psb = (struct psb_s *) p;
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200465 pr_debug("Table version: 0x%x\n", psb->tableversion);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 if (psb->tableversion != 0x12) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700467 pr_info("Sorry, only v1.2 tables supported right now\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 return -ENODEV;
469 }
470
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200471 pr_debug("Flags: 0x%x\n", psb->flags);
Dave Jonesb9e76382009-01-18 00:32:26 -0500472 if ((psb->flags & 1) == 0)
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200473 pr_debug("Mobile voltage regulator\n");
Dave Jonesb9e76382009-01-18 00:32:26 -0500474 else
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200475 pr_debug("Desktop voltage regulator\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477 latency = psb->settlingtime;
478 if (latency < 100) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700479 pr_info("BIOS set settling time to %d microseconds. Should be at least 100. Correcting.\n",
Joe Perchesb49c22a2016-04-05 13:28:24 -0700480 latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 latency = 100;
482 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200483 pr_debug("Settling Time: %d microseconds.\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500484 psb->settlingtime);
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200485 pr_debug("Has %d PST tables. (Only dumping ones "
Dave Jonesb9e76382009-01-18 00:32:26 -0500486 "relevant to this CPU).\n",
487 psb->numpst);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530489 p += sizeof(*psb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
491 pst = (struct pst_s *) p;
492
Dave Jonesb9e76382009-01-18 00:32:26 -0500493 for (j = 0; j < psb->numpst; j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 pst = (struct pst_s *) p;
495 number_scales = pst->numpstates;
496
Dave Jonesb9e76382009-01-18 00:32:26 -0500497 if ((etuple == pst->cpuid) &&
498 check_fsb(pst->fsbspeed) &&
499 (maxfid == pst->maxfid) &&
500 (startvid == pst->startvid)) {
501 print_pst_entry(pst, j);
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530502 p = (char *)pst + sizeof(*pst);
Dave Jonesb9e76382009-01-18 00:32:26 -0500503 ret = get_ranges(p);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 } else {
Dave Jones8cbe0162006-05-30 17:26:08 -0400506 unsigned int k;
Viresh Kumard5b73cd2013-08-06 22:53:06 +0530507 p = (char *)pst + sizeof(*pst);
Dave Jonesb9e76382009-01-18 00:32:26 -0500508 for (k = 0; k < number_scales; k++)
509 p += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 }
511 }
Joe Perches1c5864e2016-04-05 13:28:25 -0700512 pr_info("No PST tables match this cpuid (0x%x)\n",
Joe Perchesb49c22a2016-04-05 13:28:24 -0700513 etuple);
Joe Perches1c5864e2016-04-05 13:28:25 -0700514 pr_info("This is indicative of a broken BIOS\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
516 return -EINVAL;
517 }
518 p++;
519 }
520
521 return -ENODEV;
522}
523
524
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525/*
526 * We use the fact that the bus frequency is somehow
527 * a multiple of 100000/3 khz, then we compute sgtc according
528 * to this multiple.
529 * That way, we match more how AMD thinks all of that work.
530 * We will then get the same kind of behaviour already tested under
531 * the "well-known" other OS.
532 */
Paul Gortmaker27609842013-06-19 13:54:04 -0400533static int fixup_sgtc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534{
535 unsigned int sgtc;
536 unsigned int m;
537
538 m = fsb / 3333;
539 if ((m % 10) >= 5)
540 m += 5;
541
542 m /= 10;
543
544 sgtc = 100 * m * latency;
545 sgtc = sgtc / 3;
546 if (sgtc > 0xfffff) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700547 pr_warn("SGTC too large %d\n", sgtc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 sgtc = 0xfffff;
549 }
550 return sgtc;
551}
552
553static unsigned int powernow_get(unsigned int cpu)
554{
555 union msr_fidvidstatus fidvidstatus;
556 unsigned int cfid;
557
558 if (cpu)
559 return 0;
Dave Jonesb9e76382009-01-18 00:32:26 -0500560 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 cfid = fidvidstatus.bits.CFID;
562
Dave Jonesb9e76382009-01-18 00:32:26 -0500563 return fsb * fid_codes[cfid] / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564}
565
566
Paul Gortmaker27609842013-06-19 13:54:04 -0400567static int acer_cpufreq_pst(const struct dmi_system_id *d)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568{
Joe Perches1c5864e2016-04-05 13:28:25 -0700569 pr_warn("%s laptop with broken PST tables in BIOS detected\n",
Dave Jonesb9e76382009-01-18 00:32:26 -0500570 d->ident);
Joe Perches1c5864e2016-04-05 13:28:25 -0700571 pr_warn("You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
572 pr_warn("cpufreq scaling has been disabled as a result of this\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 return 0;
574}
575
576/*
577 * Some Athlon laptops have really fucked PST tables.
578 * A BIOS update is all that can save them.
579 * Mention this, and disable cpufreq.
580 */
Christoph Hellwig6faadbb2017-09-14 11:59:30 +0200581static const struct dmi_system_id powernow_dmi_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 {
583 .callback = acer_cpufreq_pst,
584 .ident = "Acer Aspire",
585 .matches = {
586 DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
587 DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
588 },
589 },
590 { }
591};
592
Paul Gortmaker27609842013-06-19 13:54:04 -0400593static int powernow_cpu_init(struct cpufreq_policy *policy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
595 union msr_fidvidstatus fidvidstatus;
596 int result;
597
598 if (policy->cpu != 0)
599 return -ENODEV;
600
Dave Jonesb9e76382009-01-18 00:32:26 -0500601 rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Dave Jones436fe7b2006-06-05 14:03:50 -0400603 recalibrate_cpu_khz();
Dave Jones91350ed2005-05-31 19:03:45 -0700604
605 fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 if (!fsb) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700607 pr_warn("can not determine bus frequency\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608 return -EINVAL;
609 }
Dominik Brodowski2d06d8c2011-03-27 15:04:46 +0200610 pr_debug("FSB: %3dMHz\n", fsb/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 if (dmi_check_system(powernow_dmi_table) || acpi_force) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700613 pr_info("PSB/PST known to be broken - trying ACPI instead\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 result = powernow_acpi_init();
615 } else {
Dave Jonesb9e76382009-01-18 00:32:26 -0500616 result = powernow_decode_bios(fidvidstatus.bits.MFID,
617 fidvidstatus.bits.SVID);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 if (result) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700619 pr_info("Trying ACPI perflib\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 maximum_speed = 0;
621 minimum_speed = -1;
622 latency = 0;
623 result = powernow_acpi_init();
624 if (result) {
Joe Perches1c5864e2016-04-05 13:28:25 -0700625 pr_info("ACPI and legacy methods failed\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 }
627 } else {
628 /* SGTC use the bus clock as timer */
629 latency = fixup_sgtc();
Joe Perches1c5864e2016-04-05 13:28:25 -0700630 pr_info("SGTC: %d\n", latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 }
632 }
633
634 if (result)
635 return result;
636
Joe Perches1c5864e2016-04-05 13:28:25 -0700637 pr_info("Minimum speed %d MHz - Maximum speed %d MHz\n",
Joe Perchesb49c22a2016-04-05 13:28:24 -0700638 minimum_speed/1000, maximum_speed/1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Dave Jonesb9e76382009-01-18 00:32:26 -0500640 policy->cpuinfo.transition_latency =
641 cpufreq_scale(2000000UL, fsb, latency);
Viresh Kumare2376d12018-02-26 10:38:57 +0530642 policy->freq_table = powernow_table;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643
Viresh Kumare2376d12018-02-26 10:38:57 +0530644 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645}
646
Dave Jonesb9e76382009-01-18 00:32:26 -0500647static int powernow_cpu_exit(struct cpufreq_policy *policy)
648{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649#ifdef CONFIG_X86_POWERNOW_K7_ACPI
650 if (acpi_processor_perf) {
Rafael J. Wysockib2f8dc42015-07-22 22:11:16 +0200651 acpi_processor_unregister_performance(0);
Rusty Russell2fdf66b2008-12-31 18:08:47 -0800652 free_cpumask_var(acpi_processor_perf->shared_cpu_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 kfree(acpi_processor_perf);
654 }
655#endif
656
Jesper Juhl4ae66732005-06-25 14:58:48 -0700657 kfree(powernow_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 return 0;
659}
660
Linus Torvalds221dee22007-02-26 14:55:48 -0800661static struct cpufreq_driver powernow_driver = {
Viresh Kumard63bd272013-10-03 20:28:17 +0530662 .verify = cpufreq_generic_frequency_table_verify,
Viresh Kumar9c0ebcf2013-10-25 19:45:48 +0530663 .target_index = powernow_target,
Thomas Renningere2f74f32009-11-19 12:31:01 +0100664 .get = powernow_get,
665#ifdef CONFIG_X86_POWERNOW_K7_ACPI
666 .bios_limit = acpi_processor_get_bios_limit,
667#endif
668 .init = powernow_cpu_init,
669 .exit = powernow_cpu_exit,
670 .name = "powernow-k7",
Viresh Kumard63bd272013-10-03 20:28:17 +0530671 .attr = cpufreq_generic_attr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672};
673
Dave Jonesb9e76382009-01-18 00:32:26 -0500674static int __init powernow_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Dave Jonesb9e76382009-01-18 00:32:26 -0500676 if (check_powernow() == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 return -ENODEV;
678 return cpufreq_register_driver(&powernow_driver);
679}
680
681
Dave Jonesb9e76382009-01-18 00:32:26 -0500682static void __exit powernow_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683{
684 cpufreq_unregister_driver(&powernow_driver);
685}
686
687module_param(acpi_force, int, 0444);
688MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");
689
Dave Jonesd5e80b42014-12-19 11:20:43 -0500690MODULE_AUTHOR("Dave Jones");
Dave Jonesb9e76382009-01-18 00:32:26 -0500691MODULE_DESCRIPTION("Powernow driver for AMD K7 processors.");
692MODULE_LICENSE("GPL");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694late_initcall(powernow_init);
695module_exit(powernow_exit);
696