]>
Commit | Line | Data |
---|---|---|
3e17ffbb SG |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * PCI emulation device for an x86 Primary-to-Sideband bus | |
4 | * | |
5 | * Copyright 2019 Google LLC | |
6 | * Written by Simon Glass <[email protected]> | |
7 | */ | |
8 | ||
9 | #define LOG_CATEGORY UCLASS_MISC | |
10 | #define LOG_DEBUG | |
11 | ||
12 | #include <common.h> | |
13 | #include <axi.h> | |
14 | #include <dm.h> | |
f7ae49fc | 15 | #include <log.h> |
3e17ffbb SG |
16 | #include <pci.h> |
17 | #include <asm/test.h> | |
18 | #include <p2sb.h> | |
19 | ||
20 | /** | |
21 | * struct p2sb_emul_platdata - platform data for this device | |
22 | * | |
23 | * @command: Current PCI command value | |
24 | * @bar: Current base address values | |
25 | */ | |
26 | struct p2sb_emul_platdata { | |
27 | u16 command; | |
28 | u32 bar[6]; | |
29 | }; | |
30 | ||
31 | enum { | |
32 | /* This emulator supports 16 different devices */ | |
33 | MEMMAP_SIZE = 16 << PCR_PORTID_SHIFT, | |
34 | }; | |
35 | ||
36 | static struct pci_bar { | |
37 | int type; | |
38 | u32 size; | |
39 | } barinfo[] = { | |
40 | { PCI_BASE_ADDRESS_MEM_TYPE_32, MEMMAP_SIZE }, | |
41 | { 0, 0 }, | |
42 | { 0, 0 }, | |
43 | { 0, 0 }, | |
44 | { 0, 0 }, | |
45 | { 0, 0 }, | |
46 | }; | |
47 | ||
48 | struct p2sb_emul_priv { | |
49 | u8 regs[16]; | |
50 | }; | |
51 | ||
c4e72c4a SG |
52 | static int sandbox_p2sb_emul_read_config(const struct udevice *emul, |
53 | uint offset, ulong *valuep, | |
54 | enum pci_size_t size) | |
3e17ffbb SG |
55 | { |
56 | struct p2sb_emul_platdata *plat = dev_get_platdata(emul); | |
57 | ||
58 | switch (offset) { | |
59 | case PCI_COMMAND: | |
60 | *valuep = plat->command; | |
61 | break; | |
62 | case PCI_HEADER_TYPE: | |
63 | *valuep = PCI_HEADER_TYPE_NORMAL; | |
64 | break; | |
65 | case PCI_VENDOR_ID: | |
66 | *valuep = SANDBOX_PCI_VENDOR_ID; | |
67 | break; | |
68 | case PCI_DEVICE_ID: | |
69 | *valuep = SANDBOX_PCI_P2SB_EMUL_ID; | |
70 | break; | |
71 | case PCI_CLASS_DEVICE: | |
72 | if (size == PCI_SIZE_8) { | |
73 | *valuep = SANDBOX_PCI_CLASS_SUB_CODE; | |
74 | } else { | |
75 | *valuep = (SANDBOX_PCI_CLASS_CODE << 8) | | |
76 | SANDBOX_PCI_CLASS_SUB_CODE; | |
77 | } | |
78 | break; | |
79 | case PCI_CLASS_CODE: | |
80 | *valuep = SANDBOX_PCI_CLASS_CODE; | |
81 | break; | |
82 | case PCI_BASE_ADDRESS_0: | |
83 | case PCI_BASE_ADDRESS_1: | |
84 | case PCI_BASE_ADDRESS_2: | |
85 | case PCI_BASE_ADDRESS_3: | |
86 | case PCI_BASE_ADDRESS_4: | |
87 | case PCI_BASE_ADDRESS_5: { | |
88 | int barnum; | |
89 | u32 *bar; | |
90 | ||
91 | barnum = pci_offset_to_barnum(offset); | |
92 | bar = &plat->bar[barnum]; | |
93 | ||
94 | *valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type, | |
95 | barinfo[barnum].size); | |
96 | break; | |
97 | } | |
98 | case PCI_CAPABILITY_LIST: | |
99 | *valuep = PCI_CAP_ID_PM_OFFSET; | |
100 | break; | |
101 | } | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
106 | static int sandbox_p2sb_emul_write_config(struct udevice *emul, uint offset, | |
107 | ulong value, enum pci_size_t size) | |
108 | { | |
109 | struct p2sb_emul_platdata *plat = dev_get_platdata(emul); | |
110 | ||
111 | switch (offset) { | |
112 | case PCI_COMMAND: | |
113 | plat->command = value; | |
114 | break; | |
115 | case PCI_BASE_ADDRESS_0: | |
116 | case PCI_BASE_ADDRESS_1: { | |
117 | int barnum; | |
118 | u32 *bar; | |
119 | ||
120 | barnum = pci_offset_to_barnum(offset); | |
121 | bar = &plat->bar[barnum]; | |
122 | ||
123 | log_debug("w bar %d=%lx\n", barnum, value); | |
124 | *bar = value; | |
125 | /* space indicator (bit#0) is read-only */ | |
126 | *bar |= barinfo[barnum].type; | |
127 | break; | |
128 | } | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static int sandbox_p2sb_emul_find_bar(struct udevice *emul, unsigned int addr, | |
135 | int *barnump, unsigned int *offsetp) | |
136 | { | |
137 | struct p2sb_emul_platdata *plat = dev_get_platdata(emul); | |
138 | int barnum; | |
139 | ||
140 | for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) { | |
141 | unsigned int size = barinfo[barnum].size; | |
142 | u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE; | |
143 | ||
144 | if (addr >= base && addr < base + size) { | |
145 | *barnump = barnum; | |
146 | *offsetp = addr - base; | |
147 | return 0; | |
148 | } | |
149 | } | |
150 | *barnump = -1; | |
151 | ||
152 | return -ENOENT; | |
153 | } | |
154 | ||
155 | static int sandbox_p2sb_emul_read_io(struct udevice *dev, unsigned int addr, | |
156 | ulong *valuep, enum pci_size_t size) | |
157 | { | |
158 | unsigned int offset; | |
159 | int barnum; | |
160 | int ret; | |
161 | ||
162 | ret = sandbox_p2sb_emul_find_bar(dev, addr, &barnum, &offset); | |
163 | if (ret) | |
164 | return ret; | |
165 | ||
166 | if (barnum == 4) | |
167 | *valuep = offset; | |
168 | else if (barnum == 0) | |
169 | *valuep = offset; | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
174 | static int sandbox_p2sb_emul_write_io(struct udevice *dev, unsigned int addr, | |
175 | ulong value, enum pci_size_t size) | |
176 | { | |
177 | unsigned int offset; | |
178 | int barnum; | |
179 | int ret; | |
180 | ||
181 | ret = sandbox_p2sb_emul_find_bar(dev, addr, &barnum, &offset); | |
182 | if (ret) | |
183 | return ret; | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | static int find_p2sb_channel(struct udevice *emul, uint offset, | |
189 | struct udevice **devp) | |
190 | { | |
191 | uint pid = offset >> PCR_PORTID_SHIFT; | |
192 | struct udevice *p2sb, *dev; | |
193 | int ret; | |
194 | ||
195 | ret = sandbox_pci_get_client(emul, &p2sb); | |
196 | if (ret) | |
197 | return log_msg_ret("No client", ret); | |
198 | ||
199 | device_foreach_child(dev, p2sb) { | |
200 | struct p2sb_child_platdata *pplat = | |
201 | dev_get_parent_platdata(dev); | |
202 | ||
203 | log_debug(" - child %s, pid %d, want %d\n", dev->name, | |
204 | pplat->pid, pid); | |
205 | if (pid == pplat->pid) { | |
206 | *devp = dev; | |
207 | return 0; | |
208 | } | |
209 | } | |
210 | ||
211 | return -ENOENT; | |
212 | } | |
213 | ||
214 | static int sandbox_p2sb_emul_map_physmem(struct udevice *dev, | |
215 | phys_addr_t addr, unsigned long *lenp, | |
216 | void **ptrp) | |
217 | { | |
218 | struct p2sb_emul_priv *priv = dev_get_priv(dev); | |
8a770f9e | 219 | struct udevice *child = NULL; /* Silence compiler warning */ |
3e17ffbb SG |
220 | unsigned int offset; |
221 | int barnum; | |
222 | int ret; | |
223 | ||
224 | log_debug("map %x: ", (uint)addr); | |
225 | ret = sandbox_p2sb_emul_find_bar(dev, addr, &barnum, &offset); | |
226 | if (ret) | |
227 | return log_msg_ret("Cannot find bar", ret); | |
228 | log_debug("bar %d, offset %x\n", barnum, offset); | |
229 | ||
230 | if (barnum != 0) | |
231 | return log_msg_ret("Unknown BAR", -EINVAL); | |
232 | ||
233 | ret = find_p2sb_channel(dev, offset, &child); | |
234 | if (ret) | |
235 | return log_msg_ret("Cannot find channel", ret); | |
236 | ||
237 | offset &= ((1 << PCR_PORTID_SHIFT) - 1); | |
238 | ret = axi_read(child, offset, priv->regs, AXI_SIZE_32); | |
239 | if (ret) | |
240 | return log_msg_ret("Child read failed", ret); | |
241 | *ptrp = priv->regs + (offset & 3); | |
242 | *lenp = 4; | |
243 | ||
244 | return 0; | |
245 | } | |
246 | ||
247 | static struct dm_pci_emul_ops sandbox_p2sb_emul_emul_ops = { | |
248 | .read_config = sandbox_p2sb_emul_read_config, | |
249 | .write_config = sandbox_p2sb_emul_write_config, | |
250 | .read_io = sandbox_p2sb_emul_read_io, | |
251 | .write_io = sandbox_p2sb_emul_write_io, | |
252 | .map_physmem = sandbox_p2sb_emul_map_physmem, | |
253 | }; | |
254 | ||
255 | static const struct udevice_id sandbox_p2sb_emul_ids[] = { | |
256 | { .compatible = "sandbox,p2sb-emul" }, | |
257 | { } | |
258 | }; | |
259 | ||
260 | U_BOOT_DRIVER(sandbox_p2sb_emul_emul) = { | |
261 | .name = "sandbox_p2sb_emul_emul", | |
262 | .id = UCLASS_PCI_EMUL, | |
263 | .of_match = sandbox_p2sb_emul_ids, | |
264 | .ops = &sandbox_p2sb_emul_emul_ops, | |
265 | .priv_auto_alloc_size = sizeof(struct p2sb_emul_priv), | |
266 | .platdata_auto_alloc_size = sizeof(struct p2sb_emul_platdata), | |
267 | }; | |
268 | ||
269 | static struct pci_device_id sandbox_p2sb_emul_supported[] = { | |
270 | { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_PMC_EMUL_ID) }, | |
271 | {}, | |
272 | }; | |
273 | ||
274 | U_BOOT_PCI_DEVICE(sandbox_p2sb_emul_emul, sandbox_p2sb_emul_supported); |