Driver model will share many functions with the existing PCI implementation. Move these into their own file to avoid duplication and confusion. Signed-off-by: Simon Glass <sjg@chromium.org>master
parent
fa67f90fc3
commit
aab6724c90
@ -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; |
||||||
|
} |
Loading…
Reference in new issue