xref: /netbsd-src/sys/arch/x86/acpi/acpi_cpu_md.c (revision 82f91d33d21a838c4a93b34368b52e10b791dd9b)
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