1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Advanced Micro Devices, Inc.
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2021 Oxide Computer Company
25  */
26 
27 /*
28  * PCI Mechanism 1 low-level routines with ECS support for AMD family >= 0x10
29  */
30 
31 #include <sys/controlregs.h>
32 #include <sys/cpuvar.h>
33 #include <sys/types.h>
34 #include <sys/pci.h>
35 #include <sys/pci_impl.h>
36 #include <sys/sunddi.h>
37 #include <sys/pci_cfgspace_impl.h>
38 #include <sys/x86_archext.h>
39 
40 boolean_t
41 pci_check_amd_ioecs(void)
42 {
43 	struct cpuid_regs cp;
44 	int family;
45 
46 	if (!is_x86_feature(x86_featureset, X86FSET_CPUID))
47 		return (B_FALSE);
48 
49 	/*
50 	 * Get the CPU vendor string from CPUID.
51 	 * This PCI mechanism only applies to AMD CPUs.
52 	 */
53 	cp.cp_eax = 0;
54 	(void) __cpuid_insn(&cp);
55 
56 	if ((cp.cp_ebx != 0x68747541) || /* Auth */
57 	    (cp.cp_edx != 0x69746e65) || /* enti */
58 	    (cp.cp_ecx != 0x444d4163))   /* cAMD */
59 		return (B_FALSE);
60 
61 	/*
62 	 * Get the CPU family from CPUID.
63 	 * This PCI mechanism is only available on family 0x10 or higher.
64 	 */
65 	cp.cp_eax = 1;
66 	(void) __cpuid_insn(&cp);
67 	family = ((cp.cp_eax >> 8) & 0xf) + ((cp.cp_eax >> 20) & 0xff);
68 
69 	if (family < 0x10)
70 		return (B_FALSE);
71 
72 	/*
73 	 * Set the EnableCf8ExtCfg bit in the Northbridge Configuration Register
74 	 * to enable accessing PCI ECS using in/out instructions.
75 	 */
76 	wrmsr(MSR_AMD_NB_CFG, rdmsr(MSR_AMD_NB_CFG) | AMD_GH_NB_CFG_EN_ECS);
77 	return (B_TRUE);
78 }
79 
80 /*
81  * Macro to setup PCI Extended Configuration Space (ECS) address to give to
82  * "in/out" instructions
83  */
84 #define	PCI_CADDR1_ECS(b, d, f, r) \
85 	(PCI_CADDR1((b), (d), (f), (r)) | ((((r) >> 8) & 0xf) << 24))
86 
87 /*
88  * Per PCI 2.1 section 3.7.4.1 and PCI-PCI Bridge Architecture 1.0 section
89  * 5.3.1.2:  dev=31 func=7 reg=0 means a special cycle.  We don't want to
90  * trigger that by accident, so we pretend that dev 31, func 7 doesn't
91  * exist.  If we ever want special cycle support, we'll add explicit
92  * special cycle support.
93  */
94 
95 uint8_t
96 pci_mech1_amd_getb(int bus, int device, int function, int reg)
97 {
98 	uint8_t val;
99 
100 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
101 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
102 		return (PCI_EINVAL8);
103 	}
104 
105 	if (reg > pci_iocfg_max_offset) {
106 		return (PCI_EINVAL8);
107 	}
108 
109 	mutex_enter(&pcicfg_mutex);
110 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
111 	val = inb(PCI_CONFDATA | (reg & 0x3));
112 	mutex_exit(&pcicfg_mutex);
113 	return (val);
114 }
115 
116 uint16_t
117 pci_mech1_amd_getw(int bus, int device, int function, int reg)
118 {
119 	uint16_t val;
120 
121 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
122 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
123 		return (PCI_EINVAL16);
124 	}
125 
126 	if (reg > pci_iocfg_max_offset) {
127 		return (PCI_EINVAL16);
128 	}
129 
130 	mutex_enter(&pcicfg_mutex);
131 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
132 	val =  inw(PCI_CONFDATA | (reg & 0x2));
133 	mutex_exit(&pcicfg_mutex);
134 	return (val);
135 }
136 
137 uint32_t
138 pci_mech1_amd_getl(int bus, int device, int function, int reg)
139 {
140 	uint32_t val;
141 
142 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
143 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
144 		return (PCI_EINVAL32);
145 	}
146 
147 	if (reg > pci_iocfg_max_offset) {
148 		return (PCI_EINVAL32);
149 	}
150 
151 	mutex_enter(&pcicfg_mutex);
152 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
153 	val = inl(PCI_CONFDATA);
154 	mutex_exit(&pcicfg_mutex);
155 	return (val);
156 }
157 
158 void
159 pci_mech1_amd_putb(int bus, int device, int function, int reg, uint8_t val)
160 {
161 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
162 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
163 		return;
164 	}
165 
166 	mutex_enter(&pcicfg_mutex);
167 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
168 	outb(PCI_CONFDATA | (reg & 0x3), val);
169 	mutex_exit(&pcicfg_mutex);
170 }
171 
172 void
173 pci_mech1_amd_putw(int bus, int device, int function, int reg, uint16_t val)
174 {
175 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
176 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
177 		return;
178 	}
179 
180 	mutex_enter(&pcicfg_mutex);
181 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
182 	outw(PCI_CONFDATA | (reg & 0x2), val);
183 	mutex_exit(&pcicfg_mutex);
184 }
185 
186 void
187 pci_mech1_amd_putl(int bus, int device, int function, int reg, uint32_t val)
188 {
189 	if (device == PCI_MECH1_SPEC_CYCLE_DEV &&
190 	    function == PCI_MECH1_SPEC_CYCLE_FUNC) {
191 		return;
192 	}
193 
194 	mutex_enter(&pcicfg_mutex);
195 	outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg));
196 	outl(PCI_CONFDATA, val);
197 	mutex_exit(&pcicfg_mutex);
198 }
199