source: rtems/cpukit/libpci/pci_bus.c @ 0decc806

4.115
Last change on this file since 0decc806 was e189f241, checked in by Daniel Hellstrom <daniel@…>, on 04/08/15 at 08:00:48

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