mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-29 17:43:36 +01:00 
			
		
		
		
	Move env_get() over to the new header file. Acked-by: Joe Hershberger <joe.hershberger@ni.com> Signed-off-by: Simon Glass <sjg@chromium.org>
		
			
				
	
	
		
			365 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * 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.
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <env.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;
 | |
| 	};
 | |
| }
 | |
| 
 | |
| __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 (env_get("pciconfighost") == NULL)
 | |
| 			return 1;
 | |
| #else
 | |
| 		return 1;
 | |
| #endif
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #if !defined(CONFIG_DM_PCI) || defined(CONFIG_DM_PCI_COMPAT)
 | |
| /* 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_TYPE) == 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * pci_hose_phys_to_bus(): Convert physical address to bus address
 | |
|  * @hose:	PCI hose of the root PCI controller
 | |
|  * @phys_addr:	physical address to convert
 | |
|  * @flags:	flags of pci regions
 | |
|  * @return bus address if OK, 0 on error
 | |
|  */
 | |
| 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_TYPE) == 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| #endif /* !CONFIG_DM_PCI || CONFIG_DM_PCI_COMPAT */
 |