summaryrefslogtreecommitdiffstats
path: root/cpukit/libpci/pci_cfg_read.c
diff options
context:
space:
mode:
authorDaniel Hellstrom <daniel@gaisler.com>2011-11-28 10:11:10 +0100
committerDaniel Hellstrom <daniel@gaisler.com>2015-04-17 01:10:15 +0200
commita31845f7f9b4770cf9ddd8b6820641d2f4f4c1da (patch)
tree0d7f215ec45d7c4cf6f1293af72ece2fbde1ddc3 /cpukit/libpci/pci_cfg_read.c
parentleon3,ngmp: simplify cpucounter initialization (diff)
downloadrtems-a31845f7f9b4770cf9ddd8b6820641d2f4f4c1da.tar.bz2
LIBPCI: added PCI layer to cpukit/libpci
Diffstat (limited to 'cpukit/libpci/pci_cfg_read.c')
-rw-r--r--cpukit/libpci/pci_cfg_read.c357
1 files changed, 357 insertions, 0 deletions
diff --git a/cpukit/libpci/pci_cfg_read.c b/cpukit/libpci/pci_cfg_read.c
new file mode 100644
index 0000000000..2db465db2c
--- /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;
+}
+
+/* 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)
+{
+ 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;
+ if ((dev->bus->flags & PCI_BUS_IO) == 0) {
+ res->flags |= PCI_RES_FAIL;
+ dev->flags |= PCI_DEV_RES_FAIL;
+ }
+ } else {
+ /* Memory */
+ if (size & 0x8) {
+ /* Prefetchable */
+ res->flags = PCI_RES_MEM;
+ DBG_SET_STR(str, "MEM");
+ } else {
+ res->flags = PCI_RES_MEMIO;
+ DBG_SET_STR(str, "MEMIO");
+ }
+ }
+ }
+ res->start = orig & mask;
+ size &= mask;
+ res->size = ~size + 1;
+ res->boundary = res->size;
+ res->end = res->start + res->size;
+
+ DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n",
+ PCI_DEV_EXPAND(pcidev), str, bar, res->size);
+
+ /* Check if BAR is addressable by host */
+ if (pci_read_addressable(dev, res) == 0) {
+ /* No matching bridge window contains this BAR */
+ res->flags |= PCI_RES_FAIL;
+ dev->flags |= PCI_DEV_RES_FAIL;
+ }
+}
+
+static void pci_read_devs(struct pci_bus *bus)
+{
+ uint32_t id, tmp;
+ uint16_t tmp16;
+ uint8_t header;
+ int slot, func, fail, i, maxbars, max_sord;
+ struct pci_dev *dev, **listptr;
+ struct pci_bus *bridge;
+ pci_dev_t pcidev;
+ struct pci_res *res;
+
+ DBG("Scanning bus %d\n", bus->num);
+
+ max_sord = bus->num;
+ listptr = &bus->devs;
+ for (slot = 0; slot < PCI_MAX_DEVICES; slot++) {
+
+ /* Slot address */
+ pcidev = PCI_DEV(bus->num, slot, 0);
+
+ for (func = 0; func < PCI_MAX_FUNCTIONS; func++, pcidev++) {
+
+ fail = PCI_CFG_R32(pcidev, PCI_VENDOR_ID, &id);
+ if (fail || id == 0xffffffff || id == 0) {
+ /*
+ * This slot is empty
+ */
+ if (func == 0)
+ break;
+ else
+ continue;
+ }
+
+ DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n",
+ id, bus, slot, func);
+
+ PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &tmp);
+ tmp >>= 16;
+ dev = pci_dev_create(tmp == PCI_CLASS_BRIDGE_PCI);
+ *listptr = dev;
+ listptr = &dev->next;
+
+ dev->busdevfun = pcidev;
+ dev->bus = bus;
+ PCI_CFG_R16(pcidev, PCI_VENDOR_ID, &dev->vendor);
+ PCI_CFG_R16(pcidev, PCI_DEVICE_ID, &dev->device);
+ PCI_CFG_R32(pcidev, PCI_CLASS_REVISION, &dev->classrev);
+
+ if (tmp == PCI_CLASS_BRIDGE_PCI) {
+ DBG("Found PCI-PCI Bridge 0x%x at "
+ "(bus %x, slot %x, func %x)\n",
+ id, bus, slot, func);
+ dev->flags = PCI_DEV_BRIDGE;
+ bridge = (struct pci_bus *)dev;
+
+ PCI_CFG_R32(pcidev, PCI_PRIMARY_BUS, &tmp);
+ bridge->pri = tmp & 0xff;
+ bridge->num = (tmp >> 8) & 0xff;
+ bridge->sord = (tmp >> 16) & 0xff;
+ if (bridge->sord > max_sord)
+ max_sord = bridge->sord;
+
+ DBG(" Primary %x, Secondary %x, "
+ "Subordinate %x\n",
+ bridge->pri, bridge->num, bridge->sord);
+
+ /*** Probe Bridge Spaces ***/
+
+ /* MEMIO Window - always implemented */
+ bridge->flags = PCI_BUS_MEMIO;
+ res = &bridge->dev.resources[BRIDGE_RES_MEMIO];
+ res->flags = PCI_RES_MEMIO;
+ res->bar = BRIDGE_RES_MEMIO;
+ PCI_CFG_R32(pcidev, 0x20, &tmp);
+ res->start = (tmp & 0xfff0) << 16;
+ res->end = 1 + ((tmp & 0xfff00000) | 0xfffff);
+ if (res->end <= res->start) {
+ /* Window disabled */
+ res->end = res->start = 0;
+ }
+ res->size = res->end - res->start;
+
+ /* I/O Window - optional */
+ res = &bridge->dev.resources[BRIDGE_RES_IO];
+ res->bar = BRIDGE_RES_IO;
+ PCI_CFG_R32(pcidev, 0x30, &tmp);
+ PCI_CFG_R16(pcidev, 0x1c, &tmp16);
+ if (tmp != 0 || tmp16 != 0) {
+ bridge->flags |= PCI_BUS_IO;
+ res->flags = PCI_RES_IO;
+ if (tmp16 & 0x1) {
+ bridge->flags |= PCI_BUS_IO32;
+ res->flags |= PCI_RES_IO32;
+ }
+
+ res->start = (tmp & 0xffff) << 16 |
+ (tmp16 & 0xf0) << 8;
+ res->end = 1 + ((tmp & 0xffff0000) |
+ (tmp16 & 0xf000) |
+ 0xfff);
+ if (res->end <= res->start) {
+ /* Window disabled */
+ res->end = res->start = 0;
+ }
+ res->size = res->end - res->start;
+ }
+
+ /* MEM Window - optional */
+ res = &bridge->dev.resources[BRIDGE_RES_MEM];
+ res->bar = BRIDGE_RES_MEM;
+ PCI_CFG_R32(pcidev, 0x24, &tmp);
+ if (tmp != 0) {
+ bridge->flags |= PCI_BUS_MEM;
+ res->flags = PCI_RES_MEM;
+ res->start = (tmp & 0xfff0) << 16;
+ res->end = 1 + ((tmp & 0xfff00000) |
+ 0xfffff);
+ if (res->end <= res->start) {
+ /* Window disabled */
+ res->end = res->start = 0;
+ }
+ res->size = res->end - res->start;
+ }
+
+ /* Scan Secondary Bus */
+ pci_read_devs(bridge);
+
+ /* Only 2 BARs for Bridges */
+ maxbars = 2;
+ } else {
+ /* Devices have subsytem device and vendor ID */
+ PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_VENDOR_ID,
+ &dev->subvendor);
+ PCI_CFG_R16(pcidev, PCI_SUBSYSTEM_ID,
+ &dev->subdevice);
+
+ /* Normal PCI Device has max 6 BARs */
+ maxbars = 6;
+ }
+
+ /* Probe BARs */
+ for (i = 0; i < maxbars; i++)
+ pci_read_bar(dev, i);
+ pci_read_bar(dev, DEV_RES_ROM);
+
+ /* Get System Interrupt/Vector for device.
+ * 0 means no-IRQ
+ */
+ PCI_CFG_R8(pcidev, PCI_INTERRUPT_LINE, &dev->sysirq);
+
+ /* Stop if not a multi-function device */
+ if (func == 0) {
+ pci_cfg_r8(pcidev, PCI_HEADER_TYPE, &header);
+ if ((header & PCI_MULTI_FUNCTION) == 0)
+ break;
+ }
+ }
+ }
+
+ if (bus->num == 0)
+ bus->sord = max_sord;
+}
+
+int pci_config_read(void)
+{
+ pci_system_type = PCI_SYSTEM_HOST;
+
+ /* Find all devices and buses */
+ pci_hb.flags = PCI_BUS_IO|PCI_BUS_MEMIO|PCI_BUS_MEM;
+ pci_hb.dev.flags = PCI_DEV_BRIDGE;
+ pci_read_devs(&pci_hb);
+ pci_bus_cnt = pci_hb.sord + 1;
+ if (pci_hb.devs == NULL)
+ return 0;
+
+ return 0;
+}