Commit 36d0d3b4b4974f4183609ac8b4d77a1f46acba55
1 parent
537849aaa1
Exists in
v2017.01-smarct4x
and in
34 other branches
dm: sandbox: pci: Add a PCI emulation uclass
Since sandbox does not have real devices (unless it borrows those from the host) it must use emulations. Provide a uclass which permits PCI operations to be passed through to an emulation device. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 4 changed files with 177 additions and 0 deletions Side-by-side Diff
drivers/pci/Makefile
drivers/pci/pci-emul-uclass.c
1 | +/* | |
2 | + * Copyright (c) 2014 Google, Inc | |
3 | + * Written by Simon Glass <sjg@chromium.org> | |
4 | + * | |
5 | + * SPDX-License-Identifier: GPL-2.0+ | |
6 | + */ | |
7 | + | |
8 | +#include <common.h> | |
9 | +#include <dm.h> | |
10 | +#include <fdtdec.h> | |
11 | +#include <libfdt.h> | |
12 | +#include <pci.h> | |
13 | +#include <dm/lists.h> | |
14 | + | |
15 | +DECLARE_GLOBAL_DATA_PTR; | |
16 | + | |
17 | +struct sandbox_pci_priv { | |
18 | + int dev_count; | |
19 | +}; | |
20 | + | |
21 | +int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, | |
22 | + struct udevice **emulp) | |
23 | +{ | |
24 | + struct udevice *dev; | |
25 | + int ret; | |
26 | + | |
27 | + ret = pci_bus_find_devfn(bus, find_devfn, &dev); | |
28 | + if (ret) { | |
29 | + debug("%s: Could not find emulator for dev %x\n", __func__, | |
30 | + find_devfn); | |
31 | + return ret; | |
32 | + } | |
33 | + | |
34 | + ret = device_find_first_child(dev, emulp); | |
35 | + if (ret) | |
36 | + return ret; | |
37 | + | |
38 | + return *emulp ? 0 : -ENODEV; | |
39 | +} | |
40 | + | |
41 | +static int sandbox_pci_emul_post_probe(struct udevice *dev) | |
42 | +{ | |
43 | + struct sandbox_pci_priv *priv = dev->uclass->priv; | |
44 | + | |
45 | + priv->dev_count++; | |
46 | + sandbox_set_enable_pci_map(true); | |
47 | + | |
48 | + return 0; | |
49 | +} | |
50 | + | |
51 | +static int sandbox_pci_emul_pre_remove(struct udevice *dev) | |
52 | +{ | |
53 | + struct sandbox_pci_priv *priv = dev->uclass->priv; | |
54 | + | |
55 | + priv->dev_count--; | |
56 | + sandbox_set_enable_pci_map(priv->dev_count > 0); | |
57 | + | |
58 | + return 0; | |
59 | +} | |
60 | + | |
61 | +UCLASS_DRIVER(pci_emul) = { | |
62 | + .id = UCLASS_PCI_EMUL, | |
63 | + .name = "pci_emul", | |
64 | + .post_probe = sandbox_pci_emul_post_probe, | |
65 | + .pre_remove = sandbox_pci_emul_pre_remove, | |
66 | + .priv_auto_alloc_size = sizeof(struct sandbox_pci_priv), | |
67 | +}; |
include/dm/uclass-id.h
include/pci.h
... | ... | @@ -992,6 +992,114 @@ |
992 | 992 | return pci_read_config8(pcidev, offset, valuep); |
993 | 993 | } |
994 | 994 | |
995 | +/** | |
996 | + * struct dm_pci_emul_ops - PCI device emulator operations | |
997 | + */ | |
998 | +struct dm_pci_emul_ops { | |
999 | + /** | |
1000 | + * get_devfn(): Check which device and function this emulators | |
1001 | + * | |
1002 | + * @dev: device to check | |
1003 | + * @return the device and function this emulates, or -ve on error | |
1004 | + */ | |
1005 | + int (*get_devfn)(struct udevice *dev); | |
1006 | + /** | |
1007 | + * read_config() - Read a PCI configuration value | |
1008 | + * | |
1009 | + * @dev: Emulated device to read from | |
1010 | + * @offset: Byte offset within the device's configuration space | |
1011 | + * @valuep: Place to put the returned value | |
1012 | + * @size: Access size | |
1013 | + * @return 0 if OK, -ve on error | |
1014 | + */ | |
1015 | + int (*read_config)(struct udevice *dev, uint offset, ulong *valuep, | |
1016 | + enum pci_size_t size); | |
1017 | + /** | |
1018 | + * write_config() - Write a PCI configuration value | |
1019 | + * | |
1020 | + * @dev: Emulated device to write to | |
1021 | + * @offset: Byte offset within the device's configuration space | |
1022 | + * @value: Value to write | |
1023 | + * @size: Access size | |
1024 | + * @return 0 if OK, -ve on error | |
1025 | + */ | |
1026 | + int (*write_config)(struct udevice *dev, uint offset, ulong value, | |
1027 | + enum pci_size_t size); | |
1028 | + /** | |
1029 | + * read_io() - Read a PCI I/O value | |
1030 | + * | |
1031 | + * @dev: Emulated device to read from | |
1032 | + * @addr: I/O address to read | |
1033 | + * @valuep: Place to put the returned value | |
1034 | + * @size: Access size | |
1035 | + * @return 0 if OK, -ENOENT if @addr is not mapped by this device, | |
1036 | + * other -ve value on error | |
1037 | + */ | |
1038 | + int (*read_io)(struct udevice *dev, unsigned int addr, ulong *valuep, | |
1039 | + enum pci_size_t size); | |
1040 | + /** | |
1041 | + * write_io() - Write a PCI I/O value | |
1042 | + * | |
1043 | + * @dev: Emulated device to write from | |
1044 | + * @addr: I/O address to write | |
1045 | + * @value: Value to write | |
1046 | + * @size: Access size | |
1047 | + * @return 0 if OK, -ENOENT if @addr is not mapped by this device, | |
1048 | + * other -ve value on error | |
1049 | + */ | |
1050 | + int (*write_io)(struct udevice *dev, unsigned int addr, | |
1051 | + ulong value, enum pci_size_t size); | |
1052 | + /** | |
1053 | + * map_physmem() - Map a device into sandbox memory | |
1054 | + * | |
1055 | + * @dev: Emulated device to map | |
1056 | + * @addr: Memory address, normally corresponding to a PCI BAR. | |
1057 | + * The device should have been configured to have a BAR | |
1058 | + * at this address. | |
1059 | + * @lenp: On entry, the size of the area to map, On exit it is | |
1060 | + * updated to the size actually mapped, which may be less | |
1061 | + * if the device has less space | |
1062 | + * @ptrp: Returns a pointer to the mapped address. The device's | |
1063 | + * space can be accessed as @lenp bytes starting here | |
1064 | + * @return 0 if OK, -ENOENT if @addr is not mapped by this device, | |
1065 | + * other -ve value on error | |
1066 | + */ | |
1067 | + int (*map_physmem)(struct udevice *dev, phys_addr_t addr, | |
1068 | + unsigned long *lenp, void **ptrp); | |
1069 | + /** | |
1070 | + * unmap_physmem() - undo a memory mapping | |
1071 | + * | |
1072 | + * This must be called after map_physmem() to undo the mapping. | |
1073 | + * Some devices can use this to check what has been written into | |
1074 | + * their mapped memory and perform an operations they require on it. | |
1075 | + * In this way, map/unmap can be used as a sort of handshake between | |
1076 | + * the emulated device and its users. | |
1077 | + * | |
1078 | + * @dev: Emuated device to unmap | |
1079 | + * @vaddr: Mapped memory address, as passed to map_physmem() | |
1080 | + * @len: Size of area mapped, as returned by map_physmem() | |
1081 | + * @return 0 if OK, -ve on error | |
1082 | + */ | |
1083 | + int (*unmap_physmem)(struct udevice *dev, const void *vaddr, | |
1084 | + unsigned long len); | |
1085 | +}; | |
1086 | + | |
1087 | +/* Get access to a PCI device emulator's operations */ | |
1088 | +#define pci_get_emul_ops(dev) ((struct dm_pci_emul_ops *)(dev)->driver->ops) | |
1089 | + | |
1090 | +/** | |
1091 | + * sandbox_pci_get_emul() - Get the emulation device for a PCI device | |
1092 | + * | |
1093 | + * Searches for a suitable emulator for the given PCI bus device | |
1094 | + * | |
1095 | + * @bus: PCI bus to search | |
1096 | + * @find_devfn: PCI device and function address (PCI_DEVFN()) | |
1097 | + * @emulp: Returns emulated device if found | |
1098 | + * @return 0 if found, -ENODEV if not found | |
1099 | + */ | |
1100 | +int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn, | |
1101 | + struct udevice **emulp); | |
1102 | + | |
995 | 1103 | #endif |
996 | 1104 | |
997 | 1105 | #endif /* __ASSEMBLY__ */ |