sandbox: pci: Create a new sandbox_pci_read_bar() function
[oweals/u-boot.git] / drivers / misc / swap_case.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * PCI emulation device which swaps the case of text
4  *
5  * Copyright (c) 2014 Google, Inc
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8
9 #include <common.h>
10 #include <dm.h>
11 #include <errno.h>
12 #include <pci.h>
13 #include <asm/test.h>
14 #include <linux/ctype.h>
15
16 /**
17  * struct swap_case_platdata - platform data for this device
18  *
19  * @command:    Current PCI command value
20  * @bar:        Current base address values
21  */
22 struct swap_case_platdata {
23         u16 command;
24         u32 bar[6];
25 };
26
27 enum {
28         MEM_TEXT_SIZE   = 0x100,
29 };
30
31 enum swap_case_op {
32         OP_TO_LOWER,
33         OP_TO_UPPER,
34         OP_SWAP,
35 };
36
37 static struct pci_bar {
38         int type;
39         u32 size;
40 } barinfo[] = {
41         { PCI_BASE_ADDRESS_SPACE_IO, 1 },
42         { PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE },
43         { 0, 0 },
44         { 0, 0 },
45         { 0, 0 },
46         { 0, 0 },
47 };
48
49 struct swap_case_priv {
50         enum swap_case_op op;
51         char mem_text[MEM_TEXT_SIZE];
52 };
53
54 static int sandbox_swap_case_use_ea(struct udevice *dev)
55 {
56         return !!ofnode_get_property(dev->node, "use-ea", NULL);
57 }
58
59 /* Please keep these macros in sync with ea_regs below */
60 #define PCI_CAP_ID_EA_SIZE              (sizeof(ea_regs) + 4)
61 #define PCI_CAP_ID_EA_ENTRY_CNT         4
62 /* Hardcoded EA structure, excluding 1st DW. */
63 static const u32 ea_regs[] = {
64         /* BEI=0, ES=2, BAR0 32b Base + 32b MaxOffset, I/O space */
65         (2 << 8) | 2,
66         PCI_CAP_EA_BASE_LO0,
67         0,
68         /* BEI=1, ES=2, BAR1 32b Base + 32b MaxOffset */
69         (1 << 4) | 2,
70         PCI_CAP_EA_BASE_LO1,
71         MEM_TEXT_SIZE - 1,
72         /* BEI=2, ES=3, BAR2 64b Base + 32b MaxOffset */
73         (2 << 4) | 3,
74         PCI_CAP_EA_BASE_LO2 | PCI_EA_IS_64,
75         PCI_CAP_EA_SIZE_LO,
76         PCI_CAP_EA_BASE_HI2,
77         /* BEI=4, ES=4, BAR4 64b Base + 64b MaxOffset */
78         (4 << 4) | 4,
79         PCI_CAP_EA_BASE_LO4 | PCI_EA_IS_64,
80         PCI_CAP_EA_SIZE_LO | PCI_EA_IS_64,
81         PCI_CAP_EA_BASE_HI4,
82         PCI_CAP_EA_SIZE_HI,
83 };
84
85 static int sandbox_swap_case_read_ea(struct udevice *emul, uint offset,
86                                      ulong *valuep, enum pci_size_t size)
87 {
88         u32 reg;
89
90         offset = offset - PCI_CAP_ID_EA_OFFSET - 4;
91         reg = ea_regs[offset >> 2];
92         reg >>= (offset % 4) * 8;
93
94         *valuep = reg;
95         return 0;
96 }
97
98 static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
99                                          ulong *valuep, enum pci_size_t size)
100 {
101         struct swap_case_platdata *plat = dev_get_platdata(emul);
102
103         /*
104          * The content of the EA capability structure is handled elsewhere to
105          * keep the switch/case below sane
106          */
107         if (offset > PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT &&
108             offset < PCI_CAP_ID_EA_OFFSET + PCI_CAP_ID_EA_SIZE)
109                 return sandbox_swap_case_read_ea(emul, offset, valuep, size);
110
111         switch (offset) {
112         case PCI_COMMAND:
113                 *valuep = plat->command;
114                 break;
115         case PCI_HEADER_TYPE:
116                 *valuep = 0;
117                 break;
118         case PCI_VENDOR_ID:
119                 *valuep = SANDBOX_PCI_VENDOR_ID;
120                 break;
121         case PCI_DEVICE_ID:
122                 *valuep = SANDBOX_PCI_SWAP_CASE_EMUL_ID;
123                 break;
124         case PCI_CLASS_DEVICE:
125                 if (size == PCI_SIZE_8) {
126                         *valuep = SANDBOX_PCI_CLASS_SUB_CODE;
127                 } else {
128                         *valuep = (SANDBOX_PCI_CLASS_CODE << 8) |
129                                         SANDBOX_PCI_CLASS_SUB_CODE;
130                 }
131                 break;
132         case PCI_CLASS_CODE:
133                 *valuep = SANDBOX_PCI_CLASS_CODE;
134                 break;
135         case PCI_BASE_ADDRESS_0:
136         case PCI_BASE_ADDRESS_1:
137         case PCI_BASE_ADDRESS_2:
138         case PCI_BASE_ADDRESS_3:
139         case PCI_BASE_ADDRESS_4:
140         case PCI_BASE_ADDRESS_5: {
141                 int barnum;
142                 u32 *bar;
143
144                 barnum = pci_offset_to_barnum(offset);
145                 bar = &plat->bar[barnum];
146
147                 *valuep = sandbox_pci_read_bar(*bar, barinfo[barnum].type,
148                                                barinfo[barnum].size);
149                 break;
150         }
151         case PCI_CAPABILITY_LIST:
152                 *valuep = PCI_CAP_ID_PM_OFFSET;
153                 break;
154         case PCI_CAP_ID_PM_OFFSET:
155                 *valuep = (PCI_CAP_ID_EXP_OFFSET << 8) | PCI_CAP_ID_PM;
156                 break;
157         case PCI_CAP_ID_PM_OFFSET + PCI_CAP_LIST_NEXT:
158                 *valuep = PCI_CAP_ID_EXP_OFFSET;
159                 break;
160         case PCI_CAP_ID_EXP_OFFSET:
161                 *valuep = (PCI_CAP_ID_MSIX_OFFSET << 8) | PCI_CAP_ID_EXP;
162                 break;
163         case PCI_CAP_ID_EXP_OFFSET + PCI_CAP_LIST_NEXT:
164                 *valuep = PCI_CAP_ID_MSIX_OFFSET;
165                 break;
166         case PCI_CAP_ID_MSIX_OFFSET:
167                 if (sandbox_swap_case_use_ea(emul))
168                         *valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;
169                 else
170                         *valuep = PCI_CAP_ID_MSIX;
171                 break;
172         case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
173                 if (sandbox_swap_case_use_ea(emul))
174                         *valuep = PCI_CAP_ID_EA_OFFSET;
175                 else
176                         *valuep = 0;
177                 break;
178         case PCI_CAP_ID_EA_OFFSET:
179                 *valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA;
180                 break;
181         case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT:
182                 *valuep = 0;
183                 break;
184         case PCI_EXT_CAP_ID_ERR_OFFSET:
185                 *valuep = (PCI_EXT_CAP_ID_VC_OFFSET << 20) | PCI_EXT_CAP_ID_ERR;
186                 break;
187         case PCI_EXT_CAP_ID_VC_OFFSET:
188                 *valuep = (PCI_EXT_CAP_ID_DSN_OFFSET << 20) | PCI_EXT_CAP_ID_VC;
189                 break;
190         case PCI_EXT_CAP_ID_DSN_OFFSET:
191                 *valuep = PCI_EXT_CAP_ID_DSN;
192                 break;
193         }
194
195         return 0;
196 }
197
198 static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
199                                           ulong value, enum pci_size_t size)
200 {
201         struct swap_case_platdata *plat = dev_get_platdata(emul);
202
203         switch (offset) {
204         case PCI_COMMAND:
205                 plat->command = value;
206                 break;
207         case PCI_BASE_ADDRESS_0:
208         case PCI_BASE_ADDRESS_1: {
209                 int barnum;
210                 u32 *bar;
211
212                 barnum = pci_offset_to_barnum(offset);
213                 bar = &plat->bar[barnum];
214
215                 debug("w bar %d=%lx\n", barnum, value);
216                 *bar = value;
217                 /* space indicator (bit#0) is read-only */
218                 *bar |= barinfo[barnum].type;
219                 break;
220         }
221         }
222
223         return 0;
224 }
225
226 static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr,
227                                       int *barnump, unsigned int *offsetp)
228 {
229         struct swap_case_platdata *plat = dev_get_platdata(emul);
230         int barnum;
231
232         for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
233                 unsigned int size = barinfo[barnum].size;
234                 u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE;
235
236                 if (addr >= base && addr < base + size) {
237                         *barnump = barnum;
238                         *offsetp = addr - base;
239                         return 0;
240                 }
241         }
242         *barnump = -1;
243
244         return -ENOENT;
245 }
246
247 static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
248 {
249         for (; len > 0; len--, str++) {
250                 switch (op) {
251                 case OP_TO_UPPER:
252                         *str = toupper(*str);
253                         break;
254                 case OP_TO_LOWER:
255                         *str = tolower(*str);
256                         break;
257                 case OP_SWAP:
258                         if (isupper(*str))
259                                 *str = tolower(*str);
260                         else
261                                 *str = toupper(*str);
262                         break;
263                 }
264         }
265 }
266
267 static int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
268                                      ulong *valuep, enum pci_size_t size)
269 {
270         struct swap_case_priv *priv = dev_get_priv(dev);
271         unsigned int offset;
272         int barnum;
273         int ret;
274
275         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
276         if (ret)
277                 return ret;
278
279         if (barnum == 0 && offset == 0)
280                 *valuep = (*valuep & ~0xff) | priv->op;
281
282         return 0;
283 }
284
285 static int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
286                                       ulong value, enum pci_size_t size)
287 {
288         struct swap_case_priv *priv = dev_get_priv(dev);
289         unsigned int offset;
290         int barnum;
291         int ret;
292
293         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
294         if (ret)
295                 return ret;
296         if (barnum == 0 && offset == 0)
297                 priv->op = value;
298
299         return 0;
300 }
301
302 static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC;
303 static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
304
305 static int sandbox_swap_case_map_physmem(struct udevice *dev,
306                 phys_addr_t addr, unsigned long *lenp, void **ptrp)
307 {
308         struct swap_case_priv *priv = dev_get_priv(dev);
309         unsigned int offset, avail;
310         int barnum;
311         int ret;
312
313         if (sandbox_swap_case_use_ea(dev)) {
314                 /*
315                  * only support mapping base address in EA test for now, we
316                  * don't handle mapping an offset inside a BAR.  Seems good
317                  * enough for the current test.
318                  */
319                 switch (addr) {
320                 case (phys_addr_t)PCI_CAP_EA_BASE_LO0:
321                         *ptrp = &priv->op;
322                         *lenp = 4;
323                         break;
324                 case (phys_addr_t)PCI_CAP_EA_BASE_LO1:
325                         *ptrp = priv->mem_text;
326                         *lenp = barinfo[1].size - 1;
327                         break;
328                 case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) |
329                                    PCI_CAP_EA_BASE_LO2):
330                         *ptrp = &pci_ea_bar2_magic;
331                         *lenp = PCI_CAP_EA_SIZE_LO;
332                         break;
333                 case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) |
334                                    PCI_CAP_EA_BASE_LO4):
335                         *ptrp = &pci_ea_bar4_magic;
336                         *lenp = (PCI_CAP_EA_SIZE_HI << 32) |
337                                 PCI_CAP_EA_SIZE_LO;
338                         break;
339                 default:
340                         return -ENOENT;
341                 }
342                 return 0;
343         }
344
345         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
346         if (ret)
347                 return ret;
348
349         if (barnum == 1) {
350                 *ptrp = priv->mem_text + offset;
351                 avail = barinfo[1].size - offset;
352                 if (avail > barinfo[1].size)
353                         *lenp = 0;
354                 else
355                         *lenp = min(*lenp, (ulong)avail);
356
357                 return 0;
358         }
359
360         return -ENOENT;
361 }
362
363 static int sandbox_swap_case_unmap_physmem(struct udevice *dev,
364                                            const void *vaddr, unsigned long len)
365 {
366         struct swap_case_priv *priv = dev_get_priv(dev);
367
368         sandbox_swap_case_do_op(priv->op, (void *)vaddr, len);
369
370         return 0;
371 }
372
373 static struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
374         .read_config = sandbox_swap_case_read_config,
375         .write_config = sandbox_swap_case_write_config,
376         .read_io = sandbox_swap_case_read_io,
377         .write_io = sandbox_swap_case_write_io,
378         .map_physmem = sandbox_swap_case_map_physmem,
379         .unmap_physmem = sandbox_swap_case_unmap_physmem,
380 };
381
382 static const struct udevice_id sandbox_swap_case_ids[] = {
383         { .compatible = "sandbox,swap-case" },
384         { }
385 };
386
387 U_BOOT_DRIVER(sandbox_swap_case_emul) = {
388         .name           = "sandbox_swap_case_emul",
389         .id             = UCLASS_PCI_EMUL,
390         .of_match       = sandbox_swap_case_ids,
391         .ops            = &sandbox_swap_case_emul_ops,
392         .priv_auto_alloc_size = sizeof(struct swap_case_priv),
393         .platdata_auto_alloc_size = sizeof(struct swap_case_platdata),
394 };
395
396 static struct pci_device_id sandbox_swap_case_supported[] = {
397         { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_SWAP_CASE_EMUL_ID),
398                 SWAP_CASE_DRV_DATA },
399         {},
400 };
401
402 U_BOOT_PCI_DEVICE(sandbox_swap_case_emul, sandbox_swap_case_supported);