Merge branch 'master' of git://git.denx.de/u-boot-video
[platform/kernel/u-boot.git] / drivers / misc / swap_case.c
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>
12 #include <errno.h>
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 };