Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
3f669609
Commit
3f669609
authored
Feb 13, 2003
by
Dave Jones
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[CPUFREQ] Add powernow-k7 driver for AMD mobile Athlon/Duron CPUs.
parent
7f939798
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
471 additions
and
0 deletions
+471
-0
arch/i386/Kconfig
arch/i386/Kconfig
+10
-0
arch/i386/kernel/cpu/cpufreq/Makefile
arch/i386/kernel/cpu/cpufreq/Makefile
+1
-0
arch/i386/kernel/cpu/cpufreq/powernow-k7.c
arch/i386/kernel/cpu/cpufreq/powernow-k7.c
+408
-0
arch/i386/kernel/cpu/cpufreq/powernow-k7.h
arch/i386/kernel/cpu/cpufreq/powernow-k7.h
+52
-0
No files found.
arch/i386/Kconfig
View file @
3f669609
...
...
@@ -1005,6 +1005,16 @@ config X86_POWERNOW_K6
If in doubt, say N.
config X86_POWERNOW_K7
tristate "AMD Mobile Athlon/Duron PowerNow!"
depends on CPU_FREQ
help
This adds the CPUFreq driver for mobile AMD K7 mobile processors.
For details, take a look at linux/Documentation/cpufreq.
If in doubt, say N.
config ELAN_CPUFREQ
tristate "AMD Elan"
depends on CPU_FREQ && MELAN
...
...
arch/i386/kernel/cpu/cpufreq/Makefile
View file @
3f669609
obj-$(CONFIG_X86_POWERNOW_K6)
+=
powernow-k6.o
obj-$(CONFIG_X86_POWERNOW_K7)
+=
powernow-k7.o
obj-$(CONFIG_X86_LONGHAUL)
+=
longhaul.o
obj-$(CONFIG_X86_SPEEDSTEP)
+=
speedstep.o
obj-$(CONFIG_X86_P4_CLOCKMOD)
+=
p4-clockmod.o
...
...
arch/i386/kernel/cpu/cpufreq/powernow-k7.c
0 → 100644
View file @
3f669609
/*
* $Id: powernow-k7.c,v 1.31 2003/02/12 21:16:35 davej Exp $
*
* (C) 2003 Dave Jones <davej@suse.de>
*
* Licensed under the terms of the GNU GPL License version 2.
* Based upon datasheets & sample CPUs kindly provided by AMD.
*
* BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
*
* Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt.
* - We cli/sti on stepping A0 CPUs around the FID/VID transition.
* Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect.
* - We disable half multipliers if ACPI is used on A0 stepping CPUs.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <asm/msr.h>
#include <asm/timex.h>
#include <asm/io.h>
#include "powernow-k7.h"
#define DEBUG
#ifdef DEBUG
#define dprintk(msg...) printk(msg)
#else
#define dprintk(msg...) do { } while(0);
#endif
#define PFX "powernow: "
struct
psb_s
{
u8
signature
[
10
];
u8
tableversion
;
u8
flags
;
u16
settlingtime
;
u8
reserved1
;
u8
numpst
;
};
struct
pst_s
{
u32
cpuid
;
u8
fsbspeed
;
u8
maxfid
;
u8
startvid
;
u8
numpstates
;
};
/* divide by 1000 to get VID. */
static
int
mobile_vid_table
[
32
]
=
{
2000
,
1950
,
1900
,
1850
,
1800
,
1750
,
1700
,
1650
,
1600
,
1550
,
1500
,
1450
,
1400
,
1350
,
1300
,
0
,
1275
,
1250
,
1225
,
1200
,
1175
,
1150
,
1125
,
1100
,
1075
,
1050
,
1024
,
1000
,
975
,
950
,
925
,
0
,
};
/* divide by 10 to get FID. */
static
int
fid_codes
[
32
]
=
{
110
,
115
,
120
,
125
,
50
,
55
,
60
,
65
,
70
,
75
,
80
,
85
,
90
,
95
,
10
,
105
,
30
,
190
,
40
,
200
,
130
,
135
,
140
,
210
,
150
,
225
,
160
,
165
,
170
,
180
,
-
1
,
-
1
,
};
static
struct
cpufreq_driver
powernow_driver
;
static
struct
cpufreq_frequency_table
*
powernow_table
;
static
unsigned
int
can_scale_bus
;
static
unsigned
int
can_scale_vid
;
static
unsigned
int
minimum_speed
=-
1
;
static
unsigned
int
maximum_speed
;
static
unsigned
int
number_scales
;
static
unsigned
int
fsb
;
static
unsigned
int
latency
;
static
char
have_a0
;
#ifndef rdmsrl
#define rdmsrl(msr,val) do {unsigned long l__,h__; \
rdmsr (msr, l__, h__); \
val = l__; \
val |= ((u64)h__<<32); \
} while(0);
#endif
#ifndef wrmsrl
static
void
wrmsrl
(
u32
msr
,
u64
val
)
{
u32
lo
,
hi
;
lo
=
(
u32
)
val
;
hi
=
val
>>
32
;
wrmsr
(
msr
,
lo
,
hi
);
}
#endif
static
int
check_powernow
(
void
)
{
struct
cpuinfo_x86
*
c
=
cpu_data
;
unsigned
int
maxei
,
eax
,
ebx
,
ecx
,
edx
;
if
(
c
->
x86_vendor
!=
X86_VENDOR_AMD
)
{
printk
(
KERN_INFO
PFX
"AMD processor not detected.
\n
"
);
return
0
;
}
if
(
c
->
x86
!=
6
)
{
printk
(
KERN_INFO
PFX
"This module only works with AMD K7 CPUs
\n
"
);
return
0
;
}
printk
(
KERN_INFO
PFX
"AMD K7 CPU detected.
\n
"
);
if
((
c
->
x86_model
==
6
)
&&
(
c
->
x86_mask
==
0
))
{
printk
(
KERN_INFO
PFX
"K7 660[A0] core detected, enabling errata workarounds
\n
"
);
have_a0
=
1
;
}
/* Get maximum capabilities */
maxei
=
cpuid_eax
(
0x80000000
);
if
(
maxei
<
0x80000007
)
{
/* Any powernow info ? */
printk
(
KERN_INFO
PFX
"No powernow capabilities detected
\n
"
);
return
0
;
}
cpuid
(
0x80000007
,
&
eax
,
&
ebx
,
&
ecx
,
&
edx
);
printk
(
KERN_INFO
PFX
"PowerNOW! Technology present. Can scale: "
);
if
(
edx
&
1
<<
1
)
{
printk
(
"frequency"
);
can_scale_bus
=
1
;
}
if
((
edx
&
(
1
<<
1
|
1
<<
2
))
==
0x6
)
printk
(
" and "
);
if
(
edx
&
1
<<
2
)
{
printk
(
"voltage"
);
can_scale_vid
=
1
;
}
if
(
!
(
edx
&
(
1
<<
1
|
1
<<
2
)))
{
printk
(
" nothing.
\n
"
);
return
0
;
}
printk
(
".
\n
"
);
return
1
;
}
static
int
get_ranges
(
unsigned
char
*
pst
)
{
int
j
;
u8
fid
,
vid
;
unsigned
int
speed
;
powernow_table
=
kmalloc
((
sizeof
(
struct
cpufreq_frequency_table
)
*
(
number_scales
+
1
)),
GFP_KERNEL
);
if
(
!
powernow_table
)
return
-
ENOMEM
;
memset
(
powernow_table
,
0
,
(
sizeof
(
struct
cpufreq_frequency_table
)
*
(
number_scales
+
1
)));
for
(
j
=
0
;
j
<
number_scales
;
j
++
)
{
fid
=
*
pst
++
;
powernow_table
[
j
].
frequency
=
fsb
*
fid_codes
[
fid
]
*
100
;
powernow_table
[
j
].
index
=
fid
;
/* lower 8 bits */
speed
=
fsb
*
(
fid_codes
[
fid
]
/
10
);
if
((
fid_codes
[
fid
]
%
10
)
==
5
)
{
speed
+=
fsb
/
2
;
#if defined(CONFIG_ACPI_PROCESSOR) || defined(CONFIG_ACPI_PROCESSOR_MODULE)
if
(
have_a0
==
1
)
powernow_table
[
j
].
frequency
=
CPUFREQ_ENTRY_INVALID
;
#endif
}
dprintk
(
KERN_INFO
PFX
" FID: 0x%x (%d.%dx [%dMHz])
\t
"
,
fid
,
fid_codes
[
fid
]
/
10
,
fid_codes
[
fid
]
%
10
,
speed
);
if
(
speed
<
minimum_speed
)
minimum_speed
=
speed
;
if
(
speed
>
maximum_speed
)
maximum_speed
=
speed
;
vid
=
*
pst
++
;
powernow_table
[
j
].
index
|=
(
vid
<<
8
);
/* upper 8 bits */
dprintk
(
"VID: 0x%x (%d.%dV)
\n
"
,
vid
,
mobile_vid_table
[
vid
]
/
1000
,
mobile_vid_table
[
vid
]
%
1000
);
}
dprintk
(
"
\n
"
);
powernow_table
[
number_scales
].
frequency
=
CPUFREQ_TABLE_END
;
powernow_table
[
number_scales
].
index
=
0
;
return
0
;
}
static
void
change_speed
(
unsigned
int
index
)
{
u8
fid
,
vid
;
struct
cpufreq_freqs
freqs
;
union
msr_fidvidstatus
fidvidstatus
;
union
msr_fidvidctl
fidvidctl
;
/* fid are the lower 8 bits of the index we stored into
* the cpufreq frequency table in powernow_decode_bios,
* vid are the upper 8 bits.
*/
fid
=
powernow_table
[
index
].
index
&
0xFF
;
vid
=
(
powernow_table
[
index
].
index
&
0xFF00
)
>>
8
;
freqs
.
cpu
=
0
;
rdmsrl
(
MSR_K7_FID_VID_STATUS
,
fidvidstatus
.
val
);
freqs
.
old
=
fsb
*
fid_codes
[
fidvidstatus
.
bits
.
CFID
]
*
100
;
freqs
.
new
=
powernow_table
[
index
].
frequency
;
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_PRECHANGE
);
/* Now do the magic poking into the MSRs. */
if
(
have_a0
==
1
)
/* A0 errata 5 */
__asm__
(
"
\t
cli
\n
"
);
rdmsrl
(
MSR_K7_FID_VID_CTL
,
fidvidctl
.
val
);
fidvidctl
.
bits
.
SGTC
=
latency
;
/* Stop grant timeout counter */
fidvidctl
.
bits
.
FID
=
fid
;
fidvidctl
.
bits
.
VID
=
vid
;
/* Note, we could set these lazily. Ie, only do voltage transition
if its changed since last time (Some speeds have the same voltage) */
fidvidctl
.
bits
.
FIDC
=
1
;
fidvidctl
.
bits
.
VIDC
=
1
;
wrmsrl
(
MSR_K7_FID_VID_CTL
,
fidvidctl
.
val
);
if
(
have_a0
==
1
)
__asm__
(
"
\t
sti
\n
"
);
cpufreq_notify_transition
(
&
freqs
,
CPUFREQ_POSTCHANGE
);
}
int
powernow_decode_bios
(
int
maxfid
,
int
startvid
)
{
struct
psb_s
*
psb
;
struct
pst_s
*
pst
;
struct
cpuinfo_x86
*
c
=
cpu_data
;
unsigned
int
i
,
j
;
unsigned
char
*
p
;
unsigned
int
etuple
;
unsigned
int
ret
;
etuple
=
cpuid_eax
(
0x80000001
);
etuple
&=
0xf00
;
etuple
|=
(
c
->
x86_model
<<
4
)
|
(
c
->
x86_mask
);
for
(
i
=
0xC0000
;
i
<
0xffff0
;
i
+=
16
)
{
p
=
phys_to_virt
(
i
);
if
(
memcmp
(
p
,
"AMDK7PNOW!"
,
10
)
==
0
){
dprintk
(
KERN_INFO
PFX
"Found PSB header at %p
\n
"
,
p
);
psb
=
(
struct
psb_s
*
)
p
;
dprintk
(
KERN_INFO
PFX
"Table version: 0x%x
\n
"
,
psb
->
tableversion
);
if
(
psb
->
tableversion
!=
0x12
)
{
printk
(
KERN_INFO
PFX
"Sorry, only v1.2 tables supported right now
\n
"
);
return
-
ENODEV
;
}
dprintk
(
KERN_INFO
PFX
"Flags: 0x%x ("
,
psb
->
flags
);
if
((
psb
->
flags
&
1
)
==
0
)
{
dprintk
(
"Mobile"
);
}
else
{
dprintk
(
"Desktop"
);
}
dprintk
(
" voltage regulator)
\n
"
);
latency
=
psb
->
settlingtime
;
dprintk
(
KERN_INFO
PFX
"Settling Time: %d microseconds.
\n
"
,
psb
->
settlingtime
);
dprintk
(
KERN_INFO
PFX
"Has %d PST tables. (Only dumping ones relevant to this CPU).
\n
"
,
psb
->
numpst
);
p
+=
sizeof
(
struct
psb_s
);
pst
=
(
struct
pst_s
*
)
p
;
for
(
i
=
0
;
i
<
psb
->
numpst
;
i
++
)
{
pst
=
(
struct
pst_s
*
)
p
;
number_scales
=
pst
->
numpstates
;
if
((
etuple
==
pst
->
cpuid
)
&&
(
maxfid
==
pst
->
maxfid
)
&&
(
startvid
==
pst
->
startvid
))
{
dprintk
(
KERN_INFO
PFX
"PST:%d (@%p)
\n
"
,
i
,
pst
);
dprintk
(
KERN_INFO
PFX
" cpuid: 0x%x
\t
"
,
pst
->
cpuid
);
dprintk
(
"fsb: %d
\t
"
,
pst
->
fsbspeed
);
dprintk
(
"maxFID: 0x%x
\t
"
,
pst
->
maxfid
);
dprintk
(
"startvid: 0x%x
\n
"
,
pst
->
startvid
);
fsb
=
pst
->
fsbspeed
;
ret
=
get_ranges
((
char
*
)
pst
+
sizeof
(
struct
pst_s
));
return
ret
;
}
else
{
p
=
(
char
*
)
pst
+
sizeof
(
struct
pst_s
);
for
(
j
=
0
;
j
<
number_scales
;
j
++
)
p
+=
2
;
}
}
return
-
EINVAL
;
}
p
++
;
}
return
-
ENODEV
;
}
static
int
powernow_target
(
struct
cpufreq_policy
*
policy
,
unsigned
int
target_freq
,
unsigned
int
relation
)
{
unsigned
int
newstate
;
if
(
cpufreq_frequency_table_target
(
policy
,
powernow_table
,
target_freq
,
relation
,
&
newstate
))
return
-
EINVAL
;
change_speed
(
newstate
);
return
0
;
}
static
int
powernow_verify
(
struct
cpufreq_policy
*
policy
)
{
return
cpufreq_frequency_table_verify
(
policy
,
powernow_table
);
}
static
int
__init
powernow_cpu_init
(
struct
cpufreq_policy
*
policy
)
{
union
msr_fidvidstatus
fidvidstatus
;
int
result
;
if
(
policy
->
cpu
!=
0
)
return
-
ENODEV
;
rdmsrl
(
MSR_K7_FID_VID_STATUS
,
fidvidstatus
.
val
);
result
=
powernow_decode_bios
(
fidvidstatus
.
bits
.
MFID
,
fidvidstatus
.
bits
.
SVID
);
if
(
result
)
return
result
;
printk
(
KERN_INFO
PFX
"Minimum speed %d MHz. Maximum speed %d MHz.
\n
"
,
minimum_speed
,
maximum_speed
);
policy
->
policy
=
CPUFREQ_POLICY_PERFORMANCE
;
policy
->
cpuinfo
.
transition_latency
=
latency
;
#ifdef CONFIG_CPU_FREQ_24_API
powernow_driver
.
cpu_cur_freq
[
policy
->
cpu
]
=
maximum_speed
;
#endif
return
cpufreq_frequency_table_cpuinfo
(
policy
,
powernow_table
);
}
static
int
__init
powernow_init
(
void
)
{
if
(
check_powernow
()
==
0
)
return
-
ENODEV
;
return
cpufreq_register_driver
(
&
powernow_driver
);
}
static
void
__exit
powernow_exit
(
void
)
{
cpufreq_unregister_driver
(
&
powernow_driver
);
if
(
powernow_table
)
kfree
(
powernow_table
);
}
static
struct
cpufreq_driver
powernow_driver
=
{
.
verify
=
powernow_verify
,
.
target
=
powernow_target
,
.
init
=
powernow_cpu_init
,
.
name
=
"powernow-k7"
,
};
MODULE_AUTHOR
(
"Dave Jones <davej@suse.de>"
);
MODULE_DESCRIPTION
(
"Powernow driver for AMD K7 processors."
);
MODULE_LICENSE
(
"GPL"
);
module_init
(
powernow_init
);
module_exit
(
powernow_exit
);
arch/i386/kernel/cpu/cpufreq/powernow-k7.h
0 → 100644
View file @
3f669609
/*
* $Id: powernow-k7.h,v 1.2 2003/02/10 18:26:01 davej Exp $
* (C) 2003 Dave Jones.
*
* Licensed under the terms of the GNU GPL License version 2.
*
* AMD-specific information
*
*/
#ifndef MSR_K7_FID_VID_CTL
#define MSR_K7_FID_VID_CTL 0xc0010041
#endif
#ifndef MSR_K7_FID_VID_STATUS
#define MSR_K7_FID_VID_STATUS 0xc0010042
#endif
union
msr_fidvidctl
{
struct
{
unsigned
FID
:
5
,
// 4:0
reserved1:
3
,
// 7:5
VID:
5
,
// 12:8
reserved2:
3
,
// 15:13
FIDC:
1
,
// 16
VIDC:
1
,
// 17
reserved3:
2
,
// 19:18
FIDCHGRATIO:
1
,
// 20
reserved4:
11
,
// 31-21
SGTC:
20
,
// 32:51
reserved5:
12
;
// 63:52
}
bits
;
unsigned
long
long
val
;
};
union
msr_fidvidstatus
{
struct
{
unsigned
CFID
:
5
,
// 4:0
reserved1:
3
,
// 7:5
SFID:
5
,
// 12:8
reserved2:
3
,
// 15:13
MFID:
5
,
// 20:16
reserved3:
11
,
// 31:21
CVID:
5
,
// 36:32
reserved4:
3
,
// 39:37
SVID:
5
,
// 44:40
reserved5:
3
,
// 47:45
MVID:
5
,
// 52:48
reserved6:
11
;
// 63:53
}
bits
;
unsigned
long
long
val
;
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment