/src/u-boot/drivers/power/acpi_pmc/pmc_emul.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0+ |
2 | | /* |
3 | | * PCI emulation device for an x86 Power-Management Controller (PMC) |
4 | | * |
5 | | * Copyright 2019 Google LLC |
6 | | * Written by Simon Glass <sjg@chromium.org> |
7 | | */ |
8 | | |
9 | | #include <dm.h> |
10 | | #include <log.h> |
11 | | #include <pci.h> |
12 | | #include <asm/test.h> |
13 | | #include <power/acpi_pmc.h> |
14 | | |
15 | | /** |
16 | | * struct pmc_emul_plat - platform data for this device |
17 | | * |
18 | | * @command: Current PCI command value |
19 | | * @bar: Current base address values |
20 | | */ |
21 | | struct pmc_emul_plat { |
22 | | u16 command; |
23 | | u32 bar[6]; |
24 | | }; |
25 | | |
26 | | enum { |
27 | | MEMMAP_SIZE = 0x80, |
28 | | }; |
29 | | |
30 | | static struct pci_bar { |
31 | | int type; |
32 | | u32 size; |
33 | | } barinfo[] = { |
34 | | { PCI_BASE_ADDRESS_MEM_TYPE_32, MEMMAP_SIZE }, |
35 | | { 0, 0 }, |
36 | | { 0, 0 }, |
37 | | { 0, 0 }, |
38 | | { PCI_BASE_ADDRESS_SPACE_IO, 256 }, |
39 | | { 0, 0 }, |
40 | | }; |
41 | | |
42 | | struct pmc_emul_priv { |
43 | | u8 regs[MEMMAP_SIZE]; |
44 | | }; |
45 | | |
46 | | static int sandbox_pmc_emul_read_config(const struct udevice *emul, uint offset, |
47 | | ulong *valuep, enum pci_size_t size) |
48 | 0 | { |
49 | 0 | struct pmc_emul_plat *plat = dev_get_plat(emul); |
50 | |
|
51 | 0 | switch (offset) { |
52 | 0 | case PCI_COMMAND: |
53 | 0 | *valuep = plat->command; |
54 | 0 | break; |
55 | 0 | case PCI_HEADER_TYPE: |
56 | 0 | *valuep = 0; |
57 | 0 | break; |
58 | 0 | case PCI_VENDOR_ID: |
59 | 0 | *valuep = SANDBOX_PCI_VENDOR_ID; |
60 | 0 | break; |
61 | 0 | case PCI_DEVICE_ID: |
62 | 0 | *valuep = SANDBOX_PCI_PMC_EMUL_ID; |
63 | 0 | break; |
64 | 0 | case PCI_CLASS_DEVICE: |
65 | 0 | if (size == PCI_SIZE_8) { |
66 | 0 | *valuep = SANDBOX_PCI_CLASS_SUB_CODE; |
67 | 0 | } else { |
68 | 0 | *valuep = (SANDBOX_PCI_CLASS_CODE << 8) | |
69 | 0 | SANDBOX_PCI_CLASS_SUB_CODE; |
70 | 0 | } |
71 | 0 | break; |
72 | 0 | case PCI_CLASS_CODE: |
73 | 0 | *valuep = SANDBOX_PCI_CLASS_CODE; |
74 | 0 | break; |
75 | 0 | case PCI_BASE_ADDRESS_0: |
76 | 0 | case PCI_BASE_ADDRESS_1: |
77 | 0 | case PCI_BASE_ADDRESS_2: |
78 | 0 | case PCI_BASE_ADDRESS_3: |
79 | 0 | case PCI_BASE_ADDRESS_4: |
80 | 0 | case PCI_BASE_ADDRESS_5: { |
81 | 0 | int barnum; |
82 | 0 | u32 *bar; |
83 | |
|
84 | 0 | barnum = pci_offset_to_barnum(offset); |
85 | 0 | bar = &plat->bar[barnum]; |
86 | |
|
87 | 0 | *valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type, |
88 | 0 | barinfo[barnum].size); |
89 | 0 | break; |
90 | 0 | } |
91 | 0 | case PCI_CAPABILITY_LIST: |
92 | 0 | *valuep = PCI_CAP_ID_PM_OFFSET; |
93 | 0 | break; |
94 | 0 | } |
95 | | |
96 | 0 | return 0; |
97 | 0 | } |
98 | | |
99 | | static int sandbox_pmc_emul_write_config(struct udevice *emul, uint offset, |
100 | | ulong value, enum pci_size_t size) |
101 | 0 | { |
102 | 0 | struct pmc_emul_plat *plat = dev_get_plat(emul); |
103 | |
|
104 | 0 | switch (offset) { |
105 | 0 | case PCI_COMMAND: |
106 | 0 | plat->command = value; |
107 | 0 | break; |
108 | 0 | case PCI_BASE_ADDRESS_0: |
109 | 0 | case PCI_BASE_ADDRESS_1: { |
110 | 0 | int barnum; |
111 | 0 | u32 *bar; |
112 | |
|
113 | 0 | barnum = pci_offset_to_barnum(offset); |
114 | 0 | bar = &plat->bar[barnum]; |
115 | |
|
116 | 0 | debug("w bar %d=%lx\n", barnum, value); |
117 | 0 | *bar = value; |
118 | | /* space indicator (bit#0) is read-only */ |
119 | 0 | *bar |= barinfo[barnum].type; |
120 | 0 | break; |
121 | 0 | } |
122 | 0 | } |
123 | | |
124 | 0 | return 0; |
125 | 0 | } |
126 | | |
127 | | static int sandbox_pmc_emul_find_bar(struct udevice *emul, unsigned int addr, |
128 | | int *barnump, unsigned int *offsetp) |
129 | 0 | { |
130 | 0 | struct pmc_emul_plat *plat = dev_get_plat(emul); |
131 | 0 | int barnum; |
132 | |
|
133 | 0 | for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) { |
134 | 0 | unsigned int size = barinfo[barnum].size; |
135 | 0 | u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE; |
136 | |
|
137 | 0 | if (addr >= base && addr < base + size) { |
138 | 0 | *barnump = barnum; |
139 | 0 | *offsetp = addr - base; |
140 | 0 | return 0; |
141 | 0 | } |
142 | 0 | } |
143 | 0 | *barnump = -1; |
144 | |
|
145 | 0 | return -ENOENT; |
146 | 0 | } |
147 | | |
148 | | static int sandbox_pmc_emul_read_io(struct udevice *dev, unsigned int addr, |
149 | | ulong *valuep, enum pci_size_t size) |
150 | 0 | { |
151 | 0 | unsigned int offset; |
152 | 0 | int barnum; |
153 | 0 | int ret; |
154 | |
|
155 | 0 | ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset); |
156 | 0 | if (ret) |
157 | 0 | return ret; |
158 | | |
159 | 0 | if (barnum == 4) |
160 | 0 | *valuep = offset; |
161 | 0 | else if (barnum == 0) |
162 | 0 | *valuep = offset; |
163 | |
|
164 | 0 | return 0; |
165 | 0 | } |
166 | | |
167 | | static int sandbox_pmc_emul_write_io(struct udevice *dev, unsigned int addr, |
168 | | ulong value, enum pci_size_t size) |
169 | 0 | { |
170 | 0 | unsigned int offset; |
171 | 0 | int barnum; |
172 | 0 | int ret; |
173 | |
|
174 | 0 | ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset); |
175 | 0 | if (ret) |
176 | 0 | return ret; |
177 | | |
178 | 0 | return 0; |
179 | 0 | } |
180 | | |
181 | | static int sandbox_pmc_emul_map_physmem(struct udevice *dev, |
182 | | phys_addr_t addr, unsigned long *lenp, |
183 | | void **ptrp) |
184 | 0 | { |
185 | 0 | struct pmc_emul_priv *priv = dev_get_priv(dev); |
186 | 0 | unsigned int offset, avail; |
187 | 0 | int barnum; |
188 | 0 | int ret; |
189 | |
|
190 | 0 | ret = sandbox_pmc_emul_find_bar(dev, addr, &barnum, &offset); |
191 | 0 | if (ret) |
192 | 0 | return ret; |
193 | | |
194 | 0 | if (barnum == 0) { |
195 | 0 | *ptrp = priv->regs + offset; |
196 | 0 | avail = barinfo[0].size - offset; |
197 | 0 | if (avail > barinfo[0].size) |
198 | 0 | *lenp = 0; |
199 | 0 | else |
200 | 0 | *lenp = min(*lenp, (ulong)avail); |
201 | |
|
202 | 0 | return 0; |
203 | 0 | } |
204 | | |
205 | 0 | return -ENOENT; |
206 | 0 | } |
207 | | |
208 | | static int sandbox_pmc_probe(struct udevice *dev) |
209 | 0 | { |
210 | 0 | struct pmc_emul_priv *priv = dev_get_priv(dev); |
211 | 0 | int i; |
212 | |
|
213 | 0 | for (i = 0; i < MEMMAP_SIZE; i++) |
214 | 0 | priv->regs[i] = i; |
215 | |
|
216 | 0 | return 0; |
217 | 0 | } |
218 | | |
219 | | static struct dm_pci_emul_ops sandbox_pmc_emul_emul_ops = { |
220 | | .read_config = sandbox_pmc_emul_read_config, |
221 | | .write_config = sandbox_pmc_emul_write_config, |
222 | | .read_io = sandbox_pmc_emul_read_io, |
223 | | .write_io = sandbox_pmc_emul_write_io, |
224 | | .map_physmem = sandbox_pmc_emul_map_physmem, |
225 | | }; |
226 | | |
227 | | static const struct udevice_id sandbox_pmc_emul_ids[] = { |
228 | | { .compatible = "sandbox,pmc-emul" }, |
229 | | { } |
230 | | }; |
231 | | |
232 | | U_BOOT_DRIVER(sandbox_pmc_emul_emul) = { |
233 | | .name = "sandbox_pmc_emul_emul", |
234 | | .id = UCLASS_PCI_EMUL, |
235 | | .of_match = sandbox_pmc_emul_ids, |
236 | | .ops = &sandbox_pmc_emul_emul_ops, |
237 | | .probe = sandbox_pmc_probe, |
238 | | .priv_auto = sizeof(struct pmc_emul_priv), |
239 | | .plat_auto = sizeof(struct pmc_emul_plat), |
240 | | }; |
241 | | |
242 | | static struct pci_device_id sandbox_pmc_emul_supported[] = { |
243 | | { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_PMC_EMUL_ID) }, |
244 | | {}, |
245 | | }; |
246 | | |
247 | | U_BOOT_PCI_DEVICE(sandbox_pmc_emul_emul, sandbox_pmc_emul_supported); |