shbde_pci.c (10616B)
1 /* 2 * 3 * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file. 4 * 5 * Copyright 2007-2019 Broadcom Inc. All rights reserved. 6 */ 7 8 #include <shbde_pci.h> 9 10 /* PCIe capabilities */ 11 #ifndef PCI_CAPABILITY_LIST 12 #define PCI_CAPABILITY_LIST 0x34 13 #endif 14 #ifndef PCI_CAP_ID_EXP 15 #define PCI_CAP_ID_EXP 0x10 16 #endif 17 #ifndef PCI_EXP_DEVCAP 18 #define PCI_EXP_DEVCAP 4 19 #endif 20 #ifndef PCI_EXP_DEVCTL 21 #define PCI_EXP_DEVCTL 8 22 #endif 23 #ifndef PCI_EXT_CAP_START 24 #define PCI_EXT_CAP_START 0x100 25 #endif 26 #ifndef PCI_EXT_CAP_ID 27 #define PCI_EXT_CAP_ID(_hdr) (_hdr & 0x0000ffff) 28 #endif 29 #ifndef PCI_EXT_CAP_VER 30 #define PCI_EXT_CAP_VER(_hdr) ((_hdr >> 16) & 0xf) 31 #endif 32 #ifndef PCI_EXT_CAP_NEXT 33 #define PCI_EXT_CAP_NEXT(_hdr) ((_hdr >> 20) & 0xffc) 34 #endif 35 #ifndef PCI_EXT_CAP_ID_VNDR 36 #define PCI_EXT_CAP_ID_VNDR 0x0b 37 #endif 38 39 #define LOG_OUT(_shbde, _lvl, _str, _prm) \ 40 if ((_shbde)->log_func) { \ 41 (_shbde)->log_func(_lvl, _str, _prm); \ 42 } 43 #define LOG_ERR(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_ERR, _str, _prm) 44 #define LOG_WARN(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_WARN, _str, _prm) 45 #define LOG_DBG(_shbde, _str, _prm) LOG_OUT(_shbde, SHBDE_DBG, _str, _prm) 46 47 #ifndef NULL 48 #define NULL (void *)0 49 #endif 50 51 /* 52 * Warpper functions with null-pointer checks. 53 */ 54 static unsigned int 55 pcic16_read(shbde_hal_t *shbde, void *pci_dev, 56 unsigned int addr) 57 { 58 if (!shbde || !shbde->pcic16_read) { 59 return 0; 60 } 61 return shbde->pcic16_read(pci_dev, addr); 62 } 63 64 static void 65 pcic16_write(shbde_hal_t *shbde, void *pci_dev, 66 unsigned int addr, unsigned int data) 67 { 68 if (!shbde || !shbde->pcic16_write) { 69 return; 70 } 71 shbde->pcic16_write(pci_dev, addr, data); 72 } 73 74 static unsigned int 75 pcic32_read(shbde_hal_t *shbde, void *pci_dev, 76 unsigned int addr) 77 { 78 if (!shbde || !shbde->pcic32_read) { 79 return 0; 80 } 81 return shbde->pcic32_read(pci_dev, addr); 82 } 83 84 static void * 85 pci_parent_device_get(shbde_hal_t *shbde, void *pci_dev) 86 { 87 if (!shbde || !shbde->pci_parent_device_get) { 88 return NULL; 89 } 90 return shbde->pci_parent_device_get(pci_dev); 91 } 92 93 /* 94 * Function: 95 * shbde_pci_pcie_cap 96 * Purpose: 97 * Return offset of PCIe capabilities in PCI configuration space 98 * Parameters: 99 * shbde - pointer to initialized hardware abstraction module 100 * dev - PCI device handle (passed back to PCI HAL functions) 101 * Returns: 102 * PCI_CAP_ID_EXP offset in PCI configuration space if PCIe, otherwise 0 103 */ 104 unsigned int 105 shbde_pci_pcie_cap(shbde_hal_t *shbde, void *pci_dev) 106 { 107 unsigned int cap_base, rval; 108 109 cap_base = pcic16_read(shbde, pci_dev, PCI_CAPABILITY_LIST); 110 while (cap_base) { 111 rval = pcic16_read(shbde, pci_dev, cap_base); 112 if ((rval & 0xff) == PCI_CAP_ID_EXP) { 113 break; 114 } 115 cap_base = (rval >> 8) & 0xff; 116 } 117 118 return cap_base; 119 } 120 121 /* 122 * Function: 123 * shbde_pci_is_pcie 124 * Purpose: 125 * Check if PCI device is PCIe device 126 * Parameters: 127 * shbde - pointer to initialized hardware abstraction module 128 * dev - PCI device handle (passed back to PCI HAL functions) 129 * Returns: 130 * 1 if PCIe, otherwise 0 131 */ 132 int 133 shbde_pci_is_pcie(shbde_hal_t *shbde, void *pci_dev) 134 { 135 return shbde_pci_pcie_cap(shbde, pci_dev) ? 1 : 0; 136 } 137 138 /* 139 * Function: 140 * shbde_pci_is_iproc 141 * Purpose: 142 * Check if PCI device is iProc-based 143 * Parameters: 144 * shbde - pointer to initialized hardware abstraction module 145 * dev - PCI device handle (passed back to PCI HAL functions) 146 * cmic_bar - (OUT) PCI BAR which contains switch CMIC registers 147 * Returns: 148 * 1 if iProc-based, otherwise 0 149 */ 150 int 151 shbde_pci_is_iproc(shbde_hal_t *shbde, void *pci_dev, int *cmic_bar) 152 { 153 unsigned int cap_base, rval; 154 155 if (!shbde_pci_is_pcie(shbde, pci_dev)) { 156 return 0; 157 } 158 159 /* Look for PCIe vendor-specific extended capability (VSEC) */ 160 cap_base = PCI_EXT_CAP_START; 161 while (cap_base) { 162 rval = pcic32_read(shbde, pci_dev, cap_base); 163 if (rval == 0xffffffff) { 164 /* Assume PCI HW read error */ 165 return 0; 166 } 167 168 if (PCI_EXT_CAP_ID(rval) == PCI_EXT_CAP_ID_VNDR) { 169 break; 170 } 171 cap_base = PCI_EXT_CAP_NEXT(rval); 172 } 173 if (cap_base) { 174 /* 175 * VSEC layout: 176 * 177 * 0x00: PCI Express Extended Capability Header 178 * 0x04: Vendor-Specific Header 179 * 0x08: Vendor-Specific Register 1 180 * 0x0c: Vendor-Specific Register 2 181 * ... 182 * 0x24: Vendor-Specific Register 8 183 */ 184 /* 32'b // 31:12=0 Reserved; 11:08=CMIC BAR; 07:00=iProc Configuration ID */ 185 rval = pcic32_read(shbde, pci_dev, cap_base + 8); 186 LOG_DBG(shbde, "Found VSEC", rval); 187 188 /* Determine PCI BAR of CMIC */ 189 *cmic_bar = 0; 190 if ((rval & 0x100) == 0x100) { 191 *cmic_bar = 2; 192 } 193 /* Assume iProc device */ 194 return 1; 195 } 196 197 return 0; 198 } 199 200 /* 201 * Function: 202 * shbde_pci_iproc_version_get 203 * Purpose: 204 * Get iproc, cmic versions and revisions 205 * Parameters: 206 * shbde - pointer to initialized hardware abstraction module 207 * dev - PCI device handle (passed back to PCI HAL functions) 208 * iproc_ver - (OUT) iProc version 209 * cmic_ver - (OUT) CMIC version 210 * cmic_rev - (OUT) CMIC revision 211 * Returns: 212 * 1 for no error, otherwise 0 213 */ 214 int 215 shbde_pci_iproc_version_get(shbde_hal_t *shbde, void *pci_dev, 216 unsigned int *iproc_ver, 217 unsigned int *cmic_ver, 218 unsigned int *cmic_rev) 219 { 220 unsigned int cap_base, rval; 221 222 if (!shbde_pci_is_pcie(shbde, pci_dev)) { 223 return 0; 224 } 225 226 /* Look for PCIe vendor-specific extended capability (VSEC) */ 227 cap_base = PCI_EXT_CAP_START; 228 while (cap_base) { 229 rval = pcic32_read(shbde, pci_dev, cap_base); 230 if (rval == 0xffffffff) { 231 /* Assume PCI HW read error */ 232 return 0; 233 } 234 235 if (PCI_EXT_CAP_ID(rval) == PCI_EXT_CAP_ID_VNDR) { 236 break; 237 } 238 cap_base = PCI_EXT_CAP_NEXT(rval); 239 } 240 if (cap_base) { 241 /* 242 * VSEC layout: 243 * 244 * 0x00: PCI Express Extended Capability Header 245 * 0x04: Vendor-Specific Header 246 * 0x08: Vendor-Specific Register 1 247 * 0x0c: Vendor-Specific Register 2 248 * ... 249 * 0x24: Vendor-Specific Register 8 250 */ 251 252 /* Read PCIe Vendor Specific Register 1 */ 253 /* VENODR REG FORMAT 254 * [7:0] iProc Rev = 8'h0E (for P14) 255 * [11:8] CMIC BAR = 4'h1 (BAR64-1) 256 * [15:12] CMIC Version = 4'h4 257 * [19:16] CMIC Rev = 4'h1 258 * [22:20] SBUS Version = 4'h4 259 */ 260 261 rval = pcic32_read(shbde, pci_dev, cap_base + 8); 262 LOG_DBG(shbde, "Found VSEC", rval); 263 264 /* Determine PCI BAR of CMIC */ 265 *iproc_ver = rval & 0xff; 266 *cmic_ver = (rval >> 12) & 0xf; 267 *cmic_rev = (rval >> 16) & 0xf; 268 return 1; 269 } 270 271 return 0; 272 } 273 274 /* 275 * Function: 276 * shbde_pci_max_payload_set 277 * Purpose: 278 * Set PCIe maximum payload 279 * Parameters: 280 * shbde - pointer to initialized hardware abstraction module 281 * dev - PCI device handle (passed back to PCI HAL functions) 282 * maxpayload - maximum payload (in byte) 283 * Returns: 284 * -1 if error, otherwise 0 285 * Notes: 286 * If not PCIe device, set the PCI retry count to infinte instead. 287 */ 288 int 289 shbde_pci_max_payload_set(shbde_hal_t *shbde, void *pci_dev, int maxpayload) 290 { 291 unsigned int cap_base, parent_cap_base; 292 unsigned int devcap, devctl, parent_devctl; 293 int max_val, max_cap, parent_max_val; 294 void *parent_pci_dev; 295 296 cap_base = shbde_pci_pcie_cap(shbde, pci_dev); 297 298 if (cap_base == 0) { 299 /* Not PCIe */ 300 return 0; 301 } 302 303 /* Get current device control settings */ 304 devctl = pcic16_read(shbde, pci_dev, cap_base + PCI_EXP_DEVCTL); 305 306 /* Get current max payload setting */ 307 max_val = (devctl >> 5) & 0x7; 308 309 if (maxpayload) { 310 /* Get encoding from byte value */ 311 max_val = 0; 312 while ((1 << (max_val + 7)) < maxpayload) { 313 max_val++; 314 } 315 LOG_DBG(shbde, "Set max payload size", maxpayload); 316 LOG_DBG(shbde, "Set max payload val", max_val); 317 318 /* Get max supported payload size */ 319 devcap = pcic16_read(shbde, pci_dev, cap_base + PCI_EXP_DEVCAP); 320 max_cap = (devcap & 0x7); 321 322 /* Do not exceed device capabilities */ 323 if (max_val > max_cap) { 324 max_val = max_cap; 325 LOG_DBG(shbde, 326 "Payload size exceeds device capability", 327 maxpayload); 328 } 329 330 /* Get currently set max payload size for the parent device 331 * in the PCI tree (if it exists). 332 */ 333 parent_pci_dev = pci_parent_device_get(shbde, pci_dev); 334 if (parent_pci_dev != NULL) { 335 parent_cap_base = shbde_pci_pcie_cap(shbde, parent_pci_dev); 336 parent_devctl = pcic16_read(shbde, 337 parent_pci_dev, 338 parent_cap_base + PCI_EXP_DEVCTL); 339 parent_max_val = (parent_devctl >> 5) & 0x7; 340 341 /* Do not exceed current parent max payload setting (our device 342 * should have an MPS setting <= current parent MPS setting in 343 * the tree of PCIe devices). 344 */ 345 if (max_val > parent_max_val) { 346 max_val = parent_max_val; 347 LOG_DBG(shbde, 348 "Payload size exceeds current parent device setting", 349 maxpayload); 350 } 351 } 352 353 /* Update max payload size */ 354 devctl &= ~(0x7 << 5); 355 devctl |= (max_val) << 5; 356 357 /* Update max request size */ 358 devctl &= ~(0x7 << 12); 359 devctl |= (max_val << 12); 360 } 361 362 /* Always disable relaxed ordering */ 363 devctl &= ~(1 << 4); 364 365 /* Update device control settings */ 366 pcic16_write(shbde, pci_dev, cap_base + PCI_EXP_DEVCTL, devctl); 367 368 /* Warn if non-default setting is used */ 369 if (max_val > 0) { 370 LOG_WARN(shbde, 371 "Selected payload size may not be supported by all " 372 "PCIe bridges by default.", 373 (1 << (max_val + 7))); 374 } 375 376 return 0; 377 }