mirror of
https://github.com/AsahiLinux/u-boot
synced 2025-02-17 22:49:02 +00:00
dm: pci: Add a uclass for PCI
Add a uclass for PCI controllers and a generic one for PCI devices. Adjust the 'pci' command and the existing PCI support to work with this new uclass. Keep most of the compatibility code in a separate file so that it can be removed one day. TODO: Add more header file comments to the new parts of pci.h Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
aab6724c90
commit
ff3e077bd2
10 changed files with 1081 additions and 14 deletions
|
@ -230,7 +230,9 @@ static int initr_unlock_ram_in_cache(void)
|
|||
#ifdef CONFIG_PCI
|
||||
static int initr_pci(void)
|
||||
{
|
||||
#ifndef CONFIG_DM_PCI
|
||||
pci_init();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ void pciinfo(int BusNum, int ShortPCIListing)
|
|||
unsigned char HeaderType;
|
||||
unsigned short VendorID;
|
||||
pci_dev_t dev;
|
||||
int ret;
|
||||
|
||||
if (!hose)
|
||||
return;
|
||||
|
@ -74,7 +75,10 @@ void pciinfo(int BusNum, int ShortPCIListing)
|
|||
if (pci_skip_dev(hose, dev))
|
||||
continue;
|
||||
|
||||
pci_read_config_word(dev, PCI_VENDOR_ID, &VendorID);
|
||||
ret = pci_read_config_word(dev, PCI_VENDOR_ID,
|
||||
&VendorID);
|
||||
if (ret)
|
||||
goto error;
|
||||
if ((VendorID == 0xFFFF) || (VendorID == 0x0000))
|
||||
continue;
|
||||
|
||||
|
@ -91,8 +95,12 @@ void pciinfo(int BusNum, int ShortPCIListing)
|
|||
BusNum, Device, Function);
|
||||
pci_header_show(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
error:
|
||||
printf("Cannot read bus configuration: %d\n", ret);
|
||||
}
|
||||
|
||||
|
||||
|
|
70
doc/driver-model/pci-info.txt
Normal file
70
doc/driver-model/pci-info.txt
Normal file
|
@ -0,0 +1,70 @@
|
|||
PCI with Driver Model
|
||||
=====================
|
||||
|
||||
How busses are scanned
|
||||
----------------------
|
||||
|
||||
Any config read will end up at pci_read_config(). This uses
|
||||
uclass_get_device_by_seq() to get the PCI bus for a particular bus number.
|
||||
Bus number 0 will need to be requested first, and the alias in the device
|
||||
tree file will point to the correct device:
|
||||
|
||||
|
||||
aliases {
|
||||
pci0 = &pci;
|
||||
};
|
||||
|
||||
pci: pci-controller {
|
||||
compatible = "sandbox,pci";
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
If there is no alias the devices will be numbered sequentially in the device
|
||||
tree.
|
||||
|
||||
The call to uclass_get_device by seq() will cause the PCI bus to be probed.
|
||||
This does a scan of the bus to locate available devices. These devices are
|
||||
bound to their appropriate driver if available. If there is no driver, then
|
||||
they are bound to a generic PCI driver which does nothing.
|
||||
|
||||
After probing a bus, the available devices will appear in the device tree
|
||||
under that bus.
|
||||
|
||||
Note that this is all done on a lazy basis, as needed, so until something is
|
||||
touched on PCI it will not be probed.
|
||||
|
||||
PCI devices can appear in the device tree. If they do this serves to specify
|
||||
the driver to use for the device. In this case they will be bound at
|
||||
start-up.
|
||||
|
||||
|
||||
Sandbox
|
||||
-------
|
||||
|
||||
With sandbox we need a device emulator for each device on the bus since there
|
||||
is no real PCI bus. This works by looking in the device tree node for a
|
||||
driver. For example:
|
||||
|
||||
|
||||
pci@1f,0 {
|
||||
compatible = "pci-generic";
|
||||
reg = <0xf800 0 0 0 0>;
|
||||
emul@1f,0 {
|
||||
compatible = "sandbox,swap-case";
|
||||
};
|
||||
};
|
||||
|
||||
This means that there is a 'sandbox,swap-case' driver at that bus position.
|
||||
Note that the first cell in the 'reg' value is the bus/device/function. See
|
||||
PCI_BDF() for the encoding (it is also specified in the IEEE Std 1275-1994
|
||||
PCI bus binding document, v2.1)
|
||||
|
||||
When this bus is scanned we will end up with something like this:
|
||||
|
||||
`- * pci-controller @ 05c660c8, 0
|
||||
`- pci@1f,0 @ 05c661c8, 63488
|
||||
`- emul@1f,0 @ 05c662c8
|
||||
|
||||
When accesses go to the pci@1f,0 device they are forwarded to its child, the
|
||||
emulator.
|
|
@ -0,0 +1,12 @@
|
|||
menu "PCI"
|
||||
|
||||
config DM_PCI
|
||||
bool "Enable driver mode for PCI"
|
||||
depends on DM
|
||||
help
|
||||
Use driver model for PCI. Driver model is the new method for
|
||||
orgnising devices in U-Boot. For PCI, driver model keeps track of
|
||||
available PCI devices, allows scanning of PCI buses and provides
|
||||
device configuration support.
|
||||
|
||||
endmenu
|
|
@ -5,8 +5,14 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
|
||||
ifneq ($(CONFIG_DM_PCI),)
|
||||
obj-$(CONFIG_PCI) += pci-uclass.o pci_compat.o
|
||||
else
|
||||
obj-$(CONFIG_PCI) += pci.o
|
||||
endif
|
||||
obj-$(CONFIG_PCI) += pci_common.o pci_auto.o pci_rom.o
|
||||
|
||||
obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.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
|
||||
|
|
639
drivers/pci/pci-uclass.c
Normal file
639
drivers/pci/pci-uclass.c
Normal file
|
@ -0,0 +1,639 @@
|
|||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <inttypes.h>
|
||||
#include <pci.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/root.h>
|
||||
#include <dm/device-internal.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
struct pci_controller *pci_bus_to_hose(int busnum)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_PCI, busnum, &bus);
|
||||
if (ret) {
|
||||
debug("%s: Cannot get bus %d: ret=%d\n", __func__, busnum, ret);
|
||||
return NULL;
|
||||
}
|
||||
return dev_get_uclass_priv(bus);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_get_bus_max() - returns the bus number of the last active bus
|
||||
*
|
||||
* @return last bus number, or -1 if no active buses
|
||||
*/
|
||||
static int pci_get_bus_max(void)
|
||||
{
|
||||
struct udevice *bus;
|
||||
struct uclass *uc;
|
||||
int ret = -1;
|
||||
|
||||
ret = uclass_get(UCLASS_PCI, &uc);
|
||||
uclass_foreach_dev(bus, uc) {
|
||||
if (bus->seq > ret)
|
||||
ret = bus->seq;
|
||||
}
|
||||
|
||||
debug("%s: ret=%d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int pci_last_busno(void)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
struct udevice *bus;
|
||||
struct uclass *uc;
|
||||
int ret;
|
||||
|
||||
debug("pci_last_busno\n");
|
||||
ret = uclass_get(UCLASS_PCI, &uc);
|
||||
if (ret || list_empty(&uc->dev_head))
|
||||
return -1;
|
||||
|
||||
/* Probe the last bus */
|
||||
bus = list_entry(uc->dev_head.prev, struct udevice, uclass_node);
|
||||
debug("bus = %p, %s\n", bus, bus->name);
|
||||
assert(bus);
|
||||
ret = device_probe(bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If that bus has bridges, we may have new buses now. Get the last */
|
||||
bus = list_entry(uc->dev_head.prev, struct udevice, uclass_node);
|
||||
hose = dev_get_uclass_priv(bus);
|
||||
debug("bus = %s, hose = %p\n", bus->name, hose);
|
||||
|
||||
return hose->last_busno;
|
||||
}
|
||||
|
||||
int pci_get_ff(enum pci_size_t size)
|
||||
{
|
||||
switch (size) {
|
||||
case PCI_SIZE_8:
|
||||
return 0xff;
|
||||
case PCI_SIZE_16:
|
||||
return 0xffff;
|
||||
default:
|
||||
return 0xffffffff;
|
||||
}
|
||||
}
|
||||
|
||||
int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
for (device_find_first_child(bus, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
struct pci_child_platdata *pplat;
|
||||
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
if (pplat && pplat->devfn == find_devfn) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
return pci_bus_find_devfn(bus, PCI_MASK_BUS(bdf), devp);
|
||||
}
|
||||
|
||||
static int pci_device_matches_ids(struct udevice *dev,
|
||||
struct pci_device_id *ids)
|
||||
{
|
||||
struct pci_child_platdata *pplat;
|
||||
int i;
|
||||
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
if (!pplat)
|
||||
return -EINVAL;
|
||||
for (i = 0; ids[i].vendor != 0; i++) {
|
||||
if (pplat->vendor == ids[i].vendor &&
|
||||
pplat->device == ids[i].device)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids,
|
||||
int *indexp, struct udevice **devp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
/* Scan all devices on this bus */
|
||||
for (device_find_first_child(bus, &dev);
|
||||
dev;
|
||||
device_find_next_child(&dev)) {
|
||||
if (pci_device_matches_ids(dev, ids) >= 0) {
|
||||
if ((*indexp)-- <= 0) {
|
||||
*devp = dev;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_find_device_id(struct pci_device_id *ids, int index,
|
||||
struct udevice **devp)
|
||||
{
|
||||
struct udevice *bus;
|
||||
|
||||
/* Scan all known buses */
|
||||
for (uclass_first_device(UCLASS_PCI, &bus);
|
||||
bus;
|
||||
uclass_next_device(&bus)) {
|
||||
if (!pci_bus_find_devices(bus, ids, &index, devp))
|
||||
return 0;
|
||||
}
|
||||
*devp = NULL;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int pci_bus_write_config(struct udevice *bus, pci_dev_t bdf, int offset,
|
||||
unsigned long value, enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_ops *ops;
|
||||
|
||||
ops = pci_get_ops(bus);
|
||||
if (!ops->write_config)
|
||||
return -ENOSYS;
|
||||
return ops->write_config(bus, bdf, offset, value, size);
|
||||
}
|
||||
|
||||
int pci_write_config(pci_dev_t bdf, int offset, unsigned long value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pci_bus_write_config(bus, PCI_MASK_BUS(bdf), offset, value,
|
||||
size);
|
||||
}
|
||||
|
||||
int pci_write_config32(pci_dev_t bdf, int offset, u32 value)
|
||||
{
|
||||
return pci_write_config(bdf, offset, value, PCI_SIZE_32);
|
||||
}
|
||||
|
||||
int pci_write_config16(pci_dev_t bdf, int offset, u16 value)
|
||||
{
|
||||
return pci_write_config(bdf, offset, value, PCI_SIZE_16);
|
||||
}
|
||||
|
||||
int pci_write_config8(pci_dev_t bdf, int offset, u8 value)
|
||||
{
|
||||
return pci_write_config(bdf, offset, value, PCI_SIZE_8);
|
||||
}
|
||||
|
||||
int pci_bus_read_config(struct udevice *bus, pci_dev_t bdf, int offset,
|
||||
unsigned long *valuep, enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_ops *ops;
|
||||
|
||||
ops = pci_get_ops(bus);
|
||||
if (!ops->read_config)
|
||||
return -ENOSYS;
|
||||
return ops->read_config(bus, bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
int pci_read_config(pci_dev_t bdf, int offset, unsigned long *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = uclass_get_device_by_seq(UCLASS_PCI, PCI_BUS(bdf), &bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return pci_bus_read_config(bus, PCI_MASK_BUS(bdf), offset, valuep,
|
||||
size);
|
||||
}
|
||||
|
||||
int pci_read_config32(pci_dev_t bdf, int offset, u32 *valuep)
|
||||
{
|
||||
unsigned long value;
|
||||
int ret;
|
||||
|
||||
ret = pci_read_config(bdf, offset, &value, PCI_SIZE_32);
|
||||
if (ret)
|
||||
return ret;
|
||||
*valuep = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_read_config16(pci_dev_t bdf, int offset, u16 *valuep)
|
||||
{
|
||||
unsigned long value;
|
||||
int ret;
|
||||
|
||||
ret = pci_read_config(bdf, offset, &value, PCI_SIZE_16);
|
||||
if (ret)
|
||||
return ret;
|
||||
*valuep = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_read_config8(pci_dev_t bdf, int offset, u8 *valuep)
|
||||
{
|
||||
unsigned long value;
|
||||
int ret;
|
||||
|
||||
ret = pci_read_config(bdf, offset, &value, PCI_SIZE_8);
|
||||
if (ret)
|
||||
return ret;
|
||||
*valuep = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_auto_config_devices(struct udevice *bus)
|
||||
{
|
||||
struct pci_controller *hose = bus->uclass_priv;
|
||||
unsigned int sub_bus;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
sub_bus = bus->seq;
|
||||
debug("%s: start\n", __func__);
|
||||
pciauto_config_init(hose);
|
||||
for (ret = device_find_first_child(bus, &dev);
|
||||
!ret && dev;
|
||||
ret = device_find_next_child(&dev)) {
|
||||
struct pci_child_platdata *pplat;
|
||||
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
unsigned int max_bus;
|
||||
pci_dev_t bdf;
|
||||
|
||||
bdf = PCI_ADD_BUS(bus->seq, pplat->devfn);
|
||||
debug("%s: device %s\n", __func__, dev->name);
|
||||
max_bus = pciauto_config_device(hose, bdf);
|
||||
sub_bus = max(sub_bus, max_bus);
|
||||
}
|
||||
debug("%s: done\n", __func__);
|
||||
|
||||
return sub_bus;
|
||||
}
|
||||
|
||||
int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf)
|
||||
{
|
||||
struct udevice *parent, *bus;
|
||||
int sub_bus;
|
||||
int ret;
|
||||
|
||||
debug("%s\n", __func__);
|
||||
parent = hose->bus;
|
||||
|
||||
/* Find the bus within the parent */
|
||||
ret = pci_bus_find_devfn(parent, bdf, &bus);
|
||||
if (ret) {
|
||||
debug("%s: Cannot find device %x on bus %s: %d\n", __func__,
|
||||
bdf, parent->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sub_bus = pci_get_bus_max() + 1;
|
||||
debug("%s: bus = %d/%s\n", __func__, sub_bus, bus->name);
|
||||
pciauto_prescan_setup_bridge(hose, bdf, bus->seq);
|
||||
|
||||
ret = device_probe(bus);
|
||||
if (ret) {
|
||||
debug("%s: Cannot probe bus bus %s: %d\n", __func__, bus->name,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
if (sub_bus != bus->seq) {
|
||||
printf("%s: Internal error, bus '%s' got seq %d, expected %d\n",
|
||||
__func__, bus->name, bus->seq, sub_bus);
|
||||
return -EPIPE;
|
||||
}
|
||||
sub_bus = pci_get_bus_max();
|
||||
pciauto_postscan_setup_bridge(hose, bdf, sub_bus);
|
||||
|
||||
return sub_bus;
|
||||
}
|
||||
|
||||
int pci_bind_bus_devices(struct udevice *bus)
|
||||
{
|
||||
ulong vendor, device;
|
||||
ulong header_type;
|
||||
pci_dev_t devfn, end;
|
||||
bool found_multi;
|
||||
int ret;
|
||||
|
||||
found_multi = false;
|
||||
end = PCI_DEVFN(PCI_MAX_PCI_DEVICES - 1, PCI_MAX_PCI_FUNCTIONS - 1);
|
||||
for (devfn = PCI_DEVFN(0, 0); devfn < end; devfn += PCI_DEVFN(0, 1)) {
|
||||
struct pci_child_platdata *pplat;
|
||||
struct udevice *dev;
|
||||
ulong class;
|
||||
|
||||
if (PCI_FUNC(devfn) && !found_multi)
|
||||
continue;
|
||||
/* Check only the first access, we don't expect problems */
|
||||
ret = pci_bus_read_config(bus, devfn, PCI_HEADER_TYPE,
|
||||
&header_type, PCI_SIZE_8);
|
||||
if (ret)
|
||||
goto error;
|
||||
pci_bus_read_config(bus, devfn, PCI_VENDOR_ID, &vendor,
|
||||
PCI_SIZE_16);
|
||||
if (vendor == 0xffff || vendor == 0x0000)
|
||||
continue;
|
||||
|
||||
if (!PCI_FUNC(devfn))
|
||||
found_multi = header_type & 0x80;
|
||||
|
||||
debug("%s: bus %d/%s: found device %x, function %d\n", __func__,
|
||||
bus->seq, bus->name, PCI_DEV(devfn), PCI_FUNC(devfn));
|
||||
pci_bus_read_config(bus, devfn, PCI_DEVICE_ID, &device,
|
||||
PCI_SIZE_16);
|
||||
pci_bus_read_config(bus, devfn, PCI_CLASS_DEVICE, &class,
|
||||
PCI_SIZE_16);
|
||||
|
||||
/* Find this device in the device tree */
|
||||
ret = pci_bus_find_devfn(bus, devfn, &dev);
|
||||
|
||||
/* If nothing in the device tree, bind a generic device */
|
||||
if (ret == -ENODEV) {
|
||||
char name[30], *str;
|
||||
const char *drv;
|
||||
|
||||
sprintf(name, "pci_%x:%x.%x", bus->seq,
|
||||
PCI_DEV(devfn), PCI_FUNC(devfn));
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
drv = class == PCI_CLASS_BRIDGE_PCI ?
|
||||
"pci_bridge_drv" : "pci_generic_drv";
|
||||
ret = device_bind_driver(bus, drv, str, &dev);
|
||||
}
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update the platform data */
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
pplat->devfn = devfn;
|
||||
pplat->vendor = vendor;
|
||||
pplat->device = device;
|
||||
pplat->class = class;
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
printf("Cannot read bus configuration: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pci_uclass_post_bind(struct udevice *bus)
|
||||
{
|
||||
/*
|
||||
* Scan the device tree for devices. This does not probe the PCI bus,
|
||||
* as this is not permitted while binding. It just finds devices
|
||||
* mentioned in the device tree.
|
||||
*
|
||||
* Before relocation, only bind devices marked for pre-relocation
|
||||
* use.
|
||||
*/
|
||||
return dm_scan_fdt_node(bus, gd->fdt_blob, bus->of_offset,
|
||||
gd->flags & GD_FLG_RELOC ? false : true);
|
||||
}
|
||||
|
||||
static int decode_regions(struct pci_controller *hose, const void *blob,
|
||||
int parent_node, int node)
|
||||
{
|
||||
int pci_addr_cells, addr_cells, size_cells;
|
||||
int cells_per_record;
|
||||
const u32 *prop;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
prop = fdt_getprop(blob, node, "ranges", &len);
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
pci_addr_cells = fdt_address_cells(blob, node);
|
||||
addr_cells = fdt_address_cells(blob, parent_node);
|
||||
size_cells = fdt_size_cells(blob, node);
|
||||
|
||||
/* PCI addresses are always 3-cells */
|
||||
len /= sizeof(u32);
|
||||
cells_per_record = pci_addr_cells + addr_cells + size_cells;
|
||||
hose->region_count = 0;
|
||||
debug("%s: len=%d, cells_per_record=%d\n", __func__, len,
|
||||
cells_per_record);
|
||||
for (i = 0; i < MAX_PCI_REGIONS; i++, len -= cells_per_record) {
|
||||
u64 pci_addr, addr, size;
|
||||
int space_code;
|
||||
u32 flags;
|
||||
int type;
|
||||
|
||||
if (len < cells_per_record)
|
||||
break;
|
||||
flags = fdt32_to_cpu(prop[0]);
|
||||
space_code = (flags >> 24) & 3;
|
||||
pci_addr = fdtdec_get_number(prop + 1, 2);
|
||||
prop += pci_addr_cells;
|
||||
addr = fdtdec_get_number(prop, addr_cells);
|
||||
prop += addr_cells;
|
||||
size = fdtdec_get_number(prop, size_cells);
|
||||
prop += size_cells;
|
||||
debug("%s: region %d, pci_addr=%" PRIx64 ", addr=%" PRIx64
|
||||
", size=%" PRIx64 ", space_code=%d\n", __func__,
|
||||
hose->region_count, pci_addr, addr, size, space_code);
|
||||
if (space_code & 2) {
|
||||
type = flags & (1U << 30) ? PCI_REGION_PREFETCH :
|
||||
PCI_REGION_MEM;
|
||||
} else if (space_code & 1) {
|
||||
type = PCI_REGION_IO;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
debug(" - type=%d\n", type);
|
||||
pci_set_region(hose->regions + hose->region_count++, pci_addr,
|
||||
addr, size, type);
|
||||
}
|
||||
|
||||
/* Add a region for our local memory */
|
||||
pci_set_region(hose->regions + hose->region_count++, 0, 0,
|
||||
gd->ram_size, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_uclass_pre_probe(struct udevice *bus)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
int ret;
|
||||
|
||||
debug("%s, bus=%d/%s, parent=%s\n", __func__, bus->seq, bus->name,
|
||||
bus->parent->name);
|
||||
hose = bus->uclass_priv;
|
||||
|
||||
/* For bridges, use the top-level PCI controller */
|
||||
if (device_get_uclass_id(bus->parent) == UCLASS_ROOT) {
|
||||
hose->ctlr = bus;
|
||||
ret = decode_regions(hose, gd->fdt_blob, bus->parent->of_offset,
|
||||
bus->of_offset);
|
||||
if (ret) {
|
||||
debug("%s: Cannot decode regions\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
struct pci_controller *parent_hose;
|
||||
|
||||
parent_hose = dev_get_uclass_priv(bus->parent);
|
||||
hose->ctlr = parent_hose->bus;
|
||||
}
|
||||
hose->bus = bus;
|
||||
hose->first_busno = bus->seq;
|
||||
hose->last_busno = bus->seq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pci_uclass_post_probe(struct udevice *bus)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Don't scan buses before relocation */
|
||||
if (!(gd->flags & GD_FLG_RELOC))
|
||||
return 0;
|
||||
|
||||
debug("%s: probing bus %d\n", __func__, bus->seq);
|
||||
ret = pci_bind_bus_devices(bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_PCI_PNP
|
||||
ret = pci_auto_config_devices(bus);
|
||||
#endif
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int pci_uclass_child_post_bind(struct udevice *dev)
|
||||
{
|
||||
struct pci_child_platdata *pplat;
|
||||
struct fdt_pci_addr addr;
|
||||
int ret;
|
||||
|
||||
if (dev->of_offset == -1)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We could read vendor, device, class if available. But for now we
|
||||
* just check the address.
|
||||
*/
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
ret = fdtdec_get_pci_addr(gd->fdt_blob, dev->of_offset,
|
||||
FDT_PCI_SPACE_CONFIG, "reg", &addr);
|
||||
|
||||
if (ret) {
|
||||
if (ret != -ENOENT)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* extract the bdf from fdt_pci_addr */
|
||||
pplat->devfn = addr.phys_hi & 0xffff00;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_bridge_read_config(struct udevice *bus, pci_dev_t devfn, uint offset,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
struct pci_controller *hose = bus->uclass_priv;
|
||||
pci_dev_t bdf = PCI_ADD_BUS(bus->seq, devfn);
|
||||
|
||||
return pci_bus_read_config(hose->ctlr, bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
int pci_bridge_write_config(struct udevice *bus, pci_dev_t devfn, uint offset,
|
||||
ulong value, enum pci_size_t size)
|
||||
{
|
||||
struct pci_controller *hose = bus->uclass_priv;
|
||||
pci_dev_t bdf = PCI_ADD_BUS(bus->seq, devfn);
|
||||
|
||||
return pci_bus_write_config(hose->ctlr, bdf, offset, value, size);
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(pci) = {
|
||||
.id = UCLASS_PCI,
|
||||
.name = "pci",
|
||||
.post_bind = pci_uclass_post_bind,
|
||||
.pre_probe = pci_uclass_pre_probe,
|
||||
.post_probe = pci_uclass_post_probe,
|
||||
.child_post_bind = pci_uclass_child_post_bind,
|
||||
.per_device_auto_alloc_size = sizeof(struct pci_controller),
|
||||
.per_child_platdata_auto_alloc_size =
|
||||
sizeof(struct pci_child_platdata),
|
||||
};
|
||||
|
||||
static const struct dm_pci_ops pci_bridge_ops = {
|
||||
.read_config = pci_bridge_read_config,
|
||||
.write_config = pci_bridge_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_bridge_ids[] = {
|
||||
{ .compatible = "pci-bridge" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_bridge_drv) = {
|
||||
.name = "pci_bridge_drv",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pci_bridge_ids,
|
||||
.ops = &pci_bridge_ops,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(pci_generic) = {
|
||||
.id = UCLASS_PCI_GENERIC,
|
||||
.name = "pci_generic",
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_generic_ids[] = {
|
||||
{ .compatible = "pci-generic" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_generic_drv) = {
|
||||
.name = "pci_generic_drv",
|
||||
.id = UCLASS_PCI_GENERIC,
|
||||
.of_match = pci_generic_ids,
|
||||
};
|
|
@ -432,13 +432,20 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
|
|||
|
||||
switch (class) {
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
hose->current_busno++;
|
||||
DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n",
|
||||
PCI_DEV(dev));
|
||||
|
||||
pciauto_setup_device(hose, dev, 2, hose->pci_mem,
|
||||
hose->pci_prefetch, hose->pci_io);
|
||||
|
||||
DEBUGF("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_DEV(dev));
|
||||
|
||||
#ifdef CONFIG_DM_PCI
|
||||
n = dm_pci_hose_probe_bus(hose, dev);
|
||||
if (n < 0)
|
||||
return n;
|
||||
sub_bus = (unsigned int)n;
|
||||
#else
|
||||
/* Passing in current_busno allows for sibling P2P bridges */
|
||||
hose->current_busno++;
|
||||
pciauto_prescan_setup_bridge(hose, dev, hose->current_busno);
|
||||
/*
|
||||
* need to figure out if this is a subordinate bridge on the bus
|
||||
|
@ -451,6 +458,7 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
|
|||
pciauto_postscan_setup_bridge(hose, dev, sub_bus);
|
||||
|
||||
sub_bus = hose->current_busno;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PCI_CLASS_STORAGE_IDE:
|
||||
|
@ -475,7 +483,9 @@ int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
|
|||
DEBUGF("PCI Autoconfig: Found P2CardBus bridge, device %d\n",
|
||||
PCI_DEV(dev));
|
||||
|
||||
#ifndef CONFIG_DM_PCI
|
||||
hose->current_busno++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_PCIAUTO_SKIP_HOST_BRIDGE)
|
||||
|
|
43
drivers/pci/pci_compat.c
Normal file
43
drivers/pci/pci_compat.c
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Compatibility functions for pre-driver-model code
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0+
|
||||
*/
|
||||
#define DEBUG
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <pci.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
#define PCI_HOSE_OP(rw, name, size, type) \
|
||||
int pci_hose_##rw##_config_##name(struct pci_controller *hose, \
|
||||
pci_dev_t dev, \
|
||||
int offset, type value) \
|
||||
{ \
|
||||
return pci_##rw##_config##size(dev, offset, value); \
|
||||
}
|
||||
|
||||
PCI_HOSE_OP(read, byte, 8, u8 *)
|
||||
PCI_HOSE_OP(read, word, 16, u16 *)
|
||||
PCI_HOSE_OP(read, dword, 32, u32 *)
|
||||
PCI_HOSE_OP(write, byte, 8, u8)
|
||||
PCI_HOSE_OP(write, word, 16, u16)
|
||||
PCI_HOSE_OP(write, dword, 32, u32)
|
||||
|
||||
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
|
||||
{
|
||||
struct pci_child_platdata *pplat;
|
||||
struct udevice *bus, *dev;
|
||||
|
||||
if (pci_find_device_id(ids, index, &dev))
|
||||
return -1;
|
||||
bus = dev->parent;
|
||||
pplat = dev_get_parent_platdata(dev);
|
||||
|
||||
return PCI_ADD_BUS(bus->seq, pplat->devfn);
|
||||
}
|
|
@ -34,6 +34,8 @@ enum uclass_id {
|
|||
UCLASS_I2C_GENERIC, /* Generic I2C device */
|
||||
UCLASS_I2C_EEPROM, /* I2C EEPROM device */
|
||||
UCLASS_MOD_EXP, /* RSA Mod Exp device */
|
||||
UCLASS_PCI, /* PCI bus */
|
||||
UCLASS_PCI_GENERIC, /* Generic PCI bus device */
|
||||
|
||||
UCLASS_COUNT,
|
||||
UCLASS_INVALID = -1,
|
||||
|
|
289
include/pci.h
289
include/pci.h
|
@ -457,12 +457,15 @@ static inline void pci_set_region(struct pci_region *reg,
|
|||
|
||||
typedef int pci_dev_t;
|
||||
|
||||
#define PCI_BUS(d) (((d) >> 16) & 0xff)
|
||||
#define PCI_DEV(d) (((d) >> 11) & 0x1f)
|
||||
#define PCI_FUNC(d) (((d) >> 8) & 0x7)
|
||||
#define PCI_BDF(b,d,f) ((b) << 16 | (d) << 11 | (f) << 8)
|
||||
|
||||
#define PCI_ANY_ID (~0)
|
||||
#define PCI_BUS(d) (((d) >> 16) & 0xff)
|
||||
#define PCI_DEV(d) (((d) >> 11) & 0x1f)
|
||||
#define PCI_FUNC(d) (((d) >> 8) & 0x7)
|
||||
#define PCI_DEVFN(d, f) ((d) << 11 | (f) << 8)
|
||||
#define PCI_MASK_BUS(bdf) ((bdf) & 0xffff)
|
||||
#define PCI_ADD_BUS(bus, devfn) (((bus) << 16) | (devfn))
|
||||
#define PCI_BDF(b, d, f) ((b) << 16 | PCI_DEVFN(d, f))
|
||||
#define PCI_VENDEV(v, d) (((v) << 16) | (d))
|
||||
#define PCI_ANY_ID (~0)
|
||||
|
||||
struct pci_device_id {
|
||||
unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */
|
||||
|
@ -495,7 +498,12 @@ extern void pci_cfgfunc_config_device(struct pci_controller* hose, pci_dev_t dev
|
|||
* Structure of a PCI controller (host bridge)
|
||||
*/
|
||||
struct pci_controller {
|
||||
#ifdef CONFIG_DM_PCI
|
||||
struct udevice *bus;
|
||||
struct udevice *ctlr;
|
||||
#else
|
||||
struct pci_controller *next;
|
||||
#endif
|
||||
|
||||
int first_busno;
|
||||
int last_busno;
|
||||
|
@ -511,7 +519,7 @@ struct pci_controller {
|
|||
struct pci_config_table *config_table;
|
||||
|
||||
void (*fixup_irq)(struct pci_controller *, pci_dev_t);
|
||||
|
||||
#ifndef CONFIG_DM_PCI
|
||||
/* Low-level architecture-dependent routines */
|
||||
int (*read_byte)(struct pci_controller*, pci_dev_t, int where, u8 *);
|
||||
int (*read_word)(struct pci_controller*, pci_dev_t, int where, u16 *);
|
||||
|
@ -519,17 +527,21 @@ struct pci_controller {
|
|||
int (*write_byte)(struct pci_controller*, pci_dev_t, int where, u8);
|
||||
int (*write_word)(struct pci_controller*, pci_dev_t, int where, u16);
|
||||
int (*write_dword)(struct pci_controller*, pci_dev_t, int where, u32);
|
||||
#endif
|
||||
|
||||
/* Used by auto config */
|
||||
struct pci_region *pci_mem, *pci_io, *pci_prefetch;
|
||||
|
||||
/* Used by ppc405 autoconfig*/
|
||||
struct pci_region *pci_fb;
|
||||
#ifndef CONFIG_DM_PCI
|
||||
int current_busno;
|
||||
|
||||
void *priv_data;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef CONFIG_DM_PCI
|
||||
static inline void pci_set_ops(struct pci_controller *hose,
|
||||
int (*read_byte)(struct pci_controller*,
|
||||
pci_dev_t, int where, u8 *),
|
||||
|
@ -550,6 +562,7 @@ static inline void pci_set_ops(struct pci_controller *hose,
|
|||
hose->write_word = write_word;
|
||||
hose->write_dword = write_dword;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_INDIRECT_BRIDGE
|
||||
extern void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data);
|
||||
|
@ -602,12 +615,14 @@ extern int pci_hose_write_config_word(struct pci_controller *hose,
|
|||
extern int pci_hose_write_config_dword(struct pci_controller *hose,
|
||||
pci_dev_t dev, int where, u32 val);
|
||||
|
||||
#ifndef CONFIG_DM_PCI
|
||||
extern int pci_read_config_byte(pci_dev_t dev, int where, u8 *val);
|
||||
extern int pci_read_config_word(pci_dev_t dev, int where, u16 *val);
|
||||
extern int pci_read_config_dword(pci_dev_t dev, int where, u32 *val);
|
||||
extern int pci_write_config_byte(pci_dev_t dev, int where, u8 val);
|
||||
extern int pci_write_config_word(pci_dev_t dev, int where, u16 val);
|
||||
extern int pci_write_config_dword(pci_dev_t dev, int where, u32 val);
|
||||
#endif
|
||||
|
||||
extern int pci_hose_read_config_byte_via_dword(struct pci_controller *hose,
|
||||
pci_dev_t dev, int where, u8 *val);
|
||||
|
@ -719,5 +734,265 @@ int pciauto_setup_rom(struct pci_controller *hose, pci_dev_t dev);
|
|||
pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum,
|
||||
struct pci_device_id *ids, int *indexp);
|
||||
|
||||
/* Access sizes for PCI reads and writes */
|
||||
enum pci_size_t {
|
||||
PCI_SIZE_8,
|
||||
PCI_SIZE_16,
|
||||
PCI_SIZE_32,
|
||||
};
|
||||
|
||||
struct udevice;
|
||||
|
||||
#ifdef CONFIG_DM_PCI
|
||||
/**
|
||||
* struct pci_child_platdata - information stored about each PCI device
|
||||
*
|
||||
* Every device on a PCI bus has this per-child data.
|
||||
*
|
||||
* It can be accessed using dev_get_parentdata(dev) if dev->parent is a
|
||||
* PCI bus (i.e. UCLASS_PCI)
|
||||
*
|
||||
* @devfn: Encoded device and function index - see PCI_DEVFN()
|
||||
* @vendor: PCI vendor ID (see pci_ids.h)
|
||||
* @device: PCI device ID (see pci_ids.h)
|
||||
* @class: PCI class, 3 bytes: (base, sub, prog-if)
|
||||
*/
|
||||
struct pci_child_platdata {
|
||||
int devfn;
|
||||
unsigned short vendor;
|
||||
unsigned short device;
|
||||
unsigned int class;
|
||||
};
|
||||
|
||||
/* PCI bus operations */
|
||||
struct dm_pci_ops {
|
||||
/**
|
||||
* read_config() - Read a PCI configuration value
|
||||
*
|
||||
* PCI buses must support reading and writing configuration values
|
||||
* so that the bus can be scanned and its devices configured.
|
||||
*
|
||||
* Normally PCI_BUS(@bdf) is the same as @bus->seq, but not always.
|
||||
* If bridges exist it is possible to use the top-level bus to
|
||||
* access a sub-bus. In that case @bus will be the top-level bus
|
||||
* and PCI_BUS(bdf) will be a different (higher) value
|
||||
*
|
||||
* @bus: Bus to read from
|
||||
* @bdf: Bus, device and function to read
|
||||
* @offset: Byte offset within the device's configuration space
|
||||
* @valuep: Place to put the returned value
|
||||
* @size: Access size
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*read_config)(struct udevice *bus, pci_dev_t bdf, uint offset,
|
||||
ulong *valuep, enum pci_size_t size);
|
||||
/**
|
||||
* write_config() - Write a PCI configuration value
|
||||
*
|
||||
* @bus: Bus to write to
|
||||
* @bdf: Bus, device and function to write
|
||||
* @offset: Byte offset within the device's configuration space
|
||||
* @value: Value to write
|
||||
* @size: Access size
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int (*write_config)(struct udevice *bus, pci_dev_t bdf, uint offset,
|
||||
ulong value, enum pci_size_t size);
|
||||
};
|
||||
|
||||
/* Get access to a PCI bus' operations */
|
||||
#define pci_get_ops(dev) ((struct dm_pci_ops *)(dev)->driver->ops)
|
||||
|
||||
/**
|
||||
* pci_bind_bus_devices() - scan a PCI bus and bind devices
|
||||
*
|
||||
* Scan a PCI bus looking for devices. Bind each one that is found. If
|
||||
* devices are already bound that match the scanned devices, just update the
|
||||
* child data so that the device can be used correctly (this happens when
|
||||
* the device tree describes devices we expect to see on the bus).
|
||||
*
|
||||
* Devices that are bound in this way will use a generic PCI driver which
|
||||
* does nothing. The device can still be accessed but will not provide any
|
||||
* driver interface.
|
||||
*
|
||||
* @bus: Bus containing devices to bind
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int pci_bind_bus_devices(struct udevice *bus);
|
||||
|
||||
/**
|
||||
* pci_auto_config_devices() - configure bus devices ready for use
|
||||
*
|
||||
* This works through all devices on a bus by scanning the driver model
|
||||
* data structures (normally these have been set up by pci_bind_bus_devices()
|
||||
* earlier).
|
||||
*
|
||||
* Space is allocated for each PCI base address register (BAR) so that the
|
||||
* devices are mapped into memory and I/O space ready for use.
|
||||
*
|
||||
* @bus: Bus containing devices to bind
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int pci_auto_config_devices(struct udevice *bus);
|
||||
|
||||
/**
|
||||
* pci_bus_find_bdf() - Find a device given its PCI bus address
|
||||
*
|
||||
* @bdf: PCI device address: bus, device and function -see PCI_BDF()
|
||||
* @devp: Returns the device for this address, if found
|
||||
* @return 0 if OK, -ENODEV if not found
|
||||
*/
|
||||
int pci_bus_find_bdf(pci_dev_t bdf, struct udevice **devp);
|
||||
|
||||
/**
|
||||
* pci_bus_find_devfn() - Find a device on a bus
|
||||
*
|
||||
* @find_devfn: PCI device address (device and function only)
|
||||
* @devp: Returns the device for this address, if found
|
||||
* @return 0 if OK, -ENODEV if not found
|
||||
*/
|
||||
int pci_bus_find_devfn(struct udevice *bus, pci_dev_t find_devfn,
|
||||
struct udevice **devp);
|
||||
|
||||
/**
|
||||
* pci_get_ff() - Returns a mask for the given access size
|
||||
*
|
||||
* @size: Access size
|
||||
* @return 0xff for PCI_SIZE_8, 0xffff for PCI_SIZE_16, 0xffffffff for
|
||||
* PCI_SIZE_32
|
||||
*/
|
||||
int pci_get_ff(enum pci_size_t size);
|
||||
|
||||
/**
|
||||
* pci_bus_find_devices () - Find devices on a bus
|
||||
*
|
||||
* @bus: Bus 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.
|
||||
* @devp: Returns matching device if found
|
||||
* @return 0 if found, -ENODEV if not
|
||||
*/
|
||||
int pci_bus_find_devices(struct udevice *bus, struct pci_device_id *ids,
|
||||
int *indexp, struct udevice **devp);
|
||||
|
||||
/**
|
||||
* pci_find_device_id() - Find a device on any bus
|
||||
*
|
||||
* @ids: PCI vendor/device IDs to look for, terminated by 0, 0 record
|
||||
* @index: Index number of device to find, 0 for the first match, 1 for
|
||||
* the second, etc.
|
||||
* @devp: Returns matching device if found
|
||||
* @return 0 if found, -ENODEV if not
|
||||
*/
|
||||
int pci_find_device_id(struct pci_device_id *ids, int index,
|
||||
struct udevice **devp);
|
||||
|
||||
/**
|
||||
* dm_pci_hose_probe_bus() - probe a subordinate bus, scanning it for devices
|
||||
*
|
||||
* This probes the given bus which causes it to be scanned for devices. The
|
||||
* devices will be bound but not probed.
|
||||
*
|
||||
* @hose specifies the PCI hose that will be used for the scan. This is
|
||||
* always a top-level bus with uclass UCLASS_PCI. The bus to scan is
|
||||
* in @bdf, and is a subordinate bus reachable from @hose.
|
||||
*
|
||||
* @hose: PCI hose to scan
|
||||
* @bdf: PCI bus address to scan (PCI_BUS(bdf) is the bus number)
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int dm_pci_hose_probe_bus(struct pci_controller *hose, pci_dev_t bdf);
|
||||
|
||||
/**
|
||||
* pci_bus_read_config() - Read a configuration value from a device
|
||||
*
|
||||
* TODO(sjg@chromium.org): We should be able to pass just a device and have
|
||||
* it do the right thing. It would be good to have that function also.
|
||||
*
|
||||
* @bus: Bus to read from
|
||||
* @bdf: PCI device address: bus, device and function -see PCI_BDF()
|
||||
* @valuep: Place to put the returned value
|
||||
* @size: Access size
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int pci_bus_read_config(struct udevice *bus, pci_dev_t bdf, int offset,
|
||||
unsigned long *valuep, enum pci_size_t size);
|
||||
|
||||
/**
|
||||
* pci_bus_write_config() - Write a configuration value to a device
|
||||
*
|
||||
* @bus: Bus to write from
|
||||
* @bdf: PCI device address: bus, device and function -see PCI_BDF()
|
||||
* @value: Value to write
|
||||
* @size: Access size
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int pci_bus_write_config(struct udevice *bus, pci_dev_t bdf, int offset,
|
||||
unsigned long value, enum pci_size_t size);
|
||||
|
||||
/*
|
||||
* The following functions provide access to the above without needing the
|
||||
* size parameter. We are trying to encourage the use of the 8/16/32-style
|
||||
* functions, rather than byte/word/dword. But both are supported.
|
||||
*/
|
||||
int pci_write_config32(pci_dev_t pcidev, int offset, u32 value);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_write_config_dword(pci_dev_t pcidev, int offset,
|
||||
u32 value)
|
||||
{
|
||||
return pci_write_config32(pcidev, offset, value);
|
||||
}
|
||||
|
||||
int pci_write_config16(pci_dev_t pcidev, int offset, u16 value);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_write_config_word(pci_dev_t pcidev, int offset,
|
||||
u16 value)
|
||||
{
|
||||
return pci_write_config16(pcidev, offset, value);
|
||||
}
|
||||
|
||||
int pci_write_config8(pci_dev_t pcidev, int offset, u8 value);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_write_config_byte(pci_dev_t pcidev, int offset,
|
||||
u8 value)
|
||||
{
|
||||
return pci_write_config8(pcidev, offset, value);
|
||||
}
|
||||
|
||||
int pci_read_config32(pci_dev_t pcidev, int offset, u32 *valuep);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_read_config_dword(pci_dev_t pcidev, int offset,
|
||||
u32 *valuep)
|
||||
{
|
||||
return pci_read_config32(pcidev, offset, valuep);
|
||||
}
|
||||
|
||||
int pci_read_config16(pci_dev_t pcidev, int offset, u16 *valuep);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_read_config_word(pci_dev_t pcidev, int offset,
|
||||
u16 *valuep)
|
||||
{
|
||||
return pci_read_config16(pcidev, offset, valuep);
|
||||
}
|
||||
|
||||
int pci_read_config8(pci_dev_t pcidev, int offset, u8 *valuep);
|
||||
|
||||
/* Compatibility with old naming */
|
||||
static inline int pci_read_config_byte(pci_dev_t pcidev, int offset,
|
||||
u8 *valuep)
|
||||
{
|
||||
return pci_read_config8(pcidev, offset, valuep);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* _PCI_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue