mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 23:24:38 +00:00
pci: sandbox: Support dynamically binding device driver
At present all emulated sandbox pci devices must be present in the device tree in order to be used. The real world pci uclass driver supports pci device driver matching, and we should add such support on sandbox too. Signed-off-by: Bin Meng <bmeng.cn@gmail.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
ed698aa7de
commit
4345998ae9
4 changed files with 112 additions and 11 deletions
|
@ -133,3 +133,31 @@ When this bus is scanned we will end up with something like this:
|
|||
|
||||
When accesses go to the pci@1f,0 device they are forwarded to its child, the
|
||||
emulator.
|
||||
|
||||
The sandbox PCI drivers also support dynamic driver binding, allowing device
|
||||
driver to declare the driver binding information via U_BOOT_PCI_DEVICE(),
|
||||
eliminating the need to provide any device tree node under the host controller
|
||||
node. It is required a "sandbox,dev-info" property must be provided in the
|
||||
host controller node for this functionality to work.
|
||||
|
||||
pci1: pci-controller1 {
|
||||
compatible = "sandbox,pci";
|
||||
...
|
||||
sandbox,dev-info = <0x08 0x00 0x1234 0x5678
|
||||
0x0c 0x00 0x1234 0x5678>;
|
||||
};
|
||||
|
||||
The "sandbox,dev-info" property specifies all dynamic PCI devices on this bus.
|
||||
Each dynamic PCI device is encoded as 4 cells a group. The first and second
|
||||
cells are PCI device number and function number respectively. The third and
|
||||
fourth cells are PCI vendor ID and device ID respectively.
|
||||
|
||||
When this bus is scanned we will end up with something like this:
|
||||
|
||||
pci [ + ] pci_sandbo |-- pci-controller1
|
||||
pci_emul [ ] sandbox_sw | |-- sandbox_swap_case_emul
|
||||
pci_emul [ ] sandbox_sw | `-- sandbox_swap_case_emul
|
||||
|
||||
Note the difference from the statically declared device nodes is that the
|
||||
device is directly attached to the host controller, instead of via a container
|
||||
device like pci@1f,0.
|
||||
|
|
|
@ -16,21 +16,27 @@ struct sandbox_pci_priv {
|
|||
};
|
||||
|
||||
int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
|
||||
struct udevice **emulp)
|
||||
struct udevice **containerp, struct udevice **emulp)
|
||||
{
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*containerp = NULL;
|
||||
ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(find_devfn), &dev);
|
||||
if (ret) {
|
||||
debug("%s: Could not find emulator for dev %x\n", __func__,
|
||||
find_devfn);
|
||||
return ret;
|
||||
}
|
||||
*containerp = dev;
|
||||
|
||||
ret = device_find_first_child(dev, emulp);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (device_get_uclass_id(dev) == UCLASS_PCI_GENERIC) {
|
||||
ret = device_find_first_child(dev, emulp);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
*emulp = dev;
|
||||
}
|
||||
|
||||
return *emulp ? 0 : -ENODEV;
|
||||
}
|
||||
|
|
|
@ -10,15 +10,27 @@
|
|||
#include <inttypes.h>
|
||||
#include <pci.h>
|
||||
|
||||
#define FDT_DEV_INFO_CELLS 4
|
||||
#define FDT_DEV_INFO_SIZE (FDT_DEV_INFO_CELLS * sizeof(u32))
|
||||
|
||||
#define SANDBOX_PCI_DEVFN(d, f) ((d << 3) | f)
|
||||
|
||||
struct sandbox_pci_priv {
|
||||
struct {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
} vendev[256];
|
||||
};
|
||||
|
||||
static int sandbox_pci_write_config(struct udevice *bus, pci_dev_t devfn,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_emul_ops *ops;
|
||||
struct udevice *emul;
|
||||
struct udevice *container, *emul;
|
||||
int ret;
|
||||
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &emul);
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &container, &emul);
|
||||
if (ret)
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
ops = pci_get_emul_ops(emul);
|
||||
|
@ -33,14 +45,31 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
|
|||
enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_emul_ops *ops;
|
||||
struct udevice *emul;
|
||||
struct udevice *container, *emul;
|
||||
struct sandbox_pci_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
/* Prepare the default response */
|
||||
*valuep = pci_get_ff(size);
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &emul);
|
||||
if (ret)
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &container, &emul);
|
||||
if (ret) {
|
||||
if (!container) {
|
||||
u16 vendor, device;
|
||||
|
||||
devfn = SANDBOX_PCI_DEVFN(PCI_DEV(devfn),
|
||||
PCI_FUNC(devfn));
|
||||
vendor = priv->vendev[devfn].vendor;
|
||||
device = priv->vendev[devfn].device;
|
||||
if (offset == PCI_VENDOR_ID && vendor)
|
||||
*valuep = vendor;
|
||||
else if (offset == PCI_DEVICE_ID && device)
|
||||
*valuep = device;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
}
|
||||
}
|
||||
ops = pci_get_emul_ops(emul);
|
||||
if (!ops || !ops->read_config)
|
||||
return -ENOSYS;
|
||||
|
@ -48,6 +77,41 @@ static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
|
|||
return ops->read_config(emul, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int sandbox_pci_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_pci_priv *priv = dev_get_priv(dev);
|
||||
const fdt32_t *cell;
|
||||
u8 pdev, pfn, devfn;
|
||||
int len;
|
||||
|
||||
cell = ofnode_get_property(dev_ofnode(dev), "sandbox,dev-info", &len);
|
||||
if (!cell)
|
||||
return 0;
|
||||
|
||||
if ((len % FDT_DEV_INFO_SIZE) == 0) {
|
||||
int num = len / FDT_DEV_INFO_SIZE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
debug("dev info #%d: %02x %02x %04x %04x\n", i,
|
||||
fdt32_to_cpu(cell[0]), fdt32_to_cpu(cell[1]),
|
||||
fdt32_to_cpu(cell[2]), fdt32_to_cpu(cell[3]));
|
||||
|
||||
pdev = fdt32_to_cpu(cell[0]);
|
||||
pfn = fdt32_to_cpu(cell[1]);
|
||||
if (pdev > 31 || pfn > 7)
|
||||
continue;
|
||||
devfn = SANDBOX_PCI_DEVFN(pdev, pfn);
|
||||
priv->vendev[devfn].vendor = fdt32_to_cpu(cell[2]);
|
||||
priv->vendev[devfn].device = fdt32_to_cpu(cell[3]);
|
||||
|
||||
cell += FDT_DEV_INFO_CELLS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops sandbox_pci_ops = {
|
||||
.read_config = sandbox_pci_read_config,
|
||||
.write_config = sandbox_pci_write_config,
|
||||
|
@ -63,6 +127,8 @@ U_BOOT_DRIVER(pci_sandbox) = {
|
|||
.id = UCLASS_PCI,
|
||||
.of_match = sandbox_pci_ids,
|
||||
.ops = &sandbox_pci_ops,
|
||||
.probe = sandbox_pci_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_pci_priv),
|
||||
|
||||
/* Attach an emulator if we can */
|
||||
.child_post_bind = dm_scan_fdt_dev,
|
||||
|
|
|
@ -1441,11 +1441,12 @@ struct dm_pci_emul_ops {
|
|||
*
|
||||
* @bus: PCI bus to search
|
||||
* @find_devfn: PCI device and function address (PCI_DEVFN())
|
||||
* @containerp: Returns container device if found
|
||||
* @emulp: Returns emulated device if found
|
||||
* @return 0 if found, -ENODEV if not found
|
||||
*/
|
||||
int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
|
||||
struct udevice **emulp);
|
||||
struct udevice **containerp, struct udevice **emulp);
|
||||
|
||||
#endif /* CONFIG_DM_PCI */
|
||||
|
||||
|
|
Loading…
Reference in a new issue