Followup from your reply: * Using FreeBSD defines/code would be acceptable. * Using the uintptr_t* construction may or may not work in RES2DEV because pointer arithmetic on uintptr_t* may yield uncertain results due to integer rather than byte math. (Although subtracting the two pointer types as you do it should work.) You can also cast a pointer to a uintptr_t directly, so I might use: #define RES2DEV(res) ((struct pci_dev *) \ ((uintptr_t)res - (uintptr_t)(res->bar * (sizeof(struct pci_res)))))
[...] Continuing on from "This is all the farther I got for now." My comments regarding doxygen can be delayed, but should be added to the TODOs for this code-base. Although it would be nice for this code to follow the RTEMS Coding Conventions, it may not be necessary since this is really a self-contained third-party library. If there is a style guide you do use, it may be nice to indicate it somewhere (for both pci and drvmgr). > diff --git a/cpukit/libpci/pci/irq.h b/cpukit/libpci/pci/irq.h > new file mode 100644 > index 0000000..2dacadf > --- /dev/null > +++ b/cpukit/libpci/pci/irq.h > @@ -0,0 +1,105 @@ > +/* PCI IRQ Library > + * > + * IRQ handling does not have so much with PCI to do, this library depends > + * on the BSP to implement shared interrupts. > + * We've gone away from embedding file description comments in the copyright/license block, and instead put a brief doxygen block at the start of the file, followed by the copyright/license. See: https://devel.rtems.org/wiki/Developer/Coding/Boilerplate_File_Header > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +#ifndef __PCI_IRQ_H__ > +#define __PCI_IRQ_H__ > + > +#include <bsp.h> > + > +/* PCI Handler (ISR) called when IRQ is generated by any of the PCI devices > + * connected to the same PCI IRQ Pin. This is been defined the same way as s/is/has > + * rtems_interrupt_handler in order for BSPs to "direct-map" the register > + * and unregister functions rtems_interrupt_handler_install/remove > + */ > +typedef void (*pci_isr)(void *arg); > + Why not use rtems_interrupt_handler directly? > +/* Get assigned system IRQ to a PCI Device. If no IRQ 0 is returned */ > +extern int pci_dev_irq(pci_dev_t dev); > + > +/* Register shared PCI IRQ handler, but does not enable it. The system > interrupt > + * number is read from the PCI board's PCI configuration space header iline > + * field. The iline field is initialized by the PCI subsystem during start > up, > + * the ipin field is translated into a system IRQ and written to iline. The > + * board's driver should use the iline field as the irq argument to this > + * function. > + * > + * Arguments > + * irq System IRQ number, normally taken from the PCI configuration > area > + * isr Function pointer to the ISR > + * arg Second argument to function isr > + */ > +static inline int pci_interrupt_register(int irq, const char *info, Use RTEMS_INLINE_ROUTINE instead of "static inline". https://lists.rtems.org/pipermail/users/2011-November/024226.html [...] > diff --git a/cpukit/libpci/pci_access_mem_be.c > b/cpukit/libpci/pci_access_mem_be.c > new file mode 100644 > index 0000000..f05b547 > --- /dev/null > +++ b/cpukit/libpci/pci_access_mem_be.c > @@ -0,0 +1,67 @@ > +/* PCI Access Library > + * Registers-over-Memory Space - Generic Big endian PCI bus definitions > + * > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +#include <pci.h> > + > +uint16_t pci_mem_be_ld_le16(uint16_t *adr) > +{ > + return ld_be16(adr); > +} > + > +uint16_t pci_mem_be_ld_be16(uint16_t *adr) > +{ > + return ld_le16(adr); > +} > + > +uint32_t pci_mem_be_ld_le32(uint32_t *adr) > +{ > + return ld_be32(adr); > +} > + > +uint32_t pci_mem_be_ld_be32(uint32_t *adr) > +{ > + return ld_le32(adr); > +} > + > +void pci_mem_be_st_le16(uint16_t *adr, uint16_t data) > +{ > + st_be16(adr, data); > +} > + > +void pci_mem_be_st_be16(uint16_t *adr, uint16_t data) > +{ > + st_le16(adr, data); > +} > + > +void pci_mem_be_st_le32(uint32_t *adr, uint32_t data) > +{ > + st_be32(adr, data); > +} > + > +void pci_mem_be_st_be32(uint32_t *adr, uint32_t data) > +{ > + st_le32(adr, data); > +} > + > +struct pci_memreg_ops pci_mem_be_ops = { > + .ld8 = pci_mem_ld8, > + .st8 = pci_mem_st8, > + > + .ld_le16 = pci_mem_be_ld_le16, > + .st_le16 = pci_mem_be_st_le16, > + .ld_be16 = pci_mem_be_ld_be16, > + .st_be16 = pci_mem_be_st_be16, > + > + .ld_le32 = pci_mem_be_ld_le32, > + .st_le32 = pci_mem_be_st_le32, > + .ld_be32 = pci_mem_be_ld_be32, > + .st_be32 = pci_mem_be_st_be32, > +}; functions used only through function pointers should be declared static inline (RTEMS_INLINE_ROUTINE). > diff --git a/cpukit/libpci/pci_cfg.c b/cpukit/libpci/pci_cfg.c > new file mode 100644 > index 0000000..78a1229 > --- /dev/null > +++ b/cpukit/libpci/pci_cfg.c > @@ -0,0 +1,55 @@ > +/* PCI Configuration Library > + * > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +#include <pci/cfg.h> > + > +/* Number of buses. This is set from respective library */ > +int pci_bus_cnt = 0; > + > +/* PCI Address assigned to BARs which failed to fit into the PCI Window or > + * is disabled by any other cause. > + */ > +uint32_t pci_invalid_address = 0; Should this be a pointer or maybe a uintptr_t? > + > +/* PCI System type. Configuration Library setup this */ > +int pci_system_type = PCI_SYSTEM_NONE; > + If the enum type were declared, this would get type-checked. [...] > diff --git a/cpukit/libpci/pci_cfg_auto.c b/cpukit/libpci/pci_cfg_auto.c > new file mode 100644 > index 0000000..548dd90 > --- /dev/null > +++ b/cpukit/libpci/pci_cfg_auto.c > @@ -0,0 +1,1014 @@ > +/* PCI (Auto) configuration Library. Setup PCI configuration space and IRQ. > + * > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +#include <rtems.h> > +#include <stdlib.h> > +#include <rtems/bspIo.h> > +#include <string.h> > + > +/* Configure headers */ > +#define PCI_CFG_AUTO_LIB > + > +#include <pci.h> > +#include <pci/access.h> > +#include <pci/cfg.h> > + > +/* Define PCI_INFO_ON_STARTUP to get a listing of configured devices at boot > + * time > + */ > +#undef PCI_INFO_ON_STARTUP I'm not sure what this is for. > + > +/* #define DEBUG */ Could replace this with RTEMS_DEBUG. > + > +#ifdef DEBUG > +#define DBG(x...) printk(x) > +#else > +#define DBG(x...) > +#endif > + > +/* PCI Library > + * (For debugging it might be good to use other functions or the driver's > + * directly) > + */ > +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) > +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) > +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) > +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) > +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) > +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) > + > +/* Number of PCI buses */ > +extern int pci_bus_cnt; externs should come through header files usually. > + > +int pci_config_auto_initialized = 0; > + > +/* Configuration setup */ > +struct pci_auto_setup pci_auto_cfg; > + > +/* Insert BAR into the sorted resources list. The BARs are sorted on the > + * BAR size/alignment need. > + */ > +static void pci_res_insert(struct pci_res **root, struct pci_res *res) > +{ > + struct pci_res *curr, *last; > + unsigned long curr_size_resulting_boundary, size_resulting_boundary; > + unsigned long boundary, size; > + > + res->start = 0; > + res->end = 0; > + boundary = res->boundary; > + size = res->size; > + > + /* Insert the resources depending on the boundary needs > + * Normally the boundary=size of the BAR, however when > + * PCI bridges are involved the bridge's boundary may be > + * smaller that the size due to the fact that a bridge s/that the/than the > + * may have different-sized BARs behind, the largest BAR > + * (also the BAR with the largest boundary) will decide > + * the alignment need. > + */ > + last = NULL; > + curr = *root; > + > + /* Order List after boundary, the boundary is maintained > + * when the size is on an equal boundary, normally it is > + * but may not be with bridges. So in second hand it is > + * sorted after resulting boundary - the boundary after > + * the resource. > + */ > + while (curr && (curr->boundary >= boundary)) { > + if (curr->boundary == boundary) { > + /* Find Resulting boundary of size */ > + size_resulting_boundary = 1; > + while ((size & size_resulting_boundary) == 0) > + size_resulting_boundary = > + size_resulting_boundary << 1; > + > + /* Find Resulting boundary of curr->size */ > + curr_size_resulting_boundary = 1; > + while ((curr->size & curr_size_resulting_boundary) == > 0) > + curr_size_resulting_boundary = > + curr_size_resulting_boundary << 1; > + > + if (size_resulting_boundary >= > + curr_size_resulting_boundary) > + break; > + } > + last = curr; > + curr = curr->next; > + } > + > + if (last == NULL) { > + /* Insert first in list */ > + res->next = *root; > + *root = res; > + } else { > + last->next = res; > + res->next = curr; > + } > +} > + > +#ifdef DEBUG > +void pci_res_list_print(struct pci_res *root) > +{ > + if (root == NULL) > + return; > + > + printf("RESOURCE LIST:\n"); > + while (root) { > + printf(" SIZE: 0x%08x, BOUNDARY: 0x%08x\n", root->size, > + > root->boundary); > + root = root->next; > + } > +} > +#endif > + > +/* Reorder a size/alignment ordered resources list. The idea is to > + * avoid unused due to alignment/size restriction. > + * > + * NOTE: The first element is always untouched. > + * NOTE: If less than three elements in list, nothing will be done > + * > + * Normally a BAR has the same alignment requirements as the size of the > + * BAR. However, when bridges are invloved the alignment need may be smaller s/invloved/involved > + * that the size, because a bridge resource consist or multiple BARs. s/that/than > + * For example, say that a bridge with a 256Mb and a 16Mb BAR is found, then > + * the alignment is required to be 256Mb but the size 256+16Mb. > + * > + * In order to minimize dead space on the bus, the bounadry ordered list s/bounadry/boundary > + * is reordered, example: > + * BUS0 > + * | BUS1 > + * |------------| > + * | |-- BAR0: SIZE=256Mb, ALIGNMENT=256MB > + * | |-- BAR1: SIZE=16Mb, ALIGNMENT=16MB > + * | | > + * | | > + * | | > + * | | BUS2 (BAR_BRIDGE1: SIZE=256+16, ALIGNEMENT=256) > + * | |----------| > + * | | |-- BAR2: SIZE=256Mb, ALIGNMENT=256Mb > + * | | |-- BAR3: SIZE=16Mb, ALIGNMENT=16MB > + * > + * A alignement/boundary ordered list of BUS1 will look like: s/A alignement/An alignment > + * - BAR_BRIDGE1 > + * - BAR0 (ALIGMENT NEED 256Mb) > + * - BAR1 > + * > + * However, Between BAR_BRIDGE1 and BAR0 will be a unused hole of 256-16Mb. > + * We can put BAR1 before BAR0 to avoid the problem. > + */ > +static void pci_res_reorder(struct pci_res *root) > +{ > + struct pci_res *curr, *last, *curr2, *last2; > + unsigned int start, start_next, hole_size, hole_boundary; > + > + if (root == NULL) > + return; > + > + /* Make up a start address with the boundary of the > + * First element. > + */ > + start = root->boundary + root->size; > + last = root; > + curr = root->next; > + while (curr) { > + > + /* Find start address of resource */ > + start_next = (start + (curr->boundary - 1)) & > + ~(curr->boundary - 1); > + > + /* Find hole size, the unsed space inbetween last resource s/inbetween/in between > + *and next */ /s/*and/* and > + hole_size = start_next - start; > + > + /* Find Boundary of START */ > + hole_boundary = 1; > + while ((start & hole_boundary) == 0) > + hole_boundary = hole_boundary<<1; > + > + /* Detect dead hole */ > + if (hole_size > 0) { > + /* Step through list and try to find a resource that > + * can fit into hole. Take into account hole start > + * boundary and hole size. > + */ > + last2 = curr; > + curr2 = curr->next; > + while (curr2) { > + if ((curr2->boundary <= hole_boundary) && > + (curr2->size <= hole_size)) { > + /* Found matching resource. Move it > + * first in the hole. Then rescan, now > + * that the hole has changed in > + * size/boundary. > + */ > + last2->next = curr2->next; > + curr2->next = curr; > + last->next = curr2; > + > + /* New Start address */ > + start_next = (start + > + (curr2->boundary - 1)) & > + ~(curr2->boundary - 1); > + /* Since we inserted the resource > before > + * curr we need to re-evaluate curr > one > + * more, more resources may fit into > the > + * shrunken hole. > + */ > + curr = curr2; > + break; > + } > + last2 = curr2; > + curr2 = curr2->next; > + } > + } > + > + /* No hole or nothing fitted into hole. */ s/fitted/fit > + start = start_next; > + > + last = curr; > + curr = curr->next; > + } > +} > + > +/* Find the total size required in PCI address space needed by a resource > list*/ > +static unsigned int pci_res_size(struct pci_res *root) > +{ > + struct pci_res *curr; > + unsigned int size; > + > + /* Get total size of all resources */ > + size = 0; > + curr = root; > + while (curr) { > + size = (size + (curr->boundary - 1)) & ~(curr->boundary - 1); I see this alignment code repeated, you might consider having a macro for it even if just in this file. > + size += curr->size; > + curr = curr->next; > + } > + > + return size; > +} > + > +#if 0 /* not used for now */ > +/* Free a device and secondary bus if device is a bridge */ > +static void pci_dev_free(struct pci_dev *dev) > +{ > + struct pci_dev *subdev; > + struct pci_bus *bus; > + > + if (dev->flags & PCI_DEV_BRIDGE) { > + bus = (struct pci_bus *)dev; > + for (subdev = bus->devs; subdev ; subdev = subdev->next) > + pci_dev_free(dev); > + } > + > + free(dev); > +} > +#endif Will it be used later? I don't really care for carrying around commented-out code. [...] > + > +/* This routine assumes that PCI access library has been successfully > + * initialized. All information about the PCI bus needed is found in > + * the argument. > + * What argument? > + * The PCI buses are enumerated as bridges are found, PCI devices are > + * setup with BARs and IRQs, etc. > + */ > +int pci_config_auto(void) > +{ > + uint32_t end; > + uint32_t startmemio, startmem, startio; > + struct pci_auto_setup *autocfg = &pci_auto_cfg; > +#ifdef DEBUG > + uint32_t endmemio, endmem, endio; > + uint32_t start; > +#endif > + > + if (pci_config_auto_initialized == 0) > + return -1; /* no config given to library */ > + > +#ifdef DEBUG > + DBG("\n--- PCI MEMORY AVAILABLE ---\n"); > + if (autocfg->mem_size) { > + start = autocfg->mem_start; > + end = autocfg->mem_start + autocfg->mem_size - 1; > + DBG(" MEM AVAIL [0x%08x-0x%08x]\n", start, end); > + } else { > + /* One big memory space */ > + DBG(" MEM share the space with MEMIO\n"); > + } > + /* no-prefetchable memory space need separate memory space. > + * For example PCI controller maps this region non-cachable. > + */ > + start = autocfg->memio_start; > + end = autocfg->memio_start + autocfg->memio_size - 1; > + DBG(" MEMIO AVAIL [0x%08x-0x%08x]\n", start, end); > + if (autocfg->io_size) { > + start = autocfg->io_start; > + end = autocfg->io_start + autocfg->io_size - 1; > + DBG(" I/O AVAIL [0x%08x-0x%08x]\n", start, end); > + } else { > + DBG(" I/O Space not available\n"); > + } > +#endif > + > + /* Init Host-Bridge */ > + memset(&pci_hb, 0, sizeof(pci_hb)); > + pci_hb.dev.flags = PCI_DEV_BRIDGE; > + if (autocfg->memio_size <= 0) > + return -1; > + pci_hb.flags = PCI_BUS_MEMIO; > + if (autocfg->mem_size) > + pci_hb.flags |= PCI_BUS_MEM; > + if (autocfg->io_size) > + pci_hb.flags |= PCI_BUS_IO; > + > + /* Find all PCI devices/functions on all buses. The buses will be > + * enumrated (assigned a unique PCI Bus ID 0..255). > + */ > + DBG("\n--- PCI SCANNING ---\n"); > + pci_find_devs(&pci_hb); > + pci_bus_cnt = pci_hb.sord + 1; > + if (pci_hb.devs == NULL) > + return 0; > + > + pci_system_type = PCI_SYSTEM_HOST; > + > + /* Find all resources (MEM/MEMIO/IO BARs) of all devices/functions > + * on all buses. > + * > + * Device resources behind bridges which does not support prefetchable > + * memory are already marked as non-prefetchable memory. > + * Devices which as I/O resources behind a bridge that do not support > + * I/O space are marked DISABLED. > + * > + * All BARs and Bridge Spaces are disabled after this. Only the ones > + * that are allocated an address are initilized later on. > + */ > + DBG("\n\n--- PCI RESOURCES ---\n"); > + pci_for_each_dev(pci_find_res_dev, 0); > + > + /* Add all device's resources to bus and sort them to fit in the PCI > + * Window. The device resources are propagated upwards through bridges > + * by adding a "virtual" BAR (boundary != BAR size). > + * > + * We wait with MEMIO (non-prefetchable memory) resources to after MEM > + * resources have been allocated, so that MEM resources can be changed > + * into MEMIO resources if not enough space. > + */ > + pci_add_res_bus(&pci_hb, PCI_RES_IO); > + pci_add_res_bus(&pci_hb, PCI_RES_MEM); > + > + /* Start assigning found resource according to the sorted order. */ > + > + /* Allocate resources to I/O areas */ > + if (pci_hb.busres[BUS_RES_IO]) { > + startio = autocfg->io_start; > + end = startio + autocfg->io_size; > +#ifdef DEBUG > + endio = > +#endif > + pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end); I might prefer to see the variable always assigned, and then declared unused if !DEBUG, eg endio = pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end) #ifndef DEBUG (void) endio; #endif You could even aggregate the (void)s down below. Or better yet do some error checking. [...] > diff --git a/cpukit/libpci/pci_cfg_read.c b/cpukit/libpci/pci_cfg_read.c > new file mode 100644 > index 0000000..2db465d > --- /dev/null > +++ b/cpukit/libpci/pci_cfg_read.c > @@ -0,0 +1,357 @@ > +/* Read current PCI configuration that bootloader or BIOS has already setup > + * and initialize the PCI structures. > + * > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +#include <rtems.h> > +#include <stdlib.h> > +#include <rtems/bspIo.h> > +#include <pci/cfg.h> > +#include <pci/access.h> > + > +/* PCI Library > + * (For debugging it might be good to use other functions or the driver's > + * directly) > + */ > +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) > +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) > +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) > +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) > +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) > +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) > + > +#ifdef DEBUG > +#define DBG(args...) printk(args) > +#else > +#define DBG(args...) > +#endif > + > +/* Number of buses */ > +extern int pci_bus_cnt; > + > +/* The Host Bridge bus is initialized here */ > +extern struct pci_bus pci_hb; > + > +static struct pci_dev *pci_dev_create(int isbus) > +{ > + void *ptr; > + int size; > + > + if (isbus) > + size = sizeof(struct pci_bus); > + else > + size = sizeof(struct pci_dev); > + > + ptr = malloc(size); > + if (!ptr) > + rtems_fatal_error_occurred(RTEMS_NO_MEMORY); > + memset(ptr, 0, size); > + return ptr; > +} This function is identical to one in pci_cfg_auto.c? > + > +/* Check if address is accessible from host */ > +static int pci_read_addressable(struct pci_dev *dev, struct pci_res *res) > +{ > + struct pci_bus *bus = dev->bus; > + int type = res->flags & PCI_RES_TYPE_MASK; > + struct pci_res *range0, *range1; > + > + if (type == PCI_BUS_IO && (bus->flags & PCI_BUS_IO) == 0) > + return 0; > + > + /* Assume that host bridge can access all */ > + if (bus->pri == 0) > + return 1; > + > + range1 = NULL; > + switch (type) { > + case PCI_RES_IO: > + range0 = &bus->dev.resources[BRIDGE_RES_IO]; > + break; > + case PCI_RES_MEM: > + range1 = &bus->dev.resources[BRIDGE_RES_MEM]; > + default: > + case PCI_RES_MEMIO: > + range0 = &bus->dev.resources[BRIDGE_RES_MEMIO]; > + break; > + } > + if ((res->start >= range0->start) && (res->end <= range0->end)) { > + return pci_read_addressable(&bus->dev, range0); > + } else if (range1 && (res->start >= range1->start) && > + (res->end <= range1->end)) { > + return pci_read_addressable(&bus->dev, range1); > + } > + > + return 0; > +} > + > +static void pci_read_bar(struct pci_dev *dev, int bar) > +{ This code is very similar to pci_find_bar(). Consider sharing some of it if there is no good reason to keep them separate. > + uint32_t orig, size, mask; > + struct pci_res *res = &dev->resources[bar]; > + pci_dev_t pcidev = dev->busdevfun; > + int ofs; > +#ifdef DEBUG > + char *str; > +#define DBG_SET_STR(str, val) str = (val) > +#else > +#define DBG_SET_STR(str, val) > +#endif > + > + DBG("Bus: %x, Slot: %x, function: %x, bar%d\n", > + PCI_DEV_EXPAND(pcidev), bar); > + > + res->bar = bar; > + if (bar == DEV_RES_ROM) { > + if (dev->flags & PCI_DEV_BRIDGE) > + ofs = PCI_ROM_ADDRESS1; > + else > + ofs = PCI_ROM_ADDRESS; > + } else { > + ofs = PCI_BASE_ADDRESS_0 + (bar << 2); > + } > + > + PCI_CFG_R32(pcidev, ofs, &orig); > + PCI_CFG_W32(pcidev, ofs, 0xffffffff); > + PCI_CFG_R32(pcidev, ofs, &size); > + PCI_CFG_W32(pcidev, ofs, orig); > + > + if (size == 0 || size == 0xffffffff) > + return; > + if (bar == DEV_RES_ROM) { > + mask = PCI_ROM_ADDRESS_MASK; > + DBG_SET_STR(str, "ROM"); > + if (dev->bus->flags & PCI_BUS_MEM) > + res->flags = PCI_RES_MEM; > + else > + res->flags = PCI_RES_MEMIO; > + } else if (((size & 0x1) == 0) && (size & 0x6)) { > + /* unsupported Memory type */ > + return; > + } else { > + mask = ~0xf; > + if (size & 0x1) { > + /* I/O */ > + mask = ~0x3; > + res->flags = PCI_RES_IO; > + DBG_SET_STR(str, "I/O"); > + if (size & 0xffff0000) > + res->flags |= PCI_RES_IO32; > + /* Limit size of I/O space to 256 byte */ > + size |= 0xffffff00; Is this really supposed to be |=? [...] > diff --git a/cpukit/libpci/pci_cfg_static.c b/cpukit/libpci/pci_cfg_static.c > new file mode 100644 > index 0000000..d7d4321 > --- /dev/null > +++ b/cpukit/libpci/pci_cfg_static.c > @@ -0,0 +1,157 @@ > +/* PCI (Static) Configuration Library > + * > + * COPYRIGHT (c) 2010. > + * Cobham Gaisler AB. > + * > + * The license and distribution terms for this file may be > + * found in the file LICENSE in this distribution or at > + * http://www.rtems.com/license/LICENSE. > + */ > + > +/* > + * The Host Bridge bus must be declared by user. It contains the static > + * configuration used to setup the devices/functions. > + */ > + > +/* Configure headers */ > +#define PCI_CFG_STATIC_LIB > + > +#include <stdlib.h> > +#include <pci.h> > +#include <pci/access.h> > +#include <pci/cfg.h> > + > +#define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) > +#define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) > +#define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) > +#define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) > +#define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) > +#define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) > + > +/* Number of buses */ > +extern int pci_bus_cnt; > + > +/* Enumrate one bus if device is a bridge, and all it's subordinate buses */ > +static int pci_init_dev(struct pci_dev *dev, void *unused) > +{ The code in this function is very similar to that in pci_find_devs(), which also seems to share some code with pci_read_devs(). It might be sensible to refactor/reduce some of this copy-paste. [...] > diff --git a/doc/user/c_user.texi b/doc/user/c_user.texi > index 2ff47bf..bdf0cd5 100644 > --- a/doc/user/c_user.texi > +++ b/doc/user/c_user.texi > @@ -107,6 +107,7 @@ > * Configuring a System:: > * Multiprocessing Manager:: > * Symmetric Multiprocessing Services:: > +* PCI Library:: > * Stack Bounds Checker:: > * CPU Usage Statistics:: > * Object Services:: > @@ -152,6 +153,7 @@ > @include conf.texi > @include mp.texi > @include smp.texi > +@include libpci.texi > @include stackchk.texi > @include cpuuse.texi > @include object.texi > diff --git a/doc/user/conf.t b/doc/user/conf.t > index dc10816..0c5ab32 100644 > --- a/doc/user/conf.t > +++ b/doc/user/conf.t > @@ -5237,6 +5237,59 @@ uses the Ada run-time. > None. > > @c > +@c === PCI Library === > +@c > +@section PCI Library > + > +This section defines the system configuration paramters supported s/paramters/parameters > +by @code{rtems/confdefs.h} related to configuring the PCI Library > +for RTEMS. > + > +The PCI Library startup behaviour can be configured in four diffent s/diffent/different > +ways depending on how @code{CONFIGURE_PCI_CONFIG_LIB} is defined: > + > +@itemize @bullet > +@findex PCI_LIB_AUTO > +@item @code{PCI_LIB_AUTO} is used to enable the PCI auto configuration > +software. PCI will be automatically probed, PCI buses enumerated, all > +devices and bridges will be initialized using Plug & Play software > +routines. The PCI device tree will be populated based on the PCI devices > +found in the system, PCI devices will be configured by allocating address > +region resources automatically in PCI space according to the BSP or host > +bridge driver set up. > + > +@findex PCI_LIB_READ > +@item @code{PCI_LIB_READ} is used to enable the PCI read configuration > +software. The current PCI configuration is read to create the RAM > +representation (the PCI device tree) of the PCI devices present. PCI devices > +are assumed to already have been initialized and PCI buses enumrated, it is s/enumrated/enumerated > +therefore required that a BIOS or a boot loader has set up configuration > space > +prior to booting into RTEMS. > + > +@findex PCI_LIB_STATIC > +@item @code{PCI_LIB_STATIC} is used to enable the PCI static configuration > +software. The user provides a PCI tree with information how all PCI devices s/how/about how > +are to be configured at compile time by linking in a custom > +@code{struct pci_bus pci_hb} tree. The static PCI library will not probe PCI > +for devices, instead it will assume that all devices defined by the user is s/is/are > +present, it will enumerate the PCI buses and configure all PCI devices in > +static configuration accordingly. Since probe and allocation software is not > +needed the startup is faster, have smaller footprint and does not require s/have/has > +dynamic memory allocation. > + > +@findex PCI_LIB_PERIPHERAL > +@item @code{PCI_LIB_PERIPHERAL} is used to enable the PCI peripheral > +configuration. It is similar to @code{PCI_LIB_STATIC}, but is will never > write s/is will/it will > +the configuration to the PCI devices since PCI peripherals are not allowed to > +access PCI configuration space. > + > +@end itemize > + > +Note that selecting PCI_LIB_STATIC or PCI_LIB_PERIPHERAL but not defining > +@code{pci_hb} will reuslt in link errors. Note also that in these modes > +Plug & Play is not performed. > + > +@c > @c === Go Tasks === > @c > @section Go Tasks > diff --git a/doc/user/libpci.t b/doc/user/libpci.t > new file mode 100644 > index 0000000..1eaca42 > --- /dev/null > +++ b/doc/user/libpci.t > @@ -0,0 +1,409 @@ > +@c > +@c COPYRIGHT (c) 2011 > +@c Aeroflex Gaisler AB > +@c All rights reserved. > +@c > +@c $Id: libpci.t,v v.vv xxxx/yy/zz xx:yy:zz ? Exp $ > +@c > + > +@chapter PCI Library > + > +@cindex libpci > + > +@section Introduction > + > +The Peripheral Component Interconnect (PCI) bus is a very common computer > +bus architecture that is found in almost every PC today. The PCI bus is > +normally located at the motherboard where some PCI devices are soldered > +directly onto the PCB and expansion slots allows the user to add custom > +devices easily. There is a wide range of PCI hardware available implementing > +all sorts of interfaces and functions. > + > +This section describes the PCI Library available in RTEMS used to access the > +PCI bus in a portable way across computer architectures supported by RTEMS. > + > +The PCI Library aims to be compatible with PCI 2.3 with a couple of > +limitations, for example there is no support for hot-plugging, 64-bit > +memory space and cardbus bridges. > + > +In order to support different architectures and with small foot-print > embedded > +systems in mind the PCI Library offers four different configuration options > +listed below. It is selected during compile time by defining the appropriate > +macros in confdefs.h. It is also possible to enable NONE (No Configuration) > +which can be used for debuging PCI access functions. s/debugin/debugging Is this true? It looks like "none" gives an error in confdefs.h. > +@itemize @bullet > +@item Auto Configuration (do Plug & Play) > +@item Read Configuration (read BIOS or boot loader configuration) > +@item Static Configuration (write user defined configuration) > +@item Peripheral Configuration (no access to cfg-space) > +@end itemize > + > +@section Background > + > +The PCI bus is constructed in a way where on-board devices and devices > +in expansion slots can be automatically found (probed) and configured > +using Plug & Play completely implemented in software. The bus is set up once > +during boot up. The Plug & Play information can be read and written from > +PCI configuration space. A PCI device is identified in configuration space by > +a unique bus, slot and function number. Each PCI slot can have up to 8 > +functions and interface to another PCI sub-bus by implementing a PCI-to-PCI > +bridge according to the PCI Bridge Architecture specification. > + > +Using the unique [bus:slot:func] any device can be configured regardless how > PCI s/how/of how > +is currently set up as long as all PCI buses are enumerated correctly. The > +enumration is done during probing, all bridges are given a bus numbers in s/enumration/enumeration s/numbers/number > +order for the bridges to respond to accesses from both directions. The PCI > +library can assign address ranges to which a PCI device should respond using > +Plug & Play technique or a static user defined configuration. After the > +configuration has been performed the PCI device drivers can find devices by > +the read-only PCI Class type, Vendor ID and Device ID information found in > +configuration space for each device. > + > +In some systems there is a boot loader or BIOS which have already configured > +all PCI devices, but on embedded targets it is quite common that there is no > +BIOS or boot loader, thus RTEMS must configure the PCI bus. Only the PCI host > +may do configuration space access, the host driver or BSP is responsible to > +translate the [bus:slot:func] into a valid PCI configuration space access. > + > +If the target is not a host, but a peripheral, configuration space can not be > +accessed, the peripheral is set up by the host during start up. In complex > +embedded PCI systems the peripheral may need to access other PCI boards than > +then host. In such systems a custom (static) configuration of both the host s/then/the > +and peripheral may be a convenient solution. > + > +The PCI bus defines four interrupt signals INTA#..INTD#. The interrupt > signals > +must be mapped into a system interrupt/vector, it is up to the BSP or host > +driver to know the mapping, however the BIOS or boot loader may use the > +8-bit read/write "Interrupt Line" register to pass the knowledge along to the > +OS. > + > + > + The PCI standard > +defines and recommends that the backplane route the interupt lines in a > +systematic way, however in > + Incomplete paragraph here. > +@subsection Software Components > + > +The PCI library is located in cpukit/libpci, it consists of different parts: > +@itemize @bullet > +@item PCI Host bridge driver interface > +@item Configuration routines > +@item Access (Configuration, I/O and Memory space) routines > +@item Interrupt routines (implemented by BSP) > +@item Print routines > +@item Static/peripheral configuration creation > +@item PCI shell command > +@end itemize > + > +@subsection PCI Configuration > + > +During start up the PCI bus must be configured in order for host and > peripherals > +to access one another using Memory or I/O accesses and that interrupts are > +properly handled. Three different spaces are defined and mapped separately: > +@enumerate > +@item I/O space (IO) > +@item non-prefetchable Memory space (MEMIO) > +@item prefetchable Memory space (MEM) > +@end enumerate > + > +Regions of the same type (I/O or Memory) may not overlap which is guaranteed > +by the software. MEM regions may be mapped into MEMIO regions, but MEMIO > +regions can not be mapped into MEM, for that could lead to prefetching of > +registers. The interrupt pin which a board is driving can be read out from > +PCI configuration space, however it is up to software to know how interrupt > +signals are routed between PCI-to-PCI bridges and how PCI INT[A..D]# pins are > +mapped to system IRQ. In systems where previous software (boot loader or > BIOS) > +has already set up this the configuration overwritten or simply read out. s/overwritten/is overwritten > + > +In order to support different configuration methods the following > configuration > +libraries are available can selectable by the user: delete "available can" > +@itemize @bullet > +@item Auto Configuration (run Plug & Play software) > +@item Read Configuration (relies on a boot loader or BIOS) > +@item Static Configuration (write user defined setup, no Plug & Play) > +@item Peripheral Configuration (user defined setup, no access to > configuration space) > +@end itemize > + > +A host driver can be made to support all three configuration methods, or any > +combination. It may be defined by the BSP which approach is used. > + > +The configuration software is called from the PCI driver (pci_config_init()). > + > +Regardless of configuration method a PCI device tree is created in RAM during > +initialization, the tree can be accessed to find devices and resources > without > +accessing configuration space later on. The user is responsible to create the > +device tree at compile time when using the static/peripheral method. > + > + > +@subsubsection RTEMS Configuration selection > + > +The active configuration method can be selected at compile time in the same > +way as other project parameters by including rtems/confdefs.h and setting > +@itemize @bullet > +@item CONFIGURE_INIT > +@item RTEMS_PCI_CONFIG_LIB > +@item CONFIGURE_PCI_LIB = PCI_LIB_(AUTO,STATIC,READ,PERIPHERAL) > +@end itemize > + > +See the RTEMS configuration section how to setup the PCI library. > + > + > +@subsubsection Auto Configuration > + > +The auto configuration software enumerate PCI buses and initializes all PCI s/enumerate/enumerates > +devices found using Plug & Play. The auto configuration software requires > +that a configuration setup has been registered by the driver or BSP in order > +to setup the I/O and Memory regions at the correct address ranges. PCI > +interrupt pins can optionally be routed over PCI-to-PCI bridges and mapped > +to a system interrupt number. Resources are sorted by size and required > +alignment, unused "dead" space may be created when PCI bridges are present > +due to the PCI bridge window size does not equal the alignment, to cope with > +that resources are reordered to fit smaller BARs into the dead space to > minimize BARs should probably be defined before used. > +the PCI space required. If a BAR or ROM register can not be allocated a PCI > +address region (due to too few resources available) the register will be > given > +the value of pci_invalid_address which defaults to 0. > + > +The auto configuration routines support: > +@itemize @bullet > +@item PCI 2.3 > +@item Little and big endian PCI bus > +@item one I/O 16 or 32-bit range (IO) > +@item memory space (MEMIO) > +@item prefetchable memory space (MEM), if not present MEM will be mapped into > + MEMIO > +@item multiple PCI buses - PCI-to-PCI bridges > +@item standard BARs, PCI-to-PCI bridge BARs, ROM BARs > +@item Interrupt routing over bridges > +@item Interrupt pin to system interrupt mapping > +@end itemize > + > +Not supported: > +@itemize @bullet > +@item hot-pluggable devices > +@item Cardbus bridges > +@item 64-bit memory space > +@item 16-bit and 32-bit I/O address ranges at the same time > +@end itemize > + > +In PCI 2.3 there may exist I/O BARs that must be located at the low 64kBytes > +address range, in order to support this the host driver or BSP must make sure > +that I/O addresses region is within this region. > + > + > +@subsubsection Read Configuration > + > +When a BIOS or boot loader already has setup the PCI bus the configuration > can > +be read directly from the PCI resource registers and buses are already > +enumerated, this is a much simpler approach than configuring PCI ourselves. > The > +PCI device tree is automatically created based on the current configuration > and > +devices present. After initialization is done there is no difference between > +the auto or read configuration approaches. > + > + > +@subsubsection Static Configuration > + > +To support custom configurations and small-footprint PCI systems, the user > may > +provide the PCI device tree which contains the current configuration. The > +PCI buses are enumerated and all resources are written to PCI devices during > +initialization. When this approach is selected PCI boards must be located at > +the same slots every time and devices can not be removed or added, Plug & > Play > +is not performed. Boards of the same type may of course be exchanged. > + > +The user can create a configuration by calling pci_cfg_print() on a running > +system that has had PCI setup by the auto or read configuration routines, it > +can be called from the PCI shell command. The user must provide the PCI > device > +tree named pci_hb. > + > + > +@subsubsection Peripheral Configuration > + > +On systems where a peripheral PCI device needs to access other PCI devices > than > +the host the peripheral configuration approach may be handy. Most PCI devices > +answers on the PCI host's requests and start DMA accesses into the Hosts > memory, > +however in some complex systems PCI devices may want to access other devices > +on the same bus or at another PCI bus. > + > +A PCI peripheral is not allowed to do PCI configuration cycles, which means > that > +is must either rely on the host to give it the addresses it needs, or that > the s/is/it > +addresses are predefined. > + > +This configuration approach is very similar to the static option, however the > +configuration is never written to PCI bus, instead it is only used for > drivers > +to find PCI devices and resources using the same PCI API as for the host > + > + > +@subsection PCI Access > + > +The PCI access routines are low-level routines provided for drivers, > +configuration software, etc. in order to access different regions in a way > +not dependent upon the host driver, BSP or platform. > +@itemize @bullet > +@item PCI configuration space > +@item PCI I/O space > +@item Registers over PCI memory space > +@item Translate PCI address into CPU accessible address and vice verse s/verse/versa > +@end itemize > + > +By using the access routines drivers can be made portable over different > +architectures. The access routines take the architecture endianness into > +consideration and let the host driver or BSP implement I/O space and > +configuration space access. > + > +Some non-standard hardware may also define the PCI bus big-endian, for > example > +the LEON2 AT697 PCI host bridge and some LEON3 systems may be configured that > +way. It is up to the BSP to set the appropriate PCI endianness on compile > time > +(BSP_PCI_BIG_ENDIAN) in order for inline macros to be correctly defined. > +Another possibility is to use the function pointers defined by the access > +layer to implement drivers that support "run-time endianness detection". > + > + > +@subsubsection Configuration space > + > +Configuration space is accessed using the routines listed below. The > +pci_dev_t type is used to specify a specific PCI bus, device and function. It > +is up to the host driver or BSP to create a valid access to the requested > +PCI slot. Requests made to slots that is not supported by hardware should s/is/are > +result in PCISTS_MSTABRT and/or data must be ignored (writes) or 0xffffffff > +is always returned (reads). > + > +@example > + /* Configuration Space Access Read Routines */ > + extern int pci_cfg_r8(pci_dev_t dev, int ofs, uint8_t *data); > + extern int pci_cfg_r16(pci_dev_t dev, int ofs, uint16_t *data); > + extern int pci_cfg_r32(pci_dev_t dev, int ofs, uint32_t *data); > + > + /* Configuration Space Access Write Routines */ > + extern int pci_cfg_w8(pci_dev_t dev, int ofs, uint8_t data); > + extern int pci_cfg_w16(pci_dev_t dev, int ofs, uint16_t data); > + extern int pci_cfg_w32(pci_dev_t dev, int ofs, uint32_t data); > +@end example > + > + > +@subsubsection I/O space > + > +The BSP or driver provide special routines in order to access I/O space. Some > +architectures have a special instruction accessing I/O space, others have it > +mapped into a "PCI I/O window" in the standard address space accessed by the > +CPU. The window size may vary and must be taken into consideration by the > +host driver. The below routines must be used to access I/O space. The address > +given to the functions is not the PCI I/O addresses, the caller must have > +translated PCI I/O addresses (available in the PCI BARs) into a BSP or host > +driver custom address, see @ref{Access functions} how addresses are s/how/for how > +translated. > + > +@example > +/* Read a register over PCI I/O Space */ > +extern uint8_t pci_io_r8(uint32_t adr); > +extern uint16_t pci_io_r16(uint32_t adr); > +extern uint32_t pci_io_r32(uint32_t adr); > + > +/* Write a register over PCI I/O Space */ > +extern void pci_io_w8(uint32_t adr, uint8_t data); > +extern void pci_io_w16(uint32_t adr, uint16_t data); > +extern void pci_io_w32(uint32_t adr, uint32_t data); > +@end example > + > + > +@subsubsection Registers over Memory space > + > +PCI host bridge hardware normally swap data accesses into the endianness of > the > +host architecture in order to lower the load of the CPU, peripherals can do > DMA > +without swapping. However, the host controller can not separate a standard > +memory access from a memory access to a register, registers may be mapped > into > +memory space. This leads to register content being swapped, which must be > +swapped back. The below routines makes it possible to access registers over > PCI > +memory space in a portable way on different architectures, the BSP or > +architecture must provide necessary functions in order to implement this. > + > +@example > + static inline uint16_t pci_ld_le16(volatile uint16_t *addr); > + static inline void pci_st_le16(volatile uint16_t *addr, uint16_t val); > + static inline uint32_t pci_ld_le32(volatile uint32_t *addr); > + static inline void pci_st_le32(volatile uint32_t *addr, uint32_t val); > + static inline uint16_t pci_ld_be16(volatile uint16_t *addr); > + static inline void pci_st_be16(volatile uint16_t *addr, uint16_t val); > + static inline uint32_t pci_ld_be32(volatile uint32_t *addr); > + static inline void pci_st_be32(volatile uint32_t *addr, uint32_t val); > +@end example > + > +In order to support non-standard big-endian PCI bus the above pci_* functions > +is required, pci_ld_le16 != ld_le16 on big endian PCI buses. > + > + > +@subsubsection Access functions > + > +The PCI Access Library can provide device drivers with function pointers > +executing the above Configuration, I/O and Memory space accesses. The > +functions have the same arguments and return values as the as the above s/as the as the/as the > +functions. > + > +The pci_access_func() function defined below can be used to get a function > +pointer of a specific access type. > + > +@example > + /* Get Read/Write function for accessing a register over PCI Memory Space > + * (non-inline functions). > + * > + * Arguments > + * wr 0(Read), 1(Write) > + * size 1(Byte), 2(Word), 4(Double Word) > + * func Where function pointer will be stored > + * endian PCI_LITTLE_ENDIAN or PCI_BIG_ENDIAN > + * type 1(I/O), 3(REG over MEM), 4(CFG) > + * > + * Return > + * 0 Found function > + * others No such function defined by host driver or BSP > + */ > + int pci_access_func(int wr, int size, void **func, int endian, int type); > +@end example > + > +PCI devices drivers may be written to support run-time detection of > endianess, s/devices/device > +this is mosly for debugging or for development systems. When the product is > +finally deployed macros switch to using the inline functions instead which > +have been configured for the correct endianness. > + > + > +@subsubsection PCI address translation > + > +When PCI addresses, both I/O and memory space, is not mapped 1:1 address > +translation before access is needed. If drivers read the PCI resources > directly > +using configuration space routines or in the device tree, the addresses given > +are PCI addresses. The below functions can be used to translate PCI addresses > +into CPU accessible addresses or vise versa, translation may be different for s/vise/vice > +different PCI spaces/regions. > + > +@example > + /* Translate PCI address into CPU accessible address */ > + static inline int pci_pci2cpu(uint32_t *address, int type); > + > + /* Translate CPU accessible address into PCI address (for DMA) */ > + static inline int pci_cpu2pci(uint32_t *address, int type); > +@end example > + > + > +@subsection PCI Interrupt > + > +The PCI specification defines four different interrupt lines INTA#..INTD#, > +the interrupts are low level sensitive which make it possible to support > +multiple interrupt sources on the same interrupt line. Since the lines are > +level sensitive the interrupt sources must be acknowledged before clearing > the > +interrupt contoller, or the interrupt controller must be masked. The BSP must > +provide a routine for clearing/acknowledging the interrupt controller, it is > +up to the interrupt service routine to acknowledge the interrupt source. > + > +The PCI Library relies on the BSP for implementing shared interrupt handling > +through the BSP_PCI_shared_interrupt_* functions/macros, they must be defined > +when including bsp.h. > + > +PCI device drivers may use the pci_interrupt_ routines in order to call the > +BSP specific functions in a platform independent way. The PCI interrupt > +interface has been made similar to the RTEMS IRQ extension so that a BSP can > +use the standard RTEMS interrupt functions directly. > + > + > +@subsection PCI Shell command > + > +The RTEMS shell have a PCI command 'pci' which makes it possible to > read/write s/have/has > +configuration space, print the current PCI configuration and print out a > +configuration C-file for the static or peripheral library. > -- > 1.7.0.4 All done! _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel