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 (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 }