source: rtems/cpukit/libpci/pci_cfg_read.c @ a31845f7

4.11
Last change on this file since a31845f7 was a31845f7, checked in by Daniel Hellstrom <daniel@…>, on Nov 28, 2011 at 9:11:10 AM

LIBPCI: added PCI layer to cpukit/libpci

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/*  Read current PCI configuration that bootloader or BIOS has already setup
2 *  and initialize the PCI structures.
3 *
4 *  COPYRIGHT (c) 2010.
5 *  Cobham Gaisler AB.
6 *
7 *  The license and distribution terms for this file may be
8 *  found in the file LICENSE in this distribution or at
9 *  http://www.rtems.com/license/LICENSE.
10 */
11
12#include <rtems.h>
13#include <stdlib.h>
14#include <rtems/bspIo.h>
15#include <pci/cfg.h>
16#include <pci/access.h>
17
18/* PCI Library
19 * (For debugging it might be good to use other functions or the driver's
20 *  directly)
21 */
22#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args)
23#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args)
24#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args)
25#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args)
26#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args)
27#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args)
28
29#ifdef DEBUG
30#define DBG(args...)    printk(args)
31#else
32#define DBG(args...)
33#endif
34
35/* Number of buses */
36extern int pci_bus_cnt;
37
38/* The Host Bridge bus is initialized here */
39extern struct pci_bus pci_hb;
40
41static struct pci_dev *pci_dev_create(int isbus)
42{
43        void *ptr;
44        int size;
45
46        if (isbus)
47                size = sizeof(struct pci_bus);
48        else
49                size = sizeof(struct pci_dev);
50
51        ptr = malloc(size);
52        if (!ptr)
53                rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
54        memset(ptr, 0, size);
55        return ptr;
56}
57
58/* Check if address is accessible from host */
59static int pci_read_addressable(struct pci_dev *dev, struct pci_res *res)
60{
61        struct pci_bus *bus = dev->bus;
62        int type = res->flags & PCI_RES_TYPE_MASK;
63        struct pci_res *range0, *range1;
64
65        if (type == PCI_BUS_IO && (bus->flags & PCI_BUS_IO) == 0)
66                return 0;
67
68        /* Assume that host bridge can access all */
69        if (bus->pri == 0)
70                return 1;
71
72        range1 = NULL;
73        switch (type) {
74        case PCI_RES_IO:
75                range0 = &bus->dev.resources[BRIDGE_RES_IO];
76                break;
77        case PCI_RES_MEM:
78                range1 = &bus->dev.resources[BRIDGE_RES_MEM];
79        default:
80        case PCI_RES_MEMIO:
81                range0 = &bus->dev.resources[BRIDGE_RES_MEMIO];
82                break;
83        }
84        if ((res->start >= range0->start) && (res->end <= range0->end)) {
85                return pci_read_addressable(&bus->dev, range0);
86        } else if (range1 && (res->start >= range1->start) &&
87                        (res->end <= range1->end)) {
88                return pci_read_addressable(&bus->dev, range1);
89        }
90
91        return 0;
92}
93
94static void pci_read_bar(struct pci_dev *dev, int bar)
95{
96        uint32_t orig, size, mask;
97        struct pci_res *res = &dev->resources[bar];
98        pci_dev_t pcidev = dev->busdevfun;
99        int ofs;
100#ifdef DEBUG
101        char *str;
102#define DBG_SET_STR(str, val) str = (val)
103#else
104#define DBG_SET_STR(str, val)
105#endif
106
107        DBG("Bus: %x, Slot: %x, function: %x, bar%d\n",
108                PCI_DEV_EXPAND(pcidev), bar);
109
110        res->bar = bar;
111        if (bar == DEV_RES_ROM) {
112                if (dev->flags & PCI_DEV_BRIDGE)
113                        ofs = PCI_ROM_ADDRESS1;
114                else
115                        ofs = PCI_ROM_ADDRESS;
116        } else {
117                ofs = PCI_BASE_ADDRESS_0 + (bar << 2);
118        }
119
120        PCI_CFG_R32(pcidev, ofs, &orig);
121        PCI_CFG_W32(pcidev, ofs, 0xffffffff);
122        PCI_CFG_R32(pcidev, ofs, &size);
123        PCI_CFG_W32(pcidev, ofs, orig);
124
125        if (size == 0 || size == 0xffffffff)
126                return;
127        if (bar == DEV_RES_ROM) {
128                mask = PCI_ROM_ADDRESS_MASK;
129                DBG_SET_STR(str, "ROM");
130                if (dev->bus->flags & PCI_BUS_MEM)
131                        res->flags = PCI_RES_MEM;
132                else
133                        res->flags = PCI_RES_MEMIO;
134        } else if (((size & 0x1) == 0) && (size & 0x6)) {
135                /* unsupported Memory type */
136                return;
137        } else {
138                mask = ~0xf;
139                if (size & 0x1) {
140                        /* I/O */
141                        mask = ~0x3;
142                        res->flags = PCI_RES_IO;
143                        DBG_SET_STR(str, "I/O");
144                        if (size & 0xffff0000)
145                                res->flags |= PCI_RES_IO32;
146                        /* Limit size of I/O space to 256 byte */
147                        size |= 0xffffff00;
148                        if ((dev->bus->flags & PCI_BUS_IO) == 0) {
149                                res->flags |= PCI_RES_FAIL;
150                                dev->flags |= PCI_DEV_RES_FAIL;
151                        }
152                } else {
153                        /* Memory */
154                        if (size & 0x8) {
155                                /* Prefetchable */
156                                res->flags = PCI_RES_MEM;
157                                DBG_SET_STR(str, "MEM");
158                        } else {
159                                res->flags = PCI_RES_MEMIO;
160                                DBG_SET_STR(str, "MEMIO");
161                        }
162                }
163        }
164        res->start = orig & mask;
165        size &= mask;
166        res->size = ~size + 1;
167        res->boundary = res->size;
168        res->end = res->start +  res->size;
169
170        DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n",
171                PCI_DEV_EXPAND(pcidev), str, bar, res->size);
172
173        /* Check if BAR is addressable by host */
174        if (pci_read_addressable(dev, res) == 0) {
175                /* No matching bridge window contains this BAR */
176                res->flags |= PCI_RES_FAIL;
177                dev->flags |= PCI_DEV_RES_FAIL;
178        }
179}
180
181static void pci_read_devs(struct pci_bus *bus)
182{
183        uint32_t id, tmp;
184        uint16_t tmp16;
185        uint8_t header;
186        int slot, func, fail, i, maxbars, max_sord;
187        struct pci_dev *dev, **listptr;
188        struct pci_bus *bridge;
189        pci_dev_t pcidev;
190        struct pci_res *res;
191
192        DBG("Scanning bus %d\n", bus->num);
193
194        max_sord = bus->num;
195        listptr = &bus->devs;
196        for (slot = 0; slot < PCI_MAX_DEVICES; slot++) {
197
198                /* Slot address */
199                pcidev = PCI_DEV(bus->num, slot, 0);
200
201                for (func = 0; func < PCI_MAX_FUNCTIONS; func++, pcidev++) {
202
203                        fail = PCI_CFG_R32(pcidev, PCI_VENDOR_ID, &id);
204                        if (fail || id == 0xffffffff || id == 0) {
205                                /*
206                                 * This slot is empty
207                                 */
208                                if (func == 0)
209                                        break;
210                                else
211                                        continue;
212                        }
213
214                        DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n",
215                                                        id, bus, slot, func);
216
217                        PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &tmp);
218                        tmp >>= 16;
219                        dev = pci_dev_create(tmp == PCI_CLASS_BRIDGE_PCI);
220                        *listptr = dev;
221                        listptr = &dev->next;
222
223                        dev->busdevfun = pcidev;
224                        dev->bus = bus;
225                        PCI_CFG_R16(pcidev, PCI_VENDOR_ID, &dev->vendor);
226                        PCI_CFG_R16(pcidev, PCI_DEVICE_ID, &dev->device);
227                        PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &dev->classrev);
228
229                        if (tmp == PCI_CLASS_BRIDGE_PCI) {
230                                DBG("Found PCI-PCI Bridge 0x%x at "
231                                    "(bus %x, slot %x, func %x)\n",
232                                    id, bus, slot, func);
233                                dev->flags = PCI_DEV_BRIDGE;
234                                bridge = (struct pci_bus *)dev;
235
236                                PCI_CFG_R32(pcidev, PCI_PRIMARY_BUS, &tmp);
237                                bridge->pri = tmp & 0xff;
238                                bridge->num = (tmp >> 8) & 0xff;
239                                bridge->sord = (tmp >> 16) & 0xff;
240                                if (bridge->sord > max_sord)
241                                        max_sord = bridge->sord;
242
243                                DBG("    Primary %x, Secondary %x, "
244                                    "Subordinate %x\n",
245                                    bridge->pri, bridge->num, bridge->sord);
246
247                                /*** Probe Bridge Spaces ***/
248
249                                /* MEMIO Window - always implemented */
250                                bridge->flags = PCI_BUS_MEMIO;
251                                res = &bridge->dev.resources[BRIDGE_RES_MEMIO];
252                                res->flags = PCI_RES_MEMIO;
253                                res->bar = BRIDGE_RES_MEMIO;
254                                PCI_CFG_R32(pcidev, 0x20, &tmp);
255                                res->start = (tmp & 0xfff0) << 16;
256                                res->end = 1 + ((tmp & 0xfff00000) | 0xfffff);
257                                if (res->end <= res->start) {
258                                        /* Window disabled */
259                                        res->end = res->start = 0;
260                                }
261                                res->size = res->end - res->start;
262
263                                /* I/O Window - optional */
264                                res = &bridge->dev.resources[BRIDGE_RES_IO];
265                                res->bar = BRIDGE_RES_IO;
266                                PCI_CFG_R32(pcidev, 0x30, &tmp);
267                                PCI_CFG_R16(pcidev, 0x1c, &tmp16);
268                                if (tmp != 0 || tmp16 != 0) {
269                                        bridge->flags |= PCI_BUS_IO;
270                                        res->flags = PCI_RES_IO;
271                                        if (tmp16 & 0x1) {
272                                                bridge->flags |= PCI_BUS_IO32;
273                                                res->flags |= PCI_RES_IO32;
274                                        }
275
276                                        res->start = (tmp & 0xffff) << 16 |
277                                                        (tmp16 & 0xf0) << 8;
278                                        res->end = 1 + ((tmp & 0xffff0000) |
279                                                        (tmp16 & 0xf000) |
280                                                        0xfff);
281                                        if (res->end <= res->start) {
282                                                /* Window disabled */
283                                                res->end = res->start = 0;
284                                        }
285                                        res->size = res->end - res->start;
286                                }
287
288                                /* MEM Window - optional */
289                                res = &bridge->dev.resources[BRIDGE_RES_MEM];
290                                res->bar = BRIDGE_RES_MEM;
291                                PCI_CFG_R32(pcidev, 0x24, &tmp);
292                                if (tmp != 0) {
293                                        bridge->flags |= PCI_BUS_MEM;
294                                        res->flags = PCI_RES_MEM;
295                                        res->start = (tmp & 0xfff0) << 16;
296                                        res->end = 1 + ((tmp & 0xfff00000) |
297                                                        0xfffff);
298                                        if (res->end <= res->start) {
299                                                /* Window disabled */
300                                                res->end = res->start = 0;
301                                        }
302                                        res->size = res->end - res->start;
303                                }
304
305                                /* Scan Secondary Bus */
306                                pci_read_devs(bridge);
307
308                                /* Only 2 BARs for Bridges */
309                                maxbars = 2;
310                        } else {
311                                /* Devices have subsytem device and vendor ID */
312                                PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_VENDOR_ID,
313                                                        &dev->subvendor);
314                                PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_ID,
315                                                        &dev->subdevice);
316
317                                /* Normal PCI Device has max 6 BARs */
318                                maxbars = 6;
319                        }
320
321                        /* Probe BARs */
322                        for (i = 0; i < maxbars; i++)
323                                pci_read_bar(dev, i);
324                        pci_read_bar(dev, DEV_RES_ROM);
325
326                        /* Get System Interrupt/Vector for device.
327                         * 0 means no-IRQ
328                         */
329                        PCI_CFG_R8(pcidev, PCI_INTERRUPT_LINE, &dev->sysirq);
330
331                        /* Stop if not a multi-function device */
332                        if (func == 0) {
333                                pci_cfg_r8(pcidev, PCI_HEADER_TYPE, &header);
334                                if ((header & PCI_MULTI_FUNCTION) == 0)
335                                        break;
336                        }
337                }
338        }
339
340        if (bus->num == 0)
341                bus->sord = max_sord;
342}
343
344int pci_config_read(void)
345{
346        pci_system_type = PCI_SYSTEM_HOST;
347
348        /* Find all devices and buses */
349        pci_hb.flags = PCI_BUS_IO|PCI_BUS_MEMIO|PCI_BUS_MEM;
350        pci_hb.dev.flags = PCI_DEV_BRIDGE;
351        pci_read_devs(&pci_hb);
352        pci_bus_cnt = pci_hb.sord + 1;
353        if (pci_hb.devs == NULL)
354                return 0;
355
356        return 0;
357}
Note: See TracBrowser for help on using the repository browser.