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