source: rtems/cpukit/libpci/pci_bus.c @ 1c5a7e5

4.115
Last change on this file since 1c5a7e5 was 1c5a7e5, checked in by Daniel Hellstrom <daniel@…>, on 11/28/11 at 09:16:09

LIBPCI: added DRVMGR model for PCI bus

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