1231d9860SAlexander Polakov /*
2231d9860SAlexander Polakov * Copyright (c) 2004 Martin V\xe9giard. Copyright (c) 2004-2005 Bruno Ducrot
3231d9860SAlexander Polakov * Copyright (c) 2004 FUKUDA Nobuhiko <nfukuda@spa.is.uec.ac.jp> Copyright
4231d9860SAlexander Polakov * (c) 2004, 2006 The NetBSD Foundation, Inc. All rights reserved.
5231d9860SAlexander Polakov *
6231d9860SAlexander Polakov * This code is derived from software contributed to The NetBSD Foundation by
7231d9860SAlexander Polakov * Juan Romero Pardines and Martin Vegiard.
8231d9860SAlexander Polakov *
9231d9860SAlexander Polakov * Redistribution and use in source and binary forms, with or without
10231d9860SAlexander Polakov * modification, are permitted provided that the following conditions are
11231d9860SAlexander Polakov * met: 1. Redistributions of source code must retain the above copyright
12231d9860SAlexander Polakov * notice, this list of conditions and the following disclaimer. 2.
13231d9860SAlexander Polakov * Redistributions in binary form must reproduce the above copyright notice,
14231d9860SAlexander Polakov * this list of conditions and the following disclaimer in the documentation
15231d9860SAlexander Polakov * and/or other materials provided with the distribution. THIS SOFTWARE IS
16231d9860SAlexander Polakov * PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17231d9860SAlexander Polakov * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18231d9860SAlexander Polakov * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19231d9860SAlexander Polakov * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20231d9860SAlexander Polakov * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21231d9860SAlexander Polakov * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22231d9860SAlexander Polakov * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23231d9860SAlexander Polakov * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24231d9860SAlexander Polakov * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25231d9860SAlexander Polakov * POSSIBILITY OF SUCH DAMAGE.
26231d9860SAlexander Polakov */
27231d9860SAlexander Polakov /* AMD POWERNOW K8 driver */
28231d9860SAlexander Polakov
29231d9860SAlexander Polakov #include <sys/param.h>
30231d9860SAlexander Polakov #include <sys/systm.h>
31231d9860SAlexander Polakov #include <sys/malloc.h>
32231d9860SAlexander Polakov #include <sys/kernel.h>
33231d9860SAlexander Polakov #include <sys/module.h>
34231d9860SAlexander Polakov #include <sys/sysctl.h>
35231d9860SAlexander Polakov #include <bus/isa/isa.h>
36231d9860SAlexander Polakov #include <machine/cpu.h>
37231d9860SAlexander Polakov #include <machine/pmap.h>
38231d9860SAlexander Polakov #include <machine/pc/bios.h>
39231d9860SAlexander Polakov #include <machine/cpufunc.h>
40231d9860SAlexander Polakov #include <machine/md_var.h>
41231d9860SAlexander Polakov #include <machine/specialreg.h>
42db06e065SSascha Wildner #include <machine/vmparam.h>
43231d9860SAlexander Polakov
44231d9860SAlexander Polakov #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
45231d9860SAlexander Polakov #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
46231d9860SAlexander Polakov #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
47231d9860SAlexander Polakov
48231d9860SAlexander Polakov /*
49231d9860SAlexander Polakov * MSRs and bits used by PowerNow! technology
50231d9860SAlexander Polakov */
51231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_CTL 0xc0010041
52231d9860SAlexander Polakov #define MSR_AMDK7_FIDVID_STATUS 0xc0010042
53231d9860SAlexander Polakov #define AMD_PN_FID_VID 0x06
54231d9860SAlexander Polakov
55231d9860SAlexander Polakov #define BIOS_START 0xe0000
56231d9860SAlexander Polakov #define BIOS_LEN 0x20000
57231d9860SAlexander Polakov #define BIOS_STEP 16
58231d9860SAlexander Polakov
59231d9860SAlexander Polakov #define PN8_PSB_VERSION 0x14
60231d9860SAlexander Polakov #define PN8_PSB_TO_RVO(x) ((x) & 0x03)
61231d9860SAlexander Polakov #define PN8_PSB_TO_IRT(x) (((x) >> 2) & 0x03)
62231d9860SAlexander Polakov #define PN8_PSB_TO_MVS(x) (((x) >> 4) & 0x03)
63231d9860SAlexander Polakov #define PN8_PSB_TO_BATT(x) (((x) >> 6) & 0x03)
64231d9860SAlexander Polakov /* Bitfields used by K8 */
65231d9860SAlexander Polakov #define PN8_CTR_FID(x) ((x) & 0x3f)
66231d9860SAlexander Polakov #define PN8_CTR_VID(x) (((x) & 0x1f) << 8)
67231d9860SAlexander Polakov #define PN8_CTR_PENDING(x) (((x) & 1) << 32)
68231d9860SAlexander Polakov #define PN8_STA_CFID(x) ((x) & 0x3f)
69231d9860SAlexander Polakov #define PN8_STA_SFID(x) (((x) >> 8) & 0x3f)
70231d9860SAlexander Polakov #define PN8_STA_MFID(x) (((x) >> 16) & 0x3f)
71231d9860SAlexander Polakov #define PN8_STA_PENDING(x) (((x) >> 31) & 0x01)
72231d9860SAlexander Polakov #define PN8_STA_CVID(x) (((x) >> 32) & 0x1f)
73231d9860SAlexander Polakov #define PN8_STA_SVID(x) (((x) >> 40) & 0x1f)
74231d9860SAlexander Polakov #define PN8_STA_MVID(x) (((x) >> 48) & 0x1f)
75231d9860SAlexander Polakov #define PN8_PLL_LOCK(x) ((x) * 1000/5)
76231d9860SAlexander Polakov #define WRITE_FIDVID(fid, vid, ctrl) \
77231d9860SAlexander Polakov wrmsr(MSR_AMDK7_FIDVID_CTL, \
78231d9860SAlexander Polakov (((ctrl) << 32) | (1ULL << 16) | ((vid) << 8) | (fid)))
79231d9860SAlexander Polakov #define COUNT_OFF_IRT(irt) DELAY(10 * (1 << (irt)))
80231d9860SAlexander Polakov #define COUNT_OFF_VST(vst) DELAY(20 * (vst))
81231d9860SAlexander Polakov #define FID_TO_VCO_FID(fid) \
82231d9860SAlexander Polakov (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
83231d9860SAlexander Polakov
84231d9860SAlexander Polakov #define READ_PENDING_WAIT(status) \
85231d9860SAlexander Polakov do { \
86231d9860SAlexander Polakov (status) = rdmsr(MSR_AMDK7_FIDVID_STATUS); \
87231d9860SAlexander Polakov } while (PN8_STA_PENDING(status))
88124abffeSMaurizio Lombardi #define abs(x) ( (x) < 0 ? -(x) : (x) )
89231d9860SAlexander Polakov
90231d9860SAlexander Polakov #define POWERNOW_MAX_STATES 16
91231d9860SAlexander Polakov
92231d9860SAlexander Polakov struct k8pnow_state {
93231d9860SAlexander Polakov int freq;
94231d9860SAlexander Polakov uint8_t fid;
95231d9860SAlexander Polakov uint8_t vid;
96231d9860SAlexander Polakov };
97231d9860SAlexander Polakov
98231d9860SAlexander Polakov struct k8pnow_cpu_state {
99231d9860SAlexander Polakov struct k8pnow_state state_table[POWERNOW_MAX_STATES];
100231d9860SAlexander Polakov unsigned int n_states;
101231d9860SAlexander Polakov unsigned int vst;
102231d9860SAlexander Polakov unsigned int mvs;
103231d9860SAlexander Polakov unsigned int pll;
104231d9860SAlexander Polakov unsigned int rvo;
105231d9860SAlexander Polakov unsigned int irt;
106231d9860SAlexander Polakov int low;
107231d9860SAlexander Polakov };
108231d9860SAlexander Polakov
109231d9860SAlexander Polakov struct psb_s {
110231d9860SAlexander Polakov char signature [10]; /* AMDK7PNOW! */
111231d9860SAlexander Polakov uint8_t version;
112231d9860SAlexander Polakov uint8_t flags;
113231d9860SAlexander Polakov uint16_t ttime; /* Min Settling time */
114231d9860SAlexander Polakov uint8_t reserved;
115231d9860SAlexander Polakov uint8_t n_pst;
116231d9860SAlexander Polakov };
117231d9860SAlexander Polakov struct pst_s {
118231d9860SAlexander Polakov uint32_t cpuid;
119231d9860SAlexander Polakov uint8_t pll;
120231d9860SAlexander Polakov uint8_t fid;
121231d9860SAlexander Polakov uint8_t vid;
122231d9860SAlexander Polakov uint8_t n_states;
123231d9860SAlexander Polakov };
124231d9860SAlexander Polakov
125231d9860SAlexander Polakov static struct k8pnow_cpu_state *k8pnow_current_state = NULL;
126231d9860SAlexander Polakov int cpuspeed;
127231d9860SAlexander Polakov
128231d9860SAlexander Polakov int
129231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
130231d9860SAlexander Polakov unsigned int fid, unsigned int vid);
131231d9860SAlexander Polakov int
132231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p);
133231d9860SAlexander Polakov
134231d9860SAlexander Polakov /*
135231d9860SAlexander Polakov * Given a set of pair of fid/vid, and number of performance states, compute
136231d9860SAlexander Polakov * state_table via an insertion sort.
137231d9860SAlexander Polakov */
138231d9860SAlexander Polakov int
k8pnow_decode_pst(struct k8pnow_cpu_state * cstate,uint8_t * p)139231d9860SAlexander Polakov k8pnow_decode_pst(struct k8pnow_cpu_state *cstate, uint8_t * p)
140231d9860SAlexander Polakov {
141231d9860SAlexander Polakov int i , j, n;
142231d9860SAlexander Polakov struct k8pnow_state state;
143231d9860SAlexander Polakov for (n = 0, i = 0; i < cstate->n_states; i++) {
144231d9860SAlexander Polakov state.fid = *p++;
145231d9860SAlexander Polakov state.vid = *p++;
146231d9860SAlexander Polakov
147231d9860SAlexander Polakov /*
148231d9860SAlexander Polakov * The minimum supported frequency per the data sheet is
149231d9860SAlexander Polakov * 800MHz The maximum supported frequency is 5000MHz.
150231d9860SAlexander Polakov */
151231d9860SAlexander Polakov state.freq = 800 + state.fid * 100;
152231d9860SAlexander Polakov j = n;
153231d9860SAlexander Polakov while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
154231d9860SAlexander Polakov memcpy(&cstate->state_table[j],
155231d9860SAlexander Polakov &cstate->state_table[j - 1],
156231d9860SAlexander Polakov sizeof(struct k8pnow_state));
157231d9860SAlexander Polakov --j;
158231d9860SAlexander Polakov }
159231d9860SAlexander Polakov memcpy(&cstate->state_table[j], &state,
160231d9860SAlexander Polakov sizeof(struct k8pnow_state));
161231d9860SAlexander Polakov n++;
162231d9860SAlexander Polakov }
163231d9860SAlexander Polakov return 1;
164231d9860SAlexander Polakov }
165231d9860SAlexander Polakov
166231d9860SAlexander Polakov int
k8pnow_states(struct k8pnow_cpu_state * cstate,uint32_t cpusig,unsigned int fid,unsigned int vid)167231d9860SAlexander Polakov k8pnow_states(struct k8pnow_cpu_state *cstate, uint32_t cpusig,
168231d9860SAlexander Polakov unsigned int fid, unsigned int vid)
169231d9860SAlexander Polakov {
170231d9860SAlexander Polakov struct psb_s *psb;
171231d9860SAlexander Polakov struct pst_s *pst;
172231d9860SAlexander Polakov uint8_t *p;
173231d9860SAlexander Polakov int i;
174231d9860SAlexander Polakov for (p = (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START);
175231d9860SAlexander Polakov p < (u_int8_t *) BIOS_PADDRTOVADDR(BIOS_START + BIOS_LEN); p +=
176231d9860SAlexander Polakov BIOS_STEP) {
177231d9860SAlexander Polakov if (memcmp(p, "AMDK7PNOW!", 10) == 0) {
178231d9860SAlexander Polakov psb = (struct psb_s *)p;
179231d9860SAlexander Polakov if (psb->version != PN8_PSB_VERSION)
180231d9860SAlexander Polakov return 0;
181231d9860SAlexander Polakov cstate->vst = psb->ttime;
182231d9860SAlexander Polakov cstate->rvo = PN8_PSB_TO_RVO(psb->reserved);
183231d9860SAlexander Polakov cstate->irt = PN8_PSB_TO_IRT(psb->reserved);
184231d9860SAlexander Polakov cstate->mvs = PN8_PSB_TO_MVS(psb->reserved);
185231d9860SAlexander Polakov cstate->low = PN8_PSB_TO_BATT(psb->reserved);
186231d9860SAlexander Polakov p += sizeof(struct psb_s);
187231d9860SAlexander Polakov for (i = 0; i < psb->n_pst; ++i) {
188231d9860SAlexander Polakov pst = (struct pst_s *)p;
189231d9860SAlexander Polakov cstate->pll = pst->pll;
190231d9860SAlexander Polakov cstate->n_states = pst->n_states;
191231d9860SAlexander Polakov if (cpusig == pst->cpuid &&
192231d9860SAlexander Polakov pst->fid == fid && pst->vid == vid) {
193231d9860SAlexander Polakov return (k8pnow_decode_pst(cstate,
194231d9860SAlexander Polakov p += sizeof(struct pst_s)));
195231d9860SAlexander Polakov }
196231d9860SAlexander Polakov p += sizeof(struct pst_s) + 2
197231d9860SAlexander Polakov * cstate->n_states;
198231d9860SAlexander Polakov }
199231d9860SAlexander Polakov }
200231d9860SAlexander Polakov }
201231d9860SAlexander Polakov return 0;
202231d9860SAlexander Polakov }
203231d9860SAlexander Polakov
204231d9860SAlexander Polakov static int
k8_get_curfreq(void)205231d9860SAlexander Polakov k8_get_curfreq(void)
206231d9860SAlexander Polakov {
207231d9860SAlexander Polakov unsigned int i;
208231d9860SAlexander Polakov uint64_t status;
209*74205289SSascha Wildner int cfid, cvid;
210231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate;
211231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
212231d9860SAlexander Polakov if (PN8_STA_PENDING(status))
213231d9860SAlexander Polakov return 1;
214231d9860SAlexander Polakov cfid = PN8_STA_CFID(status);
215231d9860SAlexander Polakov cvid = PN8_STA_CVID(status);
216231d9860SAlexander Polakov cstate = k8pnow_current_state;
217231d9860SAlexander Polakov for (i = 0; i < cstate->n_states; i++) {
218231d9860SAlexander Polakov if (cstate->state_table[i].fid == cfid &&
219*74205289SSascha Wildner cstate->state_table[i].vid == cvid)
220231d9860SAlexander Polakov return (cstate->state_table[i].freq);
221231d9860SAlexander Polakov }
222231d9860SAlexander Polakov /* Not reached */
223231d9860SAlexander Polakov return -1;
224231d9860SAlexander Polakov }
225231d9860SAlexander Polakov
226231d9860SAlexander Polakov static int
k8_powernow_setperf(unsigned int freq)227231d9860SAlexander Polakov k8_powernow_setperf(unsigned int freq)
228231d9860SAlexander Polakov {
229231d9860SAlexander Polakov unsigned int i;
230231d9860SAlexander Polakov uint64_t status;
231231d9860SAlexander Polakov uint32_t val;
232231d9860SAlexander Polakov int cfid , cvid, fid = 0, vid = 0;
233231d9860SAlexander Polakov int rvo;
234231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate;
235231d9860SAlexander Polakov /*
236231d9860SAlexander Polakov * We dont do a k8pnow_read_pending_wait here, need to ensure that
237231d9860SAlexander Polakov * the change pending bit isn't stuck,
238231d9860SAlexander Polakov */
239231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
240231d9860SAlexander Polakov if (PN8_STA_PENDING(status))
241231d9860SAlexander Polakov return 1;
242231d9860SAlexander Polakov cfid = PN8_STA_CFID(status);
243231d9860SAlexander Polakov cvid = PN8_STA_CVID(status);
244231d9860SAlexander Polakov cstate = k8pnow_current_state;
245231d9860SAlexander Polakov for (i = 0; i < cstate->n_states; i++) {
246231d9860SAlexander Polakov if (cstate->state_table[i].freq >= freq) {
247231d9860SAlexander Polakov fid = cstate->state_table[i].fid;
248231d9860SAlexander Polakov vid = cstate->state_table[i].vid;
249231d9860SAlexander Polakov break;
250231d9860SAlexander Polakov }
251231d9860SAlexander Polakov }
252231d9860SAlexander Polakov if (fid == cfid && vid == cvid) {
253231d9860SAlexander Polakov cpuspeed = freq;
254231d9860SAlexander Polakov return 0;
255231d9860SAlexander Polakov }
256231d9860SAlexander Polakov /*
257231d9860SAlexander Polakov * Phase 1: Raise core voltage to requested VID if frequency is going
258231d9860SAlexander Polakov * up.
259231d9860SAlexander Polakov */
260231d9860SAlexander Polakov while (cvid > vid) {
261231d9860SAlexander Polakov val = cvid - (1 << cstate->mvs);
262231d9860SAlexander Polakov WRITE_FIDVID(cfid, (val > 0) ? val : 0, 1ULL);
263231d9860SAlexander Polakov READ_PENDING_WAIT(status);
264231d9860SAlexander Polakov cvid = PN8_STA_CVID(status);
265231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst);
266231d9860SAlexander Polakov }
267231d9860SAlexander Polakov
268231d9860SAlexander Polakov /* ... then raise to voltage + RVO (if required) */
269231d9860SAlexander Polakov for (rvo = cstate->rvo; rvo > 0 && cvid > 0; --rvo) {
270231d9860SAlexander Polakov /*
271231d9860SAlexander Polakov * XXX It's not clear from spec if we have to do that in 0.25
272231d9860SAlexander Polakov * step or in MVS. Therefore do it as it's done under Linux
273231d9860SAlexander Polakov */
274231d9860SAlexander Polakov WRITE_FIDVID(cfid, cvid - 1, 1ULL);
275231d9860SAlexander Polakov READ_PENDING_WAIT(status);
276231d9860SAlexander Polakov cvid = PN8_STA_CVID(status);
277231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst);
278231d9860SAlexander Polakov }
279231d9860SAlexander Polakov /* Phase 2: change to requested core frequency */
280231d9860SAlexander Polakov if (cfid != fid) {
281231d9860SAlexander Polakov uint32_t vco_fid, vco_cfid;
282231d9860SAlexander Polakov vco_fid = FID_TO_VCO_FID(fid);
283231d9860SAlexander Polakov vco_cfid = FID_TO_VCO_FID(cfid);
284231d9860SAlexander Polakov while (abs(vco_fid - vco_cfid) > 2) {
285231d9860SAlexander Polakov if (fid > cfid) {
286231d9860SAlexander Polakov if (cfid > 6)
287231d9860SAlexander Polakov val = cfid + 2;
288231d9860SAlexander Polakov else
289231d9860SAlexander Polakov val = FID_TO_VCO_FID(cfid) + 2;
290231d9860SAlexander Polakov } else
291231d9860SAlexander Polakov val = cfid - 2;
292231d9860SAlexander Polakov WRITE_FIDVID(val, cvid, (uint64_t) cstate->pll * 1000 / 5);
293231d9860SAlexander Polakov READ_PENDING_WAIT(status);
294231d9860SAlexander Polakov cfid = PN8_STA_CFID(status);
295231d9860SAlexander Polakov COUNT_OFF_IRT(cstate->irt);
296231d9860SAlexander Polakov vco_cfid = FID_TO_VCO_FID(cfid);
297231d9860SAlexander Polakov }
298231d9860SAlexander Polakov WRITE_FIDVID(fid, cvid, (uint64_t) cstate->pll * 1000 / 5);
299231d9860SAlexander Polakov READ_PENDING_WAIT(status);
300231d9860SAlexander Polakov cfid = PN8_STA_CFID(status);
301231d9860SAlexander Polakov COUNT_OFF_IRT(cstate->irt);
302231d9860SAlexander Polakov }
303231d9860SAlexander Polakov /* Phase 3: change to requested voltage */
304231d9860SAlexander Polakov if (cvid != vid) {
305231d9860SAlexander Polakov WRITE_FIDVID(cfid, vid, 1ULL);
306231d9860SAlexander Polakov READ_PENDING_WAIT(status);
307231d9860SAlexander Polakov cvid = PN8_STA_CVID(status);
308231d9860SAlexander Polakov COUNT_OFF_VST(cstate->vst);
309231d9860SAlexander Polakov }
310231d9860SAlexander Polakov if (cfid == fid || cvid == vid)
311231d9860SAlexander Polakov cpuspeed = cstate->state_table[i].freq;
312231d9860SAlexander Polakov return 0;
313231d9860SAlexander Polakov }
314231d9860SAlexander Polakov
315231d9860SAlexander Polakov static int
powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)316231d9860SAlexander Polakov powernow_sysctl_helper(SYSCTL_HANDLER_ARGS)
317231d9860SAlexander Polakov {
318231d9860SAlexander Polakov int fq , err = 0;
319231d9860SAlexander Polakov int i;
320231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate;
321231d9860SAlexander Polakov struct k8pnow_state *state;
322231d9860SAlexander Polakov cstate = k8pnow_current_state;
323231d9860SAlexander Polakov if (req->newptr != NULL) {
324231d9860SAlexander Polakov err = SYSCTL_IN(req, &fq, sizeof(fq));
325231d9860SAlexander Polakov if (err)
326231d9860SAlexander Polakov return err;
327231d9860SAlexander Polakov if (fq != cpuspeed) {
328231d9860SAlexander Polakov for (i = cstate->n_states; i > 0; i--) {
329231d9860SAlexander Polakov state = &cstate->state_table[i - 1];
330231d9860SAlexander Polakov if (fq == state->freq) {
331231d9860SAlexander Polakov k8_powernow_setperf(fq);
332231d9860SAlexander Polakov break;
333231d9860SAlexander Polakov }
334231d9860SAlexander Polakov }
335231d9860SAlexander Polakov }
336231d9860SAlexander Polakov } else {
337231d9860SAlexander Polakov err = SYSCTL_OUT(req, &cpuspeed, sizeof(cpuspeed));
338231d9860SAlexander Polakov }
339231d9860SAlexander Polakov return err;
340231d9860SAlexander Polakov }
341231d9860SAlexander Polakov
342231d9860SAlexander Polakov static struct sysctl_ctx_list machdep_powernow_ctx;
343231d9860SAlexander Polakov static char freqs_available[80];
344231d9860SAlexander Polakov
345231d9860SAlexander Polakov static int
powernow_init(void)346231d9860SAlexander Polakov powernow_init(void)
347231d9860SAlexander Polakov {
348231d9860SAlexander Polakov uint64_t status;
349231d9860SAlexander Polakov size_t len , freq_len;
350231d9860SAlexander Polakov uint32_t maxfid, maxvid, i;
351231d9860SAlexander Polakov struct k8pnow_cpu_state *cstate;
352231d9860SAlexander Polakov struct k8pnow_state *state;
353231d9860SAlexander Polakov const char *techname;
354231d9860SAlexander Polakov u_int32_t regs [4];
355231d9860SAlexander Polakov cpuspeed = 0;
356231d9860SAlexander Polakov struct sysctl_oid *oid, *leaf;
357231d9860SAlexander Polakov
358231d9860SAlexander Polakov do_cpuid(0x80000000, regs);
359231d9860SAlexander Polakov if (regs[0] < 0x80000007)
360231d9860SAlexander Polakov return 1;
361231d9860SAlexander Polakov do_cpuid(0x80000007, regs);
362231d9860SAlexander Polakov if (!(regs[3] & AMD_PN_FID_VID))
363231d9860SAlexander Polakov return 2;
364231d9860SAlexander Polakov /* Extended CPUID signature value */
365231d9860SAlexander Polakov do_cpuid(0x80000001, regs);
366231d9860SAlexander Polakov cstate = kmalloc(sizeof(struct k8pnow_cpu_state), M_DEVBUF, M_WAITOK);
367231d9860SAlexander Polakov cstate->n_states = 0;
368231d9860SAlexander Polakov
369231d9860SAlexander Polakov status = rdmsr(MSR_AMDK7_FIDVID_STATUS);
370231d9860SAlexander Polakov maxfid = PN8_STA_MFID(status);
371231d9860SAlexander Polakov maxvid = PN8_STA_MVID(status);
372231d9860SAlexander Polakov
373231d9860SAlexander Polakov if (PN8_STA_SFID(status) != PN8_STA_MFID(status))
374231d9860SAlexander Polakov techname = "PowerNow!";
375231d9860SAlexander Polakov else
376231d9860SAlexander Polakov techname = "Cool`n'Quiet";
377231d9860SAlexander Polakov k8pnow_states(cstate, regs[0], maxfid, maxvid);
378231d9860SAlexander Polakov len = 0;
379231d9860SAlexander Polakov if (cstate->n_states) {
380231d9860SAlexander Polakov freq_len = cstate->n_states * (sizeof("9999 ") - 1) + 1;
381231d9860SAlexander Polakov kprintf("%s speeds:",
382231d9860SAlexander Polakov techname);
383231d9860SAlexander Polakov for (i = cstate->n_states; i > 0; i--) {
384231d9860SAlexander Polakov state = &cstate->state_table[i - 1];
385231d9860SAlexander Polakov kprintf(" %d", state->freq);
386231d9860SAlexander Polakov len += ksnprintf(freqs_available + len, freq_len - len, "%d%s",
387231d9860SAlexander Polakov state->freq,
388231d9860SAlexander Polakov i > 1 ? " " : "");
389231d9860SAlexander Polakov }
390231d9860SAlexander Polakov kprintf(" MHz\n");
391231d9860SAlexander Polakov k8pnow_current_state = cstate;
392231d9860SAlexander Polakov k8_powernow_setperf(k8_get_curfreq());
393231d9860SAlexander Polakov } else {
394231d9860SAlexander Polakov kfree(cstate, M_DEVBUF);
395231d9860SAlexander Polakov kprintf("powernow: no power states found\n");
396231d9860SAlexander Polakov return 3;
397231d9860SAlexander Polakov }
398231d9860SAlexander Polakov
399231d9860SAlexander Polakov /*
400231d9860SAlexander Polakov * Setup the sysctl sub-tree machdep.powernow.*
401231d9860SAlexander Polakov */
402231d9860SAlexander Polakov oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx,
403231d9860SAlexander Polakov SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, "powernow",
404231d9860SAlexander Polakov CTLFLAG_RD, NULL, "");
405231d9860SAlexander Polakov if (oid == NULL)
406231d9860SAlexander Polakov return (EOPNOTSUPP);
407231d9860SAlexander Polakov oid = SYSCTL_ADD_NODE(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
408231d9860SAlexander Polakov OID_AUTO, "frequency", CTLFLAG_RD, NULL, "");
409231d9860SAlexander Polakov if (oid == NULL)
410231d9860SAlexander Polakov return (EOPNOTSUPP);
411231d9860SAlexander Polakov leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
412231d9860SAlexander Polakov OID_AUTO, "target", CTLTYPE_INT | CTLFLAG_RW, NULL, 0,
413231d9860SAlexander Polakov powernow_sysctl_helper, "I",
414231d9860SAlexander Polakov "Target CPU frequency for AMD PowerNow!");
415231d9860SAlexander Polakov if (leaf == NULL)
416231d9860SAlexander Polakov return (EOPNOTSUPP);
417231d9860SAlexander Polakov leaf = SYSCTL_ADD_PROC(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
418231d9860SAlexander Polakov OID_AUTO, "current", CTLTYPE_INT | CTLFLAG_RD, NULL, 0,
419231d9860SAlexander Polakov powernow_sysctl_helper, "I",
420231d9860SAlexander Polakov "Current CPU frequency for AMD PowerNow!");
421231d9860SAlexander Polakov if (leaf == NULL)
422231d9860SAlexander Polakov return (EOPNOTSUPP);
423231d9860SAlexander Polakov leaf = SYSCTL_ADD_STRING(&machdep_powernow_ctx, SYSCTL_CHILDREN(oid),
424231d9860SAlexander Polakov OID_AUTO, "available", CTLFLAG_RD, freqs_available,
425231d9860SAlexander Polakov sizeof(freqs_available),
426231d9860SAlexander Polakov "CPU frequencies supported by AMD PowerNow!");
427231d9860SAlexander Polakov if (leaf == NULL)
428231d9860SAlexander Polakov return (EOPNOTSUPP);
429231d9860SAlexander Polakov return (0);
430231d9860SAlexander Polakov }
431231d9860SAlexander Polakov
432231d9860SAlexander Polakov static int
powernow_modevh(struct module * m,int what,void * arg __unused)433231d9860SAlexander Polakov powernow_modevh(struct module *m, int what, void *arg __unused)
434231d9860SAlexander Polakov {
435231d9860SAlexander Polakov int error;
436231d9860SAlexander Polakov
437231d9860SAlexander Polakov switch (what) {
438231d9860SAlexander Polakov case MOD_LOAD:
439231d9860SAlexander Polakov error = sysctl_ctx_init(&machdep_powernow_ctx);
440231d9860SAlexander Polakov if (error != 0)
441231d9860SAlexander Polakov break;
442231d9860SAlexander Polakov error = powernow_init();
443231d9860SAlexander Polakov break;
444231d9860SAlexander Polakov case MOD_UNLOAD:
445231d9860SAlexander Polakov if (k8pnow_current_state)
446231d9860SAlexander Polakov kfree(k8pnow_current_state, M_DEVBUF);
447231d9860SAlexander Polakov error = sysctl_ctx_free(&machdep_powernow_ctx);
448231d9860SAlexander Polakov break;
449231d9860SAlexander Polakov default:
450231d9860SAlexander Polakov error = EINVAL;
451231d9860SAlexander Polakov break;
452231d9860SAlexander Polakov }
453231d9860SAlexander Polakov return (error);
454231d9860SAlexander Polakov }
455231d9860SAlexander Polakov static moduledata_t powernow_mod = {
456231d9860SAlexander Polakov "powernow",
457231d9860SAlexander Polakov powernow_modevh,
458231d9860SAlexander Polakov NULL,
459231d9860SAlexander Polakov };
460231d9860SAlexander Polakov
461231d9860SAlexander Polakov DECLARE_MODULE(powernow, powernow_mod, SI_BOOT2_KLD, SI_ORDER_ANY);
462