blob: 3dac6bd82e30d3416e287587d750eb740c76645e [file] [log] [blame]
Simon Glass937bb472019-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
Simon Glass937bb472019-12-06 21:41:57 -070010
Simon Glass937bb472019-12-06 21:41:57 -070011#include <axi.h>
12#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass937bb472019-12-06 21:41:57 -070014#include <pci.h>
15#include <asm/test.h>
16#include <p2sb.h>
17
18/**
Simon Glassb75b15b2020-12-03 16:55:23 -070019 * struct p2sb_emul_plat - platform data for this device
Simon Glass937bb472019-12-06 21:41:57 -070020 *
21 * @command: Current PCI command value
22 * @bar: Current base address values
23 */
Simon Glassb75b15b2020-12-03 16:55:23 -070024struct p2sb_emul_plat {
Simon Glass937bb472019-12-06 21:41:57 -070025 u16 command;
26 u32 bar[6];
27};
28
29enum {
30 /* This emulator supports 16 different devices */
31 MEMMAP_SIZE = 16 << PCR_PORTID_SHIFT,
32};
33
34static struct pci_bar {
35 int type;
36 u32 size;
37} barinfo[] = {
38 { PCI_BASE_ADDRESS_MEM_TYPE_32, MEMMAP_SIZE },
39 { 0, 0 },
40 { 0, 0 },
41 { 0, 0 },
42 { 0, 0 },
43 { 0, 0 },
44};
45
46struct p2sb_emul_priv {
47 u8 regs[16];
48};
49
Simon Glass2a311e82020-01-27 08:49:37 -070050static int sandbox_p2sb_emul_read_config(const struct udevice *emul,
51 uint offset, ulong *valuep,
52 enum pci_size_t size)
Simon Glass937bb472019-12-06 21:41:57 -070053{
Simon Glassb75b15b2020-12-03 16:55:23 -070054 struct p2sb_emul_plat *plat = dev_get_plat(emul);
Simon Glass937bb472019-12-06 21:41:57 -070055
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{
Simon Glassb75b15b2020-12-03 16:55:23 -0700107 struct p2sb_emul_plat *plat = dev_get_plat(emul);
Simon Glass937bb472019-12-06 21:41:57 -0700108
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{
Simon Glassb75b15b2020-12-03 16:55:23 -0700135 struct p2sb_emul_plat *plat = dev_get_plat(emul);
Simon Glass937bb472019-12-06 21:41:57 -0700136 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) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700198 struct p2sb_child_plat *pplat =
Simon Glass71fa5b42020-12-03 16:55:18 -0700199 dev_get_parent_plat(dev);
Simon Glass937bb472019-12-06 21:41:57 -0700200
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);
Simon Glassa5405b12020-02-08 07:53:10 -0700217 struct udevice *child = NULL; /* Silence compiler warning */
Simon Glass937bb472019-12-06 21:41:57 -0700218 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,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700263 .priv_auto = sizeof(struct p2sb_emul_priv),
Simon Glassb75b15b2020-12-03 16:55:23 -0700264 .plat_auto = sizeof(struct p2sb_emul_plat),
Simon Glass937bb472019-12-06 21:41:57 -0700265};
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);