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>
master
Simon Glass 9 years ago
parent aab6724c90
commit ff3e077bd2
  1. 2
      common/board_r.c
  2. 14
      common/cmd_pci.c
  3. 70
      doc/driver-model/pci-info.txt
  4. 12
      drivers/pci/Kconfig
  5. 8
      drivers/pci/Makefile
  6. 639
      drivers/pci/pci-uclass.c
  7. 16
      drivers/pci/pci_auto.c
  8. 43
      drivers/pci/pci_compat.c
  9. 2
      include/dm/uclass-id.h
  10. 289
      include/pci.h

@ -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);
}

@ -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

@ -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)

@ -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,

@ -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…
Cancel
Save