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 2022 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 ASSERT(is_x86_feature(x86_featureset, X86FSET_CPUID)); 47 48 /* 49 * Get the CPU vendor string from CPUID. 50 * This PCI mechanism only applies to AMD CPUs. 51 */ 52 cp.cp_eax = 0; 53 (void) __cpuid_insn(&cp); 54 55 if ((cp.cp_ebx != 0x68747541) || /* Auth */ 56 (cp.cp_edx != 0x69746e65) || /* enti */ 57 (cp.cp_ecx != 0x444d4163)) /* cAMD */ 58 return (B_FALSE); 59 60 /* 61 * Get the CPU family from CPUID. 62 * This PCI mechanism is only available on family 0x10 or higher. 63 */ 64 cp.cp_eax = 1; 65 (void) __cpuid_insn(&cp); 66 family = ((cp.cp_eax >> 8) & 0xf) + ((cp.cp_eax >> 20) & 0xff); 67 68 if (family < 0x10) 69 return (B_FALSE); 70 71 /* 72 * Set the EnableCf8ExtCfg bit in the Northbridge Configuration Register 73 * to enable accessing PCI ECS using in/out instructions. 74 */ 75 wrmsr(MSR_AMD_NB_CFG, rdmsr(MSR_AMD_NB_CFG) | AMD_GH_NB_CFG_EN_ECS); 76 return (B_TRUE); 77 } 78 79 /* 80 * Macro to setup PCI Extended Configuration Space (ECS) address to give to 81 * "in/out" instructions 82 */ 83 #define PCI_CADDR1_ECS(b, d, f, r) \ 84 (PCI_CADDR1((b), (d), (f), (r)) | ((((r) >> 8) & 0xf) << 24)) 85 86 /* 87 * Per PCI 2.1 section 3.7.4.1 and PCI-PCI Bridge Architecture 1.0 section 88 * 5.3.1.2: dev=31 func=7 reg=0 means a special cycle. We don't want to 89 * trigger that by accident, so we pretend that dev 31, func 7 doesn't 90 * exist. If we ever want special cycle support, we'll add explicit 91 * special cycle support. 92 */ 93 94 uint8_t 95 pci_mech1_amd_getb(int bus, int device, int function, int reg) 96 { 97 uint8_t val; 98 99 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 100 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 101 return (PCI_EINVAL8); 102 } 103 104 if (reg > pci_iocfg_max_offset) { 105 return (PCI_EINVAL8); 106 } 107 108 mutex_enter(&pcicfg_mutex); 109 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 110 val = inb(PCI_CONFDATA | (reg & 0x3)); 111 mutex_exit(&pcicfg_mutex); 112 return (val); 113 } 114 115 uint16_t 116 pci_mech1_amd_getw(int bus, int device, int function, int reg) 117 { 118 uint16_t val; 119 120 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 121 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 122 return (PCI_EINVAL16); 123 } 124 125 if (reg > pci_iocfg_max_offset) { 126 return (PCI_EINVAL16); 127 } 128 129 mutex_enter(&pcicfg_mutex); 130 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 131 val = inw(PCI_CONFDATA | (reg & 0x2)); 132 mutex_exit(&pcicfg_mutex); 133 return (val); 134 } 135 136 uint32_t 137 pci_mech1_amd_getl(int bus, int device, int function, int reg) 138 { 139 uint32_t val; 140 141 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 142 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 143 return (PCI_EINVAL32); 144 } 145 146 if (reg > pci_iocfg_max_offset) { 147 return (PCI_EINVAL32); 148 } 149 150 mutex_enter(&pcicfg_mutex); 151 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 152 val = inl(PCI_CONFDATA); 153 mutex_exit(&pcicfg_mutex); 154 return (val); 155 } 156 157 void 158 pci_mech1_amd_putb(int bus, int device, int function, int reg, uint8_t val) 159 { 160 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 161 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 162 return; 163 } 164 165 mutex_enter(&pcicfg_mutex); 166 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 167 outb(PCI_CONFDATA | (reg & 0x3), val); 168 mutex_exit(&pcicfg_mutex); 169 } 170 171 void 172 pci_mech1_amd_putw(int bus, int device, int function, int reg, uint16_t val) 173 { 174 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 175 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 176 return; 177 } 178 179 mutex_enter(&pcicfg_mutex); 180 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 181 outw(PCI_CONFDATA | (reg & 0x2), val); 182 mutex_exit(&pcicfg_mutex); 183 } 184 185 void 186 pci_mech1_amd_putl(int bus, int device, int function, int reg, uint32_t val) 187 { 188 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 189 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 190 return; 191 } 192 193 mutex_enter(&pcicfg_mutex); 194 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 195 outl(PCI_CONFDATA, val); 196 mutex_exit(&pcicfg_mutex); 197 } 198