diff options
author | Dave Jones <davej@redhat.com> | 2005-05-31 22:03:46 -0400 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2005-05-31 22:03:46 -0400 |
commit | 065b807ca1f5bdbeb081e3cf75ac8de9be8ac212 (patch) | |
tree | 675e6284925bd5ee1371c3509bc086775f51c988 /arch/i386/kernel/cpu/cpufreq/powernow-k8.c | |
parent | 7f335d4ef2d50a693fad70b8fa053d0382f4a45c (diff) |
[CPUFREQ] dual-core powernow-k8
With the release of the dual-core AMD Opterons last week,
it's high time that cpufreq supported them. The attached
patch applies cleanly to 2.6.12-rc3 and updates powernow-k8
to support the latest Athlon 64 and Opteron processors.
Update the driver to version 1.40.0 and provide support
for dual-core processors.
Signed-off-by: Mark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: Dave Jones <davej@redhat.com>
Diffstat (limited to 'arch/i386/kernel/cpu/cpufreq/powernow-k8.c')
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/powernow-k8.c | 113 |
1 files changed, 76 insertions, 37 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c index a65ff7e32e5d..10cc096c0ade 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * GNU general public license version 2. See "COPYING" or | 4 | * GNU general public license version 2. See "COPYING" or |
5 | * http://www.gnu.org/licenses/gpl.html | 5 | * http://www.gnu.org/licenses/gpl.html |
6 | * | 6 | * |
7 | * Support : paul.devriendt@amd.com | 7 | * Support : mark.langsdorf@amd.com |
8 | * | 8 | * |
9 | * Based on the powernow-k7.c module written by Dave Jones. | 9 | * Based on the powernow-k7.c module written by Dave Jones. |
10 | * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs | 10 | * (C) 2003 Dave Jones <davej@codemonkey.org.uk> on behalf of SuSE Labs |
@@ -15,12 +15,13 @@ | |||
15 | * | 15 | * |
16 | * Valuable input gratefully received from Dave Jones, Pavel Machek, | 16 | * Valuable input gratefully received from Dave Jones, Pavel Machek, |
17 | * Dominik Brodowski, and others. | 17 | * Dominik Brodowski, and others. |
18 | * Originally developed by Paul Devriendt. | ||
18 | * Processor information obtained from Chapter 9 (Power and Thermal Management) | 19 | * Processor information obtained from Chapter 9 (Power and Thermal Management) |
19 | * of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD | 20 | * of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD |
20 | * Opteron Processors" available for download from www.amd.com | 21 | * Opteron Processors" available for download from www.amd.com |
21 | * | 22 | * |
22 | * Tables for specific CPUs can be infrerred from | 23 | * Tables for specific CPUs can be infrerred from |
23 | * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf | 24 | * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf |
24 | */ | 25 | */ |
25 | 26 | ||
26 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
@@ -30,6 +31,7 @@ | |||
30 | #include <linux/cpufreq.h> | 31 | #include <linux/cpufreq.h> |
31 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
32 | #include <linux/string.h> | 33 | #include <linux/string.h> |
34 | #include <linux/cpumask.h> | ||
33 | 35 | ||
34 | #include <asm/msr.h> | 36 | #include <asm/msr.h> |
35 | #include <asm/io.h> | 37 | #include <asm/io.h> |
@@ -42,7 +44,7 @@ | |||
42 | 44 | ||
43 | #define PFX "powernow-k8: " | 45 | #define PFX "powernow-k8: " |
44 | #define BFX PFX "BIOS error: " | 46 | #define BFX PFX "BIOS error: " |
45 | #define VERSION "version 1.00.09e" | 47 | #define VERSION "version 1.40.2" |
46 | #include "powernow-k8.h" | 48 | #include "powernow-k8.h" |
47 | 49 | ||
48 | /* serialize freq changes */ | 50 | /* serialize freq changes */ |
@@ -50,6 +52,10 @@ static DECLARE_MUTEX(fidvid_sem); | |||
50 | 52 | ||
51 | static struct powernow_k8_data *powernow_data[NR_CPUS]; | 53 | static struct powernow_k8_data *powernow_data[NR_CPUS]; |
52 | 54 | ||
55 | #ifndef CONFIG_SMP | ||
56 | static cpumask_t cpu_core_map[1]; | ||
57 | #endif | ||
58 | |||
53 | /* Return a frequency in MHz, given an input fid */ | 59 | /* Return a frequency in MHz, given an input fid */ |
54 | static u32 find_freq_from_fid(u32 fid) | 60 | static u32 find_freq_from_fid(u32 fid) |
55 | { | 61 | { |
@@ -274,11 +280,18 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid | |||
274 | { | 280 | { |
275 | u32 rvosteps = data->rvo; | 281 | u32 rvosteps = data->rvo; |
276 | u32 savefid = data->currfid; | 282 | u32 savefid = data->currfid; |
283 | u32 maxvid, lo; | ||
277 | 284 | ||
278 | dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n", | 285 | dprintk("ph1 (cpu%d): start, currfid 0x%x, currvid 0x%x, reqvid 0x%x, rvo 0x%x\n", |
279 | smp_processor_id(), | 286 | smp_processor_id(), |
280 | data->currfid, data->currvid, reqvid, data->rvo); | 287 | data->currfid, data->currvid, reqvid, data->rvo); |
281 | 288 | ||
289 | rdmsr(MSR_FIDVID_STATUS, lo, maxvid); | ||
290 | maxvid = 0x1f & (maxvid >> 16); | ||
291 | dprintk("ph1 maxvid=0x%x\n", maxvid); | ||
292 | if (reqvid < maxvid) /* lower numbers are higher voltages */ | ||
293 | reqvid = maxvid; | ||
294 | |||
282 | while (data->currvid > reqvid) { | 295 | while (data->currvid > reqvid) { |
283 | dprintk("ph1: curr 0x%x, req vid 0x%x\n", | 296 | dprintk("ph1: curr 0x%x, req vid 0x%x\n", |
284 | data->currvid, reqvid); | 297 | data->currvid, reqvid); |
@@ -286,8 +299,8 @@ static int core_voltage_pre_transition(struct powernow_k8_data *data, u32 reqvid | |||
286 | return 1; | 299 | return 1; |
287 | } | 300 | } |
288 | 301 | ||
289 | while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) { | 302 | while ((rvosteps > 0) && ((data->rvo + data->currvid) > reqvid)) { |
290 | if (data->currvid == 0) { | 303 | if (data->currvid == maxvid) { |
291 | rvosteps = 0; | 304 | rvosteps = 0; |
292 | } else { | 305 | } else { |
293 | dprintk("ph1: changing vid for rvo, req 0x%x\n", | 306 | dprintk("ph1: changing vid for rvo, req 0x%x\n", |
@@ -671,7 +684,7 @@ static int find_psb_table(struct powernow_k8_data *data) | |||
671 | * BIOS and Kernel Developer's Guide, which is available on | 684 | * BIOS and Kernel Developer's Guide, which is available on |
672 | * www.amd.com | 685 | * www.amd.com |
673 | */ | 686 | */ |
674 | printk(KERN_ERR PFX "BIOS error - no PSB\n"); | 687 | printk(KERN_INFO PFX "BIOS error - no PSB or ACPI _PSS objects\n"); |
675 | return -ENODEV; | 688 | return -ENODEV; |
676 | } | 689 | } |
677 | 690 | ||
@@ -695,7 +708,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) | |||
695 | struct cpufreq_frequency_table *powernow_table; | 708 | struct cpufreq_frequency_table *powernow_table; |
696 | 709 | ||
697 | if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { | 710 | if (acpi_processor_register_performance(&data->acpi_data, data->cpu)) { |
698 | dprintk("register performance failed\n"); | 711 | dprintk("register performance failed: bad ACPI data\n"); |
699 | return -EIO; | 712 | return -EIO; |
700 | } | 713 | } |
701 | 714 | ||
@@ -746,22 +759,23 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) | |||
746 | continue; | 759 | continue; |
747 | } | 760 | } |
748 | 761 | ||
749 | if (fid < HI_FID_TABLE_BOTTOM) { | 762 | /* verify only 1 entry from the lo frequency table */ |
750 | if (cntlofreq) { | 763 | if (fid < HI_FID_TABLE_BOTTOM) { |
751 | /* if both entries are the same, ignore this | 764 | if (cntlofreq) { |
752 | * one... | 765 | /* if both entries are the same, ignore this |
753 | */ | 766 | * one... |
754 | if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) || | 767 | */ |
755 | (powernow_table[i].index != powernow_table[cntlofreq].index)) { | 768 | if ((powernow_table[i].frequency != powernow_table[cntlofreq].frequency) || |
756 | printk(KERN_ERR PFX "Too many lo freq table entries\n"); | 769 | (powernow_table[i].index != powernow_table[cntlofreq].index)) { |
757 | goto err_out_mem; | 770 | printk(KERN_ERR PFX "Too many lo freq table entries\n"); |
758 | } | 771 | goto err_out_mem; |
759 | 772 | } | |
760 | dprintk("double low frequency table entry, ignoring it.\n"); | 773 | |
761 | powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; | 774 | dprintk("double low frequency table entry, ignoring it.\n"); |
762 | continue; | 775 | powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID; |
763 | } else | 776 | continue; |
764 | cntlofreq = i; | 777 | } else |
778 | cntlofreq = i; | ||
765 | } | 779 | } |
766 | 780 | ||
767 | if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) { | 781 | if (powernow_table[i].frequency != (data->acpi_data.states[i].core_frequency * 1000)) { |
@@ -816,7 +830,7 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde | |||
816 | { | 830 | { |
817 | u32 fid; | 831 | u32 fid; |
818 | u32 vid; | 832 | u32 vid; |
819 | int res; | 833 | int res, i; |
820 | struct cpufreq_freqs freqs; | 834 | struct cpufreq_freqs freqs; |
821 | 835 | ||
822 | dprintk("cpu %d transition to index %u\n", smp_processor_id(), index); | 836 | dprintk("cpu %d transition to index %u\n", smp_processor_id(), index); |
@@ -841,7 +855,8 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde | |||
841 | } | 855 | } |
842 | 856 | ||
843 | if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) { | 857 | if ((fid < HI_FID_TABLE_BOTTOM) && (data->currfid < HI_FID_TABLE_BOTTOM)) { |
844 | printk("ignoring illegal change in lo freq table-%x to 0x%x\n", | 858 | printk(KERN_ERR PFX |
859 | "ignoring illegal change in lo freq table-%x to 0x%x\n", | ||
845 | data->currfid, fid); | 860 | data->currfid, fid); |
846 | return 1; | 861 | return 1; |
847 | } | 862 | } |
@@ -850,18 +865,20 @@ static int transition_frequency(struct powernow_k8_data *data, unsigned int inde | |||
850 | smp_processor_id(), fid, vid); | 865 | smp_processor_id(), fid, vid); |
851 | 866 | ||
852 | freqs.cpu = data->cpu; | 867 | freqs.cpu = data->cpu; |
853 | |||
854 | freqs.old = find_khz_freq_from_fid(data->currfid); | 868 | freqs.old = find_khz_freq_from_fid(data->currfid); |
855 | freqs.new = find_khz_freq_from_fid(fid); | 869 | freqs.new = find_khz_freq_from_fid(fid); |
856 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | 870 | for_each_cpu_mask(i, cpu_core_map[data->cpu]) { |
871 | freqs.cpu = i; | ||
872 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
873 | } | ||
857 | 874 | ||
858 | down(&fidvid_sem); | ||
859 | res = transition_fid_vid(data, fid, vid); | 875 | res = transition_fid_vid(data, fid, vid); |
860 | up(&fidvid_sem); | ||
861 | 876 | ||
862 | freqs.new = find_khz_freq_from_fid(data->currfid); | 877 | freqs.new = find_khz_freq_from_fid(data->currfid); |
863 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | 878 | for_each_cpu_mask(i, cpu_core_map[data->cpu]) { |
864 | 879 | freqs.cpu = i; | |
880 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
881 | } | ||
865 | return res; | 882 | return res; |
866 | } | 883 | } |
867 | 884 | ||
@@ -874,6 +891,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi | |||
874 | u32 checkvid = data->currvid; | 891 | u32 checkvid = data->currvid; |
875 | unsigned int newstate; | 892 | unsigned int newstate; |
876 | int ret = -EIO; | 893 | int ret = -EIO; |
894 | int i; | ||
877 | 895 | ||
878 | /* only run on specific CPU from here on */ | 896 | /* only run on specific CPU from here on */ |
879 | oldmask = current->cpus_allowed; | 897 | oldmask = current->cpus_allowed; |
@@ -902,22 +920,41 @@ static int powernowk8_target(struct cpufreq_policy *pol, unsigned targfreq, unsi | |||
902 | data->currfid, data->currvid); | 920 | data->currfid, data->currvid); |
903 | 921 | ||
904 | if ((checkvid != data->currvid) || (checkfid != data->currfid)) { | 922 | if ((checkvid != data->currvid) || (checkfid != data->currfid)) { |
905 | printk(KERN_ERR PFX | 923 | printk(KERN_INFO PFX |
906 | "error - out of sync, fid 0x%x 0x%x, vid 0x%x 0x%x\n", | 924 | "error - out of sync, fix 0x%x 0x%x, vid 0x%x 0x%x\n", |
907 | checkfid, data->currfid, checkvid, data->currvid); | 925 | checkfid, data->currfid, checkvid, data->currvid); |
908 | } | 926 | } |
909 | 927 | ||
910 | if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate)) | 928 | if (cpufreq_frequency_table_target(pol, data->powernow_table, targfreq, relation, &newstate)) |
911 | goto err_out; | 929 | goto err_out; |
912 | 930 | ||
931 | down(&fidvid_sem); | ||
932 | |||
933 | for_each_cpu_mask(i, cpu_core_map[pol->cpu]) { | ||
934 | /* make sure the sibling is initialized */ | ||
935 | if (!powernow_data[i]) { | ||
936 | ret = 0; | ||
937 | up(&fidvid_sem); | ||
938 | goto err_out; | ||
939 | } | ||
940 | } | ||
941 | |||
913 | powernow_k8_acpi_pst_values(data, newstate); | 942 | powernow_k8_acpi_pst_values(data, newstate); |
914 | 943 | ||
915 | if (transition_frequency(data, newstate)) { | 944 | if (transition_frequency(data, newstate)) { |
916 | printk(KERN_ERR PFX "transition frequency failed\n"); | 945 | printk(KERN_ERR PFX "transition frequency failed\n"); |
917 | ret = 1; | 946 | ret = 1; |
947 | up(&fidvid_sem); | ||
918 | goto err_out; | 948 | goto err_out; |
919 | } | 949 | } |
920 | 950 | ||
951 | /* Update all the fid/vids of our siblings */ | ||
952 | for_each_cpu_mask(i, cpu_core_map[pol->cpu]) { | ||
953 | powernow_data[i]->currvid = data->currvid; | ||
954 | powernow_data[i]->currfid = data->currfid; | ||
955 | } | ||
956 | up(&fidvid_sem); | ||
957 | |||
921 | pol->cur = find_khz_freq_from_fid(data->currfid); | 958 | pol->cur = find_khz_freq_from_fid(data->currfid); |
922 | ret = 0; | 959 | ret = 0; |
923 | 960 | ||
@@ -962,7 +999,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) | |||
962 | */ | 999 | */ |
963 | 1000 | ||
964 | if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { | 1001 | if ((num_online_cpus() != 1) || (num_possible_cpus() != 1)) { |
965 | printk(KERN_INFO PFX "MP systems not supported by PSB BIOS structure\n"); | 1002 | printk(KERN_ERR PFX "MP systems not supported by PSB BIOS structure\n"); |
966 | kfree(data); | 1003 | kfree(data); |
967 | return -ENODEV; | 1004 | return -ENODEV; |
968 | } | 1005 | } |
@@ -1003,6 +1040,7 @@ static int __init powernowk8_cpu_init(struct cpufreq_policy *pol) | |||
1003 | schedule(); | 1040 | schedule(); |
1004 | 1041 | ||
1005 | pol->governor = CPUFREQ_DEFAULT_GOVERNOR; | 1042 | pol->governor = CPUFREQ_DEFAULT_GOVERNOR; |
1043 | pol->cpus = cpu_core_map[pol->cpu]; | ||
1006 | 1044 | ||
1007 | /* Take a crude guess here. | 1045 | /* Take a crude guess here. |
1008 | * That guess was in microseconds, so multiply with 1000 */ | 1046 | * That guess was in microseconds, so multiply with 1000 */ |
@@ -1069,7 +1107,7 @@ static unsigned int powernowk8_get (unsigned int cpu) | |||
1069 | return 0; | 1107 | return 0; |
1070 | } | 1108 | } |
1071 | preempt_disable(); | 1109 | preempt_disable(); |
1072 | 1110 | ||
1073 | if (query_current_values_with_pending_wait(data)) | 1111 | if (query_current_values_with_pending_wait(data)) |
1074 | goto out; | 1112 | goto out; |
1075 | 1113 | ||
@@ -1127,9 +1165,10 @@ static void __exit powernowk8_exit(void) | |||
1127 | cpufreq_unregister_driver(&cpufreq_amd64_driver); | 1165 | cpufreq_unregister_driver(&cpufreq_amd64_driver); |
1128 | } | 1166 | } |
1129 | 1167 | ||
1130 | MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com>"); | 1168 | MODULE_AUTHOR("Paul Devriendt <paul.devriendt@amd.com> and Mark Langsdorf <mark.langsdorf@amd.com."); |
1131 | MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver."); | 1169 | MODULE_DESCRIPTION("AMD Athlon 64 and Opteron processor frequency driver."); |
1132 | MODULE_LICENSE("GPL"); | 1170 | MODULE_LICENSE("GPL"); |
1133 | 1171 | ||
1134 | late_initcall(powernowk8_init); | 1172 | late_initcall(powernowk8_init); |
1135 | module_exit(powernowk8_exit); | 1173 | module_exit(powernowk8_exit); |
1174 | |||