]>
Commit | Line | Data |
---|---|---|
d2cb9b2b SG |
1 | /* |
2 | * PCI emulation device which swaps the case of text | |
3 | * | |
4 | * Copyright (c) 2014 Google, Inc | |
5 | * Written by Simon Glass <sjg@chromium.org> | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <dm.h> | |
38068820 | 12 | #include <errno.h> |
d2cb9b2b SG |
13 | #include <pci.h> |
14 | #include <asm/test.h> | |
15 | #include <linux/ctype.h> | |
16 | ||
17 | /** | |
18 | * struct swap_case_platdata - platform data for this device | |
19 | * | |
20 | * @command: Current PCI command value | |
21 | * @bar: Current base address values | |
22 | */ | |
23 | struct swap_case_platdata { | |
24 | u16 command; | |
25 | u32 bar[2]; | |
26 | }; | |
27 | ||
28 | #define offset_to_barnum(offset) \ | |
29 | (((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32)) | |
30 | ||
31 | enum { | |
32 | MEM_TEXT_SIZE = 0x100, | |
33 | }; | |
34 | ||
35 | enum swap_case_op { | |
36 | OP_TO_LOWER, | |
37 | OP_TO_UPPER, | |
38 | OP_SWAP, | |
39 | }; | |
40 | ||
41 | static struct pci_bar { | |
42 | int type; | |
43 | u32 size; | |
44 | } barinfo[] = { | |
45 | { PCI_BASE_ADDRESS_SPACE_IO, 1 }, | |
46 | { PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE }, | |
47 | { 0, 0 }, | |
48 | { 0, 0 }, | |
49 | { 0, 0 }, | |
50 | { 0, 0 }, | |
51 | }; | |
52 | ||
53 | struct swap_case_priv { | |
54 | enum swap_case_op op; | |
55 | char mem_text[MEM_TEXT_SIZE]; | |
56 | }; | |
57 | ||
58 | static int sandbox_swap_case_get_devfn(struct udevice *dev) | |
59 | { | |
60 | struct pci_child_platdata *plat = dev_get_parent_platdata(dev); | |
61 | ||
62 | return plat->devfn; | |
63 | } | |
64 | ||
65 | static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, | |
66 | ulong *valuep, enum pci_size_t size) | |
67 | { | |
68 | struct swap_case_platdata *plat = dev_get_platdata(emul); | |
69 | ||
70 | switch (offset) { | |
71 | case PCI_COMMAND: | |
72 | *valuep = plat->command; | |
73 | break; | |
74 | case PCI_HEADER_TYPE: | |
75 | *valuep = 0; | |
76 | break; | |
77 | case PCI_VENDOR_ID: | |
78 | *valuep = SANDBOX_PCI_VENDOR_ID; | |
79 | break; | |
80 | case PCI_DEVICE_ID: | |
81 | *valuep = SANDBOX_PCI_DEVICE_ID; | |
82 | break; | |
83 | case PCI_CLASS_DEVICE: | |
84 | if (size == PCI_SIZE_8) { | |
85 | *valuep = SANDBOX_PCI_CLASS_SUB_CODE; | |
86 | } else { | |
87 | *valuep = (SANDBOX_PCI_CLASS_CODE << 8) | | |
88 | SANDBOX_PCI_CLASS_SUB_CODE; | |
89 | } | |
90 | break; | |
91 | case PCI_CLASS_CODE: | |
92 | *valuep = SANDBOX_PCI_CLASS_CODE; | |
93 | break; | |
94 | case PCI_BASE_ADDRESS_0: | |
95 | case PCI_BASE_ADDRESS_1: | |
96 | case PCI_BASE_ADDRESS_2: | |
97 | case PCI_BASE_ADDRESS_3: | |
98 | case PCI_BASE_ADDRESS_4: | |
99 | case PCI_BASE_ADDRESS_5: { | |
100 | int barnum; | |
101 | u32 *bar, result; | |
102 | ||
103 | barnum = offset_to_barnum(offset); | |
104 | bar = &plat->bar[barnum]; | |
105 | ||
106 | result = *bar; | |
107 | if (*bar == 0xffffffff) { | |
108 | if (barinfo[barnum].type) { | |
109 | result = (~(barinfo[barnum].size - 1) & | |
110 | PCI_BASE_ADDRESS_IO_MASK) | | |
111 | PCI_BASE_ADDRESS_SPACE_IO; | |
112 | } else { | |
113 | result = (~(barinfo[barnum].size - 1) & | |
114 | PCI_BASE_ADDRESS_MEM_MASK) | | |
115 | PCI_BASE_ADDRESS_MEM_TYPE_32; | |
116 | } | |
117 | } | |
118 | debug("r bar %d=%x\n", barnum, result); | |
119 | *valuep = result; | |
120 | break; | |
121 | } | |
122 | } | |
123 | ||
124 | return 0; | |
125 | } | |
126 | ||
127 | static int sandbox_swap_case_write_config(struct udevice *emul, uint offset, | |
128 | ulong value, enum pci_size_t size) | |
129 | { | |
130 | struct swap_case_platdata *plat = dev_get_platdata(emul); | |
131 | ||
132 | switch (offset) { | |
133 | case PCI_COMMAND: | |
134 | plat->command = value; | |
135 | break; | |
136 | case PCI_BASE_ADDRESS_0: | |
137 | case PCI_BASE_ADDRESS_1: { | |
138 | int barnum; | |
139 | u32 *bar; | |
140 | ||
141 | barnum = offset_to_barnum(offset); | |
142 | bar = &plat->bar[barnum]; | |
143 | ||
144 | debug("w bar %d=%lx\n", barnum, value); | |
145 | *bar = value; | |
146 | break; | |
147 | } | |
148 | } | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr, | |
154 | int *barnump, unsigned int *offsetp) | |
155 | { | |
156 | struct swap_case_platdata *plat = dev_get_platdata(emul); | |
157 | int barnum; | |
158 | ||
159 | for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) { | |
160 | unsigned int size = barinfo[barnum].size; | |
161 | ||
162 | if (addr >= plat->bar[barnum] && | |
163 | addr < plat->bar[barnum] + size) { | |
164 | *barnump = barnum; | |
165 | *offsetp = addr - plat->bar[barnum]; | |
166 | return 0; | |
167 | } | |
168 | } | |
169 | *barnump = -1; | |
170 | ||
171 | return -ENOENT; | |
172 | } | |
173 | ||
174 | static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len) | |
175 | { | |
176 | for (; len > 0; len--, str++) { | |
177 | switch (op) { | |
178 | case OP_TO_UPPER: | |
179 | *str = toupper(*str); | |
180 | break; | |
181 | case OP_TO_LOWER: | |
182 | *str = tolower(*str); | |
183 | break; | |
184 | case OP_SWAP: | |
185 | if (isupper(*str)) | |
186 | *str = tolower(*str); | |
187 | else | |
188 | *str = toupper(*str); | |
189 | break; | |
190 | } | |
191 | } | |
192 | } | |
193 | ||
194 | int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr, | |
195 | ulong *valuep, enum pci_size_t size) | |
196 | { | |
197 | struct swap_case_priv *priv = dev_get_priv(dev); | |
198 | unsigned int offset; | |
199 | int barnum; | |
200 | int ret; | |
201 | ||
202 | ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); | |
203 | if (ret) | |
204 | return ret; | |
205 | ||
206 | if (barnum == 0 && offset == 0) | |
207 | *valuep = (*valuep & ~0xff) | priv->op; | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, | |
213 | ulong value, enum pci_size_t size) | |
214 | { | |
215 | struct swap_case_priv *priv = dev_get_priv(dev); | |
216 | unsigned int offset; | |
217 | int barnum; | |
218 | int ret; | |
219 | ||
220 | ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); | |
221 | if (ret) | |
222 | return ret; | |
223 | if (barnum == 0 && offset == 0) | |
224 | priv->op = value; | |
225 | ||
226 | return 0; | |
227 | } | |
228 | ||
229 | static int sandbox_swap_case_map_physmem(struct udevice *dev, | |
230 | phys_addr_t addr, unsigned long *lenp, void **ptrp) | |
231 | { | |
232 | struct swap_case_priv *priv = dev_get_priv(dev); | |
233 | unsigned int offset, avail; | |
234 | int barnum; | |
235 | int ret; | |
236 | ||
237 | ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); | |
238 | if (ret) | |
239 | return ret; | |
240 | if (barnum == 1) { | |
241 | *ptrp = priv->mem_text + offset; | |
242 | avail = barinfo[1].size - offset; | |
243 | if (avail > barinfo[1].size) | |
244 | *lenp = 0; | |
245 | else | |
246 | *lenp = min(*lenp, (ulong)avail); | |
247 | ||
248 | return 0; | |
249 | } | |
250 | ||
251 | return -ENOENT; | |
252 | } | |
253 | ||
254 | static int sandbox_swap_case_unmap_physmem(struct udevice *dev, | |
255 | const void *vaddr, unsigned long len) | |
256 | { | |
257 | struct swap_case_priv *priv = dev_get_priv(dev); | |
258 | ||
259 | sandbox_swap_case_do_op(priv->op, (void *)vaddr, len); | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | struct dm_pci_emul_ops sandbox_swap_case_emul_ops = { | |
265 | .get_devfn = sandbox_swap_case_get_devfn, | |
266 | .read_config = sandbox_swap_case_read_config, | |
267 | .write_config = sandbox_swap_case_write_config, | |
268 | .read_io = sandbox_swap_case_read_io, | |
269 | .write_io = sandbox_swap_case_write_io, | |
270 | .map_physmem = sandbox_swap_case_map_physmem, | |
271 | .unmap_physmem = sandbox_swap_case_unmap_physmem, | |
272 | }; | |
273 | ||
274 | static const struct udevice_id sandbox_swap_case_ids[] = { | |
275 | { .compatible = "sandbox,swap-case" }, | |
276 | { } | |
277 | }; | |
278 | ||
279 | U_BOOT_DRIVER(sandbox_swap_case_emul) = { | |
280 | .name = "sandbox_swap_case_emul", | |
281 | .id = UCLASS_PCI_EMUL, | |
282 | .of_match = sandbox_swap_case_ids, | |
283 | .ops = &sandbox_swap_case_emul_ops, | |
284 | .priv_auto_alloc_size = sizeof(struct swap_case_priv), | |
285 | .platdata_auto_alloc_size = sizeof(struct swap_case_platdata), | |
286 | }; |