diff options
-rw-r--r-- | drivers/pci/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/pci.c | 281 | ||||
-rw-r--r-- | drivers/pci/pci_common.c | 292 | ||||
-rw-r--r-- | include/pci.h | 14 |
4 files changed, 313 insertions, 276 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 50b7be53ca..856a5f5b53 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -6,7 +6,7 @@ # obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o -obj-$(CONFIG_PCI) += pci.o pci_auto.o pci_rom.o +obj-$(CONFIG_PCI) += pci.o pci_common.o pci_auto.o pci_rom.o obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o obj-$(CONFIG_PCI_MSC01) += pci_msc01.o diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e1296cab9e..3babd94805 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -101,25 +101,6 @@ PCI_READ_VIA_DWORD_OP(word, u16 *, 0x02) 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; - u32 bar_response; - - /* read BAR address */ - pci_read_config_dword(pdev, bar, &bar_response); - pci_bus_addr = (pci_addr_t)(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); -} - /* * */ @@ -187,106 +168,22 @@ int pci_last_busno(void) 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 = 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--) + for (bus = hose->last_busno; bus >= hose->first_busno; bus--) { #else - for (bus = hose->first_busno; bus <= hose->last_busno; bus++) + for (bus = hose->first_busno; bus <= hose->last_busno; bus++) { #endif - for (bdf = PCI_BDF(bus, 0, 0); - bdf < PCI_BDF(bus + 1, 0, 0); - bdf += PCI_BDF(0, 0, 1)) { - if (pci_skip_dev(hose, bdf)) - continue; - - 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_class(uint find_class, int index) -{ - int bus; - int devnum; - pci_dev_t bdf; - uint32_t class; - - for (bus = 0; bus <= pci_last_busno(); bus++) { - for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) { - pci_read_config_dword(PCI_BDF(bus, devnum, 0), - PCI_CLASS_REVISION, &class); - if (class >> 16 == 0xffff) - continue; - - for (bdf = PCI_BDF(bus, devnum, 0); - bdf <= PCI_BDF(bus, devnum, - PCI_MAX_PCI_FUNCTIONS - 1); - bdf += PCI_BDF(0, 0, 1)) { - pci_read_config_dword(bdf, PCI_CLASS_REVISION, - &class); - class >>= 8; - - if (class != find_class) - continue; - /* - * Decrement the index. We want to return the - * correct device, so index is 0 for the first - * matching device, 1 for the second, etc. - */ - if (index) { - index--; - continue; - } - /* Return index'th controller. */ + bdf = pci_hose_find_devices(hose, bus, ids, &index); + if (bdf != -1) return bdf; - } } } - return -ENODEV; -} - -pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) -{ - struct pci_device_id ids[2] = { {}, {0, 0} }; - - ids[0].vendor = vendor; - ids[0].device = device; - - return pci_find_devices(ids, index); + return -1; } /* @@ -355,87 +252,6 @@ pci_addr_t pci_hose_phys_to_bus (struct pci_controller *hose, 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; -} - -void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum, - u32 addr_and_ctrl) -{ - int bar; - - bar = PCI_BASE_ADDRESS_0 + barnum * 4; - pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl); -} - -u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum) -{ - u32 addr; - int bar; - - bar = PCI_BASE_ADDRESS_0 + barnum * 4; - pci_hose_read_config_dword(hose, dev, bar, &addr); - if (addr & PCI_BASE_ADDRESS_SPACE_IO) - return addr & PCI_BASE_ADDRESS_IO_MASK; - else - return addr & PCI_BASE_ADDRESS_MEM_MASK; -} - int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev, unsigned long io, @@ -576,91 +392,6 @@ void pci_cfgfunc_do_nothing(struct pci_controller *hose, */ 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 */ - -__weak 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; -} - #ifdef CONFIG_PCI_SCAN_SHOW __weak int pci_print_dev(struct pci_controller *hose, pci_dev_t dev) { diff --git a/drivers/pci/pci_common.c b/drivers/pci/pci_common.c new file mode 100644 index 0000000000..24c66bbef2 --- /dev/null +++ b/drivers/pci/pci_common.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Andreas Heppel <aheppel@sysgo.de> + * + * (C) Copyright 2002, 2003 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <errno.h> +#include <pci.h> +#include <asm/io.h> + +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; + }; +} + +pci_dev_t pci_find_class(uint find_class, int index) +{ + int bus; + int devnum; + pci_dev_t bdf; + uint32_t class; + + for (bus = 0; bus <= pci_last_busno(); bus++) { + for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) { + pci_read_config_dword(PCI_BDF(bus, devnum, 0), + PCI_CLASS_REVISION, &class); + if (class >> 16 == 0xffff) + continue; + + for (bdf = PCI_BDF(bus, devnum, 0); + bdf <= PCI_BDF(bus, devnum, + PCI_MAX_PCI_FUNCTIONS - 1); + bdf += PCI_BDF(0, 0, 1)) { + pci_read_config_dword(bdf, PCI_CLASS_REVISION, + &class); + class >>= 8; + + if (class != find_class) + continue; + /* + * Decrement the index. We want to return the + * correct device, so index is 0 for the first + * matching device, 1 for the second, etc. + */ + if (index) { + index--; + continue; + } + /* Return index'th controller. */ + return bdf; + } + } + } + + return -ENODEV; +} + +__weak 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; +} + +/* 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; + u32 bar_response; + + /* read BAR address */ + pci_read_config_dword(pdev, bar, &bar_response); + pci_bus_addr = (pci_addr_t)(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); +} + +void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum, + u32 addr_and_ctrl) +{ + int bar; + + bar = PCI_BASE_ADDRESS_0 + barnum * 4; + pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl); +} + +u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum) +{ + u32 addr; + int bar; + + bar = PCI_BASE_ADDRESS_0 + barnum * 4; + pci_hose_read_config_dword(hose, dev, bar, &addr); + if (addr & PCI_BASE_ADDRESS_SPACE_IO) + return addr & PCI_BASE_ADDRESS_IO_MASK; + else + return addr & PCI_BASE_ADDRESS_MEM_MASK; +} + +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; +} + +pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index) +{ + struct pci_device_id ids[2] = { {}, {0, 0} }; + + ids[0].vendor = vendor; + ids[0].device = device; + + return pci_find_devices(ids, index); +} + +pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum, + struct pci_device_id *ids, int *indexp) +{ + int found_multi = 0; + u16 vendor, device; + u8 header_type; + pci_dev_t bdf; + int i; + + for (bdf = PCI_BDF(busnum, 0, 0); + bdf < PCI_BDF(busnum + 1, 0, 0); + bdf += PCI_BDF(0, 0, 1)) { + if (pci_skip_dev(hose, bdf)) + continue; + + 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 ((*indexp) <= 0) + return bdf; + + (*indexp)--; + } + } + } + + return -1; +} diff --git a/include/pci.h b/include/pci.h index 004a048d2f..4e8826a03a 100644 --- a/include/pci.h +++ b/include/pci.h @@ -705,5 +705,19 @@ u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum); */ int pciauto_setup_rom(struct pci_controller *hose, pci_dev_t dev); +/** + * pci_hose_find_devices() - Find devices by vendor/device ID + * + * @hose: PCI hose to search + * @busnum: Bus number to search + * @ids: PCI vendor/device IDs to look for, terminated by 0, 0 record + * @indexp: Pointer to device index to find. To find the first matching + * device, pass 0; to find the second, pass 1, etc. This + * parameter is decremented for each non-matching device so + * can be called repeatedly. + */ +pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum, + struct pci_device_id *ids, int *indexp); + #endif /* __ASSEMBLY__ */ #endif /* _PCI_H */ |