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