openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

shbde_pci.c (11122B)


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