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