e86f547eca083cf9eba2c1a72fac6813a7be4f68
[dragonfly.git] / sys / platform / pc32 / acpica5 / acpi_pstate_machdep.c
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>
36 #include <sys/systm.h>
37 #include <sys/globaldata.h>
38
39 #include <machine/md_var.h>
40 #include <machine/cpufunc.h>
41 #include <machine/cpufreq.h>
42
43 #include "acpi.h"
44 #include "acpi_cpu_pstate.h"
45
46 #define AMD_APMI_HWPSTATE               0x80
47
48 #define AMD_MSR_PSTATE_CSR_MASK         0x7ULL
49 #define AMD1X_MSR_PSTATE_CTL            0xc0010062
50 #define AMD1X_MSR_PSTATE_ST             0xc0010063
51
52 #define AMD_MSR_PSTATE_EN               0x8000000000000000ULL
53
54 #define AMD10_MSR_PSTATE_START          0xc0010064
55 #define AMD10_MSR_PSTATE_COUNT          5
56
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)
64
65 #define AMD0F_PST_ST_FID(sval)          (((sval) >> 0) & 0x3f)
66 #define AMD0F_PST_ST_VID(sval)          (((sval) >> 6) & 0x3f)
67
68 static const struct acpi_pst_md *
69                 acpi_pst_amd_probe(void);
70 static int      acpi_pst_amd_check_csr(const struct acpi_pst_res *,
71                     const struct acpi_pst_res *);
72 static int      acpi_pst_amd1x_check_pstates(const struct acpi_pstate *, int,
73                     uint32_t, uint32_t);
74 static int      acpi_pst_amd10_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_amd_init(const struct acpi_pst_res *,
77                     const struct acpi_pst_res *);
78 static int      acpi_pst_amd1x_set_pstate(const struct acpi_pst_res *,
79                     const struct acpi_pst_res *, const struct acpi_pstate *);
80 static int      acpi_pst_amd0f_set_pstate(const struct acpi_pst_res *,
81                     const struct acpi_pst_res *, const struct acpi_pstate *);
82 static const struct acpi_pstate *
83                 acpi_pst_amd1x_get_pstate(const struct acpi_pst_res *,
84                     const struct acpi_pstate *, int);
85 static const struct acpi_pstate *
86                 acpi_pst_amd0f_get_pstate(const struct acpi_pst_res *,
87                     const struct acpi_pstate *, int);
88
89 static const struct acpi_pst_md acpi_pst_amd10 = {
90         .pmd_check_csr          = acpi_pst_amd_check_csr,
91         .pmd_check_pstates      = acpi_pst_amd10_check_pstates,
92         .pmd_init               = acpi_pst_amd_init,
93         .pmd_set_pstate         = acpi_pst_amd1x_set_pstate,
94         .pmd_get_pstate         = acpi_pst_amd1x_get_pstate
95 };
96
97 static const struct acpi_pst_md acpi_pst_amd0f = {
98         .pmd_check_csr          = acpi_pst_amd_check_csr,
99         .pmd_check_pstates      = acpi_pst_amd0f_check_pstates,
100         .pmd_init               = acpi_pst_amd_init,
101         .pmd_set_pstate         = acpi_pst_amd0f_set_pstate,
102         .pmd_get_pstate         = acpi_pst_amd0f_get_pstate
103 };
104
105 const struct acpi_pst_md *
106 acpi_pst_md_probe(void)
107 {
108         if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
109                 return acpi_pst_amd_probe();
110         return NULL;
111 }
112
113 static const struct acpi_pst_md *
114 acpi_pst_amd_probe(void)
115 {
116         uint32_t regs[4], ext_family;
117
118         if ((cpu_id & 0x00000f00) != 0x00000f00)
119                 return NULL;
120
121         /* Check whether APMI exists */
122         do_cpuid(0x80000000, regs);
123         if (regs[0] < 0x80000007)
124                 return NULL;
125
126         /* Fetch APMI */
127         do_cpuid(0x80000007, regs);
128
129         ext_family = cpu_id & 0x0ff00000;
130         switch (ext_family) {
131         case 0x00000000:        /* Family 0fh */
132                 if ((regs[3] & 0x06) == 0x06)
133                         return &acpi_pst_amd0f;
134                 break;
135
136         case 0x00100000:        /* Family 10h */
137                 if (regs[3] & 0x80)
138                         return &acpi_pst_amd10;
139                 break;
140
141         default:
142                 break;
143         }
144         return NULL;
145 }
146
147 static int
148 acpi_pst_amd_check_csr(const struct acpi_pst_res *ctrl,
149                        const struct acpi_pst_res *status)
150 {
151         if (ctrl->pr_gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
152                 kprintf("cpu%d: Invalid P-State control register\n", mycpuid);
153                 return EINVAL;
154         }
155         if (status->pr_gas.SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
156                 kprintf("cpu%d: Invalid P-State status register\n", mycpuid);
157                 return EINVAL;
158         }
159         return 0;
160 }
161
162 static int
163 acpi_pst_amd1x_check_pstates(const struct acpi_pstate *pstates, int npstates,
164                              uint32_t msr_start, uint32_t msr_end)
165 {
166         int i;
167
168         /*
169          * Make sure that related MSR P-State registers are enabled.
170          *
171          * NOTE:
172          * We don't check status register value here;
173          * it will not be used.
174          */
175         for (i = 0; i < npstates; ++i) {
176                 uint64_t pstate;
177                 uint32_t msr;
178
179                 msr = msr_start +
180                       (pstates[i].st_cval & AMD_MSR_PSTATE_CSR_MASK);
181                 if (msr >= msr_end) {
182                         kprintf("cpu%d: MSR P-State register %#08x "
183                                 "does not exist\n", mycpuid, msr);
184                         return EINVAL;
185                 }
186
187                 pstate = rdmsr(msr);
188                 if ((pstate & AMD_MSR_PSTATE_EN) == 0) {
189                         kprintf("cpu%d: MSR P-State register %#08x "
190                                 "is not enabled\n", mycpuid, msr);
191                         return EINVAL;
192                 }
193         }
194         return 0;
195 }
196
197 static int
198 acpi_pst_amd10_check_pstates(const struct acpi_pstate *pstates, int npstates)
199 {
200         /* Only P0-P4 are supported */
201         if (npstates > AMD10_MSR_PSTATE_COUNT) {
202                 kprintf("cpu%d: only P0-P4 is allowed\n", mycpuid);
203                 return EINVAL;
204         }
205
206         return acpi_pst_amd1x_check_pstates(pstates, npstates,
207                         AMD10_MSR_PSTATE_START,
208                         AMD10_MSR_PSTATE_START + AMD10_MSR_PSTATE_COUNT);
209 }
210
211 static int
212 acpi_pst_amd1x_set_pstate(const struct acpi_pst_res *ctrl __unused,
213                           const struct acpi_pst_res *status __unused,
214                           const struct acpi_pstate *pstate)
215 {
216         uint64_t cval;
217
218         cval = pstate->st_cval & AMD_MSR_PSTATE_CSR_MASK;
219         wrmsr(AMD1X_MSR_PSTATE_CTL, cval);
220
221         /*
222          * Don't check AMD1X_MSR_PSTATE_ST here, since it is
223          * affected by various P-State limits.
224          *
225          * For details:
226          * AMD Family 10h Processor BKDG Rev 3.20 (#31116)
227          * 2.4.2.4 P-state Transition Behavior
228          */
229
230         return 0;
231 }
232
233 static const struct acpi_pstate *
234 acpi_pst_amd1x_get_pstate(const struct acpi_pst_res *status __unused,
235                           const struct acpi_pstate *pstates, int npstates)
236 {
237         uint64_t sval;
238         int i;
239
240         sval = rdmsr(AMD1X_MSR_PSTATE_ST) & AMD_MSR_PSTATE_CSR_MASK;
241         for (i = 0; i < npstates; ++i) {
242                 if ((pstates[i].st_sval & AMD_MSR_PSTATE_CSR_MASK) == sval)
243                         return &pstates[i];
244         }
245         return NULL;
246 }
247
248 static int
249 acpi_pst_amd0f_check_pstates(const struct acpi_pstate *pstates, int npstates)
250 {
251         struct amd0f_fidvid fv_max, fv_min;
252         int i;
253
254         amd0f_fidvid_limit(&fv_min, &fv_max);
255
256         for (i = 0; i < npstates; ++i) {
257                 const struct acpi_pstate *p = &pstates[i];
258                 uint32_t fid, vid, mvs, rvo;
259                 int mvs_mv, rvo_mv;
260
261                 fid = AMD0F_PST_CTL_FID(p->st_cval);
262                 vid = AMD0F_PST_CTL_VID(p->st_cval);
263
264                 if (fid > fv_max.fid || fid < fv_min.fid) {
265                         kprintf("cpu%d: Invalid FID %#x [%#x, %#x]\n",
266                                 mycpuid, fid, fv_min.fid, fv_max.fid);
267                         return EINVAL;
268                 }
269                 if (vid < fv_max.vid || vid > fv_min.vid) {
270                         kprintf("cpu%d: Invalid VID %#x [%#x, %#x]\n",
271                                 mycpuid, vid, fv_max.vid, fv_min.fid);
272                         return EINVAL;
273                 }
274
275                 mvs = AMD0F_PST_CTL_MVS(p->st_cval);
276                 rvo = AMD0F_PST_CTL_RVO(p->st_cval);
277
278                 /* Only 0 is allowed, i.e. 25mV stepping */
279                 if (mvs != 0) {
280                         kprintf("cpu%d: Invalid MVS %#x\n", mycpuid, mvs);
281                         return EINVAL;
282                 }
283
284                 /* -> mV */
285                 mvs_mv = 25 * (1 << mvs);
286                 rvo_mv = 25 * rvo;
287                 if (rvo_mv % mvs_mv != 0) {
288                         kprintf("cpu%d: Invalid MVS/RVO (%#x/%#x)\n",
289                                 mycpuid, mvs, rvo);
290                         return EINVAL;
291                 }
292         }
293         return 0;
294 }
295
296 static int
297 acpi_pst_amd0f_set_pstate(const struct acpi_pst_res *ctrl __unused,
298                           const struct acpi_pst_res *status __unused,
299                           const struct acpi_pstate *pstate)
300 {
301         struct amd0f_fidvid fv;
302         struct amd0f_xsit xsit;
303
304         fv.fid = AMD0F_PST_CTL_FID(pstate->st_cval);
305         fv.vid = AMD0F_PST_CTL_VID(pstate->st_cval);
306
307         xsit.rvo = AMD0F_PST_CTL_RVO(pstate->st_cval);
308         xsit.mvs = AMD0F_PST_CTL_MVS(pstate->st_cval);
309         xsit.vst = AMD0F_PST_CTL_VST(pstate->st_cval);
310         xsit.pll_time = AMD0F_PST_CTL_PLLTIME(pstate->st_cval);
311         xsit.irt = AMD0F_PST_CTL_IRT(pstate->st_cval);
312
313         return amd0f_set_fidvid(&fv, &xsit);
314 }
315
316 static const struct acpi_pstate *
317 acpi_pst_amd0f_get_pstate(const struct acpi_pst_res *status __unused,
318                           const struct acpi_pstate *pstates, int npstates)
319 {
320         struct amd0f_fidvid fv;
321         int error, i;
322
323         error = amd0f_get_fidvid(&fv);
324         if (error)
325                 return NULL;
326
327         for (i = 0; i < npstates; ++i) {
328                 const struct acpi_pstate *p = &pstates[i];
329
330                 if (fv.fid == AMD0F_PST_ST_FID(p->st_sval) &&
331                     fv.vid == AMD0F_PST_ST_VID(p->st_sval))
332                         return p;
333         }
334         return NULL;
335 }
336
337 static int
338 acpi_pst_amd_init(const struct acpi_pst_res *ctrl __unused,
339                   const struct acpi_pst_res *status __unused)
340 {
341         return 0;
342 }