source: rtems/cpukit/libpci/pci_bus.c @ 56ea46ba

4.11
Last change on this file since 56ea46ba was 56ea46ba, checked in by Daniel Hellstrom <daniel@…>, on Apr 7, 2015 at 12:25:23 PM

DRVMGR: PCI BUS converted to BSD header

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