summaryrefslogblamecommitdiffstats
path: root/cpukit/libpci/pci_cfg_read.c
blob: 09e1aab6c453887a55f675aad4b488258afcf188 (plain) (tree)
1
2
3
4
5
6
7

                                           


                                                                            
                                         
  



















                                                                              







                        

                         
















                                                                         


                                             















                                                            




















                                                                         
                                  

































                                                                         
                                          
                    
                                        
                
                                               









                                             
                                           


































































                                                                   
                                                     



                                                    
                                                                       
 
                                                                     












                                                                                
                                                              
                                   
                                                                         




                                                


                                                                        
 
                                                         





                                                                   
                                                                         










































































                                                                                
                                                                   
                                                                         
                                                                  













                                                                         
                                                                       


                                                                 

                                                                          






















                                                            
/* 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;
}