* (C) Copyright 2002, 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier: GPL-2.0+
*/
/*
- * PCI routines
+ * Old PCI routines
+ *
+ * Do not change this file. Instead, convert your board to use CONFIG_DM_PCI
+ * and change pci-uclass.c.
*/
#include <common.h>
#include <command.h>
+#include <errno.h>
#include <asm/processor.h>
#include <asm/io.h>
#include <pci.h>
+DECLARE_GLOBAL_DATA_PTR;
+
#define PCI_HOSE_OP(rw, size, type) \
int pci_hose_##rw##_config_##size(struct pci_controller *hose, \
pci_dev_t dev, \
PCI_WRITE_VIA_DWORD_OP(byte, u8, 0x03, 0x000000ff)
PCI_WRITE_VIA_DWORD_OP(word, u16, 0x02, 0x0000ffff)
-/* Get a virtual address associated with a BAR region */
-void *pci_map_bar(pci_dev_t pdev, int bar, int flags)
-{
- pci_addr_t pci_bus_addr;
- pci_addr_t bar_response;
-
- /* read BAR address */
- pci_read_config_dword(pdev, bar, &bar_response);
- pci_bus_addr = bar_response & ~0xf;
-
- /*
- * Pass "0" as the length argument to pci_bus_to_virt. The arg
- * isn't actualy used on any platform because u-boot assumes a static
- * linear mapping. In the future, this could read the BAR size
- * and pass that as the size if needed.
- */
- return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE);
-}
-
/*
*
*/
static struct pci_controller* hose_head;
+struct pci_controller *pci_get_hose_head(void)
+{
+ if (gd->hose)
+ return gd->hose;
+
+ return hose_head;
+}
+
void pci_register_hose(struct pci_controller* hose)
{
struct pci_controller **phose = &hose_head;
{
struct pci_controller *hose;
- for (hose = hose_head; hose; hose = hose->next) {
+ for (hose = pci_get_hose_head(); hose; hose = hose->next) {
if (bus >= hose->first_busno && bus <= hose->last_busno)
return hose;
}
{
struct pci_controller *hose;
- for (hose = hose_head; hose; hose = hose->next) {
+ for (hose = pci_get_hose_head(); hose; hose = hose->next) {
if (hose->cfg_addr == cfg_addr)
return hose;
}
int pci_last_busno(void)
{
- struct pci_controller *hose = hose_head;
+ struct pci_controller *hose = pci_get_hose_head();
if (!hose)
return -1;
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
{
struct pci_controller * hose;
- u16 vendor, device;
- u8 header_type;
pci_dev_t bdf;
- int i, bus, found_multi = 0;
+ int bus;
- for (hose = hose_head; hose; hose = hose->next) {
+ for (hose = pci_get_hose_head(); hose; hose = hose->next) {
#ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE
- for (bus = hose->last_busno; bus >= hose->first_busno; bus--)
-#else
- for (bus = hose->first_busno; bus <= hose->last_busno; bus++)
-#endif
- for (bdf = PCI_BDF(bus, 0, 0);
-#if defined(CONFIG_ELPPC) || defined(CONFIG_PPMC7XX)
- bdf < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1,
- PCI_MAX_PCI_FUNCTIONS - 1);
+ for (bus = hose->last_busno; bus >= hose->first_busno; bus--) {
#else
- bdf < PCI_BDF(bus + 1, 0, 0);
+ for (bus = hose->first_busno; bus <= hose->last_busno; bus++) {
#endif
- bdf += PCI_BDF(0, 0, 1)) {
- if (!PCI_FUNC(bdf)) {
- pci_read_config_byte(bdf,
- PCI_HEADER_TYPE,
- &header_type);
-
- found_multi = header_type & 0x80;
- } else {
- if (!found_multi)
- continue;
- }
-
- pci_read_config_word(bdf,
- PCI_VENDOR_ID,
- &vendor);
- pci_read_config_word(bdf,
- PCI_DEVICE_ID,
- &device);
-
- for (i = 0; ids[i].vendor != 0; i++) {
- if (vendor == ids[i].vendor &&
- device == ids[i].device) {
- if (index <= 0)
- return bdf;
-
- index--;
- }
- }
- }
- }
-
- return -1;
-}
-
-pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
-{
- static struct pci_device_id ids[2] = {{}, {0, 0}};
-
- ids[0].vendor = vendor;
- ids[0].device = device;
-
- return pci_find_devices(ids, index);
-}
-
-/*
- *
- */
-
-int __pci_hose_phys_to_bus(struct pci_controller *hose,
- phys_addr_t phys_addr,
- unsigned long flags,
- unsigned long skip_mask,
- pci_addr_t *ba)
-{
- struct pci_region *res;
- pci_addr_t bus_addr;
- int i;
-
- for (i = 0; i < hose->region_count; i++) {
- res = &hose->regions[i];
-
- if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
- continue;
-
- if (res->flags & skip_mask)
- continue;
-
- bus_addr = phys_addr - res->phys_start + res->bus_start;
-
- if (bus_addr >= res->bus_start &&
- bus_addr < res->bus_start + res->size) {
- *ba = bus_addr;
- return 0;
+ bdf = pci_hose_find_devices(hose, bus, ids, &index);
+ if (bdf != -1)
+ return bdf;
}
}
- return 1;
-}
-
-pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose,
- phys_addr_t phys_addr,
- unsigned long flags)
-{
- pci_addr_t bus_addr = 0;
- int ret;
-
- if (!hose) {
- puts("pci_hose_phys_to_bus: invalid hose\n");
- return bus_addr;
- }
-
- /*
- * if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set
- */
- if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
- ret = __pci_hose_phys_to_bus(hose, phys_addr,
- flags, PCI_REGION_SYS_MEMORY, &bus_addr);
- if (!ret)
- return bus_addr;
- }
-
- ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr);
-
- if (ret)
- puts("pci_hose_phys_to_bus: invalid physical address\n");
-
- return bus_addr;
-}
-
-int __pci_hose_bus_to_phys(struct pci_controller *hose,
- pci_addr_t bus_addr,
- unsigned long flags,
- unsigned long skip_mask,
- phys_addr_t *pa)
-{
- struct pci_region *res;
- int i;
-
- for (i = 0; i < hose->region_count; i++) {
- res = &hose->regions[i];
-
- if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
- continue;
-
- if (res->flags & skip_mask)
- continue;
-
- if (bus_addr >= res->bus_start &&
- bus_addr < res->bus_start + res->size) {
- *pa = (bus_addr - res->bus_start + res->phys_start);
- return 0;
- }
- }
-
- return 1;
-}
-
-phys_addr_t pci_hose_bus_to_phys(struct pci_controller* hose,
- pci_addr_t bus_addr,
- unsigned long flags)
-{
- phys_addr_t phys_addr = 0;
- int ret;
-
- if (!hose) {
- puts("pci_hose_bus_to_phys: invalid hose\n");
- return phys_addr;
- }
-
- /*
- * if PCI_REGION_MEM is set we do a two pass search with preference
- * on matches that don't have PCI_REGION_SYS_MEMORY set
- */
- if ((flags & PCI_REGION_MEM) == PCI_REGION_MEM) {
- ret = __pci_hose_bus_to_phys(hose, bus_addr,
- flags, PCI_REGION_SYS_MEMORY, &phys_addr);
- if (!ret)
- return phys_addr;
- }
-
- ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
-
- if (ret)
- puts("pci_hose_bus_to_phys: invalid physical address\n");
-
- return phys_addr;
+ return -1;
}
-/*
- *
- */
-
int pci_hose_config_device(struct pci_controller *hose,
pci_dev_t dev,
unsigned long io,
pci_addr_t mem,
unsigned long command)
{
- pci_addr_t bar_response;
+ u32 bar_response;
unsigned int old_command;
pci_addr_t bar_value;
pci_size_t bar_size;
/* Disable interrupt line, if device says it wants to use interrupts */
pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin);
if (pin != 0) {
- pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE, 0xff);
+ pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE,
+ PCI_INTERRUPT_LINE_DISABLE);
}
pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command);
*/
extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
-#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI_SCAN_SHOW)
-const char * pci_class_str(u8 class)
-{
- switch (class) {
- case PCI_CLASS_NOT_DEFINED:
- return "Build before PCI Rev2.0";
- break;
- case PCI_BASE_CLASS_STORAGE:
- return "Mass storage controller";
- break;
- case PCI_BASE_CLASS_NETWORK:
- return "Network controller";
- break;
- case PCI_BASE_CLASS_DISPLAY:
- return "Display controller";
- break;
- case PCI_BASE_CLASS_MULTIMEDIA:
- return "Multimedia device";
- break;
- case PCI_BASE_CLASS_MEMORY:
- return "Memory controller";
- break;
- case PCI_BASE_CLASS_BRIDGE:
- return "Bridge device";
- break;
- case PCI_BASE_CLASS_COMMUNICATION:
- return "Simple comm. controller";
- break;
- case PCI_BASE_CLASS_SYSTEM:
- return "Base system peripheral";
- break;
- case PCI_BASE_CLASS_INPUT:
- return "Input device";
- break;
- case PCI_BASE_CLASS_DOCKING:
- return "Docking station";
- break;
- case PCI_BASE_CLASS_PROCESSOR:
- return "Processor";
- break;
- case PCI_BASE_CLASS_SERIAL:
- return "Serial bus controller";
- break;
- case PCI_BASE_CLASS_INTELLIGENT:
- return "Intelligent controller";
- break;
- case PCI_BASE_CLASS_SATELLITE:
- return "Satellite controller";
- break;
- case PCI_BASE_CLASS_CRYPT:
- return "Cryptographic device";
- break;
- case PCI_BASE_CLASS_SIGNAL_PROCESSING:
- return "DSP";
- break;
- case PCI_CLASS_OTHERS:
- return "Does not fit any class";
- break;
- default:
- return "???";
- break;
- };
-}
-#endif /* CONFIG_CMD_PCI || CONFIG_PCI_SCAN_SHOW */
-
-int __pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
-{
- /*
- * Check if pci device should be skipped in configuration
- */
- if (dev == PCI_BDF(hose->first_busno, 0, 0)) {
-#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */
- /*
- * Only skip configuration if "pciconfighost" is not set
- */
- if (getenv("pciconfighost") == NULL)
- return 1;
-#else
- return 1;
-#endif
- }
-
- return 0;
-}
-int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
- __attribute__((weak, alias("__pci_skip_dev")));
-
#ifdef CONFIG_PCI_SCAN_SHOW
-int __pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
+__weak int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
{
if (dev == PCI_BDF(hose->first_busno, 0, 0))
return 0;
return 1;
}
-int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
- __attribute__((weak, alias("__pci_print_dev")));
#endif /* CONFIG_PCI_SCAN_SHOW */
int pci_hose_scan_bus(struct pci_controller *hose, int bus)
pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device);
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
+#ifdef CONFIG_PCI_FIXUP_DEV
+ board_pci_fixup_dev(hose, dev, vendor, device, class);
+#endif
+
#ifdef CONFIG_PCI_SCAN_SHOW
indent++;
#endif
#ifdef CONFIG_PCI_PNP
- sub_bus = max(pciauto_config_device(hose, dev), sub_bus);
+ sub_bus = max((unsigned int)pciauto_config_device(hose, dev),
+ sub_bus);
#else
cfg = pci_find_config(hose, class, vendor, device,
PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
if (cfg) {
cfg->config_device(hose, dev, cfg);
- sub_bus = max(sub_bus, hose->current_busno);
+ sub_bus = max(sub_bus,
+ (unsigned int)hose->current_busno);
}
#endif
int pci_hose_scan(struct pci_controller *hose)
{
#if defined(CONFIG_PCI_BOOTDELAY)
- static int pcidelay_done;
char *s;
int i;
- if (!pcidelay_done) {
+ if (!gd->pcidelay_done) {
/* wait "pcidelay" ms (if defined)... */
s = getenv("pcidelay");
if (s) {
for (i = 0; i < val; i++)
udelay(1000);
}
- pcidelay_done = 1;
+ gd->pcidelay_done = 1;
}
#endif /* CONFIG_PCI_BOOTDELAY */
+#ifdef CONFIG_PCI_SCAN_SHOW
+ puts("PCI:\n");
+#endif
+
/*
* Start scan at current_busno.
* PCIe will start scan at first_busno+1.
/* now call board specific pci_init()... */
pci_init_board();
}
+
+/* Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it.
+ * */
+int pci_hose_find_capability(struct pci_controller *hose, pci_dev_t dev,
+ int cap)
+{
+ int pos;
+ u8 hdr_type;
+
+ pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &hdr_type);
+
+ pos = pci_hose_find_cap_start(hose, dev, hdr_type & 0x7F);
+
+ if (pos)
+ pos = pci_find_cap(hose, dev, pos, cap);
+
+ return pos;
+}
+
+/* Find the header pointer to the Capabilities*/
+int pci_hose_find_cap_start(struct pci_controller *hose, pci_dev_t dev,
+ u8 hdr_type)
+{
+ u16 status;
+
+ pci_hose_read_config_word(hose, dev, PCI_STATUS, &status);
+
+ if (!(status & PCI_STATUS_CAP_LIST))
+ return 0;
+
+ switch (hdr_type) {
+ case PCI_HEADER_TYPE_NORMAL:
+ case PCI_HEADER_TYPE_BRIDGE:
+ return PCI_CAPABILITY_LIST;
+ case PCI_HEADER_TYPE_CARDBUS:
+ return PCI_CB_CAPABILITY_LIST;
+ default:
+ return 0;
+ }
+}
+
+int pci_find_cap(struct pci_controller *hose, pci_dev_t dev, int pos, int cap)
+{
+ int ttl = PCI_FIND_CAP_TTL;
+ u8 id;
+ u8 next_pos;
+
+ while (ttl--) {
+ pci_hose_read_config_byte(hose, dev, pos, &next_pos);
+ if (next_pos < CAP_START_POS)
+ break;
+ next_pos &= ~3;
+ pos = (int) next_pos;
+ pci_hose_read_config_byte(hose, dev,
+ pos + PCI_CAP_LIST_ID, &id);
+ if (id == 0xff)
+ break;
+ if (id == cap)
+ return pos;
+ pos += PCI_CAP_LIST_NEXT;
+ }
+ return 0;
+}
+
+/**
+ * pci_find_next_ext_capability - Find an extended capability
+ *
+ * Returns the address of the next matching extended capability structure
+ * within the device's PCI configuration space or 0 if the device does
+ * not support it. Some capabilities can occur several times, e.g., the
+ * vendor-specific capability, and this provides a way to find them all.
+ */
+int pci_find_next_ext_capability(struct pci_controller *hose, pci_dev_t dev,
+ int start, int cap)
+{
+ u32 header;
+ int ttl, pos = PCI_CFG_SPACE_SIZE;
+
+ /* minimum 8 bytes per capability */
+ ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
+
+ if (start)
+ pos = start;
+
+ pci_hose_read_config_dword(hose, dev, pos, &header);
+ if (header == 0xffffffff || header == 0)
+ return 0;
+
+ while (ttl-- > 0) {
+ if (PCI_EXT_CAP_ID(header) == cap && pos != start)
+ return pos;
+
+ pos = PCI_EXT_CAP_NEXT(header);
+ if (pos < PCI_CFG_SPACE_SIZE)
+ break;
+
+ pci_hose_read_config_dword(hose, dev, pos, &header);
+ if (header == 0xffffffff || header == 0)
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * pci_hose_find_ext_capability - Find an extended capability
+ *
+ * Returns the address of the requested extended capability structure
+ * within the device's PCI configuration space or 0 if the device does
+ * not support it.
+ */
+int pci_hose_find_ext_capability(struct pci_controller *hose, pci_dev_t dev,
+ int cap)
+{
+ return pci_find_next_ext_capability(hose, dev, 0, cap);
+}