The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/common/io/pci_cap.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 #include <sys/note.h>
   23 #include <sys/conf.h>
   24 #include <sys/debug.h>
   25 #include <sys/sunddi.h>
   26 #include <sys/pci.h>
   27 #include <sys/pcie.h>
   28 #include <sys/bitmap.h>
   29 #include <sys/autoconf.h>
   30 #include <sys/sysmacros.h>
   31 #include <sys/pci_cap.h>
   32 
   33 /*
   34  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   35  * Use is subject to license terms.
   36  */
   37 
   38 /*
   39  * Generic PCI Capabilites Interface for all pci platforms
   40  */
   41 
   42 #ifdef DEBUG
   43 uint_t  pci_cap_debug = 0;
   44 #endif
   45 
   46 /* Cap Base Macro */
   47 #define PCI_CAP_BASE(h, id, base_p) (*base_p ? DDI_SUCCESS : \
   48         (id ? PCI_CAP_LOCATE(h, id, base_p) : DDI_FAILURE))
   49 
   50 /*
   51  * pci_cap_probe: returns the capid and base based upon a given index
   52  */
   53 int
   54 pci_cap_probe(ddi_acc_handle_t h, uint16_t index,
   55         uint32_t *id_p, uint16_t *base_p)
   56 {
   57         int i, search_ext = 0;
   58         uint16_t base, pcix_cmd, status;
   59         uint32_t id, xcaps_hdr; /* Extended Caps Header Word */
   60 
   61         status = pci_config_get16(h, PCI_CONF_STAT);
   62 
   63         if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
   64                 return (DDI_FAILURE);
   65 
   66         /* PCIE and PCIX Version 2 contain Extended Config Space */
   67         for (i = 0, base = pci_config_get8(h, PCI_CONF_CAP_PTR);
   68             base && i < index; base = pci_config_get8(h, base
   69             + PCI_CAP_NEXT_PTR), i++) {
   70 
   71                 if ((id = pci_config_get8(h, base)) == 0xff)
   72                         break;
   73 
   74                 if (id == PCI_CAP_ID_PCI_E)
   75                         search_ext = 1;
   76                 else if (id == PCI_CAP_ID_PCIX) {
   77                         if ((pcix_cmd = pci_config_get16(h, base +
   78                             PCI_PCIX_COMMAND)) != PCI_CAP_EINVAL16)
   79                                 continue;
   80                         if ((pcix_cmd & PCI_PCIX_VER_MASK) == PCI_PCIX_VER_2)
   81                                 search_ext = 1;
   82                 }
   83         }
   84 
   85         if (base && i == index) {
   86                 if ((id = pci_config_get8(h, base)) != 0xff)
   87                         goto found;
   88         }
   89 
   90         if (!search_ext)
   91                 return (DDI_FAILURE);
   92 
   93         for (base = PCIE_EXT_CAP; base && i < index; i++) {
   94                 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
   95                         break;
   96 
   97                 id = (xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT)
   98                     & PCIE_EXT_CAP_ID_MASK;
   99                 base = (xcaps_hdr >> PCIE_EXT_CAP_NEXT_PTR_SHIFT)
  100                     & PCIE_EXT_CAP_NEXT_PTR_MASK;
  101         }
  102 
  103         if (!base || i < index)
  104                 return (DDI_FAILURE);
  105 
  106         if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
  107                 return (DDI_FAILURE);
  108 
  109         id = ((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) & PCIE_EXT_CAP_ID_MASK) |
  110             PCI_CAP_XCFG_FLAG;
  111 found:
  112         PCI_CAP_DBG("pci_cap_probe: index=%x, id=%x, base=%x\n",
  113             index, id, base);
  114 
  115         *id_p = id;
  116         *base_p = base;
  117         return (DDI_SUCCESS);
  118 
  119 }
  120 
  121 /*
  122  * pci_lcap_locate: Helper function locates a base in conventional config space.
  123  */
  124 int
  125 pci_lcap_locate(ddi_acc_handle_t h, uint8_t id, uint16_t *base_p)
  126 {
  127         uint8_t header;
  128         uint16_t status, base;
  129 
  130         status = pci_config_get16(h, PCI_CONF_STAT);
  131 
  132         if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
  133                 return (DDI_FAILURE);
  134 
  135         header = pci_config_get8(h, PCI_CONF_HEADER);
  136         switch (header & PCI_HEADER_TYPE_M) {
  137         case PCI_HEADER_ZERO:
  138                 base = PCI_CONF_CAP_PTR;
  139                 break;
  140         case PCI_HEADER_PPB:
  141                 base = PCI_BCNF_CAP_PTR;
  142                 break;
  143         case PCI_HEADER_CARDBUS:
  144                 base = PCI_CBUS_CAP_PTR;
  145                 break;
  146         default:
  147                 cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
  148                     __func__, header);
  149                 return (DDI_FAILURE);
  150         }
  151 
  152         for (base = pci_config_get8(h, base); base;
  153             base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
  154                 if (pci_config_get8(h, base) == id) {
  155                         *base_p = base;
  156                         return (DDI_SUCCESS);
  157                 }
  158         }
  159 
  160         *base_p = PCI_CAP_NEXT_PTR_NULL;
  161         return (DDI_FAILURE);
  162 }
  163 
  164 /*
  165  * pci_xcap_locate: Helper function locates a base in extended config space.
  166  */
  167 int
  168 pci_xcap_locate(ddi_acc_handle_t h, uint16_t id, uint16_t *base_p)
  169 {
  170         uint16_t status, base;
  171         uint32_t xcaps_hdr;
  172 
  173         status = pci_config_get16(h, PCI_CONF_STAT);
  174 
  175         if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
  176                 return (DDI_FAILURE);
  177 
  178         for (base = PCIE_EXT_CAP; base; base = (xcaps_hdr >>
  179             PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
  180 
  181                 if ((xcaps_hdr = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
  182                         break;
  183 
  184                 if (((xcaps_hdr >> PCIE_EXT_CAP_ID_SHIFT) &
  185                     PCIE_EXT_CAP_ID_MASK) == id) {
  186                         *base_p = base;
  187                         return (DDI_SUCCESS);
  188                 }
  189         }
  190 
  191         *base_p = PCI_CAP_NEXT_PTR_NULL;
  192         return (DDI_FAILURE);
  193 }
  194 
  195 /*
  196  * There can be multiple pci caps with a Hypertransport technology cap ID
  197  * Each is distiguished by a type register in the upper half of the cap
  198  * header (the "command" register part).
  199  *
  200  * This returns the location of a hypertransport capability whose upper
  201  * 16-bits of the cap header matches <reg_val> after masking the value
  202  * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return
  203  * the first HT cap found
  204  */
  205 int
  206 pci_htcap_locate(ddi_acc_handle_t h, uint16_t reg_mask, uint16_t reg_val,
  207     uint16_t *base_p)
  208 {
  209         uint8_t header;
  210         uint16_t status, base;
  211 
  212         status = pci_config_get16(h, PCI_CONF_STAT);
  213 
  214         if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
  215                 return (DDI_FAILURE);
  216 
  217         header = pci_config_get8(h, PCI_CONF_HEADER);
  218         switch (header & PCI_HEADER_TYPE_M) {
  219         case PCI_HEADER_ZERO:
  220                 base = PCI_CONF_CAP_PTR;
  221                 break;
  222         case PCI_HEADER_PPB:
  223                 base = PCI_BCNF_CAP_PTR;
  224                 break;
  225         default:
  226                 cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
  227                     __func__, header);
  228                 return (DDI_FAILURE);
  229         }
  230 
  231         for (base = pci_config_get8(h, base); base;
  232             base = pci_config_get8(h, base + PCI_CAP_NEXT_PTR)) {
  233                 if (pci_config_get8(h, base) == PCI_CAP_ID_HT &&
  234                     (pci_config_get16(h, base + PCI_CAP_ID_REGS_OFF) &
  235                     reg_mask) == reg_val) {
  236                         *base_p = base;
  237                         return (DDI_SUCCESS);
  238                 }
  239         }
  240 
  241         *base_p = PCI_CAP_NEXT_PTR_NULL;
  242         return (DDI_FAILURE);
  243 }
  244 
  245 /*
  246  * pci_cap_get: This function uses the base or capid to get a byte, word,
  247  * or dword. If access by capid is requested, the function uses the capid to
  248  * locate the base. Access by a base results in better performance
  249  * because no cap list traversal is required.
  250  */
  251 uint32_t
  252 pci_cap_get(ddi_acc_handle_t h, pci_cap_config_size_t size,
  253         uint32_t id, uint16_t base, uint16_t offset)
  254 {
  255         uint32_t data;
  256 
  257         if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
  258                 return (PCI_CAP_EINVAL32);
  259 
  260         /*
  261          * Each access to a PCI Configuration Space should be checked
  262          * by the calling function. A returned value of the 2's complement
  263          * of -1 indicates that either the device is offlined or it does not
  264          * exist.
  265          */
  266         offset += base;
  267 
  268         switch (size) {
  269         case PCI_CAP_CFGSZ_8:
  270                 data = pci_config_get8(h, offset);
  271                 break;
  272         case PCI_CAP_CFGSZ_16:
  273                 data = pci_config_get16(h, offset);
  274                 break;
  275         case PCI_CAP_CFGSZ_32:
  276                 data = pci_config_get32(h, offset);
  277                 break;
  278         default:
  279                 data = PCI_CAP_EINVAL32;
  280         }
  281 
  282         PCI_CAP_DBG("pci_cap_get: %p[x%x]=x%x\n", (void *)h, offset, data);
  283         return (data);
  284 }
  285 
  286 /*
  287  * pci_cap_put: This function uses the caps ptr or capid to put a byte, word,
  288  * or dword. If access by capid is requested, the function uses the capid to
  289  * locate the base. Access by base results in better performance
  290  * because no cap list traversal is required.
  291  */
  292 int
  293 pci_cap_put(ddi_acc_handle_t h, pci_cap_config_size_t size,
  294         uint32_t id, uint16_t base, uint16_t offset,
  295         uint32_t data)
  296 {
  297 
  298         /*
  299          * use the pci_config_size_t to switch for the appropriate read
  300          */
  301         if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
  302                 return (DDI_FAILURE);
  303 
  304         offset += base;
  305 
  306         switch (size) {
  307         case PCI_CAP_CFGSZ_8:
  308                 pci_config_put8(h, offset, data);
  309                 break;
  310         case PCI_CAP_CFGSZ_16:
  311                 pci_config_put16(h, offset, data);
  312                 break;
  313         case PCI_CAP_CFGSZ_32:
  314                 pci_config_put32(h, offset, data);
  315                 break;
  316         default:
  317                 return (DDI_FAILURE);
  318         }
  319 
  320         PCI_CAP_DBG("pci_cap_put: data=%x\n", data);
  321         return (DDI_SUCCESS);
  322 }
  323 
  324 /*
  325  * Cache the entire Cap Structure.  The caller is required to allocate and free
  326  * buffer.
  327  */
  328 int
  329 pci_cap_read(ddi_acc_handle_t h, uint32_t id, uint16_t base,
  330         uint32_t *buf_p, uint32_t nwords)
  331 {
  332 
  333         int i;
  334         uint32_t *ptr;
  335 
  336         ASSERT(nwords < 1024);
  337 
  338         if (PCI_CAP_BASE(h, id, &base) != DDI_SUCCESS)
  339                 return (DDI_FAILURE);
  340 
  341         for (ptr = buf_p, i = 0; i < nwords; i++, base += 4) {
  342                 if ((*ptr++ = pci_config_get32(h, base)) == PCI_CAP_EINVAL32)
  343                         return (DDI_FAILURE);
  344         }
  345 
  346         return (DDI_SUCCESS);
  347 }

Cache object: 3bb9dc1aea3d9e780a0b2655ce5b410e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.