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