source: rtems/cpukit/libpci/pci_cfg_auto.c

Last change on this file was 1b33b9d, checked in by Joel Sherrill <joel@…>, on 03/18/22 at 15:03:47

cpukit/libpci: Change license to BSD-2

Updates #3053.

  • Property mode set to 100644
File size: 27.8 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/*  PCI (Auto) configuration Library. Setup PCI configuration space and IRQ.
4 *
5 *  COPYRIGHT (c) 2010 Cobham Gaisler AB.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <rtems.h>
30#include <stdlib.h>
31#include <rtems/bspIo.h>
32#include <string.h>
33
34/* Configure headers */
35#define PCI_CFG_AUTO_LIB
36
37#include <pci.h>
38#include <pci/access.h>
39#include <pci/cfg.h>
40
41#include "pci_internal.h"
42
43/* #define DEBUG */
44
45#ifdef DEBUG
46#define DBG(x...) printk(x)
47#else
48#define DBG(x...)
49#endif
50
51/* PCI Library
52 * (For debugging it might be good to use other functions or the driver's
53 *  directly)
54 */
55#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args)
56#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args)
57#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args)
58#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args)
59#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args)
60#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args)
61
62int pci_config_auto_initialized = 0;
63
64/* Configuration setup */
65struct pci_auto_setup pci_auto_cfg;
66
67/* Insert BAR into the sorted resources list. The BARs are sorted on the
68 * BAR size/alignment need.
69 */
70static void pci_res_insert(struct pci_res **root, struct pci_res *res)
71{
72        struct pci_res *curr, *last;
73        unsigned long curr_size_resulting_boundary, size_resulting_boundary;
74        unsigned long boundary, size;
75
76        res->start = 0;
77        res->end = 0;
78        boundary = res->boundary;
79        size = res->size;
80
81        /* Insert the resources depending on the boundary needs
82         * Normally the boundary=size of the BAR, however when
83         * PCI bridges are involved the bridge's boundary may be
84         * smaller than the size due to the fact that a bridge
85         * may have different-sized BARs behind, the largest BAR
86         * (also the BAR with the largest boundary) will decide
87         * the alignment need.
88         */
89        last = NULL;
90        curr = *root;
91
92        /* Order List after boundary, the boundary is maintained
93         * when the size is on an equal boundary, normally it is
94         * but may not be with bridges. So in second hand it is
95         * sorted after resulting boundary - the boundary after
96         * the resource.
97         */
98        while (curr && (curr->boundary >= boundary)) {
99                if (curr->boundary == boundary) {
100                        /* Find Resulting boundary of size */
101                        size_resulting_boundary = 1;
102                        while ((size & size_resulting_boundary) == 0)
103                                size_resulting_boundary =
104                                        size_resulting_boundary << 1;
105
106                        /* Find Resulting boundary of curr->size */
107                        curr_size_resulting_boundary = 1;
108                        while ((curr->size & curr_size_resulting_boundary) == 0)
109                                curr_size_resulting_boundary =
110                                        curr_size_resulting_boundary << 1;
111
112                        if (size_resulting_boundary >=
113                            curr_size_resulting_boundary)
114                                break;
115                }
116                last = curr;
117                curr = curr->next;
118        }
119
120        if (last == NULL) {
121                /* Insert first in list */
122                res->next = *root;
123                *root = res;
124        } else {
125                last->next = res;
126                res->next = curr;
127        }
128}
129
130#ifdef DEBUG
131void pci_res_list_print(struct pci_res *root)
132{
133        if (root == NULL)
134                return;
135
136        printf("RESOURCE LIST:\n");
137        while (root) {
138                printf(" SIZE: 0x%08x, BOUNDARY: 0x%08x\n", root->size,
139                                                                root->boundary);
140                root = root->next;
141        }
142}
143#endif
144
145/* Reorder a size/alignment ordered resources list. The idea is to
146 * avoid unused due to alignment/size restriction.
147 *
148 * NOTE: The first element is always untouched.
149 * NOTE: If less than three elements in list, nothing will be done
150 *
151 * Normally a BAR has the same alignment requirements as the size of the
152 * BAR. However, when bridges are involved the alignment need may be smaller
153 * than the size, because a bridge resource consist or multiple BARs.
154 * For example, say that a bridge with a 256Mb and a 16Mb BAR is found, then
155 * the alignment is required to be 256Mb but the size 256+16Mb.
156 *
157 * In order to minimize dead space on the bus, the boundary ordered list
158 * is reordered, example:
159 *  BUS0
160 *  |            BUS1
161 *  |------------|
162 *  |            |-- BAR0: SIZE=256Mb, ALIGNMENT=256MB
163 *  |            |-- BAR1: SIZE=16Mb, ALIGNMENT=16MB
164 *  |            |
165 *  |            |
166 *  |            |
167 *  |            |          BUS2 (BAR_BRIDGE1: SIZE=256+16, ALIGNEMENT=256)
168 *  |            |----------|
169 *  |            |          |-- BAR2: SIZE=256Mb, ALIGNMENT=256Mb
170 *  |            |          |-- BAR3: SIZE=16Mb, ALIGNMENT=16MB
171 *
172 * A alignment/boundary ordered list of BUS1 will look like:
173 *      - BAR_BRIDGE1
174 *      - BAR0            (ALIGMENT NEED 256Mb)
175 *      - BAR1
176 *
177 * However, Between BAR_BRIDGE1 and BAR0 will be a unused hole of 256-16Mb.
178 * We can put BAR1 before BAR0 to avoid the problem.
179 */
180static void pci_res_reorder(struct pci_res *root)
181{
182        struct pci_res *curr, *last, *curr2, *last2;
183        unsigned int start, start_next, hole_size, hole_boundary;
184
185        if (root == NULL)
186                return;
187
188        /* Make up a start address with the boundary of the
189         * First element.
190         */
191        start = root->boundary + root->size;
192        last = root;
193        curr = root->next;
194        while (curr) {
195
196                /* Find start address of resource */
197                start_next = (start + (curr->boundary - 1)) &
198                                        ~(curr->boundary - 1);
199
200                /* Find hole size, the unsed space in between last resource
201                 * and next */
202                hole_size = start_next - start;
203
204                /* Find Boundary of START */
205                hole_boundary = 1;
206                while ((start & hole_boundary) == 0)
207                        hole_boundary = hole_boundary<<1;
208
209                /* Detect dead hole */
210                if (hole_size > 0) {
211                        /* Step through list and try to find a resource that
212                         * can fit into hole. Take into account hole start
213                         * boundary and hole size.
214                         */
215                        last2 = curr;
216                        curr2 = curr->next;
217                        while (curr2) {
218                                if ((curr2->boundary <= hole_boundary) &&
219                                         (curr2->size <= hole_size)) {
220                                        /* Found matching resource. Move it
221                                         * first in the hole. Then rescan, now
222                                         * that the hole has changed in
223                                         * size/boundary.
224                                         */
225                                        last2->next = curr2->next;
226                                        curr2->next = curr;
227                                        last->next = curr2;
228
229                                        /* New Start address */
230                                        start_next = (start +
231                                                     (curr2->boundary - 1)) &
232                                                     ~(curr2->boundary - 1);
233                                        /* Since we inserted the resource before
234                                         * curr we need to re-evaluate curr one
235                                         * more, more resources may fit into the
236                                         * shrunken hole.
237                                         */
238                                        curr = curr2;
239                                        break;
240                                }
241                                last2 = curr2;
242                                curr2 = curr2->next;
243                        }
244                }
245
246                /* No hole or nothing fit into hole. */
247                start = start_next;
248
249                last = curr;
250                curr = curr->next;
251        }
252}
253
254/* Find the total size required in PCI address space needed by a resource list*/
255static unsigned int pci_res_size(struct pci_res *root)
256{
257        struct pci_res *curr;
258        unsigned int size;
259
260        /* Get total size of all resources */
261        size = 0;
262        curr = root;
263        while (curr) {
264                size = (size + (curr->boundary - 1)) & ~(curr->boundary - 1);
265                size += curr->size;
266                curr = curr->next;
267        }
268
269        return size;
270}
271
272#if 0 /* not used for now */
273/* Free a device and secondary bus if device is a bridge */
274static void pci_dev_free(struct pci_dev *dev)
275{
276        struct pci_dev *subdev;
277        struct pci_bus *bus;
278
279        if (dev->flags & PCI_DEV_BRIDGE) {
280                bus = (struct pci_bus *)dev;
281                for (subdev = bus->devs; subdev ; subdev = subdev->next)
282                        pci_dev_free(dev);
283        }
284
285        free(dev);
286}
287#endif
288
289static struct pci_dev *pci_dev_create(int isbus)
290{
291        void *ptr;
292        int size;
293
294        if (isbus)
295                size = sizeof(struct pci_bus);
296        else
297                size = sizeof(struct pci_dev);
298
299        ptr = calloc(1, size);
300        if (!ptr)
301                rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
302        return ptr;
303}
304
305static void pci_find_devs(struct pci_bus *bus)
306{
307        uint32_t id, tmp;
308        uint8_t header;
309        int slot, func, fail;
310        struct pci_dev *dev, **listptr;
311        struct pci_bus *bridge;
312        pci_dev_t pcidev;
313
314        DBG("Scanning bus %d\n", bus->num);
315
316        listptr = &bus->devs;
317        for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
318
319                /* Slot address */
320                pcidev = PCI_DEV(bus->num, slot, 0);
321
322                for (func = 0; func <= PCI_FUNCMAX; func++, pcidev++) {
323
324                        fail = PCI_CFG_R32(pcidev, PCIR_VENDOR, &id);
325                        if (fail || id == 0xffffffff || id == 0) {
326                                /*
327                                 * This slot is empty
328                                 */
329                                if (func == 0)
330                                        break;
331                                else
332                                        continue;
333                        }
334
335                        DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n",
336                                                        id, bus, slot, func);
337
338                        /* Set command to reset values, it disables bus
339                         * mastering and address responses.
340                         */
341                        PCI_CFG_W16(pcidev, PCIR_COMMAND, 0);
342
343                        /* Clear any already set status bits */
344                        PCI_CFG_W16(pcidev, PCIR_STATUS, 0xf900);
345
346                        /* Set latency timer to 64 */
347                        PCI_CFG_W8(pcidev, PCIR_LATTIMER, 64);
348
349                        PCI_CFG_R32(pcidev, PCIR_REVID, &tmp);
350                        tmp >>= 16;
351                        dev = pci_dev_create(tmp == PCID_PCI2PCI_BRIDGE);
352                        *listptr = dev;
353                        listptr = &dev->next;
354
355                        dev->busdevfun = pcidev;
356                        dev->bus = bus;
357                        PCI_CFG_R16(pcidev, PCIR_VENDOR, &dev->vendor);
358                        PCI_CFG_R16(pcidev, PCIR_DEVICE, &dev->device);
359                        PCI_CFG_R32(pcidev, PCIR_REVID, &dev->classrev);
360
361                        if (tmp == PCID_PCI2PCI_BRIDGE) {
362                                DBG("Found PCI-PCI Bridge 0x%x at "
363                                    "(bus %x, slot %x, func %x)\n",
364                                    id, bus, slot, func);
365                                dev->flags = PCI_DEV_BRIDGE;
366                                dev->subvendor = 0;
367                                dev->subdevice = 0;
368                                bridge = (struct pci_bus *)dev;
369                                bridge->num = bus->sord + 1;
370                                bridge->pri = bus->num;
371                                bridge->sord = bus->sord + 1;
372
373                                /* Configure bridge (no support for 64-bit) */
374                                PCI_CFG_W32(pcidev, 0x28, 0);
375                                PCI_CFG_W32(pcidev, 0x2C, 0);
376                                tmp = (64 << 24) | (0xff << 16) |
377                                      (bridge->num << 8) | bridge->pri;
378                                PCI_CFG_W32(pcidev, PCIR_PRIBUS_1, tmp);
379
380                                /* Scan Secondary Bus */
381                                pci_find_devs(bridge);
382
383                                /* sord might have been updated */
384                                PCI_CFG_W8(pcidev, 0x1a, bridge->sord);
385                                bus->sord = bridge->sord;
386
387                                DBG("PCI-PCI BRIDGE: Primary %x, Secondary %x, "
388                                    "Subordinate %x\n",
389                                    bridge->pri, bridge->num, bridge->sord);
390                        } else {
391                                /* Disable Cardbus CIS Pointer */
392                                PCI_CFG_W32(pcidev, PCIR_CIS, 0);
393
394                                /* Devices have subsytem device and vendor ID */
395                                PCI_CFG_R16(pcidev, PCIR_SUBVEND_0,
396                                                        &dev->subvendor);
397                                PCI_CFG_R16(pcidev, PCIR_SUBDEV_0,
398                                                        &dev->subdevice);
399                        }
400
401                        /* Stop if not a multi-function device */
402                        if (func == 0) {
403                                pci_cfg_r8(pcidev, PCIR_HDRTYPE, &header);
404                                if ((header & PCIM_MFDEV) == 0)
405                                        break;
406                        }
407                }
408        }
409}
410
411static void pci_find_bar(struct pci_dev *dev, int bar)
412{
413        uint32_t size, disable, mask;
414        struct pci_res *res = &dev->resources[bar];
415        pci_dev_t pcidev = dev->busdevfun;
416        int ofs;
417#ifdef DEBUG
418        char *str;
419#define DBG_SET_STR(str, val) str = (val)
420#else
421#define DBG_SET_STR(str, val)
422#endif
423
424        DBG("Bus: %x, Slot: %x, function: %x, bar%d\n",
425                PCI_DEV_EXPAND(pcidev), bar);
426
427        res->bar = bar;
428        if (bar == DEV_RES_ROM) {
429                if (dev->flags & PCI_DEV_BRIDGE)
430                        ofs = PCIR_BIOS_1;
431                else
432                        ofs = PCIR_BIOS;
433                disable = 0; /* ROM BARs have a unique enable bit per BAR */
434        } else {
435                ofs = PCIR_BAR(0) + (bar << 2);
436                disable = pci_invalid_address;
437        }
438
439        PCI_CFG_W32(pcidev, ofs, 0xffffffff);
440        PCI_CFG_R32(pcidev, ofs, &size);
441        PCI_CFG_W32(pcidev, ofs, disable);
442
443        if (size == 0 || size == 0xffffffff)
444                return;
445        if (bar == DEV_RES_ROM) {
446                mask = PCIM_BIOS_ADDR_MASK;
447                DBG_SET_STR(str, "ROM");
448                if (dev->bus->flags & PCI_BUS_MEM)
449                        res->flags = PCI_RES_MEM;
450                else
451                        res->flags = PCI_RES_MEMIO;
452        } else if (((size & 0x1) == 0) && (size & 0x6)) {
453                /* unsupported Memory type */
454                PCI_CFG_W32(pcidev, ofs, 0);
455                return;
456        } else {
457                mask = ~0xf;
458                if (size & 0x1) {
459                        /* I/O */
460                        mask = ~0x3;
461                        res->flags = PCI_RES_IO;
462                        DBG_SET_STR(str, "I/O");
463                        if (size & 0xffff0000)
464                                res->flags |= PCI_RES_IO32;
465                        /* Limit size of I/O space to 256 byte */
466                        size |= 0xffffff00;
467                        if ((dev->bus->flags & PCI_BUS_IO) == 0) {
468                                res->flags |= PCI_RES_FAIL;
469                                dev->flags |= PCI_DEV_RES_FAIL;
470                        }
471                } else {
472                        /* Memory. We convert Prefetchable Memory BARs to Memory
473                         * BARs in case the Bridge does not support prefetchable
474                         * memory.
475                         */
476                        if ((size & 0x8) && (dev->bus->flags & PCI_BUS_MEM)) {
477                                /* Prefetchable and Bus supports it */
478                                res->flags = PCI_RES_MEM;
479                                DBG_SET_STR(str, "MEM");
480                        } else {
481                                res->flags = PCI_RES_MEMIO;
482                                DBG_SET_STR(str, "MEMIO");
483                        }
484                }
485        }
486        size &= mask;
487        res->size = ~size + 1;
488        res->boundary = ~size + 1;
489
490        DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n",
491                PCI_DEV_EXPAND(pcidev), str, bar, res->size);
492}
493
494static int pci_find_res_dev(struct pci_dev *dev, void *unused)
495{
496        struct pci_bus *bridge;
497        uint32_t tmp;
498        uint16_t tmp16;
499        pci_dev_t pcidev = dev->busdevfun;
500        int i, maxbars;
501
502        if (dev->flags & PCI_DEV_BRIDGE) {
503                /* PCI-PCI Bridge */
504                bridge = (struct pci_bus *)dev;
505
506                /* Only 2 Bridge BARs */
507                maxbars = 2;
508
509                /* Probe Bridge Spaces (MEMIO space always implemented), the
510                 * probe disables all space-decoding at the same time
511                 */
512                PCI_CFG_W32(pcidev, 0x30, 0);
513                PCI_CFG_W16(pcidev, 0x1c, 0x00f0);
514                PCI_CFG_R16(pcidev, 0x1c, &tmp16);
515                if (tmp16 != 0) {
516                        bridge->flags |= PCI_BUS_IO;
517                        if (tmp16 & 0x1)
518                                bridge->flags |= PCI_BUS_IO32;
519                }
520
521                PCI_CFG_W32(pcidev, 0x24, 0x0000ffff);
522                PCI_CFG_R32(pcidev, 0x24, &tmp);
523                if (tmp != 0)
524                        bridge->flags |= PCI_BUS_MEM;
525
526                PCI_CFG_W32(pcidev, 0x20, 0x0000ffff);
527                bridge->flags |= PCI_BUS_MEMIO;
528        } else {
529                /* Normal PCI Device as max 6 BARs */
530                maxbars = 6;
531        }
532
533        /* Probe BARs */
534        for (i = 0; i < maxbars; i++)
535                pci_find_bar(dev, i);
536        pci_find_bar(dev, DEV_RES_ROM);
537
538        return 0;
539}
540
541static int pci_add_res_dev(struct pci_dev *dev, void *arg);
542
543static void pci_add_res_bus(struct pci_bus *bus, int type)
544{
545        int tindex = type - 1;
546
547        /* Clear old resources */
548        bus->busres[tindex] = NULL;
549
550        /* Add resources of devices behind bridge if bridge supports
551         * resource type. If MEM space not supported by bridge, they are
552         * converted to MEMIO in the process.
553         */
554        if (!((type == PCI_BUS_IO) && ((bus->flags & PCI_BUS_IO) == 0))) {
555                pci_for_each_child(bus, pci_add_res_dev, (void *)type, 0);
556
557                /* Reorder Bus resources to fit more optimally (avoid dead
558                 * PCI space). Currently they are sorted by boundary and size.
559                 *
560                 * This is especially important when multiple buses (bridges)
561                 * are present.
562                 */
563                pci_res_reorder(bus->busres[tindex]);
564        }
565}
566
567static int pci_add_res_dev(struct pci_dev *dev, void *arg)
568{
569        int tindex, type = (int)arg;
570        struct pci_bus *bridge;
571        struct pci_res *res, *first_busres;
572        int i;
573        uint32_t bbound;
574
575        /* Type index in Bus resource */
576        tindex = type - 1;
577
578        if (dev->flags & PCI_DEV_BRIDGE) {
579                /* PCI-PCI Bridge. Add all sub-bus resources first */
580                bridge = (struct pci_bus *)dev;
581
582                /* Add all child device's resources to this type */
583                pci_add_res_bus(bridge, type);
584
585                /* Propagate the resources from child bus to BAR on
586                 * this bus, by adding a "fake" BAR per type.
587                 */
588                res = &bridge->dev.resources[BUS_RES_START + tindex];
589                res->bar = BUS_RES_START + tindex;
590                res->start = 0;
591                res->end = 0;
592                res->flags = 0; /* mark BAR resource not available */
593                first_busres = bridge->busres[tindex];
594                if (first_busres) {
595                        res->flags = type;
596                        res->size = pci_res_size(first_busres);
597                        res->boundary = first_busres->boundary;
598                        if (type == PCI_RES_IO) {
599                                bbound = 0x1000; /* Bridge I/O min 4KB */
600                        } else {
601                                bbound = 0x100000; /* Bridge MEM min 1MB */
602
603                                /* Convert MEM to MEMIO if not supported by
604                                 * this bridge
605                                 */
606                                if ((bridge->flags & PCI_BUS_MEM) == 0)
607                                        res->flags = PCI_RES_MEMIO;
608                        }
609                        /* Fulfil minimum bridge boundary */
610                        if (res->boundary < bbound)
611                                res->boundary = bbound;
612                        /* Make sure that size is atleast bridge boundary */
613                        if (res->size > bbound && (res->size & (bbound-1)))
614                                res->size = (res->size | (bbound-1)) + 1;
615                }
616        }
617
618        /* Normal PCI Device as max 6 BARs and a ROM Bar.
619         * Insert BARs into the sorted resource list.
620         */
621        for (i = 0; i < DEV_RES_CNT; i++) {
622                res = &dev->resources[i];
623                if ((res->flags & PCI_RES_TYPE_MASK) != type)
624                        continue;
625                pci_res_insert(&dev->bus->busres[tindex], res);
626        }
627
628        return 0;
629}
630
631/* Function assumes that base is properly aligned to the requirement of the
632 * largest BAR in the system.
633 */
634static uint32_t pci_alloc_res(struct pci_bus *bus, int type,
635                            uint32_t start, uint32_t end)
636{
637        struct pci_dev *dev;
638        struct pci_res *res, **prev_next;
639        unsigned long starttmp;
640        struct pci_bus *bridge;
641        int removed, sec_type;
642
643        /* The resources are sorted on their size (size and alignment is the
644         * same)
645         */
646        prev_next = &bus->busres[type - 1];
647        while ((res = *prev_next) != NULL) {
648
649                dev = RES2DEV(res);
650                removed = 0;
651
652                /* Align start to this reource's need, only needed after
653                 * a bridge resource has been allocated.
654                 */
655                starttmp = (start + (res->boundary-1)) & ~(res->boundary-1);
656
657                if ((starttmp + res->size - 1) > end) {
658                        /* Not enough memory available for this resource */
659                        printk("PCI[%x:%x:%x]: DEV BAR%d (%d): no resource "
660                               "assigned\n",
661                               PCI_DEV_EXPAND(dev->busdevfun),
662                               res->bar, res->flags & PCI_RES_TYPE_MASK);
663                        res->start = res->end = 0;
664
665                        /* If this resources is a bridge window to the
666                         * secondary bus, the secondary resources are not
667                         * changed which has the following effect:
668                         *  I/O    :  Will never be assigned
669                         *  MEMIO  :  Will never be assigned
670                         *  MEM    :  Will stay marked as MEM, but bridge window
671                         *            is changed into MEMIO, when the window is
672                         *            assigned a MEMIO address the secondary
673                         *            resources will also be assigned.
674                         */
675
676                        if (type == PCI_RES_MEM) {
677                                /* Try prefetchable as non-prefetchable mem */
678                                res->flags &= ~PCI_RES_MEM_PREFETCH;
679                                /* Remove resource from MEM list, ideally we
680                                 * should regenerate this list in order to fit
681                                 * the comming BARs more optimially...
682                                 */
683                                *prev_next = res->next;
684                                /* We should not update prev_next here since
685                                 * we just removed the resource from the list
686                                 */
687                                removed = 1;
688                        } else {
689                                res->flags |= PCI_RES_FAIL;
690                                dev->flags |= PCI_DEV_RES_FAIL;
691                        }
692                } else {
693                        start = starttmp;
694
695                        res->start = start;
696                        res->end = start + res->size;
697
698                        /* "Virtual BAR" on a bridge? A bridge resource need all
699                         * its child devices resources allocated
700                         */
701                        if ((res->bar != DEV_RES_ROM) &&
702                            (dev->flags & PCI_DEV_BRIDGE) &&
703                            (res->bar >= BUS_RES_START)) {
704                                bridge = (struct pci_bus *)dev;
705                                /* If MEM bar was changed into a MEMIO the
706                                 * secondary MEM resources are still set to MEM,
707                                 */
708                                if (type == PCI_BUS_MEMIO &&
709                                    res->bar == BRIDGE_RES_MEM)
710                                        sec_type = PCI_RES_MEM;
711                                else
712                                        sec_type = type;
713
714                                pci_alloc_res(bridge, sec_type, res->start,
715                                                res->end);
716                        }
717
718                        start += res->size;
719                }
720                if (removed == 0)
721                        prev_next = &res->next;
722        }
723
724        return start;
725}
726
727static void pci_set_bar(struct pci_dev *dev, int residx)
728{
729        uint32_t tmp;
730        uint16_t tmp16;
731        pci_dev_t pcidev;
732        struct pci_res *res;
733        int is_bridge, ofs;
734
735        res = &dev->resources[residx];
736        pcidev = dev->busdevfun;
737
738        if ((res->flags == 0) || (res->flags & PCI_RES_FAIL))
739                return;
740
741        is_bridge = dev->flags & PCI_DEV_BRIDGE;
742
743        if (res->bar == DEV_RES_ROM) {
744                /* ROM: 32-bit prefetchable memory BAR */
745                if (is_bridge)
746                        ofs = PCIR_BIOS_1;
747                else
748                        ofs = PCIR_BIOS;
749                PCI_CFG_W32(pcidev, ofs, res->start | PCIM_BIOS_ENABLE);
750                DBG("PCI[%x:%x:%x]: ROM BAR: 0x%x-0x%x\n",
751                        PCI_DEV_EXPAND(pcidev), res->start, res->end);
752        } else if (is_bridge && (res->bar == BRIDGE_RES_IO)) {
753                /* PCI Bridge I/O BAR */
754                DBG("PCI[%x:%x:%x]: BAR 1C: 0x%x-0x%x\n",
755                        PCI_DEV_EXPAND(pcidev), res->start, res->end);
756
757                /* Limit and Base */
758                tmp16 = ((res->end-1) & 0x0000f000) |
759                        ((res->start & 0x0000f000) >> 8);
760                tmp = ((res->end-1) & 0xffff0000) | (res->start >> 16);
761
762                DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x [0x30: 0x%x]\n",
763                        PCI_DEV_EXPAND(pcidev), 0x1C, tmp, tmp2);
764                PCI_CFG_W16(pcidev, 0x1C, tmp16);
765                PCI_CFG_W32(pcidev, 0x30, tmp);
766        } else if (is_bridge && (res->bar >= BRIDGE_RES_MEMIO)) {
767                /* PCI Bridge MEM and MEMIO Space */
768
769                /* Limit and Base */
770                tmp = ((res->end-1) & 0xfff00000) | (res->start >> 16);
771
772                DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x\n",
773                        PCI_DEV_EXPAND(pcidev),
774                        0x20 + (res->bar-BRIDGE_RES_MEMIO)*4, tmp);
775                PCI_CFG_W32(pcidev, 0x20+(res->bar-BRIDGE_RES_MEMIO)*4, tmp);
776        } else {
777                /* PCI Device */
778                DBG("PCI[%x:%x:%x]: DEV BAR%d: 0x%08x\n",
779                        PCI_DEV_EXPAND(pcidev), res->bar, res->start);
780                ofs = PCIR_BAR(0) + res->bar*4;
781                PCI_CFG_W32(pcidev, ofs, res->start);
782        }
783
784        /* Enable Memory or I/O responses */
785        if ((res->flags & PCI_RES_TYPE_MASK) == PCI_RES_IO)
786                pci_io_enable(pcidev);
787        else
788                pci_mem_enable(pcidev);
789
790        /* Enable Master if bridge */
791        if (is_bridge)
792                pci_master_enable(pcidev);
793}
794
795static int pci_set_res_dev(struct pci_dev *dev, void *unused)
796{
797        int i, maxbars;
798
799        if (dev->flags & PCI_DEV_BRIDGE)
800                maxbars = 2 + 3; /* 2 BARs + 3 Bridge-Windows "Virtual BARs" */
801        else
802                maxbars = 6; /* Normal PCI Device as max 6 BARs. */
803
804        /* Set BAR resources with previous allocated values */
805        for (i = 0; i < maxbars; i++)
806                pci_set_bar(dev, i);
807        pci_set_bar(dev, DEV_RES_ROM);
808
809        return 0;
810}
811
812/* Route IRQ through PCI-PCI Bridges */
813static int pci_route_irq(pci_dev_t dev, int irq_pin)
814{
815        int slot_grp;
816
817        if (PCI_DEV_BUS(dev) == 0)
818                return irq_pin;
819
820        slot_grp = PCI_DEV_SLOT(dev) & 0x3;
821
822        return (((irq_pin - 1) + slot_grp) & 0x3) + 1;
823}
824
825/* Put assigned system IRQ into PCI interrupt line information field.
826 * This is to make it possible for drivers to read system IRQ / Vector from
827 * configuration space later on.
828 *
829 * 1. Get Interrupt PIN
830 * 2. Route PIN to host bridge
831 * 3. Get System interrupt number assignment for PIN
832 * 4. Set Interrupt LINE
833 */
834static int pci_set_irq_dev(struct pci_dev *dev, void *cfg)
835{
836        struct pci_auto_setup *autocfg = cfg;
837        uint8_t irq_pin, irq_line, *psysirq;
838        pci_dev_t pcidev;
839
840        psysirq = &dev->sysirq;
841        pcidev = dev->busdevfun;
842        PCI_CFG_R8(pcidev, PCIR_INTPIN, &irq_pin);
843
844        /* perform IRQ routing until we reach host bridge */
845        while (dev->bus && irq_pin != 0) {
846                irq_pin = autocfg->irq_route(dev->busdevfun, irq_pin);
847                dev = &dev->bus->dev;
848        }
849
850        /* Get IRQ from PIN on PCI bus0 */
851        if (irq_pin != 0 && autocfg->irq_map)
852                irq_line = autocfg->irq_map(dev->busdevfun, irq_pin);
853        else
854                irq_line = 0;
855
856        *psysirq = irq_line;
857
858        /* Set System Interrupt/Vector for device. 0 means no-IRQ */
859        PCI_CFG_W8(pcidev, PCIR_INTLINE, irq_line);
860
861        return 0;
862}
863
864/* This routine assumes that PCI access library has been successfully
865 * initialized. All information about the PCI bus needed is found in
866 * the pci_auto_cfg structure passed on by pci_config_register().
867 *
868 * The PCI buses are enumerated as bridges are found, PCI devices are
869 * setup with BARs and IRQs, etc.
870 */
871int pci_config_auto(void)
872{
873        uint32_t end;
874        uint32_t startmemio, startmem, startio;
875        struct pci_auto_setup *autocfg = &pci_auto_cfg;
876#ifdef DEBUG
877        uint32_t endmemio, endmem, endio;
878        uint32_t start;
879#endif
880
881        if (pci_config_auto_initialized == 0)
882                return -1; /* no config given to library */
883
884#ifdef DEBUG
885        DBG("\n--- PCI MEMORY AVAILABLE ---\n");
886        if (autocfg->mem_size) {
887                start = autocfg->mem_start;
888                end = autocfg->mem_start + autocfg->mem_size - 1;
889                DBG(" MEM AVAIL [0x%08x-0x%08x]\n", start, end);
890        } else {
891                /* One big memory space */
892                DBG(" MEM share the space with MEMIO\n");
893        }
894        /* no-prefetchable memory space need separate memory space.
895         * For example PCI controller maps this region non-cachable.
896         */
897        start = autocfg->memio_start;
898        end = autocfg->memio_start + autocfg->memio_size - 1;
899        DBG(" MEMIO AVAIL [0x%08x-0x%08x]\n", start, end);
900        if (autocfg->io_size) {
901                start = autocfg->io_start;
902                end = autocfg->io_start + autocfg->io_size - 1;
903                DBG(" I/O AVAIL [0x%08x-0x%08x]\n", start, end);
904        } else {
905                DBG(" I/O Space not available\n");
906        }
907#endif
908
909        /* Init Host-Bridge */
910        memset(&pci_hb, 0, sizeof(pci_hb));
911        pci_hb.dev.flags = PCI_DEV_BRIDGE;
912        if (autocfg->memio_size <= 0)
913                return -1;
914        pci_hb.flags = PCI_BUS_MEMIO;
915        if (autocfg->mem_size)
916                pci_hb.flags |= PCI_BUS_MEM;
917        if (autocfg->io_size)
918                pci_hb.flags |= PCI_BUS_IO;
919
920        /* Find all PCI devices/functions on all buses. The buses will be
921         * enumrated (assigned a unique PCI Bus ID 0..255).
922         */
923        DBG("\n--- PCI SCANNING ---\n");
924        pci_find_devs(&pci_hb);
925        pci_bus_cnt = pci_hb.sord + 1;
926        if (pci_hb.devs == NULL)
927                return 0;
928
929        pci_system_type = PCI_SYSTEM_HOST;
930
931        /* Find all resources (MEM/MEMIO/IO BARs) of all devices/functions
932         * on all buses.
933         *
934         * Device resources behind bridges which does not support prefetchable
935         * memory are already marked as non-prefetchable memory.
936         * Devices which as I/O resources behind a bridge that do not support
937         * I/O space are marked DISABLED.
938         *
939         * All BARs and Bridge Spaces are disabled after this. Only the ones
940         * that are allocated an address are initilized later on.
941         */
942        DBG("\n\n--- PCI RESOURCES ---\n");
943        pci_for_each_dev(pci_find_res_dev, 0);
944
945        /* Add all device's resources to bus and sort them to fit in the PCI
946         * Window. The device resources are propagated upwards through bridges
947         * by adding a "virtual" BAR (boundary != BAR size).
948         *
949         * We wait with MEMIO (non-prefetchable memory) resources to after MEM
950         * resources have been allocated, so that MEM resources can be changed
951         * into MEMIO resources if not enough space.
952         */
953        pci_add_res_bus(&pci_hb, PCI_RES_IO);
954        pci_add_res_bus(&pci_hb, PCI_RES_MEM);
955
956        /* Start assigning found resource according to the sorted order. */
957
958        /* Allocate resources to I/O areas */
959        if (pci_hb.busres[BUS_RES_IO]) {
960                startio = autocfg->io_start;
961                end = startio + autocfg->io_size;
962#ifdef DEBUG
963                endio =
964#endif
965                        pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end);
966        }
967
968        /* Allocate resources to prefetchable memory */
969        if (pci_hb.busres[BUS_RES_MEM]) {
970                startmem = autocfg->mem_start;
971                end = startmem + autocfg->mem_size;
972#ifdef DEBUG
973                endmem =
974#endif
975                        pci_alloc_res(&pci_hb, PCI_RES_MEM, startmem, end);
976        }
977
978        /* Add non-prefetchable memory resources and not fitting prefetchable
979         * memory resources.
980         *
981         * Some prefetchable memory resources may not have fitted into PCI
982         * window. Prefetchable memory can be mapped into non-prefetchable
983         * memory window. The failing BARs have been marked as MEMIO instead.
984         */
985        pci_add_res_bus(&pci_hb, PCI_RES_MEMIO);
986
987        /* Allocate resources to non-prefetchable memory */
988        if (pci_hb.busres[BUS_RES_MEMIO]) {
989                startmemio = autocfg->memio_start;
990                end = startmemio + autocfg->memio_size;
991#ifdef DEBUG
992                endmemio =
993#endif
994                        pci_alloc_res(&pci_hb, PCI_RES_MEMIO, startmemio, end);
995        }
996
997        DBG("\n--- PCI ALLOCATED SPACE RANGES ---\n");
998        DBG(" MEM NON-PREFETCHABLE: [0x%08x-0x%08x]\n", startmemio, endmemio);
999        DBG(" MEM PREFETCHABLE:     [0x%08x-0x%08x]\n", startmem, endmem);
1000        DBG(" I/O:                  [0x%08x-0x%08x]\n", startio, endio);
1001
1002        /* Set all allocated BARs and Bridge Windows */
1003        pci_for_each_dev(pci_set_res_dev, NULL);
1004
1005        /* Initialize IRQs of all devices. According to the PCI-PCI bridge
1006         * specification the IRQs are routed differently depending on slot
1007         * number. Drivers can override the default routing if a motherboard
1008         * requires it.
1009         */
1010        if ((autocfg->options & CFGOPT_NOSETUP_IRQ) == 0) {
1011                if (autocfg->irq_route == NULL) /* use standard irq routing */
1012                        autocfg->irq_route = pci_route_irq;
1013                pci_for_each_dev(pci_set_irq_dev, autocfg);
1014        }
1015
1016        DBG("PCI resource allocation done\n");
1017
1018        return 0;
1019}
1020
1021void pci_config_auto_register(void *config)
1022{
1023        pci_config_auto_initialized = 1;
1024        memcpy(&pci_auto_cfg, config, sizeof(struct pci_auto_setup));
1025}
Note: See TracBrowser for help on using the repository browser.