Merge branch 'master' into amd64
[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 #include <machine/md_var.h>
39
40 #include "acpi.h"
41 #include "acpi_cpu_pstate.h"
42
43 #define AMD_APMI_HWPSTATE       0x80
44
45 #define AMD_MSR_PSTATE_CSR_MASK 0x7ULL
46 #define AMD1XH_MSR_PSTATE_CTL   0xc0010062
47 #define AMD1XH_MSR_PSTATE_ST    0xc0010063
48
49 #define AMD_MSR_PSTATE_EN       0x8000000000000000ULL
50
51 #define AMD10H_MSR_PSTATE_START 0xc0010064
52 #define AMD10H_MSR_PSTATE_COUNT 5
53
54 static const struct acpi_pst_md *
55                 acpi_pst_amd_probe(void);
56 static int      acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *,
57                     const ACPI_RESOURCE_GENERIC_REGISTER *);
58 static int      acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *, int,
59                     uint32_t, uint32_t);
60 static int      acpi_pst_amd10h_check_pstates(const struct acpi_pstate *, int);
61 static int      acpi_pst_amd1xh_set_pstate(
62                     const ACPI_RESOURCE_GENERIC_REGISTER *,
63                     const ACPI_RESOURCE_GENERIC_REGISTER *,
64                     const struct acpi_pstate *);
65 static const struct acpi_pstate *
66                 acpi_pst_amd1xh_get_pstate(
67                     const ACPI_RESOURCE_GENERIC_REGISTER *,
68                     const struct acpi_pstate *, int);
69
70 static const struct acpi_pst_md acpi_pst_amd10h = {
71         .pmd_check_csr          = acpi_pst_amd_check_csr,
72         .pmd_check_pstates      = acpi_pst_amd10h_check_pstates,
73         .pmd_set_pstate         = acpi_pst_amd1xh_set_pstate,
74         .pmd_get_pstate         = acpi_pst_amd1xh_get_pstate
75 };
76
77 const struct acpi_pst_md *
78 acpi_pst_md_probe(void)
79 {
80         if (strcmp(cpu_vendor, "AuthenticAMD") == 0)
81                 return acpi_pst_amd_probe();
82         return NULL;
83 }
84
85 static const struct acpi_pst_md *
86 acpi_pst_amd_probe(void)
87 {
88         uint32_t regs[4], ext_family;
89
90         if ((cpu_id & 0x00000f00) != 0x00000f00)
91                 return NULL;
92
93         /* Check whether APMI exists */
94         do_cpuid(0x80000000, regs);
95         if (regs[0] < 0x80000007)
96                 return NULL;
97
98         /* Fetch APMI */
99         do_cpuid(0x80000007, regs);
100
101         ext_family = cpu_id & 0x0ff00000;
102         switch (ext_family) {
103         case 0x00100000:        /* Family 10h */
104                 if (regs[3] & 0x80)
105                         return &acpi_pst_amd10h;
106                 break;
107
108         default:
109                 break;
110         }
111         return NULL;
112 }
113
114 static int
115 acpi_pst_amd_check_csr(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl,
116                        const ACPI_RESOURCE_GENERIC_REGISTER *status)
117 {
118         if (ctrl->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
119                 kprintf("cpu%d: Invalid P-State control register\n", mycpuid);
120                 return EINVAL;
121         }
122         if (status->SpaceId != ACPI_ADR_SPACE_FIXED_HARDWARE) {
123                 kprintf("cpu%d: Invalid P-State status register\n", mycpuid);
124                 return EINVAL;
125         }
126         return 0;
127 }
128
129 static int
130 acpi_pst_amd1xh_check_pstates(const struct acpi_pstate *pstates, int npstates,
131                               uint32_t msr_start, uint32_t msr_end)
132 {
133         int i;
134
135         /*
136          * Make sure that related MSR P-State registers are enabled.
137          *
138          * NOTE:
139          * We don't check status register value here;
140          * it will not be used.
141          */
142         for (i = 0; i < npstates; ++i) {
143                 uint64_t pstate;
144                 uint32_t msr;
145
146                 msr = msr_start +
147                       (pstates[i].st_cval & AMD_MSR_PSTATE_CSR_MASK);
148                 if (msr >= msr_end) {
149                         kprintf("cpu%d: MSR P-State register %#08x "
150                                 "does not exist\n", mycpuid, msr);
151                         return EINVAL;
152                 }
153
154                 pstate = rdmsr(msr);
155                 if ((pstate & AMD_MSR_PSTATE_EN) == 0) {
156                         kprintf("cpu%d: MSR P-State register %#08x "
157                                 "is not enabled\n", mycpuid, msr);
158                         return EINVAL;
159                 }
160         }
161         return 0;
162 }
163
164 static int
165 acpi_pst_amd10h_check_pstates(const struct acpi_pstate *pstates, int npstates)
166 {
167         /* Only P0-P4 are supported */
168         if (npstates > AMD10H_MSR_PSTATE_COUNT) {
169                 kprintf("cpu%d: only P0-P4 is allowed\n", mycpuid);
170                 return EINVAL;
171         }
172
173         return acpi_pst_amd1xh_check_pstates(pstates, npstates,
174                         AMD10H_MSR_PSTATE_START,
175                         AMD10H_MSR_PSTATE_START + AMD10H_MSR_PSTATE_COUNT);
176 }
177
178 static int
179 acpi_pst_amd1xh_set_pstate(const ACPI_RESOURCE_GENERIC_REGISTER *ctrl __unused,
180         const ACPI_RESOURCE_GENERIC_REGISTER *status __unused,
181         const struct acpi_pstate *pstate)
182 {
183         uint64_t cval;
184
185         cval = pstate->st_cval & AMD_MSR_PSTATE_CSR_MASK;
186         wrmsr(AMD1XH_MSR_PSTATE_CTL, cval);
187
188         /*
189          * Don't check AMD1XH_MSR_PSTATE_ST here, since it is
190          * affected by various P-State limits.
191          *
192          * For details:
193          * AMD Family 10h Processor BKDG Rev 3.20 (#31116)
194          * 2.4.2.4 P-state Transition Behavior
195          */
196
197         return 0;
198 }
199
200 static const struct acpi_pstate *
201 acpi_pst_amd1xh_get_pstate(
202         const ACPI_RESOURCE_GENERIC_REGISTER *status __unused,
203         const struct acpi_pstate *pstates, int npstates)
204 {
205         uint64_t sval;
206         int i;
207
208         sval = rdmsr(AMD1XH_MSR_PSTATE_ST) & AMD_MSR_PSTATE_CSR_MASK;
209         for (i = 0; i < npstates; ++i) {
210                 if ((pstates[i].st_sval & AMD_MSR_PSTATE_CSR_MASK) == sval)
211                         return &pstates[i];
212         }
213         return NULL;
214 }