Commit | Line | Data |
---|---|---|
e774ca6d MD |
1 | /* |
2 | * Copyright (c) 2009 The DragonFly Project. All rights reserved. | |
3 | * | |
4 | * This code is derived from software contributed to The DragonFly Project | |
5 | * by Sepherosa Ziehau <sepherosa@gmail.com> | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in | |
15 | * the documentation and/or other materials provided with the | |
16 | * distribution. | |
17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
18 | * contributors may be used to endorse or promote products derived | |
19 | * from this software without specific, prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
32 | * SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <sys/param.h> | |
5e06f1b9 | 36 | #include <sys/kernel.h> |
e774ca6d MD |
37 | #include <sys/systm.h> |
38 | #include <sys/globaldata.h> | |
39 | ||
40 | #include <machine/md_var.h> | |
41 | #include <machine/cpufunc.h> | |
42 | #include <machine/cpufreq.h> | |
86b7a03a | 43 | #include <machine/cputypes.h> |
e774ca6d MD |
44 | #include <machine/specialreg.h> |
45 | ||
46 | #include "acpi.h" | |
47 | #include "acpi_cpu_pstate.h" | |
48 | ||
49 | #define AMD_APMI_HWPSTATE 0x80 | |
50 | ||
51 | #define AMD_MSR_PSTATE_CSR_MASK 0x7ULL | |
52 | #define AMD1X_MSR_PSTATE_CTL 0xc0010062 | |
53 | #define AMD1X_MSR_PSTATE_ST 0xc0010063 | |
54 | ||
55 | #define AMD_MSR_PSTATE_EN 0x8000000000000000ULL | |
56 | ||
b4fac0a6 | 57 | #define AMD1X_MSR_PSTATE_START 0xc0010064 |
e774ca6d | 58 | #define AMD10_MSR_PSTATE_COUNT 5 |
465a6ec2 | 59 | #define AMD11_MSR_PSTATE_COUNT 8 /* starting from 11h */ |
e774ca6d MD |
60 | |
61 | #define AMD0F_PST_CTL_FID(cval) (((cval) >> 0) & 0x3f) | |
62 | #define AMD0F_PST_CTL_VID(cval) (((cval) >> 6) & 0x1f) | |
63 | #define AMD0F_PST_CTL_VST(cval) (((cval) >> 11) & 0x7f) | |
64 | #define AMD0F_PST_CTL_MVS(cval) (((cval) >> 18) & 0x3) | |
65 | #define AMD0F_PST_CTL_PLLTIME(cval) (((cval) >> 20) & 0x7f) | |
66 | #define AMD0F_PST_CTL_RVO(cval) (((cval) >> 28) & 0x3) | |
67 | #define AMD0F_PST_CTL_IRT(cval) (((cval) >> 30) & 0x3) | |
68 | ||
69 | #define AMD0F_PST_ST_FID(sval) (((sval) >> 0) & 0x3f) | |
70 | #define AMD0F_PST_ST_VID(sval) (((sval) >> 6) & 0x3f) | |
71 | ||
72 | #define INTEL_MSR_MISC_ENABLE 0x1a0 | |
73 | #define INTEL_MSR_MISC_EST_EN 0x10000ULL | |
74 | ||
75 | #define INTEL_MSR_PERF_STATUS 0x198 | |
76 | #define INTEL_MSR_PERF_CTL 0x199 | |
77 | #define INTEL_MSR_PERF_MASK 0xffffULL | |
78 | ||
79 | static const struct acpi_pst_md * | |
80 | acpi_pst_amd_probe(void); | |
81 | static int acpi_pst_amd_check_csr(const struct acpi_pst_res *, | |
82 | const struct acpi_pst_res *); | |
b4fac0a6 | 83 | static int acpi_pst_amd1x_check_pstates1(const struct acpi_pstate *, int, |
e774ca6d | 84 | uint32_t, uint32_t); |
b4fac0a6 | 85 | static int acpi_pst_amd1x_check_pstates(const struct acpi_pstate *, int); |
e774ca6d MD |
86 | static int acpi_pst_amd0f_check_pstates(const struct acpi_pstate *, int); |
87 | static int acpi_pst_amd_init(const struct acpi_pst_res *, | |
88 | const struct acpi_pst_res *); | |
89 | static int acpi_pst_amd1x_set_pstate(const struct acpi_pst_res *, | |
90 | const struct acpi_pst_res *, const struct acpi_pstate *); | |
91 | static int acpi_pst_amd0f_set_pstate(const struct acpi_pst_res *, | |
92 | const struct acpi_pst_res *, const struct acpi_pstate *); | |
93 | static const struct acpi_pstate * | |
94 | acpi_pst_amd1x_get_pstate(const struct acpi_pst_res *, | |
95 | const struct acpi_pstate *, int); | |
96 | static const struct acpi_pstate * | |
97 | acpi_pst_amd0f_get_pstate(const struct acpi_pst_res *, | |
98 | const struct acpi_pstate *, int); | |
99 | ||
100 | static const struct acpi_pst_md * | |
101 | acpi_pst_intel_probe(void); | |
102 | static int acpi_pst_intel_check_csr(const struct acpi_pst_res *, | |
103 | const struct acpi_pst_res *); | |
104 | static int acpi_pst_intel_check_pstates(const struct acpi_pstate *, int); | |
105 | static int acpi_pst_intel_init(const struct acpi_pst_res *, | |
106 | const struct acpi_pst_res *); | |
107 | static int acpi_pst_intel_set_pstate(const struct acpi_pst_res *, | |
108 | const struct acpi_pst_res *, const struct acpi_pstate *); | |
109 | static const struct acpi_pstate * | |
110 | acpi_pst_intel_get_pstate(const struct acpi_pst_res *, | |
111 | const struct acpi_pstate *, int); | |
112 | ||
113 | static int acpi_pst_md_gas_asz(const ACPI_GENERIC_ADDRESS *); | |
114 | static int acpi_pst_md_gas_verify(const ACPI_GENERIC_ADDRESS *); | |
115 | static uint32_t acpi_pst_md_res_read(const struct acpi_pst_res *); | |
116 | static void acpi_pst_md_res_write(const struct acpi_pst_res *, uint32_t); | |
117 | ||
b4fac0a6 | 118 | static const struct acpi_pst_md acpi_pst_amd1x = { |
e774ca6d | 119 | .pmd_check_csr = acpi_pst_amd_check_csr, |
b4fac0a6 | 120 | .pmd_check_pstates = acpi_pst_amd1x_check_pstates, |
e774ca6d MD |
121 | .pmd_init = acpi_pst_amd_init, |
122 | .pmd_set_pstate = acpi_pst_amd1x_set_pstate, | |
123 | .pmd_get_pstate = acpi_pst_amd1x_get_pstate | |
124 | }; | |
125 | ||
126 | static const struct acpi_pst_md acpi_pst_amd0f = { | |
127 | .pmd_check_csr = acpi_pst_amd_check_csr, | |
128 | .pmd_check_pstates = acpi_pst_amd0f_check_pstates, | |
129 | .pmd_init = acpi_pst_amd_init, | |
130 | .pmd_set_pstate = acpi_pst_amd0f_set_pstate, | |
131 | .pmd_get_pstate = acpi_pst_amd0f_get_pstate | |
132 | }; | |
133 | ||
134 | static const struct acpi_pst_md acpi_pst_intel = { | |
135 | .pmd_check_csr = acpi_pst_intel_check_csr, | |
136 | .pmd_check_pstates = acpi_pst_intel_check_pstates, | |
137 | .pmd_init = acpi_pst_intel_init, | |
138 | .pmd_set_pstate = acpi_pst_intel_set_pstate, | |
139 | .pmd_get_pstate = acpi_pst_intel_get_pstate | |
140 | }; | |
141 | ||
5e06f1b9 SZ |
142 | static int acpi_pst_stringent_check = 1; |
143 | TUNABLE_INT("hw.acpi.cpu.pstate.strigent_check", &acpi_pst_stringent_check); | |
144 | ||
465a6ec2 SZ |
145 | static int acpi_pst_amd1x_msr_pstate_count = AMD10_MSR_PSTATE_COUNT; |
146 | ||
e774ca6d MD |
147 | const struct acpi_pst_md * |
148 | acpi_pst_md_probe(void) | |
149 | { | |
86b7a03a | 150 | if (cpu_vendor_id == CPU_VENDOR_AMD) |
e774ca6d | 151 | return acpi_pst_amd_probe(); |
86b7a03a | 152 | else if (cpu_vendor_id == CPU_VENDOR_INTEL) |
e774ca6d MD |
153 | return acpi_pst_intel_probe(); |
154 | return NULL; | |
155 | } | |
156 | ||
157 | static const struct acpi_pst_md * | |
158 | acpi_pst_amd_probe(void) | |
159 | { | |
e876e4b5 | 160 | uint32_t regs[4]; |
e774ca6d | 161 | |
e876e4b5 SZ |
162 | /* Only Family >= 0fh has P-State support */ |
163 | if (CPUID_TO_FAMILY(cpu_id) < 0xf) | |
e774ca6d MD |
164 | return NULL; |
165 | ||
166 | /* Check whether APMI exists */ | |
e876e4b5 | 167 | if (cpu_exthigh < 0x80000007) |
e774ca6d MD |
168 | return NULL; |
169 | ||
170 | /* Fetch APMI */ | |
171 | do_cpuid(0x80000007, regs); | |
172 | ||
e876e4b5 | 173 | if (CPUID_TO_FAMILY(cpu_id) == 0xf) { /* Family 0fh */ |
e774ca6d MD |
174 | if ((regs[3] & 0x06) == 0x06) |
175 | return &acpi_pst_amd0f; | |
e876e4b5 | 176 | } else if (CPUID_TO_FAMILY(cpu_id) >= 0x10) { /* Family >= 10h */ |
465a6ec2 SZ |
177 | if (CPUID_TO_FAMILY(cpu_id) >= 0x11) { |
178 | acpi_pst_amd1x_msr_pstate_count = | |
179 | AMD11_MSR_PSTATE_COUNT; | |
180 | } | |
e774ca6d | 181 | if (regs[3] & 0x80) |
b4fac0a6 | 182 | return &acpi_pst_amd1x; |
e774ca6d MD |
183 | } |
184 | return NULL; | |
185 | } | |
186 | ||
187 | static int | |
188 | acpi_pst_amd_check_csr(const struct acpi_pst_res *ctrl, | |
189 | const struct acpi_pst_res *status) | |
190 | { | |
191 | if (ctrl->pr_gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) { | |
192 | kprintf("cpu%d: Invalid P-State control register\n", mycpuid); | |
193 | return EINVAL; | |
194 | } | |
195 | if (status->pr_gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) { | |
196 | kprintf("cpu%d: Invalid P-State status register\n", mycpuid); | |
197 | return EINVAL; | |
198 | } | |
199 | return 0; | |
200 | } | |
201 | ||
202 | static int | |
b4fac0a6 SZ |
203 | acpi_pst_amd1x_check_pstates1(const struct acpi_pstate *pstates, int npstates, |
204 | uint32_t msr_start, uint32_t msr_end) | |
e774ca6d MD |
205 | { |
206 | int i; | |
207 | ||
208 | /* | |
209 | * Make sure that related MSR P-State registers are enabled. | |
210 | * | |
211 | * NOTE: | |
212 | * We don't check status register value here; | |
213 | * it will not be used. | |
214 | */ | |
215 | for (i = 0; i < npstates; ++i) { | |
216 | uint64_t pstate; | |
217 | uint32_t msr; | |
218 | ||
219 | msr = msr_start + | |
220 | (pstates[i].st_cval & AMD_MSR_PSTATE_CSR_MASK); | |
221 | if (msr >= msr_end) { | |
222 | kprintf("cpu%d: MSR P-State register %#08x " | |
223 | "does not exist\n", mycpuid, msr); | |
224 | return EINVAL; | |
225 | } | |
226 | ||
227 | pstate = rdmsr(msr); | |
228 | if ((pstate & AMD_MSR_PSTATE_EN) == 0) { | |
229 | kprintf("cpu%d: MSR P-State register %#08x " | |
230 | "is not enabled\n", mycpuid, msr); | |
231 | return EINVAL; | |
232 | } | |
233 | } | |
234 | return 0; | |
235 | } | |
236 | ||
237 | static int | |
b4fac0a6 | 238 | acpi_pst_amd1x_check_pstates(const struct acpi_pstate *pstates, int npstates) |
e774ca6d | 239 | { |
465a6ec2 SZ |
240 | if (npstates > acpi_pst_amd1x_msr_pstate_count) { |
241 | kprintf("cpu%d: only %d P-states are allowed\n", mycpuid, | |
242 | acpi_pst_amd1x_msr_pstate_count); | |
e774ca6d MD |
243 | return EINVAL; |
244 | } | |
245 | ||
b4fac0a6 SZ |
246 | return acpi_pst_amd1x_check_pstates1(pstates, npstates, |
247 | AMD1X_MSR_PSTATE_START, | |
248 | AMD1X_MSR_PSTATE_START + acpi_pst_amd1x_msr_pstate_count); | |
e774ca6d MD |
249 | } |
250 | ||
251 | static int | |
252 | acpi_pst_amd1x_set_pstate(const struct acpi_pst_res *ctrl __unused, | |
253 | const struct acpi_pst_res *status __unused, | |
254 | const struct acpi_pstate *pstate) | |
255 | { | |
256 | uint64_t cval; | |
257 | ||
258 | cval = pstate->st_cval & AMD_MSR_PSTATE_CSR_MASK; | |
259 | wrmsr(AMD1X_MSR_PSTATE_CTL, cval); | |
260 | ||
261 | /* | |
262 | * Don't check AMD1X_MSR_PSTATE_ST here, since it is | |
263 | * affected by various P-State limits. | |
264 | * | |
265 | * For details: | |
266 | * AMD Family 10h Processor BKDG Rev 3.20 (#31116) | |
267 | * 2.4.2.4 P-state Transition Behavior | |
268 | */ | |
269 | ||
270 | return 0; | |
271 | } | |
272 | ||
273 | static const struct acpi_pstate * | |
274 | acpi_pst_amd1x_get_pstate(const struct acpi_pst_res *status __unused, | |
275 | const struct acpi_pstate *pstates, int npstates) | |
276 | { | |
277 | uint64_t sval; | |
278 | int i; | |
279 | ||
280 | sval = rdmsr(AMD1X_MSR_PSTATE_ST) & AMD_MSR_PSTATE_CSR_MASK; | |
281 | for (i = 0; i < npstates; ++i) { | |
282 | if ((pstates[i].st_sval & AMD_MSR_PSTATE_CSR_MASK) == sval) | |
283 | return &pstates[i]; | |
284 | } | |
285 | return NULL; | |
286 | } | |
287 | ||
288 | static int | |
289 | acpi_pst_amd0f_check_pstates(const struct acpi_pstate *pstates, int npstates) | |
290 | { | |
291 | struct amd0f_fidvid fv_max, fv_min; | |
292 | int i; | |
293 | ||
294 | amd0f_fidvid_limit(&fv_min, &fv_max); | |
295 | ||
ed468a9a SZ |
296 | if (fv_min.fid == fv_max.fid && fv_min.vid == fv_max.vid) { |
297 | kprintf("cpu%d: only one P-State is supported\n", mycpuid); | |
5e06f1b9 SZ |
298 | if (acpi_pst_stringent_check) |
299 | return EOPNOTSUPP; | |
ed468a9a SZ |
300 | } |
301 | ||
e774ca6d MD |
302 | for (i = 0; i < npstates; ++i) { |
303 | const struct acpi_pstate *p = &pstates[i]; | |
304 | uint32_t fid, vid, mvs, rvo; | |
305 | int mvs_mv, rvo_mv; | |
306 | ||
307 | fid = AMD0F_PST_CTL_FID(p->st_cval); | |
308 | vid = AMD0F_PST_CTL_VID(p->st_cval); | |
309 | ||
ed468a9a SZ |
310 | if (i == 0) { |
311 | if (vid != fv_max.vid) { | |
312 | kprintf("cpu%d: max VID mismatch " | |
313 | "real %u, lim %d\n", mycpuid, | |
314 | vid, fv_max.vid); | |
315 | } | |
316 | if (fid != fv_max.fid) { | |
317 | kprintf("cpu%d: max FID mismatch " | |
318 | "real %u, lim %d\n", mycpuid, | |
319 | fid, fv_max.fid); | |
320 | } | |
321 | } else if (i == npstates - 1) { | |
322 | if (vid != fv_min.vid) { | |
323 | kprintf("cpu%d: min VID mismatch " | |
324 | "real %u, lim %d\n", mycpuid, | |
325 | vid, fv_min.vid); | |
326 | } | |
327 | if (fid != fv_min.fid) { | |
328 | kprintf("cpu%d: min FID mismatch " | |
329 | "real %u, lim %d\n", mycpuid, | |
330 | fid, fv_min.fid); | |
331 | } | |
332 | } else { | |
333 | if (fid >= fv_max.fid || fid < (fv_min.fid + 0x8)) { | |
334 | kprintf("cpu%d: Invalid FID %#x, " | |
335 | "out [%#x, %#x]\n", mycpuid, fid, | |
336 | fv_min.fid + 0x8, fv_max.fid); | |
5e06f1b9 SZ |
337 | if (acpi_pst_stringent_check) |
338 | return EINVAL; | |
ed468a9a SZ |
339 | } |
340 | if (vid < fv_max.vid || vid > fv_min.vid) { | |
341 | kprintf("cpu%d: Invalid VID %#x, " | |
342 | "in [%#x, %#x]\n", mycpuid, vid, | |
343 | fv_max.vid, fv_min.vid); | |
5e06f1b9 SZ |
344 | if (acpi_pst_stringent_check) |
345 | return EINVAL; | |
ed468a9a | 346 | } |
e774ca6d MD |
347 | } |
348 | ||
349 | mvs = AMD0F_PST_CTL_MVS(p->st_cval); | |
350 | rvo = AMD0F_PST_CTL_RVO(p->st_cval); | |
351 | ||
352 | /* Only 0 is allowed, i.e. 25mV stepping */ | |
353 | if (mvs != 0) { | |
354 | kprintf("cpu%d: Invalid MVS %#x\n", mycpuid, mvs); | |
355 | return EINVAL; | |
356 | } | |
357 | ||
358 | /* -> mV */ | |
359 | mvs_mv = 25 * (1 << mvs); | |
360 | rvo_mv = 25 * rvo; | |
361 | if (rvo_mv % mvs_mv != 0) { | |
362 | kprintf("cpu%d: Invalid MVS/RVO (%#x/%#x)\n", | |
363 | mycpuid, mvs, rvo); | |
364 | return EINVAL; | |
365 | } | |
366 | } | |
367 | return 0; | |
368 | } | |
369 | ||
370 | static int | |
371 | acpi_pst_amd0f_set_pstate(const struct acpi_pst_res *ctrl __unused, | |
372 | const struct acpi_pst_res *status __unused, | |
373 | const struct acpi_pstate *pstate) | |
374 | { | |
375 | struct amd0f_fidvid fv; | |
376 | struct amd0f_xsit xsit; | |
377 | ||
378 | fv.fid = AMD0F_PST_CTL_FID(pstate->st_cval); | |
379 | fv.vid = AMD0F_PST_CTL_VID(pstate->st_cval); | |
380 | ||
381 | xsit.rvo = AMD0F_PST_CTL_RVO(pstate->st_cval); | |
382 | xsit.mvs = AMD0F_PST_CTL_MVS(pstate->st_cval); | |
383 | xsit.vst = AMD0F_PST_CTL_VST(pstate->st_cval); | |
384 | xsit.pll_time = AMD0F_PST_CTL_PLLTIME(pstate->st_cval); | |
385 | xsit.irt = AMD0F_PST_CTL_IRT(pstate->st_cval); | |
386 | ||
387 | return amd0f_set_fidvid(&fv, &xsit); | |
388 | } | |
389 | ||
390 | static const struct acpi_pstate * | |
391 | acpi_pst_amd0f_get_pstate(const struct acpi_pst_res *status __unused, | |
392 | const struct acpi_pstate *pstates, int npstates) | |
393 | { | |
394 | struct amd0f_fidvid fv; | |
395 | int error, i; | |
396 | ||
397 | error = amd0f_get_fidvid(&fv); | |
398 | if (error) | |
399 | return NULL; | |
400 | ||
401 | for (i = 0; i < npstates; ++i) { | |
402 | const struct acpi_pstate *p = &pstates[i]; | |
403 | ||
404 | if (fv.fid == AMD0F_PST_ST_FID(p->st_sval) && | |
405 | fv.vid == AMD0F_PST_ST_VID(p->st_sval)) | |
406 | return p; | |
407 | } | |
408 | return NULL; | |
409 | } | |
410 | ||
411 | static int | |
412 | acpi_pst_amd_init(const struct acpi_pst_res *ctrl __unused, | |
413 | const struct acpi_pst_res *status __unused) | |
414 | { | |
415 | return 0; | |
416 | } | |
417 | ||
418 | static const struct acpi_pst_md * | |
419 | acpi_pst_intel_probe(void) | |
420 | { | |
e774ca6d MD |
421 | if ((cpu_feature2 & CPUID2_EST) == 0) |
422 | return NULL; | |
423 | ||
7d338ab4 SZ |
424 | if (CPUID_TO_FAMILY(cpu_id) >= 0xf || CPUID_TO_FAMILY(cpu_id) == 0x6) |
425 | return &acpi_pst_intel; | |
426 | ||
427 | return NULL; | |
e774ca6d MD |
428 | } |
429 | ||
430 | static int | |
431 | acpi_pst_intel_check_csr(const struct acpi_pst_res *ctrl, | |
432 | const struct acpi_pst_res *status) | |
433 | { | |
434 | int error; | |
435 | ||
436 | if (ctrl->pr_gas.SpaceId != status->pr_gas.SpaceId) { | |
437 | kprintf("cpu%d: P-State control(%d)/status(%d) registers have " | |
438 | "different SpaceId", mycpuid, | |
439 | ctrl->pr_gas.SpaceId, status->pr_gas.SpaceId); | |
440 | return EINVAL; | |
441 | } | |
442 | ||
443 | switch (ctrl->pr_gas.SpaceId) { | |
444 | case ACPI_ADR_SPACE_FIXED_HARDWARE: | |
445 | if (ctrl->pr_res != NULL || status->pr_res != NULL) { | |
446 | /* XXX should panic() */ | |
447 | kprintf("cpu%d: Allocated resource for fixed hardware " | |
448 | "registers\n", mycpuid); | |
449 | return EINVAL; | |
450 | } | |
451 | break; | |
452 | ||
453 | case ACPI_ADR_SPACE_SYSTEM_IO: | |
454 | if (ctrl->pr_res == NULL) { | |
455 | kprintf("cpu%d: ioport allocation failed for control " | |
456 | "register\n", mycpuid); | |
457 | return ENXIO; | |
458 | } | |
459 | error = acpi_pst_md_gas_verify(&ctrl->pr_gas); | |
460 | if (error) { | |
461 | kprintf("cpu%d: Invalid control register GAS\n", | |
462 | mycpuid); | |
463 | return error; | |
464 | } | |
465 | ||
466 | if (status->pr_res == NULL) { | |
467 | kprintf("cpu%d: ioport allocation failed for status " | |
468 | "register\n", mycpuid); | |
469 | return ENXIO; | |
470 | } | |
471 | error = acpi_pst_md_gas_verify(&status->pr_gas); | |
472 | if (error) { | |
473 | kprintf("cpu%d: Invalid status register GAS\n", | |
474 | mycpuid); | |
475 | return error; | |
476 | } | |
477 | break; | |
478 | ||
479 | default: | |
480 | kprintf("cpu%d: Invalid P-State control/status register " | |
481 | "SpaceId %d\n", mycpuid, ctrl->pr_gas.SpaceId); | |
482 | return EOPNOTSUPP; | |
483 | } | |
484 | return 0; | |
485 | } | |
486 | ||
487 | static int | |
488 | acpi_pst_intel_check_pstates(const struct acpi_pstate *pstates __unused, | |
489 | int npstates __unused) | |
490 | { | |
491 | return 0; | |
492 | } | |
493 | ||
494 | static int | |
495 | acpi_pst_intel_init(const struct acpi_pst_res *ctrl __unused, | |
496 | const struct acpi_pst_res *status __unused) | |
497 | { | |
e774ca6d MD |
498 | uint64_t misc_enable; |
499 | ||
7d338ab4 SZ |
500 | if (CPUID_TO_FAMILY(cpu_id) == 0xf || |
501 | (CPUID_TO_FAMILY(cpu_id) == 0x6 && CPUID_TO_MODEL(cpu_id) < 0xd)) { | |
e774ca6d MD |
502 | /* EST enable bit is reserved in INTEL_MSR_MISC_ENABLE */ |
503 | return 0; | |
504 | } | |
505 | ||
506 | misc_enable = rdmsr(INTEL_MSR_MISC_ENABLE); | |
507 | if ((misc_enable & INTEL_MSR_MISC_EST_EN) == 0) { | |
508 | misc_enable |= INTEL_MSR_MISC_EST_EN; | |
509 | wrmsr(INTEL_MSR_MISC_ENABLE, misc_enable); | |
510 | ||
511 | misc_enable = rdmsr(INTEL_MSR_MISC_ENABLE); | |
512 | if ((misc_enable & INTEL_MSR_MISC_EST_EN) == 0) { | |
513 | kprintf("cpu%d: Can't enable EST\n", mycpuid); | |
514 | return EIO; | |
515 | } | |
516 | } | |
517 | return 0; | |
518 | } | |
519 | ||
520 | static int | |
521 | acpi_pst_intel_set_pstate(const struct acpi_pst_res *ctrl, | |
522 | const struct acpi_pst_res *status __unused, | |
523 | const struct acpi_pstate *pstate) | |
524 | { | |
525 | if (ctrl->pr_res != NULL) { | |
526 | acpi_pst_md_res_write(ctrl, pstate->st_cval); | |
527 | } else { | |
528 | uint64_t ctl; | |
529 | ||
530 | ctl = rdmsr(INTEL_MSR_PERF_CTL); | |
531 | ctl &= ~INTEL_MSR_PERF_MASK; | |
532 | ctl |= (pstate->st_cval & INTEL_MSR_PERF_MASK); | |
533 | wrmsr(INTEL_MSR_PERF_CTL, ctl); | |
534 | } | |
535 | return 0; | |
536 | } | |
537 | ||
538 | static const struct acpi_pstate * | |
539 | acpi_pst_intel_get_pstate(const struct acpi_pst_res *status, | |
540 | const struct acpi_pstate *pstates, int npstates) | |
541 | { | |
542 | int i; | |
543 | ||
544 | if (status->pr_res != NULL) { | |
545 | uint32_t st; | |
546 | ||
547 | st = acpi_pst_md_res_read(status); | |
548 | for (i = 0; i < npstates; ++i) { | |
549 | if (pstates[i].st_sval == st) | |
550 | return &pstates[i]; | |
551 | } | |
552 | } else { | |
553 | uint64_t sval; | |
554 | ||
555 | sval = rdmsr(INTEL_MSR_PERF_STATUS) & INTEL_MSR_PERF_MASK; | |
556 | for (i = 0; i < npstates; ++i) { | |
557 | if ((pstates[i].st_sval & INTEL_MSR_PERF_MASK) == sval) | |
558 | return &pstates[i]; | |
559 | } | |
560 | } | |
561 | return NULL; | |
562 | } | |
563 | ||
564 | static int | |
565 | acpi_pst_md_gas_asz(const ACPI_GENERIC_ADDRESS *gas) | |
566 | { | |
567 | int asz; | |
568 | ||
569 | if (gas->AccessWidth != 0) | |
570 | asz = gas->AccessWidth; | |
571 | else | |
572 | asz = gas->BitWidth / NBBY; | |
573 | switch (asz) { | |
574 | case 1: | |
575 | case 2: | |
576 | case 4: | |
577 | break; | |
578 | default: | |
579 | asz = 0; | |
580 | break; | |
581 | } | |
582 | return asz; | |
583 | } | |
584 | ||
585 | static int | |
586 | acpi_pst_md_gas_verify(const ACPI_GENERIC_ADDRESS *gas) | |
587 | { | |
588 | int reg, end, asz; | |
589 | ||
590 | if (gas->BitOffset % NBBY != 0) | |
591 | return EINVAL; | |
592 | ||
593 | end = gas->BitWidth / NBBY; | |
594 | reg = gas->BitOffset / NBBY; | |
595 | ||
596 | if (reg >= end) | |
597 | return EINVAL; | |
598 | ||
599 | asz = acpi_pst_md_gas_asz(gas); | |
600 | if (asz == 0) | |
601 | return EINVAL; | |
602 | ||
603 | if (reg + asz > end) | |
604 | return EINVAL; | |
605 | return 0; | |
606 | } | |
607 | ||
608 | static uint32_t | |
609 | acpi_pst_md_res_read(const struct acpi_pst_res *res) | |
610 | { | |
611 | int asz, reg; | |
612 | ||
613 | KKASSERT(res->pr_res != NULL); | |
614 | asz = acpi_pst_md_gas_asz(&res->pr_gas); | |
615 | reg = res->pr_gas.BitOffset / NBBY; | |
616 | ||
617 | switch (asz) { | |
618 | case 1: | |
619 | return bus_space_read_1(res->pr_bt, res->pr_bh, reg); | |
620 | case 2: | |
621 | return bus_space_read_2(res->pr_bt, res->pr_bh, reg); | |
622 | case 4: | |
623 | return bus_space_read_4(res->pr_bt, res->pr_bh, reg); | |
624 | } | |
ed20d0e3 | 625 | panic("unsupported access width %d", asz); |
e774ca6d MD |
626 | |
627 | /* NEVER REACHED */ | |
628 | return 0; | |
629 | } | |
630 | ||
631 | static void | |
632 | acpi_pst_md_res_write(const struct acpi_pst_res *res, uint32_t val) | |
633 | { | |
634 | int asz, reg; | |
635 | ||
636 | KKASSERT(res->pr_res != NULL); | |
637 | asz = acpi_pst_md_gas_asz(&res->pr_gas); | |
638 | reg = res->pr_gas.BitOffset / NBBY; | |
639 | ||
640 | switch (asz) { | |
641 | case 1: | |
642 | bus_space_write_1(res->pr_bt, res->pr_bh, reg, val); | |
643 | break; | |
644 | case 2: | |
645 | bus_space_write_2(res->pr_bt, res->pr_bh, reg, val); | |
646 | break; | |
647 | case 4: | |
648 | bus_space_write_4(res->pr_bt, res->pr_bh, reg, val); | |
649 | break; | |
650 | default: | |
ed20d0e3 | 651 | panic("unsupported access width %d", asz); |
e774ca6d MD |
652 | } |
653 | } |