Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:20

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*  PCI bus driver.
0004  *
0005  *  COPYRIGHT (c) 2008 Cobham Gaisler AB.
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  *
0016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0017  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0019  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0020  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0021  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0022  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0025  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0026  * POSSIBILITY OF SUCH DAMAGE.
0027  */
0028 
0029 /* General part of PCI Bus driver. The driver is typically
0030  * initialized from the PCI host driver separating the host
0031  * driver from the common parts in PCI drivers.
0032  * The PCI library must be initialized before starting the
0033  * PCI bus driver. The PCI library have set up BARs and
0034  * assigned system IRQs for targets.
0035  * This PCI bus driver rely on the PCI library (pci.c) for
0036  * interrupt registeration (pci_interrupt_register) and PCI
0037  * target set up.
0038  */
0039 
0040 /* Use PCI Configuration libarary pci_hb RAM device structure to find devices,
0041  * undefine to access PCI configuration space directly.
0042  */
0043 #define USE_PCI_CFG_LIB
0044 
0045 /* On small systems undefine PCIBUS_INFO to avoid sprintf get dragged in */
0046 #define PCIBUS_INFO
0047 
0048 #include <inttypes.h>
0049 #include <stdlib.h>
0050 #include <stdio.h>
0051 #include <string.h>
0052 
0053 #include <pci.h>
0054 #ifdef USE_PCI_CFG_LIB
0055 #include <pci/cfg.h>
0056 #endif
0057 #include <pci/irq.h>
0058 
0059 #include <drvmgr/drvmgr.h>
0060 #include <drvmgr/pci_bus.h>
0061 
0062 #ifdef DEBUG 
0063 #define DBG(args...) printk(args)
0064 #else
0065 #define DBG(args...)
0066 #endif
0067 
0068 int pcibus_bus_init1(struct drvmgr_bus *bus);
0069 int pcibus_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev);
0070 int pcibus_int_register(
0071     struct drvmgr_dev *dev,
0072     int index,
0073     const char *info,
0074     drvmgr_isr isr,
0075     void *arg);
0076 int pcibus_int_unregister(
0077     struct drvmgr_dev *dev,
0078     int index,
0079     drvmgr_isr isr,
0080     void *arg);
0081 int pcibus_int_clear(
0082     struct drvmgr_dev *dev,
0083     int index);
0084 static int pcibus_get_freq(
0085     struct drvmgr_dev *dev,
0086     int options,
0087     unsigned int *freq_hz);
0088 
0089 int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params);
0090 
0091 void pcibus_dev_info(
0092     struct drvmgr_dev *dev,
0093     void (*print_line)(void *p, char *str),
0094     void *p);
0095 
0096 struct drvmgr_bus_ops pcibus_ops = {
0097     .init = {
0098         pcibus_bus_init1,
0099         NULL,
0100         NULL,
0101         NULL
0102     },
0103     .remove     = NULL,
0104     .unite      = pcibus_unite,
0105     .int_register   = pcibus_int_register,
0106     .int_unregister = pcibus_int_unregister,
0107 #if 0
0108     .int_enable = pcibus_int_enable,
0109     .int_disable    = pcibus_int_disable,
0110 #endif
0111     .int_clear  = pcibus_int_clear,
0112     .int_mask   = NULL,
0113     .int_unmask = NULL,
0114     .get_params = pcibus_get_params,
0115     .get_freq   = pcibus_get_freq,
0116 #ifdef PCIBUS_INFO
0117     .get_info_dev   = pcibus_dev_info,
0118 #endif
0119 };
0120 
0121 struct drvmgr_func pcibus_funcs[] = {
0122     DRVMGR_FUNC(PCI_FUNC_MREG_R8, NULL),
0123     DRVMGR_FUNC(PCI_FUNC_MREG_R16, NULL),
0124     DRVMGR_FUNC(PCI_FUNC_MREG_R32, NULL),
0125     DRVMGR_FUNC(PCI_FUNC_MREG_W8, NULL),
0126     DRVMGR_FUNC(PCI_FUNC_MREG_W16, NULL),
0127     DRVMGR_FUNC(PCI_FUNC_MREG_W32, NULL),
0128     DRVMGR_FUNC_END
0129 };
0130 
0131 /* Driver resources configuration for the PCI bus. It is declared weak so that
0132  * the user may override it from the project file, if the default settings are
0133  * not enough.
0134  */
0135 struct drvmgr_bus_res pcibus_drv_resources __attribute__((weak)) = {
0136     .next = NULL,
0137     .resource = {
0138         DRVMGR_RES_EMPTY,
0139     },
0140 };
0141 
0142 struct pcibus_priv {
0143     struct drvmgr_dev   *dev;
0144 };
0145 
0146 static int compatible(struct pci_dev_id *id, struct pci_dev_id_match *drv)
0147 {
0148     if (((drv->vendor==PCI_ID_ANY) || (id->vendor==drv->vendor)) &&
0149         ((drv->device==PCI_ID_ANY) || (id->device==drv->device)) &&
0150         ((drv->subvendor==PCI_ID_ANY) || (id->subvendor==drv->subvendor)) &&
0151         ((drv->subdevice==PCI_ID_ANY) || (id->subdevice==drv->subdevice)) &&
0152         ((id->class & drv->class_mask) == drv->class))
0153         return 1;
0154     else
0155         return 0;
0156 }
0157 
0158 int pcibus_unite(struct drvmgr_drv *drv,
0159             struct drvmgr_dev *dev)
0160 {
0161     struct pci_drv_info *pdrv;
0162     struct pci_dev_id_match *drvid;
0163     struct pci_dev_info *pci;
0164 
0165     if (!drv || !dev || !dev->parent)
0166         return 0;
0167 
0168     if ((drv->bus_type != DRVMGR_BUS_TYPE_PCI) ||
0169          (dev->parent->bus_type != DRVMGR_BUS_TYPE_PCI))
0170         return 0;
0171 
0172     pci = (struct pci_dev_info *)dev->businfo;
0173     if (!pci)
0174         return 0;
0175 
0176     pdrv = (struct pci_drv_info *)drv;
0177     drvid = pdrv->ids;
0178     if (!drvid)
0179         return 0;
0180     while (drvid->vendor != 0) {
0181         if (compatible(&pci->id, drvid)) {
0182             /* Unite device and driver */
0183             DBG("DRV %p and DEV %p united\n", drv, dev);
0184             return 1;
0185         }
0186         drvid++;
0187     }
0188 
0189     return 0;
0190 }
0191 
0192 static int pcibus_int_get(struct drvmgr_dev *dev, int index)
0193 {
0194     int irq;
0195 
0196     /* Relative (positive) or absolute (negative) IRQ number */
0197     if (index > 0) {
0198         /* PCI devices only have one IRQ per function */
0199         return -1;
0200     } else if (index == 0) {
0201         /* IRQ Index relative to Cores base IRQ */
0202 
0203         /* Get Base IRQ */
0204         irq = ((struct pci_dev_info *)dev->businfo)->irq;
0205         if (irq <= 0)
0206             return -1;
0207     } else {
0208         /* Absolute IRQ number */
0209         irq = -index;
0210     }
0211     return irq;
0212 }
0213 
0214 /* Use standard PCI facility to register interrupt handler */
0215 int pcibus_int_register(
0216     struct drvmgr_dev *dev,
0217     int index,
0218     const char *info,
0219     drvmgr_isr isr,
0220     void *arg)
0221 {
0222 #ifdef DEBUG
0223     struct drvmgr_dev *busdev = dev->parent->dev;
0224 #endif
0225     int irq;
0226 
0227     /* Get IRQ number from index and device information */
0228     irq = pcibus_int_get(dev, index);
0229     if (irq < 0)
0230         return -1;
0231 
0232     DBG("Register PCI interrupt on %p for dev %p (IRQ: %d)\n",
0233         busdev, dev, irq);
0234 
0235     return pci_interrupt_register(irq, info, isr, arg);
0236 }
0237 
0238 /* Use standard PCI facility to unregister interrupt handler */
0239 int pcibus_int_unregister(
0240     struct drvmgr_dev *dev,
0241     int index,
0242     drvmgr_isr isr,
0243     void *arg)
0244 {
0245 #ifdef DEBUG
0246     struct drvmgr_dev *busdev = dev->parent->dev;
0247 #endif
0248     int irq;
0249 
0250     /* Get IRQ number from index and device information */
0251     irq = pcibus_int_get(dev, index);
0252     if (irq < 0)
0253         return -1;
0254 
0255     DBG("Unregister PCI interrupt on %p for dev %p (IRQ: %d)\n",
0256         busdev, dev, irq);
0257 
0258     return pci_interrupt_unregister(irq, isr, arg);
0259 }
0260 
0261 /* Use standard PCI facility to clear interrupt */
0262 int pcibus_int_clear(
0263     struct drvmgr_dev *dev,
0264     int index)
0265 {
0266     int irq;
0267 
0268     /* Get IRQ number from index and device information */
0269     irq = pcibus_int_get(dev, index);
0270     if (irq < 0)
0271         return -1;
0272 
0273     pci_interrupt_clear(irq);
0274 
0275     return 0;
0276 }
0277 
0278 static int pcibus_get_freq(
0279     struct drvmgr_dev *dev,
0280     int options,
0281     unsigned int *freq_hz)
0282 {
0283     /* Standard PCI Bus frequency */
0284     *freq_hz = 33000000;
0285     return 0;
0286 }
0287 
0288 int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
0289 {
0290     /* No device prefix */
0291     params->dev_prefix = NULL;
0292 
0293     return 0;
0294 }
0295 
0296 #ifdef PCIBUS_INFO
0297 void pcibus_dev_info(
0298     struct drvmgr_dev *dev,
0299     void (*print_line)(void *p, char *str),
0300     void *p)
0301 {
0302     struct pci_dev_info *devinfo;
0303     struct pcibus_res *pcibusres;
0304     struct pci_res *res;
0305     char buf[64];
0306     int i;
0307     char *str1, *res_types[3] = {" IO16", "MEMIO", "  MEM"};
0308     uint32_t pcistart;
0309 
0310     if (!dev)
0311         return;
0312 
0313     devinfo = (struct pci_dev_info *)dev->businfo;
0314     if (!devinfo)
0315         return;
0316 
0317     if ((devinfo->id.class >> 8) == PCID_PCI2PCI_BRIDGE)
0318         print_line(p, "PCI BRIDGE DEVICE");
0319     else
0320         print_line(p, "PCI DEVICE");
0321     sprintf(buf, "LOCATION:    BUS:SLOT:FUNCTION [%x:%x:%x]",
0322             PCI_DEV_EXPAND(devinfo->pcidev));
0323     print_line(p, buf);
0324     sprintf(buf, "PCIID        0x%" PRIx16 "", devinfo->pcidev);
0325     print_line(p, buf);
0326     sprintf(buf, "VENDOR ID:   %04x", devinfo->id.vendor);
0327     print_line(p, buf);
0328     sprintf(buf, "DEVICE ID:   %04x", devinfo->id.device);
0329     print_line(p, buf);
0330     sprintf(buf, "SUBVEN ID:   %04x", devinfo->id.subvendor);
0331     print_line(p, buf);
0332     sprintf(buf, "SUBDEV ID:   %04x", devinfo->id.subdevice);
0333     print_line(p, buf);
0334     sprintf(buf, "CLASS:       %" PRIx32, devinfo->id.class);
0335     print_line(p, buf);
0336     sprintf(buf, "REVISION:    %x", devinfo->rev);
0337     print_line(p, buf);
0338     sprintf(buf, "IRQ:         %d", devinfo->irq);
0339     print_line(p, buf);
0340     sprintf(buf, "PCIDEV ptr:  %p", devinfo->pci_device);
0341     print_line(p, buf);
0342 
0343     /* List Resources */
0344     print_line(p, "RESOURCES");
0345     for (i = 0; i < PCIDEV_RES_CNT; i++) {
0346         pcibusres = &devinfo->resources[i];
0347 
0348         str1 = "  RES";
0349         pcistart = -1;
0350         res = pcibusres->res;
0351         if (res && (res->flags & PCI_RES_TYPE_MASK)) {
0352             str1 = res_types[(res->flags & PCI_RES_TYPE_MASK) - 1];
0353             if (res->flags & PCI_RES_IO32)
0354                 str1 = " IO32";
0355             pcistart = res->start;
0356         }
0357 
0358         if (res && (res->flags & PCI_RES_FAIL)) {
0359             sprintf(buf, " %s[%d]:  NOT ASSIGNED", str1, i);
0360             print_line(p, buf);
0361             continue;
0362         }
0363         if (!pcibusres->size)
0364             continue;
0365 
0366         sprintf(buf, " %s[%d]:  %08" PRIx32 "-%08" PRIx32
0367             " [PCIADR %" PRIx32 "]",
0368             str1, i, pcibusres->address,
0369             pcibusres->address + pcibusres->size - 1, pcistart);
0370         print_line(p, buf);
0371     }
0372 }
0373 #endif
0374 
0375 #ifdef USE_PCI_CFG_LIB
0376 
0377 static int pcibus_dev_register(struct pci_dev *dev, void *arg)
0378 {
0379     struct drvmgr_bus *pcibus = arg;
0380     struct drvmgr_dev *newdev;
0381     struct pci_dev_info *pciinfo;
0382     int i, type;
0383     struct pcibus_res *pcibusres;
0384     struct pci_res *pcires;
0385 
0386     pci_dev_t pcidev = dev->busdevfun;
0387 
0388     DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev));
0389 
0390     /* Allocate a device */
0391     drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info));
0392     newdev->next = NULL;
0393     newdev->parent = pcibus; /* Ourselfs */
0394     newdev->minor_drv = 0;
0395     newdev->minor_bus = 0;
0396     newdev->priv = NULL;
0397     newdev->drv = NULL;
0398     newdev->name = (char *)(newdev + 1);
0399     newdev->next_in_drv = NULL;
0400     newdev->bus = NULL;
0401 
0402     /* Init PnP information, Assign Core interfaces with this device */
0403     pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24);
0404 
0405     /* Read Device and Vendor */
0406     pciinfo->id.vendor = dev->vendor;
0407     pciinfo->id.device = dev->device;
0408     pciinfo->id.subvendor = dev->subvendor;
0409     pciinfo->id.subdevice = dev->subdevice;
0410     pciinfo->rev = dev->classrev & 0xff;
0411     pciinfo->id.class = (dev->classrev >> 8) & 0xffffff;
0412 
0413     /* Read IRQ information set by PCI layer */
0414     pciinfo->irq = dev->sysirq;
0415 
0416     /* Save Location on PCI bus */
0417     pciinfo->pcidev = pcidev;
0418 
0419     /* Connect device with PCI data structure */
0420     pciinfo->pci_device = dev;
0421 
0422     /* Build resources so that PCI device drivers doesn't have to scan
0423      * configuration space themselves, also the address is translated
0424      * into CPU accessible addresses.
0425      */
0426     for (i = 0; i < PCIDEV_RES_CNT; i++) {
0427         pcibusres = &pciinfo->resources[i];
0428         pcires = &dev->resources[i];
0429         type = pcires->flags & PCI_RES_TYPE_MASK;
0430         if (type == 0 || (pcires->flags & PCI_RES_FAIL))
0431             continue; /* size=0 */
0432 
0433         pcibusres->address = pcires->start;
0434         if (pci_pci2cpu(&pcibusres->address, type))
0435             continue; /* size=0 */
0436         pcibusres->res = pcires;
0437         pcibusres->size = pcires->end - pcires->start;
0438     }
0439 
0440     /* Connect device with PCI information */
0441     newdev->businfo = (void *)pciinfo;
0442 
0443     /* Create Device Name */
0444     sprintf(newdev->name, "PCI_%x:%x:%x_%04x:%04x",
0445         PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev),
0446         pciinfo->id.vendor, pciinfo->id.device);
0447 
0448     /* Register New Device */
0449     drvmgr_dev_register(newdev);
0450 
0451     return 0;
0452 }
0453 
0454 #else
0455 
0456 static int pcibus_dev_register(pci_dev_t pcidev, void *arg)
0457 {
0458     struct drvmgr_bus *pcibus = arg;
0459     struct drvmgr_dev *newdev;
0460     struct pci_dev_info *pciinfo;
0461 
0462     DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev));
0463 
0464     /* Allocate a device */
0465     drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info));
0466     newdev->next = NULL;
0467     newdev->parent = pcibus; /* Ourselfs */
0468     newdev->minor_drv = 0;
0469     newdev->minor_bus = 0;
0470     newdev->priv = NULL;
0471     newdev->drv = NULL;
0472     newdev->name = (char *)(newdev + 1);
0473     newdev->next_in_drv = NULL;
0474     newdev->bus = NULL;
0475 
0476     /* Init PnP information, Assign Core interfaces with this device */
0477     pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24);
0478 
0479     /* Read Device and Vendor */
0480     pci_cfg_r16(pcidev, PCIR_VENDOR, &pciinfo->id.vendor);
0481     pci_cfg_r16(pcidev, PCIR_DEVICE, &pciinfo->id.device);
0482     pci_cfg_r32(pcidev, PCIR_REVID, &pciinfo->id.class);
0483     pciinfo->rev = pciinfo->id.class & 0xff;
0484     pciinfo->id.class = pciinfo->id.class >> 8;
0485 
0486     /* Devices have subsytem device and vendor ID */
0487     if ((pciinfo->id.class >> 8) != PCID_PCI2PCI_BRIDGE) {
0488         pci_cfg_r16(pcidev, PCIR_SUBVEND_0,
0489                             &pciinfo->id.subvendor);
0490         pci_cfg_r16(pcidev, PCIR_SUBDEV_0, &pciinfo->id.subdevice);
0491     } else {
0492         pciinfo->id.subvendor = 0;
0493         pciinfo->id.subdevice = 0;
0494     }
0495 
0496     /* Read IRQ information set by PCI layer */
0497     pci_cfg_r8(pcidev, PCIR_INTLINE, &pciinfo->irq);
0498 
0499     /* Save Location */
0500     pciinfo->pcidev = pcidev;
0501 
0502     /* There is no way we can know this information this way */
0503     pciinfo->pci_device = NULL;
0504 
0505     /* Connect device with PCI information */
0506     newdev->businfo = (void *)pciinfo;
0507 
0508     /* Create Device Name */
0509     sprintf(newdev->name, "PCI_%d:%d:%d_%04x:%04x",
0510         PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev),
0511         pciinfo->id.vendor, pciinfo->id.device);
0512 
0513     /* Register New Device */
0514     drvmgr_dev_register(newdev);
0515 
0516     return 0;
0517 }
0518 
0519 #endif
0520 
0521 /* Register all AMBA devices available on the AMBAPP bus */
0522 static int pcibus_devs_register(struct drvmgr_bus *bus)
0523 {
0524     /* return value 0=DRVMGR_OK works with pci_for_each/pci_for_each_dev */
0525 #ifdef USE_PCI_CFG_LIB
0526     /* Walk the PCI device tree in RAM */
0527     return pci_for_each_dev(pcibus_dev_register, bus);
0528 #else
0529     /* Scan PCI Configuration space */
0530     return pci_for_each(pcibus_dev_register, bus);
0531 #endif
0532 }
0533 
0534 /*** DEVICE FUNCTIONS ***/
0535 
0536 int pcibus_register(struct drvmgr_dev *dev, struct pcibus_config *config)
0537 {
0538     struct pcibus_priv *priv;
0539     int i, fid, rc;
0540 
0541     DBG("PCI BUS: initializing\n");
0542 
0543     /* Create BUS */
0544     drvmgr_alloc_bus(&dev->bus, sizeof(struct pcibus_priv));
0545     dev->bus->bus_type = DRVMGR_BUS_TYPE_PCI;
0546     dev->bus->next = NULL;
0547     dev->bus->dev = dev;
0548     dev->bus->children = NULL;
0549     dev->bus->ops = &pcibus_ops;
0550     dev->bus->dev_cnt = 0;
0551     dev->bus->reslist = NULL;
0552     dev->bus->maps_up = config->maps_up;
0553     dev->bus->maps_down = config->maps_down;
0554     dev->bus->funcs = &pcibus_funcs[0];
0555 
0556     /* Copy function definitions from PCI Layer */
0557     for (i=0; i<6; i++) {
0558         fid = pcibus_funcs[i].funcid;
0559         rc = pci_access_func(RW_DIR(fid), RW_SIZE(fid),
0560                 &pcibus_funcs[i].func, PCI_LITTLE_ENDIAN, 3);
0561         if (rc != 0)
0562             DBG("PCI BUS: MEMREG 0x%x function not defined\n", fid);
0563     }
0564 
0565     /* Add resource configuration if user overrided the default empty cfg */
0566     if (pcibus_drv_resources.resource[0].drv_id != 0)
0567         drvmgr_bus_res_add(dev->bus, &pcibus_drv_resources);
0568 
0569     /* Init BUS private structures */
0570     priv = (struct pcibus_priv *)(dev->bus + 1);
0571     dev->bus->priv = priv;
0572 
0573     /* Register BUS */
0574     drvmgr_bus_register(dev->bus);
0575 
0576     return DRVMGR_OK;
0577 }
0578 
0579 /*** BUS INITIALIZE FUNCTIONS ***/
0580 
0581 int pcibus_bus_init1(struct drvmgr_bus *bus)
0582 {
0583     return pcibus_devs_register(bus);
0584 }