2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/globaldata.h>
39 #include <machine/md_var.h>
40 #include <machine/cpufunc.h>
41 #include <machine/cpufreq.h>
44 #include "acpi_cpu_pstate.h"
46 #define AMD_APMI_HWPSTATE 0x80
48 #define AMD_MSR_PSTATE_CSR_MASK 0x7ULL
49 #define AMD1XH_MSR_PSTATE_CTL 0xc0010062
50 #define AMD1XH_MSR_PSTATE_ST 0xc0010063
52 #define AMD_MSR_PSTATE_EN 0x8000000000000000ULL
54 #define AMD10H_MSR_PSTATE_START 0xc0010064
55 #define AMD10H_MSR_PSTATE_COUNT 5
57 #define AMD0F_PST_CTL_FID(cval) (((cval) >> 0) & 0x3f)
58 #define AMD0F_PST_CTL_VID(cval) (((cval) >> 6) & 0x1f)
59 #define AMD0F_PST_CTL_VST(cval) (((cval) >> 11) & 0x7f)
60 #define AMD0F_PST_CTL_MVS(cval) (((cval) >> 18) & 0x3)
61 #define AMD0F_PST_CTL_PLLTIME(cval) (((cval) >> 20) & 0x7f)
62 #define AMD0F_PST_CTL_RVO(cval) (((cval) >> 28) & 0x3)
63 #define AMD0F_PST_CTL_IRT(cval) (((cval) >> 30) & 0x3)
65 #define AMD0F_PST_ST_FID(sval) (((sval) >> 0) & 0x3f)
66 #define AMD0F_PST_ST_VID(sval) (((sval) >> 6) & 0x3f)
68 static const struct acpi_pst_md *
69 acpi_pst_amd_probe(void);
70 static int acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *,
71 const ACPI_RESOURCE_GENERIC_REGISTER *);
72 static int acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *, int,
74 static int acpi_pst_amd10h_check_pstates(const struct acpi_pstate *, int);
75 static int acpi_pst_amd0f_check_pstates(const struct acpi_pstate *, int);
76 static int acpi_pst_amd1xh_set_pstate(
77 const ACPI_RESOURCE_GENERIC_REGISTER *,
78 const ACPI_RESOURCE_GENERIC_REGISTER *,
79 const struct acpi_pstate *);
80 static int acpi_pst_amd0f_set_pstate(
81 const ACPI_RESOURCE_GENERIC_REGISTER *,
82 const ACPI_RESOURCE_GENERIC_REGISTER *,
83 const struct acpi_pstate *);
84 static const struct acpi_pstate *
85 acpi_pst_amd1xh_get_pstate(
86 const ACPI_RESOURCE_GENERIC_REGISTER *,
87 const struct acpi_pstate *, int);
88 static const struct acpi_pstate *
89 acpi_pst_amd0f_get_pstate(
90 const ACPI_RESOURCE_GENERIC_REGISTER *,
91 const struct acpi_pstate *, int);
93 static const struct acpi_pst_md acpi_pst_amd10h = {
94 .pmd_check_csr = acpi_pst_amd_check_csr,
95 .pmd_check_pstates = acpi_pst_amd10h_check_pstates,
96 .pmd_set_pstate = acpi_pst_amd1xh_set_pstate,
97 .pmd_get_pstate = acpi_pst_amd1xh_get_pstate
100 static const struct acpi_pst_md acpi_pst_amd0fh = {
101 .pmd_check_csr = acpi_pst_amd_check_csr,
102 .pmd_check_pstates = acpi_pst_amd0f_check_pstates,
103 .pmd_set_pstate = acpi_pst_amd0f_set_pstate,
104 .pmd_get_pstate = acpi_pst_amd0f_get_pstate
107 const struct acpi_pst_md *
108 acpi_pst_md_probe(void)
110 if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
111 return acpi_pst_amd_probe();
115 static const struct acpi_pst_md *
116 acpi_pst_amd_probe(void)
118 uint32_t regs[4], ext_family;
120 if ((cpu_id & 0x00000f00) != 0x00000f00)
123 /* Check whether APMI exists */
124 do_cpuid(0x80000000, regs);
125 if (regs[0] < 0x80000007)
129 do_cpuid(0x80000007, regs);
131 ext_family = cpu_id & 0x0ff00000;
132 switch (ext_family) {
133 case 0x00000000: /* Family 0fh */
134 if ((regs[3] & 0x06) == 0x06)
135 return &acpi_pst_amd0fh;
138 case 0x00100000: /* Family 10h */
140 return &acpi_pst_amd10h;
150 acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl,
151 const ACPI_RESOURCE_GENERIC_REGISTER *status)
153 if (ctrl->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
154 kprintf("cpu%d: Invalid P-State control register\n", mycpuid);
157 if (status->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
158 kprintf("cpu%d: Invalid P-State status register\n", mycpuid);
165 acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *pstates, int npstates,
166 uint32_t msr_start, uint32_t msr_end)
171 * Make sure that related MSR P-State registers are enabled.
174 * We don't check status register value here;
175 * it will not be used.
177 for (i = 0; i < npstates; ++i) {
182 (pstates[i].st_cval & AMD_MSR_PSTATE_CSR_MASK);
183 if (msr >= msr_end) {
184 kprintf("cpu%d: MSR P-State register %#08x "
185 "does not exist\n", mycpuid, msr);
190 if ((pstate & AMD_MSR_PSTATE_EN) == 0) {
191 kprintf("cpu%d: MSR P-State register %#08x "
192 "is not enabled\n", mycpuid, msr);
200 acpi_pst_amd10h_check_pstates(const struct acpi_pstate *pstates, int npstates)
202 /* Only P0-P4 are supported */
203 if (npstates > AMD10H_MSR_PSTATE_COUNT) {
204 kprintf("cpu%d: only P0-P4 is allowed\n", mycpuid);
208 return acpi_pst_amd1xh_check_pstates(pstates, npstates,
209 AMD10H_MSR_PSTATE_START,
210 AMD10H_MSR_PSTATE_START + AMD10H_MSR_PSTATE_COUNT);
214 acpi_pst_amd1xh_set_pstate(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl __unused,
215 const ACPI_RESOURCE_GENERIC_REGISTER *status __unused,
216 const struct acpi_pstate *pstate)
220 cval = pstate->st_cval & AMD_MSR_PSTATE_CSR_MASK;
221 wrmsr(AMD1XH_MSR_PSTATE_CTL, cval);
224 * Don't check AMD1XH_MSR_PSTATE_ST here, since it is
225 * affected by various P-State limits.
228 * AMD Family 10h Processor BKDG Rev 3.20 (#31116)
229 * 2.4.2.4 P-state Transition Behavior
235 static const struct acpi_pstate *
236 acpi_pst_amd1xh_get_pstate(
237 const ACPI_RESOURCE_GENERIC_REGISTER *status __unused,
238 const struct acpi_pstate *pstates, int npstates)
243 sval = rdmsr(AMD1XH_MSR_PSTATE_ST) & AMD_MSR_PSTATE_CSR_MASK;
244 for (i = 0; i < npstates; ++i) {
245 if ((pstates[i].st_sval & AMD_MSR_PSTATE_CSR_MASK) == sval)
252 acpi_pst_amd0f_check_pstates(const struct acpi_pstate *pstates, int npstates)
254 struct amd0f_fidvid fv_max, fv_min;
257 amd0f_fidvid_limit(&fv_min, &fv_max);
259 for (i = 0; i < npstates; ++i) {
260 const struct acpi_pstate *p = &pstates[i];
261 uint32_t fid, vid, mvs, rvo;
264 fid = AMD0F_PST_CTL_FID(p->st_cval);
265 vid = AMD0F_PST_CTL_VID(p->st_cval);
267 if (fid > fv_max.fid || fid < fv_min.fid) {
268 kprintf("cpu%d: Invalid FID %#x [%#x, %#x]\n",
269 mycpuid, fid, fv_min.fid, fv_max.fid);
272 if (vid < fv_max.vid || vid > fv_min.vid) {
273 kprintf("cpu%d: Invalid VID %#x [%#x, %#x]\n",
274 mycpuid, vid, fv_max.vid, fv_min.fid);
278 mvs = AMD0F_PST_CTL_MVS(p->st_cval);
279 rvo = AMD0F_PST_CTL_RVO(p->st_cval);
281 /* Only 0 is allowed, i.e. 25mV stepping */
283 kprintf("cpu%d: Invalid MVS %#x\n", mycpuid, mvs);
288 mvs_mv = 25 * (1 << mvs);
290 if (rvo_mv % mvs_mv != 0) {
291 kprintf("cpu%d: Invalid MVS/RVO (%#x/%#x)\n",
300 acpi_pst_amd0f_set_pstate(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl __unused,
301 const ACPI_RESOURCE_GENERIC_REGISTER *status __unused,
302 const struct acpi_pstate *pstate)
304 struct amd0f_fidvid fv;
305 struct amd0f_xsit xsit;
307 fv.fid = AMD0F_PST_CTL_FID(pstate->st_cval);
308 fv.vid = AMD0F_PST_CTL_VID(pstate->st_cval);
310 xsit.rvo = AMD0F_PST_CTL_RVO(pstate->st_cval);
311 xsit.mvs = AMD0F_PST_CTL_MVS(pstate->st_cval);
312 xsit.vst = AMD0F_PST_CTL_VST(pstate->st_cval);
313 xsit.pll_time = AMD0F_PST_CTL_PLLTIME(pstate->st_cval);
314 xsit.irt = AMD0F_PST_CTL_IRT(pstate->st_cval);
316 return amd0f_set_fidvid(&fv, &xsit);
319 static const struct acpi_pstate *
320 acpi_pst_amd0f_get_pstate(const ACPI_RESOURCE_GENERIC_REGISTER * ctrl __unused,
321 const struct acpi_pstate *pstates, int npstates)
323 struct amd0f_fidvid fv;
326 error = amd0f_get_fidvid(&fv);
330 for (i = 0; i < npstates; ++i) {
331 const struct acpi_pstate *p = &pstates[i];
333 if (fv.fid == AMD0F_PST_ST_FID(p->st_sval) &&
334 fv.vid == AMD0F_PST_ST_VID(p->st_sval))