1*82f91d33Snia /* $NetBSD: acpi_cpu_md.c,v 1.84 2020/10/25 16:39:00 nia Exp $ */
24d861e5bSjruoho
34d861e5bSjruoho /*-
497b3ad9cSjruoho * Copyright (c) 2010, 2011 Jukka Ruohonen <jruohonen@iki.fi>
54d861e5bSjruoho * All rights reserved.
64d861e5bSjruoho *
74d861e5bSjruoho * Redistribution and use in source and binary forms, with or without
84d861e5bSjruoho * modification, are permitted provided that the following conditions
94d861e5bSjruoho * are met:
104d861e5bSjruoho *
114d861e5bSjruoho * 1. Redistributions of source code must retain the above copyright
124d861e5bSjruoho * notice, this list of conditions and the following disclaimer.
134d861e5bSjruoho * 2. Redistributions in binary form must reproduce the above copyright
144d861e5bSjruoho * notice, this list of conditions and the following disclaimer in the
154d861e5bSjruoho * documentation and/or other materials provided with the distribution.
164d861e5bSjruoho *
174d861e5bSjruoho * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
184d861e5bSjruoho * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194d861e5bSjruoho * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204d861e5bSjruoho * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
214d861e5bSjruoho * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
224d861e5bSjruoho * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
234d861e5bSjruoho * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
244d861e5bSjruoho * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
254d861e5bSjruoho * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
264d861e5bSjruoho * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
274d861e5bSjruoho * SUCH DAMAGE.
284d861e5bSjruoho */
294d861e5bSjruoho #include <sys/cdefs.h>
30*82f91d33Snia __KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.84 2020/10/25 16:39:00 nia Exp $");
314d861e5bSjruoho
324d861e5bSjruoho #include <sys/param.h>
334d861e5bSjruoho #include <sys/bus.h>
34804fdee3Sjruoho #include <sys/cpufreq.h>
35cd966b24Sjruoho #include <sys/device.h>
364d861e5bSjruoho #include <sys/kcore.h>
3753e8f6c9Sjruoho #include <sys/sysctl.h>
38ec701f3fSjruoho #include <sys/xcall.h>
394d861e5bSjruoho
404d861e5bSjruoho #include <x86/cpu.h>
4153e8f6c9Sjruoho #include <x86/cpufunc.h>
424d861e5bSjruoho #include <x86/cputypes.h>
4353e8f6c9Sjruoho #include <x86/cpuvar.h>
4453e8f6c9Sjruoho #include <x86/machdep.h>
457abb5a02Smsaitoh #include <x86/x86/tsc.h>
464d861e5bSjruoho
474d861e5bSjruoho #include <dev/acpi/acpica.h>
484d861e5bSjruoho #include <dev/acpi/acpi_cpu.h>
494d861e5bSjruoho
501f0e85dcSjruoho #include <dev/pci/pcivar.h>
511f0e85dcSjruoho #include <dev/pci/pcidevs.h>
521f0e85dcSjruoho
5399d6864dSjruoho #include <machine/acpi_machdep.h>
5499d6864dSjruoho
55e1de7439Sjruoho /*
566315c7d6Sjruoho * Intel IA32_MISC_ENABLE.
576315c7d6Sjruoho */
586315c7d6Sjruoho #define MSR_MISC_ENABLE_EST __BIT(16)
596315c7d6Sjruoho #define MSR_MISC_ENABLE_TURBO __BIT(38)
606315c7d6Sjruoho
616315c7d6Sjruoho /*
62e1de7439Sjruoho * AMD C1E.
63e1de7439Sjruoho */
64e1de7439Sjruoho #define MSR_CMPHALT 0xc0010055
65e1de7439Sjruoho
66e1de7439Sjruoho #define MSR_CMPHALT_SMI __BIT(27)
67e1de7439Sjruoho #define MSR_CMPHALT_C1E __BIT(28)
68e1de7439Sjruoho #define MSR_CMPHALT_BMSTS __BIT(29)
69465d00f8Sjruoho
7042a7d04bSjruoho /*
717738a236Sjruoho * AMD families 10h, 11h, 12h, 14h, and 15h.
7242a7d04bSjruoho */
7342a7d04bSjruoho #define MSR_10H_LIMIT 0xc0010061
742ea8b860Sjruoho #define MSR_10H_CONTROL 0xc0010062
752ea8b860Sjruoho #define MSR_10H_STATUS 0xc0010063
762ea8b860Sjruoho #define MSR_10H_CONFIG 0xc0010064
772ea8b860Sjruoho
7842a7d04bSjruoho /*
7942a7d04bSjruoho * AMD family 0Fh.
8042a7d04bSjruoho */
8142a7d04bSjruoho #define MSR_0FH_CONTROL 0xc0010041
8242a7d04bSjruoho #define MSR_0FH_STATUS 0xc0010042
8342a7d04bSjruoho
8442a7d04bSjruoho #define MSR_0FH_STATUS_CFID __BITS( 0, 5)
8542a7d04bSjruoho #define MSR_0FH_STATUS_CVID __BITS(32, 36)
8642a7d04bSjruoho #define MSR_0FH_STATUS_PENDING __BITS(31, 31)
8742a7d04bSjruoho
8842a7d04bSjruoho #define MSR_0FH_CONTROL_FID __BITS( 0, 5)
8942a7d04bSjruoho #define MSR_0FH_CONTROL_VID __BITS( 8, 12)
9042a7d04bSjruoho #define MSR_0FH_CONTROL_CHG __BITS(16, 16)
9142a7d04bSjruoho #define MSR_0FH_CONTROL_CNT __BITS(32, 51)
9242a7d04bSjruoho
9342a7d04bSjruoho #define ACPI_0FH_STATUS_FID __BITS( 0, 5)
9442a7d04bSjruoho #define ACPI_0FH_STATUS_VID __BITS( 6, 10)
9542a7d04bSjruoho
9642a7d04bSjruoho #define ACPI_0FH_CONTROL_FID __BITS( 0, 5)
9742a7d04bSjruoho #define ACPI_0FH_CONTROL_VID __BITS( 6, 10)
9842a7d04bSjruoho #define ACPI_0FH_CONTROL_VST __BITS(11, 17)
9942a7d04bSjruoho #define ACPI_0FH_CONTROL_MVS __BITS(18, 19)
10042a7d04bSjruoho #define ACPI_0FH_CONTROL_PLL __BITS(20, 26)
10142a7d04bSjruoho #define ACPI_0FH_CONTROL_RVO __BITS(28, 29)
10242a7d04bSjruoho #define ACPI_0FH_CONTROL_IRT __BITS(30, 31)
10342a7d04bSjruoho
10442a7d04bSjruoho #define FID_TO_VCO_FID(fidd) (((fid) < 8) ? (8 + ((fid) << 1)) : (fid))
10542a7d04bSjruoho
1064d861e5bSjruoho static char native_idle_text[16];
1074d861e5bSjruoho void (*native_idle)(void) = NULL;
1084d861e5bSjruoho
109d3e53912Sdyoung static int acpicpu_md_quirk_piix4(const struct pci_attach_args *);
110b66af12bSjruoho static void acpicpu_md_pstate_hwf_reset(void *, void *);
11142a7d04bSjruoho static int acpicpu_md_pstate_fidvid_get(struct acpicpu_softc *,
11242a7d04bSjruoho uint32_t *);
11342a7d04bSjruoho static int acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *);
11442a7d04bSjruoho static int acpicpu_md_pstate_fidvid_read(uint32_t *, uint32_t *);
11542a7d04bSjruoho static void acpicpu_md_pstate_fidvid_write(uint32_t, uint32_t,
11642a7d04bSjruoho uint32_t, uint32_t);
117a448b8a1Sjruoho static int acpicpu_md_pstate_sysctl_init(void);
11853e8f6c9Sjruoho static int acpicpu_md_pstate_sysctl_get(SYSCTLFN_PROTO);
11953e8f6c9Sjruoho static int acpicpu_md_pstate_sysctl_set(SYSCTLFN_PROTO);
12053e8f6c9Sjruoho static int acpicpu_md_pstate_sysctl_all(SYSCTLFN_PROTO);
12153e8f6c9Sjruoho
12253e8f6c9Sjruoho extern struct acpicpu_softc **acpicpu_sc;
123a448b8a1Sjruoho static struct sysctllog *acpicpu_log = NULL;
1244d861e5bSjruoho
125cd966b24Sjruoho struct cpu_info *
acpicpu_md_match(device_t parent,cfdata_t match,void * aux)126cd966b24Sjruoho acpicpu_md_match(device_t parent, cfdata_t match, void *aux)
127cd966b24Sjruoho {
128cd966b24Sjruoho struct cpufeature_attach_args *cfaa = aux;
129cd966b24Sjruoho
130cd966b24Sjruoho if (strcmp(cfaa->name, "frequency") != 0)
131cd966b24Sjruoho return NULL;
132cd966b24Sjruoho
133cd966b24Sjruoho return cfaa->ci;
134cd966b24Sjruoho }
135cd966b24Sjruoho
136cd966b24Sjruoho struct cpu_info *
acpicpu_md_attach(device_t parent,device_t self,void * aux)137cd966b24Sjruoho acpicpu_md_attach(device_t parent, device_t self, void *aux)
138cd966b24Sjruoho {
139cd966b24Sjruoho struct cpufeature_attach_args *cfaa = aux;
140cd966b24Sjruoho
141cd966b24Sjruoho return cfaa->ci;
142cd966b24Sjruoho }
143cd966b24Sjruoho
1444d861e5bSjruoho uint32_t
acpicpu_md_flags(void)145eb43e911Sjruoho acpicpu_md_flags(void)
1464d861e5bSjruoho {
1474d861e5bSjruoho struct cpu_info *ci = curcpu();
1481f0e85dcSjruoho struct pci_attach_args pa;
1498b94ac2fSjruoho uint32_t family, val = 0;
15026cf8b05Sjruoho uint32_t regs[4];
15120f20f38Sjruoho uint64_t msr;
1524d861e5bSjruoho
15399d6864dSjruoho if (acpi_md_ncpus() == 1)
1544d861e5bSjruoho val |= ACPICPU_FLAG_C_BM;
1554d861e5bSjruoho
1564d861e5bSjruoho if ((ci->ci_feat_val[1] & CPUID2_MONITOR) != 0)
15753e8f6c9Sjruoho val |= ACPICPU_FLAG_C_FFH;
1584d861e5bSjruoho
159aa0d38cbSjruoho /*
160aa0d38cbSjruoho * By default, assume that the local APIC timer
161aa0d38cbSjruoho * as well as TSC are stalled during C3 sleep.
162aa0d38cbSjruoho */
163497fed0aSjruoho val |= ACPICPU_FLAG_C_APIC | ACPICPU_FLAG_C_TSC;
1646cf2f2d1Sjruoho
1657abb5a02Smsaitoh /*
1667abb5a02Smsaitoh * Detect whether TSC is invariant. If it is not, we keep the flag to
1677abb5a02Smsaitoh * note that TSC will not run at constant rate. Depending on the CPU,
1687abb5a02Smsaitoh * this may affect P- and T-state changes, but especially relevant
1697abb5a02Smsaitoh * are C-states; with variant TSC, states larger than C1 may
1707abb5a02Smsaitoh * completely stop the counter.
1717abb5a02Smsaitoh */
1727abb5a02Smsaitoh if (tsc_is_invariant())
1737abb5a02Smsaitoh val &= ~ACPICPU_FLAG_C_TSC;
1747abb5a02Smsaitoh
1754d861e5bSjruoho switch (cpu_vendor) {
1764d861e5bSjruoho
17777b2a836Sjmcneill case CPUVENDOR_IDT:
1782ea8b860Sjruoho
17953e8f6c9Sjruoho if ((ci->ci_feat_val[1] & CPUID2_EST) != 0)
18053e8f6c9Sjruoho val |= ACPICPU_FLAG_P_FFH;
18153e8f6c9Sjruoho
1826b9ff107Sjruoho if ((ci->ci_feat_val[0] & CPUID_ACPI) != 0)
1836b9ff107Sjruoho val |= ACPICPU_FLAG_T_FFH;
1846b9ff107Sjruoho
1856cf2f2d1Sjruoho break;
1866cf2f2d1Sjruoho
1876cf2f2d1Sjruoho case CPUVENDOR_INTEL:
1886cf2f2d1Sjruoho
189aa0d38cbSjruoho /*
190aa0d38cbSjruoho * Bus master control and arbitration should be
191aa0d38cbSjruoho * available on all supported Intel CPUs (to be
192aa0d38cbSjruoho * sure, this is double-checked later from the
193aa0d38cbSjruoho * firmware data). These flags imply that it is
194aa0d38cbSjruoho * not necessary to flush caches before C3 state.
195aa0d38cbSjruoho */
1961f0e85dcSjruoho val |= ACPICPU_FLAG_C_BM | ACPICPU_FLAG_C_ARB;
1976cf2f2d1Sjruoho
198aa0d38cbSjruoho /*
199aa0d38cbSjruoho * Check if we can use "native", MSR-based,
200aa0d38cbSjruoho * access. If not, we have to resort to I/O.
201aa0d38cbSjruoho */
2026cf2f2d1Sjruoho if ((ci->ci_feat_val[1] & CPUID2_EST) != 0)
2036cf2f2d1Sjruoho val |= ACPICPU_FLAG_P_FFH;
2046cf2f2d1Sjruoho
2056cf2f2d1Sjruoho if ((ci->ci_feat_val[0] & CPUID_ACPI) != 0)
2066cf2f2d1Sjruoho val |= ACPICPU_FLAG_T_FFH;
2076cf2f2d1Sjruoho
2086cf2f2d1Sjruoho /*
209497fed0aSjruoho * Check whether MSR_APERF, MSR_MPERF, and Turbo
210497fed0aSjruoho * Boost are available. Also see if we might have
211497fed0aSjruoho * an invariant local APIC timer ("ARAT").
2124362bda1Sjruoho */
2134362bda1Sjruoho if (cpuid_level >= 0x06) {
2144362bda1Sjruoho
21566d1c2c3Sjruoho x86_cpuid(0x00000006, regs);
2164362bda1Sjruoho
217cff1577aSjruoho if ((regs[2] & CPUID_DSPM_HWF) != 0)
2184e1517c4Sjruoho val |= ACPICPU_FLAG_P_HWF;
2194362bda1Sjruoho
220cff1577aSjruoho if ((regs[0] & CPUID_DSPM_IDA) != 0)
221503e356fSjruoho val |= ACPICPU_FLAG_P_TURBO;
222497fed0aSjruoho
223cff1577aSjruoho if ((regs[0] & CPUID_DSPM_ARAT) != 0)
224497fed0aSjruoho val &= ~ACPICPU_FLAG_C_APIC;
22503783bb5Snat
2264362bda1Sjruoho }
2274362bda1Sjruoho
2284d861e5bSjruoho break;
2294d861e5bSjruoho
2304d861e5bSjruoho case CPUVENDOR_AMD:
2314d861e5bSjruoho
23242a7d04bSjruoho x86_cpuid(0x80000000, regs);
23342a7d04bSjruoho
23442a7d04bSjruoho if (regs[0] < 0x80000007)
23542a7d04bSjruoho break;
23642a7d04bSjruoho
23742a7d04bSjruoho x86_cpuid(0x80000007, regs);
23842a7d04bSjruoho
239b1a32cacSmsaitoh family = CPUID_TO_FAMILY(ci->ci_signature);
2408b94ac2fSjruoho
2418b94ac2fSjruoho switch (family) {
2422ea8b860Sjruoho
2436cf2f2d1Sjruoho case 0x0f:
24442a7d04bSjruoho
24519a0877bSjruoho /*
246392bbef3Sjruoho * Disable C1E if present.
247392bbef3Sjruoho */
248392bbef3Sjruoho if (rdmsr_safe(MSR_CMPHALT, &msr) != EFAULT)
249392bbef3Sjruoho val |= ACPICPU_FLAG_C_C1E;
250392bbef3Sjruoho
251392bbef3Sjruoho /*
25219a0877bSjruoho * Evaluate support for the "FID/VID
25319a0877bSjruoho * algorithm" also used by powernow(4).
25419a0877bSjruoho */
25542a7d04bSjruoho if ((regs[3] & CPUID_APM_FID) == 0)
25642a7d04bSjruoho break;
25742a7d04bSjruoho
25842a7d04bSjruoho if ((regs[3] & CPUID_APM_VID) == 0)
25942a7d04bSjruoho break;
26042a7d04bSjruoho
26142a7d04bSjruoho val |= ACPICPU_FLAG_P_FFH | ACPICPU_FLAG_P_FIDVID;
26242a7d04bSjruoho break;
26342a7d04bSjruoho
2642ea8b860Sjruoho case 0x10:
2652ea8b860Sjruoho case 0x11:
26620f20f38Sjruoho
267392bbef3Sjruoho /*
268392bbef3Sjruoho * Disable C1E if present.
269392bbef3Sjruoho */
27020f20f38Sjruoho if (rdmsr_safe(MSR_CMPHALT, &msr) != EFAULT)
2714c82088cSjmcneill val |= ACPICPU_FLAG_C_C1E;
27220f20f38Sjruoho
2734c82088cSjmcneill /* FALLTHROUGH */
2744c82088cSjmcneill
275c7e2f672Sjruoho case 0x12:
2764c82088cSjmcneill case 0x14: /* AMD Fusion */
2777738a236Sjruoho case 0x15: /* AMD Bulldozer */
2782ea8b860Sjruoho
279eb5a69e0Sjruoho /*
2807abb5a02Smsaitoh * Like with Intel, detect MSR-based P-states,
2817abb5a02Smsaitoh * and AMD's "turbo" (Core Performance Boost),
2827abb5a02Smsaitoh * respectively.
283eb5a69e0Sjruoho */
28426cf8b05Sjruoho if ((regs[3] & CPUID_APM_HWP) != 0)
2852ea8b860Sjruoho val |= ACPICPU_FLAG_P_FFH;
28626cf8b05Sjruoho
28726cf8b05Sjruoho if ((regs[3] & CPUID_APM_CPB) != 0)
28826cf8b05Sjruoho val |= ACPICPU_FLAG_P_TURBO;
289e1de7439Sjruoho
290eb5a69e0Sjruoho /*
291eb5a69e0Sjruoho * Also check for APERF and MPERF,
292eb5a69e0Sjruoho * first available in the family 10h.
293eb5a69e0Sjruoho */
294eb5a69e0Sjruoho if (cpuid_level >= 0x06) {
295eb5a69e0Sjruoho
296eb5a69e0Sjruoho x86_cpuid(0x00000006, regs);
297eb5a69e0Sjruoho
29866d1c2c3Sjruoho if ((regs[2] & CPUID_DSPM_HWF) != 0)
2994e1517c4Sjruoho val |= ACPICPU_FLAG_P_HWF;
300eb5a69e0Sjruoho }
301eb5a69e0Sjruoho
302e1de7439Sjruoho break;
3032ea8b860Sjruoho }
3042ea8b860Sjruoho
3054d861e5bSjruoho break;
3064d861e5bSjruoho }
3074d861e5bSjruoho
3081f0e85dcSjruoho /*
3091f0e85dcSjruoho * There are several erratums for PIIX4.
3101f0e85dcSjruoho */
311eb43e911Sjruoho if (pci_find_device(&pa, acpicpu_md_quirk_piix4) != 0)
3121f0e85dcSjruoho val |= ACPICPU_FLAG_PIIX4;
3131f0e85dcSjruoho
3144d861e5bSjruoho return val;
3154d861e5bSjruoho }
3164d861e5bSjruoho
3171f0e85dcSjruoho static int
acpicpu_md_quirk_piix4(const struct pci_attach_args * pa)318d3e53912Sdyoung acpicpu_md_quirk_piix4(const struct pci_attach_args *pa)
3191f0e85dcSjruoho {
3201f0e85dcSjruoho
3211f0e85dcSjruoho /*
3221f0e85dcSjruoho * XXX: The pci_find_device(9) function only
3231f0e85dcSjruoho * deals with attached devices. Change this
3241f0e85dcSjruoho * to use something like pci_device_foreach().
3251f0e85dcSjruoho */
3261f0e85dcSjruoho if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
3271f0e85dcSjruoho return 0;
3281f0e85dcSjruoho
3291f0e85dcSjruoho if (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82371AB_ISA ||
3301f0e85dcSjruoho PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_INTEL_82440MX_PMC)
3311f0e85dcSjruoho return 1;
3321f0e85dcSjruoho
3331f0e85dcSjruoho return 0;
3341f0e85dcSjruoho }
3351f0e85dcSjruoho
336e1de7439Sjruoho void
acpicpu_md_quirk_c1e(void)337eb43e911Sjruoho acpicpu_md_quirk_c1e(void)
338e1de7439Sjruoho {
339e1de7439Sjruoho const uint64_t c1e = MSR_CMPHALT_SMI | MSR_CMPHALT_C1E;
340e1de7439Sjruoho uint64_t val;
341e1de7439Sjruoho
34220f20f38Sjruoho val = rdmsr(MSR_CMPHALT);
343e1de7439Sjruoho
344e1de7439Sjruoho if ((val & c1e) != 0)
345e1de7439Sjruoho wrmsr(MSR_CMPHALT, val & ~c1e);
346e1de7439Sjruoho }
347e1de7439Sjruoho
3484d861e5bSjruoho int
acpicpu_md_cstate_start(struct acpicpu_softc * sc)349eb43e911Sjruoho acpicpu_md_cstate_start(struct acpicpu_softc *sc)
3504d861e5bSjruoho {
3514d861e5bSjruoho const size_t size = sizeof(native_idle_text);
3524f4a7a5dSjruoho struct acpicpu_cstate *cs;
3534f4a7a5dSjruoho bool ipi = false;
3544f4a7a5dSjruoho int i;
3554d861e5bSjruoho
35619a0877bSjruoho /*
35719a0877bSjruoho * Save the cpu_idle(9) loop used by default.
35819a0877bSjruoho */
3594d861e5bSjruoho x86_cpu_idle_get(&native_idle, native_idle_text, size);
3604f4a7a5dSjruoho
3614f4a7a5dSjruoho for (i = 0; i < ACPI_C_STATE_COUNT; i++) {
3624f4a7a5dSjruoho
3634f4a7a5dSjruoho cs = &sc->sc_cstate[i];
3644f4a7a5dSjruoho
3654f4a7a5dSjruoho if (cs->cs_method == ACPICPU_C_STATE_HALT) {
3664f4a7a5dSjruoho ipi = true;
3674f4a7a5dSjruoho break;
3684f4a7a5dSjruoho }
3694f4a7a5dSjruoho }
3704f4a7a5dSjruoho
3714f4a7a5dSjruoho x86_cpu_idle_set(acpicpu_cstate_idle, "acpi", ipi);
3724d861e5bSjruoho
3734d861e5bSjruoho return 0;
3744d861e5bSjruoho }
3754d861e5bSjruoho
3764d861e5bSjruoho int
acpicpu_md_cstate_stop(void)377eb43e911Sjruoho acpicpu_md_cstate_stop(void)
3784d861e5bSjruoho {
3794fb79b4eSjruoho static char text[16];
3804fb79b4eSjruoho void (*func)(void);
3814f4a7a5dSjruoho bool ipi;
3824d861e5bSjruoho
3834fb79b4eSjruoho x86_cpu_idle_get(&func, text, sizeof(text));
3844fb79b4eSjruoho
3854fb79b4eSjruoho if (func == native_idle)
3864fb79b4eSjruoho return EALREADY;
3874fb79b4eSjruoho
3884f4a7a5dSjruoho ipi = (native_idle != x86_cpu_idle_halt) ? false : true;
3894f4a7a5dSjruoho x86_cpu_idle_set(native_idle, native_idle_text, ipi);
3904d861e5bSjruoho
391ec701f3fSjruoho /*
392ec701f3fSjruoho * Run a cross-call to ensure that all CPUs are
393ec701f3fSjruoho * out from the ACPI idle-loop before detachment.
394ec701f3fSjruoho */
395edcef67eSuwe xc_barrier(0);
3964d861e5bSjruoho
3974d861e5bSjruoho return 0;
3984d861e5bSjruoho }
3994d861e5bSjruoho
40041270640Sjruoho /*
40157c78461Sjruoho * Called with interrupts enabled.
40241270640Sjruoho */
40312051873Smaxv void __nocsan
acpicpu_md_cstate_enter(int method,int state)404eb43e911Sjruoho acpicpu_md_cstate_enter(int method, int state)
4054d861e5bSjruoho {
40641270640Sjruoho struct cpu_info *ci = curcpu();
4074d861e5bSjruoho
40857c78461Sjruoho KASSERT(ci->ci_ilevel == IPL_NONE);
40957c78461Sjruoho
4104d861e5bSjruoho switch (method) {
4114d861e5bSjruoho
4124d861e5bSjruoho case ACPICPU_C_STATE_FFH:
41341270640Sjruoho
41441270640Sjruoho x86_monitor(&ci->ci_want_resched, 0, 0);
41541270640Sjruoho
4164f4a7a5dSjruoho if (__predict_false(ci->ci_want_resched != 0))
41741270640Sjruoho return;
41841270640Sjruoho
4194d861e5bSjruoho x86_mwait((state - 1) << 4, 0);
4204d861e5bSjruoho break;
4214d861e5bSjruoho
4224d861e5bSjruoho case ACPICPU_C_STATE_HALT:
4234d861e5bSjruoho
42457c78461Sjruoho x86_disable_intr();
42557c78461Sjruoho
42657c78461Sjruoho if (__predict_false(ci->ci_want_resched != 0)) {
42757c78461Sjruoho x86_enable_intr();
42841270640Sjruoho return;
42957c78461Sjruoho }
43041270640Sjruoho
43141270640Sjruoho x86_stihlt();
4324d861e5bSjruoho break;
4334d861e5bSjruoho }
4344d861e5bSjruoho }
43553e8f6c9Sjruoho
43653e8f6c9Sjruoho int
acpicpu_md_pstate_start(struct acpicpu_softc * sc)43797b3ad9cSjruoho acpicpu_md_pstate_start(struct acpicpu_softc *sc)
43853e8f6c9Sjruoho {
4394fb79b4eSjruoho uint64_t xc, val;
4400e8d2e5aSjruoho
441849fd36fSjruoho switch (cpu_vendor) {
442849fd36fSjruoho
443849fd36fSjruoho case CPUVENDOR_IDT:
444849fd36fSjruoho case CPUVENDOR_INTEL:
445849fd36fSjruoho
44697b3ad9cSjruoho /*
44797b3ad9cSjruoho * Make sure EST is enabled.
44897b3ad9cSjruoho */
4496315c7d6Sjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_FFH) != 0) {
4506315c7d6Sjruoho
4510e8d2e5aSjruoho val = rdmsr(MSR_MISC_ENABLE);
4520e8d2e5aSjruoho
4536315c7d6Sjruoho if ((val & MSR_MISC_ENABLE_EST) == 0) {
4540e8d2e5aSjruoho
4556315c7d6Sjruoho val |= MSR_MISC_ENABLE_EST;
4560e8d2e5aSjruoho wrmsr(MSR_MISC_ENABLE, val);
4570e8d2e5aSjruoho val = rdmsr(MSR_MISC_ENABLE);
4580e8d2e5aSjruoho
4596315c7d6Sjruoho if ((val & MSR_MISC_ENABLE_EST) == 0)
4600e8d2e5aSjruoho return ENOTTY;
4610e8d2e5aSjruoho }
462eb5a69e0Sjruoho }
463849fd36fSjruoho }
46497b3ad9cSjruoho
46597b3ad9cSjruoho /*
4664fb79b4eSjruoho * Reset the APERF and MPERF counters.
4674fb79b4eSjruoho */
4684fb79b4eSjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_HWF) != 0) {
4694fb79b4eSjruoho xc = xc_broadcast(0, acpicpu_md_pstate_hwf_reset, NULL, NULL);
4704fb79b4eSjruoho xc_wait(xc);
4714fb79b4eSjruoho }
4724fb79b4eSjruoho
4734fb79b4eSjruoho return acpicpu_md_pstate_sysctl_init();
4744fb79b4eSjruoho }
4754fb79b4eSjruoho
4764fb79b4eSjruoho int
acpicpu_md_pstate_stop(void)4774fb79b4eSjruoho acpicpu_md_pstate_stop(void)
4784fb79b4eSjruoho {
4794fb79b4eSjruoho
4804fb79b4eSjruoho if (acpicpu_log == NULL)
4814fb79b4eSjruoho return EALREADY;
4824fb79b4eSjruoho
4834fb79b4eSjruoho sysctl_teardown(&acpicpu_log);
4844fb79b4eSjruoho acpicpu_log = NULL;
4854fb79b4eSjruoho
4864fb79b4eSjruoho return 0;
4874fb79b4eSjruoho }
4884fb79b4eSjruoho
4894fb79b4eSjruoho int
acpicpu_md_pstate_init(struct acpicpu_softc * sc)4904fb79b4eSjruoho acpicpu_md_pstate_init(struct acpicpu_softc *sc)
4914fb79b4eSjruoho {
4924fb79b4eSjruoho struct cpu_info *ci = sc->sc_ci;
4934fb79b4eSjruoho struct acpicpu_pstate *ps, msr;
4944fb79b4eSjruoho uint32_t family, i = 0;
4954fb79b4eSjruoho
4964fb79b4eSjruoho (void)memset(&msr, 0, sizeof(struct acpicpu_pstate));
4974fb79b4eSjruoho
4984fb79b4eSjruoho switch (cpu_vendor) {
4994fb79b4eSjruoho
5004fb79b4eSjruoho case CPUVENDOR_IDT:
5014fb79b4eSjruoho case CPUVENDOR_INTEL:
5024fb79b4eSjruoho
5034fb79b4eSjruoho /*
504465d00f8Sjruoho * If the so-called Turbo Boost is present,
505465d00f8Sjruoho * the P0-state is always the "turbo state".
5065bfb3adeSjruoho * It is shown as the P1 frequency + 1 MHz.
507465d00f8Sjruoho *
508465d00f8Sjruoho * For discussion, see:
509465d00f8Sjruoho *
510465d00f8Sjruoho * Intel Corporation: Intel Turbo Boost Technology
511465d00f8Sjruoho * in Intel Core(tm) Microarchitectures (Nehalem)
512465d00f8Sjruoho * Based Processors. White Paper, November 2008.
513465d00f8Sjruoho */
5146315c7d6Sjruoho if (sc->sc_pstate_count >= 2 &&
5153c37c8c0Sjruoho (sc->sc_flags & ACPICPU_FLAG_P_TURBO) != 0) {
5165bfb3adeSjruoho
5175bfb3adeSjruoho ps = &sc->sc_pstate[0];
5185bfb3adeSjruoho
5195bfb3adeSjruoho if (ps->ps_freq == sc->sc_pstate[1].ps_freq + 1)
5205bfb3adeSjruoho ps->ps_flags |= ACPICPU_FLAG_P_TURBO;
5215bfb3adeSjruoho }
522465d00f8Sjruoho
5238116cea3Sjruoho msr.ps_control_addr = MSR_PERF_CTL;
5248116cea3Sjruoho msr.ps_control_mask = __BITS(0, 15);
5258116cea3Sjruoho
5268116cea3Sjruoho msr.ps_status_addr = MSR_PERF_STATUS;
5278116cea3Sjruoho msr.ps_status_mask = __BITS(0, 15);
5288116cea3Sjruoho break;
5298116cea3Sjruoho
5308116cea3Sjruoho case CPUVENDOR_AMD:
5318116cea3Sjruoho
532465d00f8Sjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_FIDVID) != 0)
533465d00f8Sjruoho msr.ps_flags |= ACPICPU_FLAG_P_FIDVID;
534465d00f8Sjruoho
535b1a32cacSmsaitoh family = CPUID_TO_FAMILY(ci->ci_signature);
5368b94ac2fSjruoho
5378b94ac2fSjruoho switch (family) {
5382ea8b860Sjruoho
53942a7d04bSjruoho case 0x0f:
54042a7d04bSjruoho msr.ps_control_addr = MSR_0FH_CONTROL;
54142a7d04bSjruoho msr.ps_status_addr = MSR_0FH_STATUS;
54242a7d04bSjruoho break;
54342a7d04bSjruoho
5442ea8b860Sjruoho case 0x10:
5452ea8b860Sjruoho case 0x11:
546c7e2f672Sjruoho case 0x12:
54761f6cea6Sjruoho case 0x14:
54861f6cea6Sjruoho case 0x15:
5492ea8b860Sjruoho msr.ps_control_addr = MSR_10H_CONTROL;
5502ea8b860Sjruoho msr.ps_control_mask = __BITS(0, 2);
5512ea8b860Sjruoho
5522ea8b860Sjruoho msr.ps_status_addr = MSR_10H_STATUS;
5532ea8b860Sjruoho msr.ps_status_mask = __BITS(0, 2);
5542ea8b860Sjruoho break;
5552ea8b860Sjruoho
5562ea8b860Sjruoho default:
5576315c7d6Sjruoho /*
5586315c7d6Sjruoho * If we have an unknown AMD CPU, rely on XPSS.
5596315c7d6Sjruoho */
5608116cea3Sjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_XPSS) == 0)
5618116cea3Sjruoho return EOPNOTSUPP;
5622ea8b860Sjruoho }
5638116cea3Sjruoho
5648116cea3Sjruoho break;
5658116cea3Sjruoho
5668116cea3Sjruoho default:
5678116cea3Sjruoho return ENODEV;
5688116cea3Sjruoho }
5698116cea3Sjruoho
5707aac1c3eSjruoho /*
5717aac1c3eSjruoho * Fill the P-state structures with MSR addresses that are
57257af947cSjruoho * known to be correct. If we do not know the addresses,
57357af947cSjruoho * leave the values intact. If a vendor uses XPSS, we do
574aa0d38cbSjruoho * not necessarily need to do anything to support new CPUs.
5757aac1c3eSjruoho */
5768116cea3Sjruoho while (i < sc->sc_pstate_count) {
5778116cea3Sjruoho
5788116cea3Sjruoho ps = &sc->sc_pstate[i];
5798116cea3Sjruoho
58042a7d04bSjruoho if (msr.ps_flags != 0)
58142a7d04bSjruoho ps->ps_flags |= msr.ps_flags;
58242a7d04bSjruoho
58357af947cSjruoho if (msr.ps_status_addr != 0)
5848116cea3Sjruoho ps->ps_status_addr = msr.ps_status_addr;
5858116cea3Sjruoho
58657af947cSjruoho if (msr.ps_status_mask != 0)
5878116cea3Sjruoho ps->ps_status_mask = msr.ps_status_mask;
5888116cea3Sjruoho
58957af947cSjruoho if (msr.ps_control_addr != 0)
5908116cea3Sjruoho ps->ps_control_addr = msr.ps_control_addr;
5918116cea3Sjruoho
59257af947cSjruoho if (msr.ps_control_mask != 0)
5938116cea3Sjruoho ps->ps_control_mask = msr.ps_control_mask;
5948116cea3Sjruoho
5958116cea3Sjruoho i++;
5968116cea3Sjruoho }
5978116cea3Sjruoho
5988116cea3Sjruoho return 0;
5998116cea3Sjruoho }
6008116cea3Sjruoho
60197b3ad9cSjruoho /*
60297b3ad9cSjruoho * Read the IA32_APERF and IA32_MPERF counters. The first
60397b3ad9cSjruoho * increments at the rate of the fixed maximum frequency
60497b3ad9cSjruoho * configured during the boot, whereas APERF counts at the
60597b3ad9cSjruoho * rate of the actual frequency. Note that the MSRs must be
60697b3ad9cSjruoho * read without delay, and that only the ratio between
60797b3ad9cSjruoho * IA32_APERF and IA32_MPERF is architecturally defined.
60897b3ad9cSjruoho *
60983e392c1Sjruoho * The function thus returns the percentage of the actual
61083e392c1Sjruoho * frequency in terms of the maximum frequency of the calling
61183e392c1Sjruoho * CPU since the last call. A value zero implies an error.
61283e392c1Sjruoho *
61397b3ad9cSjruoho * For further details, refer to:
61497b3ad9cSjruoho *
61597b3ad9cSjruoho * Intel Corporation: Intel 64 and IA-32 Architectures
61697b3ad9cSjruoho * Software Developer's Manual. Section 13.2, Volume 3A:
61797b3ad9cSjruoho * System Programming Guide, Part 1. July, 2008.
618eb5a69e0Sjruoho *
619eb5a69e0Sjruoho * Advanced Micro Devices: BIOS and Kernel Developer's
620eb5a69e0Sjruoho * Guide (BKDG) for AMD Family 10h Processors. Section
621eb5a69e0Sjruoho * 2.4.5, Revision 3.48, April 2010.
62297b3ad9cSjruoho */
6236315c7d6Sjruoho uint8_t
acpicpu_md_pstate_hwf(struct cpu_info * ci)624b66af12bSjruoho acpicpu_md_pstate_hwf(struct cpu_info *ci)
6256315c7d6Sjruoho {
6266315c7d6Sjruoho struct acpicpu_softc *sc;
6276315c7d6Sjruoho uint64_t aperf, mperf;
6286315c7d6Sjruoho uint8_t rv = 0;
6296315c7d6Sjruoho
6306315c7d6Sjruoho sc = acpicpu_sc[ci->ci_acpiid];
6316315c7d6Sjruoho
6326315c7d6Sjruoho if (__predict_false(sc == NULL))
63383e392c1Sjruoho return 0;
63483e392c1Sjruoho
6354e1517c4Sjruoho if (__predict_false((sc->sc_flags & ACPICPU_FLAG_P_HWF) == 0))
63683e392c1Sjruoho return 0;
63797b3ad9cSjruoho
63897b3ad9cSjruoho aperf = sc->sc_pstate_aperf;
63997b3ad9cSjruoho mperf = sc->sc_pstate_mperf;
64097b3ad9cSjruoho
641b66af12bSjruoho x86_disable_intr();
642b66af12bSjruoho
64383e392c1Sjruoho sc->sc_pstate_aperf = rdmsr(MSR_APERF);
64483e392c1Sjruoho sc->sc_pstate_mperf = rdmsr(MSR_MPERF);
64597b3ad9cSjruoho
646b66af12bSjruoho x86_enable_intr();
647b66af12bSjruoho
64897b3ad9cSjruoho aperf = sc->sc_pstate_aperf - aperf;
64997b3ad9cSjruoho mperf = sc->sc_pstate_mperf - mperf;
65097b3ad9cSjruoho
65197b3ad9cSjruoho if (__predict_true(mperf != 0))
65297b3ad9cSjruoho rv = (aperf * 100) / mperf;
65397b3ad9cSjruoho
65497b3ad9cSjruoho return rv;
65597b3ad9cSjruoho }
65697b3ad9cSjruoho
65797b3ad9cSjruoho static void
acpicpu_md_pstate_hwf_reset(void * arg1,void * arg2)658b66af12bSjruoho acpicpu_md_pstate_hwf_reset(void *arg1, void *arg2)
65997b3ad9cSjruoho {
660b66af12bSjruoho struct cpu_info *ci = curcpu();
6616315c7d6Sjruoho struct acpicpu_softc *sc;
66297b3ad9cSjruoho
6636315c7d6Sjruoho sc = acpicpu_sc[ci->ci_acpiid];
66497b3ad9cSjruoho
6656315c7d6Sjruoho if (__predict_false(sc == NULL))
6666315c7d6Sjruoho return;
6670f6b94e4Sjruoho
668b66af12bSjruoho x86_disable_intr();
6690f6b94e4Sjruoho
6706315c7d6Sjruoho wrmsr(MSR_APERF, 0);
6716315c7d6Sjruoho wrmsr(MSR_MPERF, 0);
67297b3ad9cSjruoho
673b66af12bSjruoho x86_enable_intr();
674b66af12bSjruoho
67597b3ad9cSjruoho sc->sc_pstate_aperf = 0;
67697b3ad9cSjruoho sc->sc_pstate_mperf = 0;
67797b3ad9cSjruoho }
67897b3ad9cSjruoho
6798116cea3Sjruoho int
acpicpu_md_pstate_get(struct acpicpu_softc * sc,uint32_t * freq)68053e8f6c9Sjruoho acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
68153e8f6c9Sjruoho {
68283024899Sjruoho struct acpicpu_pstate *ps = NULL;
68353e8f6c9Sjruoho uint64_t val;
68453e8f6c9Sjruoho uint32_t i;
68553e8f6c9Sjruoho
68642a7d04bSjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_FIDVID) != 0)
68742a7d04bSjruoho return acpicpu_md_pstate_fidvid_get(sc, freq);
68842a7d04bSjruoho
689c9111546Sjruoho /*
690c9111546Sjruoho * Pick any P-state for the status address.
691c9111546Sjruoho */
69283024899Sjruoho for (i = 0; i < sc->sc_pstate_count; i++) {
69383024899Sjruoho
69483024899Sjruoho ps = &sc->sc_pstate[i];
69583024899Sjruoho
69642a7d04bSjruoho if (__predict_true(ps->ps_freq != 0))
69783024899Sjruoho break;
69883024899Sjruoho }
69983024899Sjruoho
70083024899Sjruoho if (__predict_false(ps == NULL))
7012ea8b860Sjruoho return ENODEV;
70283024899Sjruoho
7032f766899Sjruoho if (__predict_false(ps->ps_status_addr == 0))
70483024899Sjruoho return EINVAL;
70583024899Sjruoho
70683024899Sjruoho val = rdmsr(ps->ps_status_addr);
70783024899Sjruoho
7082f766899Sjruoho if (__predict_true(ps->ps_status_mask != 0))
70983024899Sjruoho val = val & ps->ps_status_mask;
71053e8f6c9Sjruoho
711c9111546Sjruoho /*
712c9111546Sjruoho * Search for the value from known P-states.
713c9111546Sjruoho */
714c72ee204Sjruoho for (i = 0; i < sc->sc_pstate_count; i++) {
71553e8f6c9Sjruoho
71653e8f6c9Sjruoho ps = &sc->sc_pstate[i];
71753e8f6c9Sjruoho
71842a7d04bSjruoho if (__predict_false(ps->ps_freq == 0))
71953e8f6c9Sjruoho continue;
72053e8f6c9Sjruoho
72122b91511Sjruoho if (val == ps->ps_status) {
72253e8f6c9Sjruoho *freq = ps->ps_freq;
72353e8f6c9Sjruoho return 0;
72453e8f6c9Sjruoho }
72553e8f6c9Sjruoho }
72653e8f6c9Sjruoho
72733432cfcSjruoho /*
72833432cfcSjruoho * If the value was not found, try APERF/MPERF.
72933432cfcSjruoho * The state is P0 if the return value is 100 %.
73033432cfcSjruoho */
73133432cfcSjruoho if ((sc->sc_flags & ACPICPU_FLAG_P_HWF) != 0) {
73233432cfcSjruoho
733804fdee3Sjruoho KASSERT(sc->sc_pstate_count > 0);
734804fdee3Sjruoho KASSERT(sc->sc_pstate[0].ps_freq != 0);
735804fdee3Sjruoho
73633432cfcSjruoho if (acpicpu_md_pstate_hwf(sc->sc_ci) == 100) {
73733432cfcSjruoho *freq = sc->sc_pstate[0].ps_freq;
73833432cfcSjruoho return 0;
73933432cfcSjruoho }
74033432cfcSjruoho }
74133432cfcSjruoho
74253e8f6c9Sjruoho return EIO;
74353e8f6c9Sjruoho }
74453e8f6c9Sjruoho
74553e8f6c9Sjruoho int
acpicpu_md_pstate_set(struct acpicpu_pstate * ps)74653e8f6c9Sjruoho acpicpu_md_pstate_set(struct acpicpu_pstate *ps)
74753e8f6c9Sjruoho {
748b3a05fedSjruoho uint64_t val = 0;
74953e8f6c9Sjruoho
750a7e3599bSjruoho if (__predict_false(ps->ps_control_addr == 0))
751a7e3599bSjruoho return EINVAL;
752a7e3599bSjruoho
75342a7d04bSjruoho if ((ps->ps_flags & ACPICPU_FLAG_P_FIDVID) != 0)
75442a7d04bSjruoho return acpicpu_md_pstate_fidvid_set(ps);
75542a7d04bSjruoho
756b3a05fedSjruoho /*
757b3a05fedSjruoho * If the mask is set, do a read-modify-write.
758b3a05fedSjruoho */
759b3a05fedSjruoho if (__predict_true(ps->ps_control_mask != 0)) {
760b3a05fedSjruoho val = rdmsr(ps->ps_control_addr);
761b3a05fedSjruoho val &= ~ps->ps_control_mask;
762b3a05fedSjruoho }
76383024899Sjruoho
764b3a05fedSjruoho val |= ps->ps_control;
76583024899Sjruoho
766c9111546Sjruoho wrmsr(ps->ps_control_addr, val);
767465d00f8Sjruoho DELAY(ps->ps_latency);
768c9111546Sjruoho
769465d00f8Sjruoho return 0;
770465d00f8Sjruoho }
77183024899Sjruoho
77242a7d04bSjruoho static int
acpicpu_md_pstate_fidvid_get(struct acpicpu_softc * sc,uint32_t * freq)77342a7d04bSjruoho acpicpu_md_pstate_fidvid_get(struct acpicpu_softc *sc, uint32_t *freq)
77442a7d04bSjruoho {
77542a7d04bSjruoho struct acpicpu_pstate *ps;
77642a7d04bSjruoho uint32_t fid, i, vid;
77742a7d04bSjruoho uint32_t cfid, cvid;
77842a7d04bSjruoho int rv;
77942a7d04bSjruoho
78042a7d04bSjruoho /*
78142a7d04bSjruoho * AMD family 0Fh needs special treatment.
78242a7d04bSjruoho * While it wants to use ACPI, it does not
78342a7d04bSjruoho * comply with the ACPI specifications.
78442a7d04bSjruoho */
78542a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(&cfid, &cvid);
78642a7d04bSjruoho
78742a7d04bSjruoho if (rv != 0)
78842a7d04bSjruoho return rv;
78942a7d04bSjruoho
79042a7d04bSjruoho for (i = 0; i < sc->sc_pstate_count; i++) {
79142a7d04bSjruoho
79242a7d04bSjruoho ps = &sc->sc_pstate[i];
79342a7d04bSjruoho
79442a7d04bSjruoho if (__predict_false(ps->ps_freq == 0))
79542a7d04bSjruoho continue;
79642a7d04bSjruoho
79742a7d04bSjruoho fid = __SHIFTOUT(ps->ps_status, ACPI_0FH_STATUS_FID);
79842a7d04bSjruoho vid = __SHIFTOUT(ps->ps_status, ACPI_0FH_STATUS_VID);
79942a7d04bSjruoho
80042a7d04bSjruoho if (cfid == fid && cvid == vid) {
80142a7d04bSjruoho *freq = ps->ps_freq;
80242a7d04bSjruoho return 0;
80342a7d04bSjruoho }
80442a7d04bSjruoho }
80542a7d04bSjruoho
80642a7d04bSjruoho return EIO;
80742a7d04bSjruoho }
80842a7d04bSjruoho
80942a7d04bSjruoho static int
acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate * ps)81042a7d04bSjruoho acpicpu_md_pstate_fidvid_set(struct acpicpu_pstate *ps)
81142a7d04bSjruoho {
81242a7d04bSjruoho const uint64_t ctrl = ps->ps_control;
81342a7d04bSjruoho uint32_t cfid, cvid, fid, i, irt;
81442a7d04bSjruoho uint32_t pll, vco_cfid, vco_fid;
81542a7d04bSjruoho uint32_t val, vid, vst;
81642a7d04bSjruoho int rv;
81742a7d04bSjruoho
81842a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(&cfid, &cvid);
81942a7d04bSjruoho
82042a7d04bSjruoho if (rv != 0)
82142a7d04bSjruoho return rv;
82242a7d04bSjruoho
82342a7d04bSjruoho fid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_FID);
82442a7d04bSjruoho vid = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VID);
82542a7d04bSjruoho irt = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_IRT);
82642a7d04bSjruoho vst = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_VST);
82742a7d04bSjruoho pll = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_PLL);
82842a7d04bSjruoho
82942a7d04bSjruoho vst = vst * 20;
83042a7d04bSjruoho pll = pll * 1000 / 5;
83142a7d04bSjruoho irt = 10 * __BIT(irt);
83242a7d04bSjruoho
83342a7d04bSjruoho /*
83442a7d04bSjruoho * Phase 1.
83542a7d04bSjruoho */
83642a7d04bSjruoho while (cvid > vid) {
83742a7d04bSjruoho
83842a7d04bSjruoho val = 1 << __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_MVS);
83942a7d04bSjruoho val = (val > cvid) ? 0 : cvid - val;
84042a7d04bSjruoho
84142a7d04bSjruoho acpicpu_md_pstate_fidvid_write(cfid, val, 1, vst);
84242a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid);
84342a7d04bSjruoho
84442a7d04bSjruoho if (rv != 0)
84542a7d04bSjruoho return rv;
84642a7d04bSjruoho }
84742a7d04bSjruoho
84842a7d04bSjruoho i = __SHIFTOUT(ctrl, ACPI_0FH_CONTROL_RVO);
84942a7d04bSjruoho
85042a7d04bSjruoho for (; i > 0 && cvid > 0; --i) {
85142a7d04bSjruoho
85242a7d04bSjruoho acpicpu_md_pstate_fidvid_write(cfid, cvid - 1, 1, vst);
85342a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid);
85442a7d04bSjruoho
85542a7d04bSjruoho if (rv != 0)
85642a7d04bSjruoho return rv;
85742a7d04bSjruoho }
85842a7d04bSjruoho
85942a7d04bSjruoho /*
86042a7d04bSjruoho * Phase 2.
86142a7d04bSjruoho */
86242a7d04bSjruoho if (cfid != fid) {
86342a7d04bSjruoho
86442a7d04bSjruoho vco_fid = FID_TO_VCO_FID(fid);
86542a7d04bSjruoho vco_cfid = FID_TO_VCO_FID(cfid);
86642a7d04bSjruoho
86742a7d04bSjruoho while (abs(vco_fid - vco_cfid) > 2) {
86842a7d04bSjruoho
86942a7d04bSjruoho if (fid <= cfid)
87042a7d04bSjruoho val = cfid - 2;
87142a7d04bSjruoho else {
87242a7d04bSjruoho val = (cfid > 6) ? cfid + 2 :
87342a7d04bSjruoho FID_TO_VCO_FID(cfid) + 2;
87442a7d04bSjruoho }
87542a7d04bSjruoho
87642a7d04bSjruoho acpicpu_md_pstate_fidvid_write(val, cvid, pll, irt);
87742a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL);
87842a7d04bSjruoho
87942a7d04bSjruoho if (rv != 0)
88042a7d04bSjruoho return rv;
88142a7d04bSjruoho
88242a7d04bSjruoho vco_cfid = FID_TO_VCO_FID(cfid);
88342a7d04bSjruoho }
88442a7d04bSjruoho
88542a7d04bSjruoho acpicpu_md_pstate_fidvid_write(fid, cvid, pll, irt);
88642a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(&cfid, NULL);
88742a7d04bSjruoho
88842a7d04bSjruoho if (rv != 0)
88942a7d04bSjruoho return rv;
89042a7d04bSjruoho }
89142a7d04bSjruoho
89242a7d04bSjruoho /*
89342a7d04bSjruoho * Phase 3.
89442a7d04bSjruoho */
89542a7d04bSjruoho if (cvid != vid) {
89642a7d04bSjruoho
89742a7d04bSjruoho acpicpu_md_pstate_fidvid_write(cfid, vid, 1, vst);
89842a7d04bSjruoho rv = acpicpu_md_pstate_fidvid_read(NULL, &cvid);
89942a7d04bSjruoho
90042a7d04bSjruoho if (rv != 0)
90142a7d04bSjruoho return rv;
90242a7d04bSjruoho }
90342a7d04bSjruoho
90442a7d04bSjruoho return 0;
90542a7d04bSjruoho }
90642a7d04bSjruoho
90742a7d04bSjruoho static int
acpicpu_md_pstate_fidvid_read(uint32_t * cfid,uint32_t * cvid)90842a7d04bSjruoho acpicpu_md_pstate_fidvid_read(uint32_t *cfid, uint32_t *cvid)
90942a7d04bSjruoho {
91042a7d04bSjruoho int i = ACPICPU_P_STATE_RETRY * 100;
91142a7d04bSjruoho uint64_t val;
91242a7d04bSjruoho
91342a7d04bSjruoho do {
91442a7d04bSjruoho val = rdmsr(MSR_0FH_STATUS);
91542a7d04bSjruoho
91642a7d04bSjruoho } while (__SHIFTOUT(val, MSR_0FH_STATUS_PENDING) != 0 && --i >= 0);
91742a7d04bSjruoho
91842a7d04bSjruoho if (i == 0)
91942a7d04bSjruoho return EAGAIN;
92042a7d04bSjruoho
92142a7d04bSjruoho if (cfid != NULL)
92242a7d04bSjruoho *cfid = __SHIFTOUT(val, MSR_0FH_STATUS_CFID);
92342a7d04bSjruoho
92442a7d04bSjruoho if (cvid != NULL)
92542a7d04bSjruoho *cvid = __SHIFTOUT(val, MSR_0FH_STATUS_CVID);
92642a7d04bSjruoho
92742a7d04bSjruoho return 0;
92842a7d04bSjruoho }
92942a7d04bSjruoho
93042a7d04bSjruoho static void
acpicpu_md_pstate_fidvid_write(uint32_t fid,uint32_t vid,uint32_t cnt,uint32_t tmo)93142a7d04bSjruoho acpicpu_md_pstate_fidvid_write(uint32_t fid,
93242a7d04bSjruoho uint32_t vid, uint32_t cnt, uint32_t tmo)
93342a7d04bSjruoho {
934c9111546Sjruoho uint64_t val = 0;
93542a7d04bSjruoho
936c9111546Sjruoho val |= __SHIFTIN(fid, MSR_0FH_CONTROL_FID);
937c9111546Sjruoho val |= __SHIFTIN(vid, MSR_0FH_CONTROL_VID);
938c9111546Sjruoho val |= __SHIFTIN(cnt, MSR_0FH_CONTROL_CNT);
939c9111546Sjruoho val |= __SHIFTIN(0x1, MSR_0FH_CONTROL_CHG);
94042a7d04bSjruoho
941c9111546Sjruoho wrmsr(MSR_0FH_CONTROL, val);
94242a7d04bSjruoho DELAY(tmo);
94342a7d04bSjruoho }
94442a7d04bSjruoho
9456b9ff107Sjruoho int
acpicpu_md_tstate_get(struct acpicpu_softc * sc,uint32_t * percent)9466b9ff107Sjruoho acpicpu_md_tstate_get(struct acpicpu_softc *sc, uint32_t *percent)
9476b9ff107Sjruoho {
9486b9ff107Sjruoho struct acpicpu_tstate *ts;
949dd6aeafaSjruoho uint64_t val;
9506b9ff107Sjruoho uint32_t i;
9516b9ff107Sjruoho
952dd6aeafaSjruoho val = rdmsr(MSR_THERM_CONTROL);
9536b9ff107Sjruoho
9546b9ff107Sjruoho for (i = 0; i < sc->sc_tstate_count; i++) {
9556b9ff107Sjruoho
9566b9ff107Sjruoho ts = &sc->sc_tstate[i];
9576b9ff107Sjruoho
9586b9ff107Sjruoho if (ts->ts_percent == 0)
9596b9ff107Sjruoho continue;
9606b9ff107Sjruoho
96122b91511Sjruoho if (val == ts->ts_status) {
9626b9ff107Sjruoho *percent = ts->ts_percent;
9636b9ff107Sjruoho return 0;
9646b9ff107Sjruoho }
9656b9ff107Sjruoho }
9666b9ff107Sjruoho
9676b9ff107Sjruoho return EIO;
9686b9ff107Sjruoho }
9696b9ff107Sjruoho
9706b9ff107Sjruoho int
acpicpu_md_tstate_set(struct acpicpu_tstate * ts)9716b9ff107Sjruoho acpicpu_md_tstate_set(struct acpicpu_tstate *ts)
9726b9ff107Sjruoho {
973c9111546Sjruoho uint64_t val;
974c9111546Sjruoho uint8_t i;
9756b9ff107Sjruoho
976c9111546Sjruoho val = ts->ts_control;
9773649cbb1Sjruoho val = val & __BITS(0, 4);
97883024899Sjruoho
979c9111546Sjruoho wrmsr(MSR_THERM_CONTROL, val);
9806b9ff107Sjruoho
98189188f9dSjruoho if (ts->ts_status == 0) {
98289188f9dSjruoho DELAY(ts->ts_latency);
9836b9ff107Sjruoho return 0;
98489188f9dSjruoho }
9856b9ff107Sjruoho
9866b9ff107Sjruoho for (i = val = 0; i < ACPICPU_T_STATE_RETRY; i++) {
9876b9ff107Sjruoho
988dd6aeafaSjruoho val = rdmsr(MSR_THERM_CONTROL);
9896b9ff107Sjruoho
99022b91511Sjruoho if (val == ts->ts_status)
991c9111546Sjruoho return 0;
9926b9ff107Sjruoho
9936b9ff107Sjruoho DELAY(ts->ts_latency);
9946b9ff107Sjruoho }
9956b9ff107Sjruoho
996c9111546Sjruoho return EAGAIN;
9976b9ff107Sjruoho }
998a448b8a1Sjruoho
999a448b8a1Sjruoho /*
1000a448b8a1Sjruoho * A kludge for backwards compatibility.
1001a448b8a1Sjruoho */
1002a448b8a1Sjruoho static int
acpicpu_md_pstate_sysctl_init(void)1003a448b8a1Sjruoho acpicpu_md_pstate_sysctl_init(void)
1004a448b8a1Sjruoho {
1005a448b8a1Sjruoho const struct sysctlnode *fnode, *mnode, *rnode;
1006a448b8a1Sjruoho int rv;
1007a448b8a1Sjruoho
1008a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, NULL, &rnode,
1009a448b8a1Sjruoho CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
1010a448b8a1Sjruoho NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
1011a448b8a1Sjruoho
1012a448b8a1Sjruoho if (rv != 0)
1013a448b8a1Sjruoho goto fail;
1014a448b8a1Sjruoho
1015a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, &rnode, &mnode,
1016*82f91d33Snia 0, CTLTYPE_NODE, "cpu", NULL,
1017a448b8a1Sjruoho NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1018a448b8a1Sjruoho
1019a448b8a1Sjruoho if (rv != 0)
1020a448b8a1Sjruoho goto fail;
1021a448b8a1Sjruoho
1022a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, &mnode, &fnode,
1023a448b8a1Sjruoho 0, CTLTYPE_NODE, "frequency", NULL,
1024a448b8a1Sjruoho NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1025a448b8a1Sjruoho
1026a448b8a1Sjruoho if (rv != 0)
1027a448b8a1Sjruoho goto fail;
1028a448b8a1Sjruoho
1029a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, &fnode, &rnode,
1030a448b8a1Sjruoho CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
1031a448b8a1Sjruoho acpicpu_md_pstate_sysctl_set, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1032a448b8a1Sjruoho
1033a448b8a1Sjruoho if (rv != 0)
1034a448b8a1Sjruoho goto fail;
1035a448b8a1Sjruoho
1036a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, &fnode, &rnode,
1037a448b8a1Sjruoho CTLFLAG_READONLY, CTLTYPE_INT, "current", NULL,
1038a448b8a1Sjruoho acpicpu_md_pstate_sysctl_get, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1039a448b8a1Sjruoho
1040a448b8a1Sjruoho if (rv != 0)
1041a448b8a1Sjruoho goto fail;
1042a448b8a1Sjruoho
1043a448b8a1Sjruoho rv = sysctl_createv(&acpicpu_log, 0, &fnode, &rnode,
1044a448b8a1Sjruoho CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
1045a448b8a1Sjruoho acpicpu_md_pstate_sysctl_all, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1046a448b8a1Sjruoho
1047a448b8a1Sjruoho if (rv != 0)
1048a448b8a1Sjruoho goto fail;
1049a448b8a1Sjruoho
1050a448b8a1Sjruoho return 0;
1051a448b8a1Sjruoho
1052a448b8a1Sjruoho fail:
1053a448b8a1Sjruoho if (acpicpu_log != NULL) {
1054a448b8a1Sjruoho sysctl_teardown(&acpicpu_log);
1055a448b8a1Sjruoho acpicpu_log = NULL;
1056a448b8a1Sjruoho }
1057a448b8a1Sjruoho
1058a448b8a1Sjruoho return rv;
1059a448b8a1Sjruoho }
1060a448b8a1Sjruoho
1061a448b8a1Sjruoho static int
acpicpu_md_pstate_sysctl_get(SYSCTLFN_ARGS)1062a448b8a1Sjruoho acpicpu_md_pstate_sysctl_get(SYSCTLFN_ARGS)
1063a448b8a1Sjruoho {
1064a448b8a1Sjruoho struct sysctlnode node;
1065a448b8a1Sjruoho uint32_t freq;
1066a448b8a1Sjruoho int err;
1067a448b8a1Sjruoho
1068804fdee3Sjruoho freq = cpufreq_get(curcpu());
1069a448b8a1Sjruoho
1070804fdee3Sjruoho if (freq == 0)
1071804fdee3Sjruoho return ENXIO;
1072a448b8a1Sjruoho
1073a448b8a1Sjruoho node = *rnode;
1074a448b8a1Sjruoho node.sysctl_data = &freq;
1075a448b8a1Sjruoho
1076a448b8a1Sjruoho err = sysctl_lookup(SYSCTLFN_CALL(&node));
1077a448b8a1Sjruoho
1078a448b8a1Sjruoho if (err != 0 || newp == NULL)
1079a448b8a1Sjruoho return err;
1080a448b8a1Sjruoho
1081a448b8a1Sjruoho return 0;
1082a448b8a1Sjruoho }
1083a448b8a1Sjruoho
1084a448b8a1Sjruoho static int
acpicpu_md_pstate_sysctl_set(SYSCTLFN_ARGS)1085a448b8a1Sjruoho acpicpu_md_pstate_sysctl_set(SYSCTLFN_ARGS)
1086a448b8a1Sjruoho {
1087a448b8a1Sjruoho struct sysctlnode node;
1088a448b8a1Sjruoho uint32_t freq;
1089a448b8a1Sjruoho int err;
1090a448b8a1Sjruoho
1091804fdee3Sjruoho freq = cpufreq_get(curcpu());
1092a448b8a1Sjruoho
1093804fdee3Sjruoho if (freq == 0)
1094804fdee3Sjruoho return ENXIO;
1095a448b8a1Sjruoho
1096a448b8a1Sjruoho node = *rnode;
1097a448b8a1Sjruoho node.sysctl_data = &freq;
1098a448b8a1Sjruoho
1099a448b8a1Sjruoho err = sysctl_lookup(SYSCTLFN_CALL(&node));
1100a448b8a1Sjruoho
1101a448b8a1Sjruoho if (err != 0 || newp == NULL)
1102a448b8a1Sjruoho return err;
1103a448b8a1Sjruoho
1104804fdee3Sjruoho cpufreq_set_all(freq);
1105a448b8a1Sjruoho
1106a448b8a1Sjruoho return 0;
1107a448b8a1Sjruoho }
1108a448b8a1Sjruoho
1109a448b8a1Sjruoho static int
acpicpu_md_pstate_sysctl_all(SYSCTLFN_ARGS)1110a448b8a1Sjruoho acpicpu_md_pstate_sysctl_all(SYSCTLFN_ARGS)
1111a448b8a1Sjruoho {
1112a448b8a1Sjruoho struct cpu_info *ci = curcpu();
1113a448b8a1Sjruoho struct acpicpu_softc *sc;
1114a448b8a1Sjruoho struct sysctlnode node;
1115a448b8a1Sjruoho char buf[1024];
1116a448b8a1Sjruoho size_t len;
1117a448b8a1Sjruoho uint32_t i;
1118a448b8a1Sjruoho int err;
1119a448b8a1Sjruoho
1120a448b8a1Sjruoho sc = acpicpu_sc[ci->ci_acpiid];
1121a448b8a1Sjruoho
1122a448b8a1Sjruoho if (sc == NULL)
1123a448b8a1Sjruoho return ENXIO;
1124a448b8a1Sjruoho
1125a448b8a1Sjruoho (void)memset(&buf, 0, sizeof(buf));
1126a448b8a1Sjruoho
1127a448b8a1Sjruoho mutex_enter(&sc->sc_mtx);
1128a448b8a1Sjruoho
1129a448b8a1Sjruoho for (len = 0, i = sc->sc_pstate_max; i < sc->sc_pstate_count; i++) {
1130a448b8a1Sjruoho
1131a448b8a1Sjruoho if (sc->sc_pstate[i].ps_freq == 0)
1132a448b8a1Sjruoho continue;
1133a448b8a1Sjruoho
113441b25bacSchristos if (len >= sizeof(buf))
113541b25bacSchristos break;
1136a448b8a1Sjruoho len += snprintf(buf + len, sizeof(buf) - len, "%u%s",
1137a448b8a1Sjruoho sc->sc_pstate[i].ps_freq,
1138a448b8a1Sjruoho i < (sc->sc_pstate_count - 1) ? " " : "");
1139a448b8a1Sjruoho }
1140a448b8a1Sjruoho
1141a448b8a1Sjruoho mutex_exit(&sc->sc_mtx);
1142a448b8a1Sjruoho
1143a448b8a1Sjruoho node = *rnode;
1144a448b8a1Sjruoho node.sysctl_data = buf;
1145a448b8a1Sjruoho
1146a448b8a1Sjruoho err = sysctl_lookup(SYSCTLFN_CALL(&node));
1147a448b8a1Sjruoho
1148a448b8a1Sjruoho if (err != 0 || newp == NULL)
1149a448b8a1Sjruoho return err;
1150a448b8a1Sjruoho
1151a448b8a1Sjruoho return 0;
1152a448b8a1Sjruoho }
1153a448b8a1Sjruoho
1154