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