sandbox: pci: Move pci_offset_to_barnum() to pci.h
[platform/kernel/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, result;
143
144                 barnum = pci_offset_to_barnum(offset);
145                 bar = &plat->bar[barnum];
146
147                 result = *bar;
148                 if (*bar == 0xffffffff) {
149                         if (barinfo[barnum].type) {
150                                 result = (~(barinfo[barnum].size - 1) &
151                                         PCI_BASE_ADDRESS_IO_MASK) |
152                                         PCI_BASE_ADDRESS_SPACE_IO;
153                         } else {
154                                 result = (~(barinfo[barnum].size - 1) &
155                                         PCI_BASE_ADDRESS_MEM_MASK) |
156                                         PCI_BASE_ADDRESS_MEM_TYPE_32;
157                         }
158                 }
159                 debug("r bar %d=%x\n", barnum, result);
160                 *valuep = result;
161                 break;
162         }
163         case PCI_CAPABILITY_LIST:
164                 *valuep = PCI_CAP_ID_PM_OFFSET;
165                 break;
166         case PCI_CAP_ID_PM_OFFSET:
167                 *valuep = (PCI_CAP_ID_EXP_OFFSET << 8) | PCI_CAP_ID_PM;
168                 break;
169         case PCI_CAP_ID_PM_OFFSET + PCI_CAP_LIST_NEXT:
170                 *valuep = PCI_CAP_ID_EXP_OFFSET;
171                 break;
172         case PCI_CAP_ID_EXP_OFFSET:
173                 *valuep = (PCI_CAP_ID_MSIX_OFFSET << 8) | PCI_CAP_ID_EXP;
174                 break;
175         case PCI_CAP_ID_EXP_OFFSET + PCI_CAP_LIST_NEXT:
176                 *valuep = PCI_CAP_ID_MSIX_OFFSET;
177                 break;
178         case PCI_CAP_ID_MSIX_OFFSET:
179                 if (sandbox_swap_case_use_ea(emul))
180                         *valuep = (PCI_CAP_ID_EA_OFFSET << 8) | PCI_CAP_ID_MSIX;
181                 else
182                         *valuep = PCI_CAP_ID_MSIX;
183                 break;
184         case PCI_CAP_ID_MSIX_OFFSET + PCI_CAP_LIST_NEXT:
185                 if (sandbox_swap_case_use_ea(emul))
186                         *valuep = PCI_CAP_ID_EA_OFFSET;
187                 else
188                         *valuep = 0;
189                 break;
190         case PCI_CAP_ID_EA_OFFSET:
191                 *valuep = (PCI_CAP_ID_EA_ENTRY_CNT << 16) | PCI_CAP_ID_EA;
192                 break;
193         case PCI_CAP_ID_EA_OFFSET + PCI_CAP_LIST_NEXT:
194                 *valuep = 0;
195                 break;
196         case PCI_EXT_CAP_ID_ERR_OFFSET:
197                 *valuep = (PCI_EXT_CAP_ID_VC_OFFSET << 20) | PCI_EXT_CAP_ID_ERR;
198                 break;
199         case PCI_EXT_CAP_ID_VC_OFFSET:
200                 *valuep = (PCI_EXT_CAP_ID_DSN_OFFSET << 20) | PCI_EXT_CAP_ID_VC;
201                 break;
202         case PCI_EXT_CAP_ID_DSN_OFFSET:
203                 *valuep = PCI_EXT_CAP_ID_DSN;
204                 break;
205         }
206
207         return 0;
208 }
209
210 static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
211                                           ulong value, enum pci_size_t size)
212 {
213         struct swap_case_platdata *plat = dev_get_platdata(emul);
214
215         switch (offset) {
216         case PCI_COMMAND:
217                 plat->command = value;
218                 break;
219         case PCI_BASE_ADDRESS_0:
220         case PCI_BASE_ADDRESS_1: {
221                 int barnum;
222                 u32 *bar;
223
224                 barnum = pci_offset_to_barnum(offset);
225                 bar = &plat->bar[barnum];
226
227                 debug("w bar %d=%lx\n", barnum, value);
228                 *bar = value;
229                 /* space indicator (bit#0) is read-only */
230                 *bar |= barinfo[barnum].type;
231                 break;
232         }
233         }
234
235         return 0;
236 }
237
238 static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr,
239                                       int *barnump, unsigned int *offsetp)
240 {
241         struct swap_case_platdata *plat = dev_get_platdata(emul);
242         int barnum;
243
244         for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
245                 unsigned int size = barinfo[barnum].size;
246                 u32 base = plat->bar[barnum] & ~PCI_BASE_ADDRESS_SPACE;
247
248                 if (addr >= base && addr < base + size) {
249                         *barnump = barnum;
250                         *offsetp = addr - base;
251                         return 0;
252                 }
253         }
254         *barnump = -1;
255
256         return -ENOENT;
257 }
258
259 static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
260 {
261         for (; len > 0; len--, str++) {
262                 switch (op) {
263                 case OP_TO_UPPER:
264                         *str = toupper(*str);
265                         break;
266                 case OP_TO_LOWER:
267                         *str = tolower(*str);
268                         break;
269                 case OP_SWAP:
270                         if (isupper(*str))
271                                 *str = tolower(*str);
272                         else
273                                 *str = toupper(*str);
274                         break;
275                 }
276         }
277 }
278
279 static int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
280                                      ulong *valuep, enum pci_size_t size)
281 {
282         struct swap_case_priv *priv = dev_get_priv(dev);
283         unsigned int offset;
284         int barnum;
285         int ret;
286
287         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
288         if (ret)
289                 return ret;
290
291         if (barnum == 0 && offset == 0)
292                 *valuep = (*valuep & ~0xff) | priv->op;
293
294         return 0;
295 }
296
297 static int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
298                                       ulong value, enum pci_size_t size)
299 {
300         struct swap_case_priv *priv = dev_get_priv(dev);
301         unsigned int offset;
302         int barnum;
303         int ret;
304
305         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
306         if (ret)
307                 return ret;
308         if (barnum == 0 && offset == 0)
309                 priv->op = value;
310
311         return 0;
312 }
313
314 static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC;
315 static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
316
317 static int sandbox_swap_case_map_physmem(struct udevice *dev,
318                 phys_addr_t addr, unsigned long *lenp, void **ptrp)
319 {
320         struct swap_case_priv *priv = dev_get_priv(dev);
321         unsigned int offset, avail;
322         int barnum;
323         int ret;
324
325         if (sandbox_swap_case_use_ea(dev)) {
326                 /*
327                  * only support mapping base address in EA test for now, we
328                  * don't handle mapping an offset inside a BAR.  Seems good
329                  * enough for the current test.
330                  */
331                 switch (addr) {
332                 case (phys_addr_t)PCI_CAP_EA_BASE_LO0:
333                         *ptrp = &priv->op;
334                         *lenp = 4;
335                         break;
336                 case (phys_addr_t)PCI_CAP_EA_BASE_LO1:
337                         *ptrp = priv->mem_text;
338                         *lenp = barinfo[1].size - 1;
339                         break;
340                 case (phys_addr_t)((PCI_CAP_EA_BASE_HI2 << 32) |
341                                    PCI_CAP_EA_BASE_LO2):
342                         *ptrp = &pci_ea_bar2_magic;
343                         *lenp = PCI_CAP_EA_SIZE_LO;
344                         break;
345                 case (phys_addr_t)((PCI_CAP_EA_BASE_HI4 << 32) |
346                                    PCI_CAP_EA_BASE_LO4):
347                         *ptrp = &pci_ea_bar4_magic;
348                         *lenp = (PCI_CAP_EA_SIZE_HI << 32) |
349                                 PCI_CAP_EA_SIZE_LO;
350                         break;
351                 default:
352                         return -ENOENT;
353                 }
354                 return 0;
355         }
356
357         ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
358         if (ret)
359                 return ret;
360
361         if (barnum == 1) {
362                 *ptrp = priv->mem_text + offset;
363                 avail = barinfo[1].size - offset;
364                 if (avail > barinfo[1].size)
365                         *lenp = 0;
366                 else
367                         *lenp = min(*lenp, (ulong)avail);
368
369                 return 0;
370         }
371
372         return -ENOENT;
373 }
374
375 static int sandbox_swap_case_unmap_physmem(struct udevice *dev,
376                                            const void *vaddr, unsigned long len)
377 {
378         struct swap_case_priv *priv = dev_get_priv(dev);
379
380         sandbox_swap_case_do_op(priv->op, (void *)vaddr, len);
381
382         return 0;
383 }
384
385 static struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
386         .read_config = sandbox_swap_case_read_config,
387         .write_config = sandbox_swap_case_write_config,
388         .read_io = sandbox_swap_case_read_io,
389         .write_io = sandbox_swap_case_write_io,
390         .map_physmem = sandbox_swap_case_map_physmem,
391         .unmap_physmem = sandbox_swap_case_unmap_physmem,
392 };
393
394 static const struct udevice_id sandbox_swap_case_ids[] = {
395         { .compatible = "sandbox,swap-case" },
396         { }
397 };
398
399 U_BOOT_DRIVER(sandbox_swap_case_emul) = {
400         .name           = "sandbox_swap_case_emul",
401         .id             = UCLASS_PCI_EMUL,
402         .of_match       = sandbox_swap_case_ids,
403         .ops            = &sandbox_swap_case_emul_ops,
404         .priv_auto_alloc_size = sizeof(struct swap_case_priv),
405         .platdata_auto_alloc_size = sizeof(struct swap_case_platdata),
406 };
407
408 static struct pci_device_id sandbox_swap_case_supported[] = {
409         { PCI_VDEVICE(SANDBOX, SANDBOX_PCI_SWAP_CASE_EMUL_ID),
410                 SWAP_CASE_DRV_DATA },
411         {},
412 };
413
414 U_BOOT_PCI_DEVICE(sandbox_swap_case_emul, sandbox_swap_case_supported);