source: rtems/cpukit/libpci/pci_bus.c @ 9c12bcfd

Last change on this file since 9c12bcfd was 9c12bcfd, checked in by Sebastian Huber <sebastian.huber@…>, on Jan 7, 2019 at 8:32:16 AM

Fix format warnings

  • Property mode set to 100644
File size: 13.8 KB
Line 
1/*  PCI bus driver.
2 *
3 *  COPYRIGHT (c) 2008 Cobham Gaisler AB.
4 *
5 *  The license and distribution terms for this file may be
6 *  found in the file LICENSE in this distribution or at
7 *  http://www.rtems.org/license/LICENSE.
8 */
9
10/* General part of PCI Bus driver. The driver is typically
11 * initialized from the PCI host driver separating the host
12 * driver from the common parts in PCI drivers.
13 * The PCI library must be initialized before starting the
14 * PCI bus driver. The PCI library have set up BARs and
15 * assigned system IRQs for targets.
16 * This PCI bus driver rely on the PCI library (pci.c) for
17 * interrupt registeration (pci_interrupt_register) and PCI
18 * target set up.
19 */
20
21/* Use PCI Configuration libarary pci_hb RAM device structure to find devices,
22 * undefine to access PCI configuration space directly.
23 */
24#define USE_PCI_CFG_LIB
25
26/* On small systems undefine PCIBUS_INFO to avoid sprintf get dragged in */
27#define PCIBUS_INFO
28
29#include <inttypes.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33
34#include <pci.h>
35#ifdef USE_PCI_CFG_LIB
36#include <pci/cfg.h>
37#endif
38#include <pci/irq.h>
39
40#include <drvmgr/drvmgr.h>
41#include <drvmgr/pci_bus.h>
42
43#ifdef DEBUG
44#define DBG(args...) printk(args)
45#else
46#define DBG(args...)
47#endif
48
49int pcibus_bus_init1(struct drvmgr_bus *bus);
50int pcibus_unite(struct drvmgr_drv *drv, struct drvmgr_dev *dev);
51int pcibus_int_register(
52        struct drvmgr_dev *dev,
53        int index,
54        const char *info,
55        drvmgr_isr isr,
56        void *arg);
57int pcibus_int_unregister(
58        struct drvmgr_dev *dev,
59        int index,
60        drvmgr_isr isr,
61        void *arg);
62int pcibus_int_clear(
63        struct drvmgr_dev *dev,
64        int index);
65static int pcibus_get_freq(
66        struct drvmgr_dev *dev,
67        int options,
68        unsigned int *freq_hz);
69
70int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params);
71
72void pcibus_dev_info(
73        struct drvmgr_dev *dev,
74        void (*print_line)(void *p, char *str),
75        void *p);
76
77struct drvmgr_bus_ops pcibus_ops = {
78        .init = {
79                pcibus_bus_init1,
80                NULL,
81                NULL,
82                NULL
83        },
84        .remove         = NULL,
85        .unite          = pcibus_unite,
86        .int_register   = pcibus_int_register,
87        .int_unregister = pcibus_int_unregister,
88#if 0
89        .int_enable     = pcibus_int_enable,
90        .int_disable    = pcibus_int_disable,
91#endif
92        .int_clear      = pcibus_int_clear,
93        .int_mask       = NULL,
94        .int_unmask     = NULL,
95        .get_params     = pcibus_get_params,
96        .get_freq       = pcibus_get_freq,
97#ifdef PCIBUS_INFO
98        .get_info_dev   = pcibus_dev_info,
99#endif
100};
101
102struct drvmgr_func pcibus_funcs[] = {
103        DRVMGR_FUNC(PCI_FUNC_MREG_R8, NULL),
104        DRVMGR_FUNC(PCI_FUNC_MREG_R16, NULL),
105        DRVMGR_FUNC(PCI_FUNC_MREG_R32, NULL),
106        DRVMGR_FUNC(PCI_FUNC_MREG_W8, NULL),
107        DRVMGR_FUNC(PCI_FUNC_MREG_W16, NULL),
108        DRVMGR_FUNC(PCI_FUNC_MREG_W32, NULL),
109        DRVMGR_FUNC_END
110};
111
112/* Driver resources configuration for the PCI bus. It is declared weak so that
113 * the user may override it from the project file, if the default settings are
114 * not enough.
115 */
116struct drvmgr_bus_res pcibus_drv_resources __attribute__((weak)) = {
117        .next = NULL,
118        .resource = {
119                DRVMGR_RES_EMPTY,
120        },
121};
122
123struct pcibus_priv {
124        struct drvmgr_dev       *dev;
125};
126
127static int compatible(struct pci_dev_id *id, struct pci_dev_id_match *drv)
128{
129        if (((drv->vendor==PCI_ID_ANY) || (id->vendor==drv->vendor)) &&
130            ((drv->device==PCI_ID_ANY) || (id->device==drv->device)) &&
131            ((drv->subvendor==PCI_ID_ANY) || (id->subvendor==drv->subvendor)) &&
132            ((drv->subdevice==PCI_ID_ANY) || (id->subdevice==drv->subdevice)) &&
133            ((id->class & drv->class_mask) == drv->class))
134                return 1;
135        else
136                return 0;
137}
138
139int pcibus_unite(struct drvmgr_drv *drv,
140                        struct drvmgr_dev *dev)
141{
142        struct pci_drv_info *pdrv;
143        struct pci_dev_id_match *drvid;
144        struct pci_dev_info *pci;
145
146        if (!drv || !dev || !dev->parent)
147                return 0;
148
149        if ((drv->bus_type != DRVMGR_BUS_TYPE_PCI) ||
150             (dev->parent->bus_type != DRVMGR_BUS_TYPE_PCI))
151                return 0;
152
153        pci = (struct pci_dev_info *)dev->businfo;
154        if (!pci)
155                return 0;
156
157        pdrv = (struct pci_drv_info *)drv;
158        drvid = pdrv->ids;
159        if (!drvid)
160                return 0;
161        while (drvid->vendor != 0) {
162                if (compatible(&pci->id, drvid)) {
163                        /* Unite device and driver */
164                        DBG("DRV %p and DEV %p united\n", drv, dev);
165                        return 1;
166                }
167                drvid++;
168        }
169
170        return 0;
171}
172
173static int pcibus_int_get(struct drvmgr_dev *dev, int index)
174{
175        int irq;
176
177        /* Relative (positive) or absolute (negative) IRQ number */
178        if (index > 0) {
179                /* PCI devices only have one IRQ per function */
180                return -1;
181        } else if (index == 0) {
182                /* IRQ Index relative to Cores base IRQ */
183
184                /* Get Base IRQ */
185                irq = ((struct pci_dev_info *)dev->businfo)->irq;
186                if (irq <= 0)
187                        return -1;
188        } else {
189                /* Absolute IRQ number */
190                irq = -index;
191        }
192        return irq;
193}
194
195/* Use standard PCI facility to register interrupt handler */
196int pcibus_int_register(
197        struct drvmgr_dev *dev,
198        int index,
199        const char *info,
200        drvmgr_isr isr,
201        void *arg)
202{
203#ifdef DEBUG
204        struct drvmgr_dev *busdev = dev->parent->dev;
205#endif
206        int irq;
207
208        /* Get IRQ number from index and device information */
209        irq = pcibus_int_get(dev, index);
210        if (irq < 0)
211                return -1;
212
213        DBG("Register PCI interrupt on %p for dev %p (IRQ: %d)\n",
214                busdev, dev, irq);
215
216        return pci_interrupt_register(irq, info, isr, arg);
217}
218
219/* Use standard PCI facility to unregister interrupt handler */
220int pcibus_int_unregister(
221        struct drvmgr_dev *dev,
222        int index,
223        drvmgr_isr isr,
224        void *arg)
225{
226#ifdef DEBUG
227        struct drvmgr_dev *busdev = dev->parent->dev;
228#endif
229        int irq;
230
231        /* Get IRQ number from index and device information */
232        irq = pcibus_int_get(dev, index);
233        if (irq < 0)
234                return -1;
235
236        DBG("Unregister PCI interrupt on %p for dev %p (IRQ: %d)\n",
237                busdev, dev, irq);
238
239        return pci_interrupt_unregister(irq, isr, arg);
240}
241
242/* Use standard PCI facility to clear interrupt */
243int pcibus_int_clear(
244        struct drvmgr_dev *dev,
245        int index)
246{
247        int irq;
248
249        /* Get IRQ number from index and device information */
250        irq = pcibus_int_get(dev, index);
251        if (irq < 0)
252                return -1;
253
254        pci_interrupt_clear(irq);
255
256        return 0;
257}
258
259static int pcibus_get_freq(
260        struct drvmgr_dev *dev,
261        int options,
262        unsigned int *freq_hz)
263{
264        /* Standard PCI Bus frequency */
265        *freq_hz = 33000000;
266        return 0;
267}
268
269int pcibus_get_params(struct drvmgr_dev *dev, struct drvmgr_bus_params *params)
270{
271        /* No device prefix */
272        params->dev_prefix = NULL;
273
274        return 0;
275}
276
277#ifdef PCIBUS_INFO
278void pcibus_dev_info(
279        struct drvmgr_dev *dev,
280        void (*print_line)(void *p, char *str),
281        void *p)
282{
283        struct pci_dev_info *devinfo;
284        struct pcibus_res *pcibusres;
285        struct pci_res *res;
286        char buf[64];
287        int i;
288        char *str1, *res_types[3] = {" IO16", "MEMIO", "  MEM"};
289        uint32_t pcistart;
290
291        if (!dev)
292                return;
293
294        devinfo = (struct pci_dev_info *)dev->businfo;
295        if (!devinfo)
296                return;
297
298        if ((devinfo->id.class >> 8) == PCID_PCI2PCI_BRIDGE)
299                print_line(p, "PCI BRIDGE DEVICE");
300        else
301                print_line(p, "PCI DEVICE");
302        sprintf(buf, "LOCATION:    BUS:SLOT:FUNCTION [%x:%x:%x]",
303                        PCI_DEV_EXPAND(devinfo->pcidev));
304        print_line(p, buf);
305        sprintf(buf, "PCIID        0x%" PRIx16 "", devinfo->pcidev);
306        print_line(p, buf);
307        sprintf(buf, "VENDOR ID:   %04x", devinfo->id.vendor);
308        print_line(p, buf);
309        sprintf(buf, "DEVICE ID:   %04x", devinfo->id.device);
310        print_line(p, buf);
311        sprintf(buf, "SUBVEN ID:   %04x", devinfo->id.subvendor);
312        print_line(p, buf);
313        sprintf(buf, "SUBDEV ID:   %04x", devinfo->id.subdevice);
314        print_line(p, buf);
315        sprintf(buf, "CLASS:       %" PRIx32, devinfo->id.class);
316        print_line(p, buf);
317        sprintf(buf, "REVISION:    %x", devinfo->rev);
318        print_line(p, buf);
319        sprintf(buf, "IRQ:         %d", devinfo->irq);
320        print_line(p, buf);
321        sprintf(buf, "PCIDEV ptr:  %p", devinfo->pci_device);
322        print_line(p, buf);
323
324        /* List Resources */
325        print_line(p, "RESOURCES");
326        for (i = 0; i < PCIDEV_RES_CNT; i++) {
327                pcibusres = &devinfo->resources[i];
328
329                str1 = "  RES";
330                pcistart = -1;
331                res = pcibusres->res;
332                if (res && (res->flags & PCI_RES_TYPE_MASK)) {
333                        str1 = res_types[(res->flags & PCI_RES_TYPE_MASK) - 1];
334                        if (res->flags & PCI_RES_IO32)
335                                str1 = " IO32";
336                        pcistart = res->start;
337                }
338
339                if (res && (res->flags & PCI_RES_FAIL)) {
340                        sprintf(buf, " %s[%d]:  NOT ASSIGNED", str1, i);
341                        print_line(p, buf);
342                        continue;
343                }
344                if (!pcibusres->size)
345                        continue;
346
347                sprintf(buf, " %s[%d]:  %08" PRIx32 "-%08" PRIx32
348                        " [PCIADR %" PRIx32 "]",
349                        str1, i, pcibusres->address,
350                        pcibusres->address + pcibusres->size - 1, pcistart);
351                print_line(p, buf);
352        }
353}
354#endif
355
356#ifdef USE_PCI_CFG_LIB
357
358static int pcibus_dev_register(struct pci_dev *dev, void *arg)
359{
360        struct drvmgr_bus *pcibus = arg;
361        struct drvmgr_dev *newdev;
362        struct pci_dev_info *pciinfo;
363        int i, type;
364        struct pcibus_res *pcibusres;
365        struct pci_res *pcires;
366
367        pci_dev_t pcidev = dev->busdevfun;
368
369        DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev));
370
371        /* Allocate a device */
372        drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info));
373        newdev->next = NULL;
374        newdev->parent = pcibus; /* Ourselfs */
375        newdev->minor_drv = 0;
376        newdev->minor_bus = 0;
377        newdev->priv = NULL;
378        newdev->drv = NULL;
379        newdev->name = (char *)(newdev + 1);
380        newdev->next_in_drv = NULL;
381        newdev->bus = NULL;
382
383        /* Init PnP information, Assign Core interfaces with this device */
384        pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24);
385
386        /* Read Device and Vendor */
387        pciinfo->id.vendor = dev->vendor;
388        pciinfo->id.device = dev->device;
389        pciinfo->id.subvendor = dev->subvendor;
390        pciinfo->id.subdevice = dev->subdevice;
391        pciinfo->rev = dev->classrev & 0xff;
392        pciinfo->id.class = (dev->classrev >> 8) & 0xffffff;
393
394        /* Read IRQ information set by PCI layer */
395        pciinfo->irq = dev->sysirq;
396
397        /* Save Location on PCI bus */
398        pciinfo->pcidev = pcidev;
399
400        /* Connect device with PCI data structure */
401        pciinfo->pci_device = dev;
402
403        /* Build resources so that PCI device drivers doesn't have to scan
404         * configuration space themselves, also the address is translated
405         * into CPU accessible addresses.
406         */
407        for (i = 0; i < PCIDEV_RES_CNT; i++) {
408                pcibusres = &pciinfo->resources[i];
409                pcires = &dev->resources[i];
410                type = pcires->flags & PCI_RES_TYPE_MASK;
411                if (type == 0 || (pcires->flags & PCI_RES_FAIL))
412                        continue; /* size=0 */
413
414                pcibusres->address = pcires->start;
415                if (pci_pci2cpu(&pcibusres->address, type))
416                        continue; /* size=0 */
417                pcibusres->res = pcires;
418                pcibusres->size = pcires->end - pcires->start;
419        }
420
421        /* Connect device with PCI information */
422        newdev->businfo = (void *)pciinfo;
423
424        /* Create Device Name */
425        sprintf(newdev->name, "PCI_%x:%x:%x_%04x:%04x",
426                PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev),
427                pciinfo->id.vendor, pciinfo->id.device);
428
429        /* Register New Device */
430        drvmgr_dev_register(newdev);
431
432        return 0;
433}
434
435#else
436
437static int pcibus_dev_register(pci_dev_t pcidev, void *arg)
438{
439        struct drvmgr_bus *pcibus = arg;
440        struct drvmgr_dev *newdev;
441        struct pci_dev_info *pciinfo;
442
443        DBG("PCI DEV REGISTER: %x:%x:%x\n", PCI_DEV_EXPAND(pcidev));
444
445        /* Allocate a device */
446        drvmgr_alloc_dev(&newdev, 24 + sizeof(struct pci_dev_info));
447        newdev->next = NULL;
448        newdev->parent = pcibus; /* Ourselfs */
449        newdev->minor_drv = 0;
450        newdev->minor_bus = 0;
451        newdev->priv = NULL;
452        newdev->drv = NULL;
453        newdev->name = (char *)(newdev + 1);
454        newdev->next_in_drv = NULL;
455        newdev->bus = NULL;
456
457        /* Init PnP information, Assign Core interfaces with this device */
458        pciinfo = (struct pci_dev_info *)((char *)(newdev + 1) + 24);
459
460        /* Read Device and Vendor */
461        pci_cfg_r16(pcidev, PCIR_VENDOR, &pciinfo->id.vendor);
462        pci_cfg_r16(pcidev, PCIR_DEVICE, &pciinfo->id.device);
463        pci_cfg_r32(pcidev, PCIR_REVID, &pciinfo->id.class);
464        pciinfo->rev = pciinfo->id.class & 0xff;
465        pciinfo->id.class = pciinfo->id.class >> 8;
466
467        /* Devices have subsytem device and vendor ID */
468        if ((pciinfo->id.class >> 8) != PCID_PCI2PCI_BRIDGE) {
469                pci_cfg_r16(pcidev, PCIR_SUBVEND_0,
470                                                        &pciinfo->id.subvendor);
471                pci_cfg_r16(pcidev, PCIR_SUBDEV_0, &pciinfo->id.subdevice);
472        } else {
473                pciinfo->id.subvendor = 0;
474                pciinfo->id.subdevice = 0;
475        }
476
477        /* Read IRQ information set by PCI layer */
478        pci_cfg_r8(pcidev, PCIR_INTLINE, &pciinfo->irq);
479
480        /* Save Location */
481        pciinfo->pcidev = pcidev;
482
483        /* There is no way we can know this information this way */
484        pciinfo->pci_device = NULL;
485
486        /* Connect device with PCI information */
487        newdev->businfo = (void *)pciinfo;
488
489        /* Create Device Name */
490        sprintf(newdev->name, "PCI_%d:%d:%d_%04x:%04x",
491                PCI_DEV_BUS(pcidev), PCI_DEV_SLOT(pcidev), PCI_DEV_FUNC(pcidev),
492                pciinfo->id.vendor, pciinfo->id.device);
493
494        /* Register New Device */
495        drvmgr_dev_register(newdev);
496
497        return 0;
498}
499
500#endif
501
502/* Register all AMBA devices available on the AMBAPP bus */
503static int pcibus_devs_register(struct drvmgr_bus *bus)
504{
505        /* return value 0=DRVMGR_OK works with pci_for_each/pci_for_each_dev */
506#ifdef USE_PCI_CFG_LIB
507        /* Walk the PCI device tree in RAM */
508        return pci_for_each_dev(pcibus_dev_register, bus);
509#else
510        /* Scan PCI Configuration space */
511        return pci_for_each(pcibus_dev_register, bus);
512#endif
513}
514
515/*** DEVICE FUNCTIONS ***/
516
517int pcibus_register(struct drvmgr_dev *dev, struct pcibus_config *config)
518{
519        struct pcibus_priv *priv;
520        int i, fid, rc;
521
522        DBG("PCI BUS: initializing\n");
523
524        /* Create BUS */
525        drvmgr_alloc_bus(&dev->bus, sizeof(struct pcibus_priv));
526        dev->bus->bus_type = DRVMGR_BUS_TYPE_PCI;
527        dev->bus->next = NULL;
528        dev->bus->dev = dev;
529        dev->bus->children = NULL;
530        dev->bus->ops = &pcibus_ops;
531        dev->bus->dev_cnt = 0;
532        dev->bus->reslist = NULL;
533        dev->bus->maps_up = config->maps_up;
534        dev->bus->maps_down = config->maps_down;
535        dev->bus->funcs = &pcibus_funcs[0];
536
537        /* Copy function definitions from PCI Layer */
538        for (i=0; i<6; i++) {
539                fid = pcibus_funcs[i].funcid;
540                rc = pci_access_func(RW_DIR(fid), RW_SIZE(fid),
541                                &pcibus_funcs[i].func, PCI_LITTLE_ENDIAN, 3);
542                if (rc != 0)
543                        DBG("PCI BUS: MEMREG 0x%x function not defined\n", fid);
544        }
545
546        /* Add resource configuration if user overrided the default empty cfg */
547        if (pcibus_drv_resources.resource[0].drv_id != 0)
548                drvmgr_bus_res_add(dev->bus, &pcibus_drv_resources);
549
550        /* Init BUS private structures */
551        priv = (struct pcibus_priv *)(dev->bus + 1);
552        dev->bus->priv = priv;
553
554        /* Register BUS */
555        drvmgr_bus_register(dev->bus);
556
557        return DRVMGR_OK;
558}
559
560/*** BUS INITIALIZE FUNCTIONS ***/
561
562int pcibus_bus_init1(struct drvmgr_bus *bus)
563{
564        return pcibus_devs_register(bus);
565}
Note: See TracBrowser for help on using the repository browser.