/* SPDX-License-Identifier: BSD-2-Clause */
/* Read current PCI configuration that bootloader or BIOS has already setup
* and initialize the PCI structures.
*
* COPYRIGHT (c) 2010 Cobham Gaisler AB.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <rtems.h>
#include <stdlib.h>
#include <rtems/bspIo.h>
#include <pci/cfg.h>
#include <pci/access.h>
#include "pci_internal.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
/* 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 = calloc(1, size);
if (!ptr)
rtems_fatal_error_occurred(RTEMS_NO_MEMORY);
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];
/* Fall through */
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 = PCIR_BIOS_1;
else
ofs = PCIR_BIOS;
} else {
ofs = PCIR_BAR(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 = PCIM_BIOS_ADDR_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_SLOTMAX; slot++) {
/* Slot address */
pcidev = PCI_DEV(bus->num, slot, 0);
for (func = 0; func <= PCI_FUNCMAX; func++, pcidev++) {
fail = PCI_CFG_R32(pcidev, PCIR_VENDOR, &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, PCIR_REVID, &tmp);
tmp >>= 16;
dev = pci_dev_create(tmp == PCID_PCI2PCI_BRIDGE);
*listptr = dev;
listptr = &dev->next;
dev->busdevfun = pcidev;
dev->bus = bus;
PCI_CFG_R16(pcidev, PCIR_VENDOR, &dev->vendor);
PCI_CFG_R16(pcidev, PCIR_DEVICE, &dev->device);
PCI_CFG_R32(pcidev, PCIR_REVID, &dev->classrev);
if (tmp == PCID_PCI2PCI_BRIDGE) {
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, PCIR_PRIBUS_1, &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, PCIR_SUBVEND_0,
&dev->subvendor);
PCI_CFG_R16(pcidev, PCIR_SUBDEV_0,
&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, PCIR_INTLINE, &dev->sysirq);
/* Stop if not a multi-function device */
if (func == 0) {
pci_cfg_r8(pcidev, PCIR_HDRTYPE, &header);
if ((header & PCIM_MFDEV) == 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;
}