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 */ 25 26 /* 27 * PCI Mechanism 1 low-level routines with ECS support for AMD family >= 0x10 28 */ 29 30 #include <sys/controlregs.h> 31 #include <sys/cpuvar.h> 32 #include <sys/types.h> 33 #include <sys/pci.h> 34 #include <sys/pci_impl.h> 35 #include <sys/sunddi.h> 36 #include <sys/pci_cfgspace_impl.h> 37 #include <sys/x86_archext.h> 38 39 boolean_t 40 pci_check_amd_ioecs(void) 41 { 42 struct cpuid_regs cp; 43 int family; 44 45 if ((x86_feature & X86_CPUID) == 0) 46 return (B_FALSE); 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 (0xff); 102 } 103 104 mutex_enter(&pcicfg_mutex); 105 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 106 val = inb(PCI_CONFDATA | (reg & 0x3)); 107 mutex_exit(&pcicfg_mutex); 108 return (val); 109 } 110 111 uint16_t 112 pci_mech1_amd_getw(int bus, int device, int function, int reg) 113 { 114 uint16_t val; 115 116 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 117 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 118 return (0xffff); 119 } 120 121 mutex_enter(&pcicfg_mutex); 122 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 123 val = inw(PCI_CONFDATA | (reg & 0x2)); 124 mutex_exit(&pcicfg_mutex); 125 return (val); 126 } 127 128 uint32_t 129 pci_mech1_amd_getl(int bus, int device, int function, int reg) 130 { 131 uint32_t val; 132 133 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 134 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 135 return (0xffffffffu); 136 } 137 138 mutex_enter(&pcicfg_mutex); 139 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 140 val = inl(PCI_CONFDATA); 141 mutex_exit(&pcicfg_mutex); 142 return (val); 143 } 144 145 void 146 pci_mech1_amd_putb(int bus, int device, int function, int reg, uint8_t val) 147 { 148 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 149 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 150 return; 151 } 152 153 mutex_enter(&pcicfg_mutex); 154 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 155 outb(PCI_CONFDATA | (reg & 0x3), val); 156 mutex_exit(&pcicfg_mutex); 157 } 158 159 void 160 pci_mech1_amd_putw(int bus, int device, int function, int reg, uint16_t val) 161 { 162 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 163 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 164 return; 165 } 166 167 mutex_enter(&pcicfg_mutex); 168 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 169 outw(PCI_CONFDATA | (reg & 0x2), val); 170 mutex_exit(&pcicfg_mutex); 171 } 172 173 void 174 pci_mech1_amd_putl(int bus, int device, int function, int reg, uint32_t val) 175 { 176 if (device == PCI_MECH1_SPEC_CYCLE_DEV && 177 function == PCI_MECH1_SPEC_CYCLE_FUNC) { 178 return; 179 } 180 181 mutex_enter(&pcicfg_mutex); 182 outl(PCI_CONFADD, PCI_CADDR1_ECS(bus, device, function, reg)); 183 outl(PCI_CONFDATA, val); 184 mutex_exit(&pcicfg_mutex); 185 } 186