1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * PCI emulation device which swaps the case of text
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Copyright (c) 2014 Google, Inc
5*4882a593Smuzhiyun * Written by Simon Glass <sjg@chromium.org>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
8*4882a593Smuzhiyun */
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <common.h>
11*4882a593Smuzhiyun #include <dm.h>
12*4882a593Smuzhiyun #include <errno.h>
13*4882a593Smuzhiyun #include <pci.h>
14*4882a593Smuzhiyun #include <asm/test.h>
15*4882a593Smuzhiyun #include <linux/ctype.h>
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun /**
18*4882a593Smuzhiyun * struct swap_case_platdata - platform data for this device
19*4882a593Smuzhiyun *
20*4882a593Smuzhiyun * @command: Current PCI command value
21*4882a593Smuzhiyun * @bar: Current base address values
22*4882a593Smuzhiyun */
23*4882a593Smuzhiyun struct swap_case_platdata {
24*4882a593Smuzhiyun u16 command;
25*4882a593Smuzhiyun u32 bar[2];
26*4882a593Smuzhiyun };
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define offset_to_barnum(offset) \
29*4882a593Smuzhiyun (((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32))
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun enum {
32*4882a593Smuzhiyun MEM_TEXT_SIZE = 0x100,
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun enum swap_case_op {
36*4882a593Smuzhiyun OP_TO_LOWER,
37*4882a593Smuzhiyun OP_TO_UPPER,
38*4882a593Smuzhiyun OP_SWAP,
39*4882a593Smuzhiyun };
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun static struct pci_bar {
42*4882a593Smuzhiyun int type;
43*4882a593Smuzhiyun u32 size;
44*4882a593Smuzhiyun } barinfo[] = {
45*4882a593Smuzhiyun { PCI_BASE_ADDRESS_SPACE_IO, 1 },
46*4882a593Smuzhiyun { PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE },
47*4882a593Smuzhiyun { 0, 0 },
48*4882a593Smuzhiyun { 0, 0 },
49*4882a593Smuzhiyun { 0, 0 },
50*4882a593Smuzhiyun { 0, 0 },
51*4882a593Smuzhiyun };
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun struct swap_case_priv {
54*4882a593Smuzhiyun enum swap_case_op op;
55*4882a593Smuzhiyun char mem_text[MEM_TEXT_SIZE];
56*4882a593Smuzhiyun };
57*4882a593Smuzhiyun
sandbox_swap_case_get_devfn(struct udevice * dev)58*4882a593Smuzhiyun static int sandbox_swap_case_get_devfn(struct udevice *dev)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun struct pci_child_platdata *plat = dev_get_parent_platdata(dev);
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun return plat->devfn;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun
sandbox_swap_case_read_config(struct udevice * emul,uint offset,ulong * valuep,enum pci_size_t size)65*4882a593Smuzhiyun static int sandbox_swap_case_read_config(struct udevice *emul, uint offset,
66*4882a593Smuzhiyun ulong *valuep, enum pci_size_t size)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun struct swap_case_platdata *plat = dev_get_platdata(emul);
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun switch (offset) {
71*4882a593Smuzhiyun case PCI_COMMAND:
72*4882a593Smuzhiyun *valuep = plat->command;
73*4882a593Smuzhiyun break;
74*4882a593Smuzhiyun case PCI_HEADER_TYPE:
75*4882a593Smuzhiyun *valuep = 0;
76*4882a593Smuzhiyun break;
77*4882a593Smuzhiyun case PCI_VENDOR_ID:
78*4882a593Smuzhiyun *valuep = SANDBOX_PCI_VENDOR_ID;
79*4882a593Smuzhiyun break;
80*4882a593Smuzhiyun case PCI_DEVICE_ID:
81*4882a593Smuzhiyun *valuep = SANDBOX_PCI_DEVICE_ID;
82*4882a593Smuzhiyun break;
83*4882a593Smuzhiyun case PCI_CLASS_DEVICE:
84*4882a593Smuzhiyun if (size == PCI_SIZE_8) {
85*4882a593Smuzhiyun *valuep = SANDBOX_PCI_CLASS_SUB_CODE;
86*4882a593Smuzhiyun } else {
87*4882a593Smuzhiyun *valuep = (SANDBOX_PCI_CLASS_CODE << 8) |
88*4882a593Smuzhiyun SANDBOX_PCI_CLASS_SUB_CODE;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun break;
91*4882a593Smuzhiyun case PCI_CLASS_CODE:
92*4882a593Smuzhiyun *valuep = SANDBOX_PCI_CLASS_CODE;
93*4882a593Smuzhiyun break;
94*4882a593Smuzhiyun case PCI_BASE_ADDRESS_0:
95*4882a593Smuzhiyun case PCI_BASE_ADDRESS_1:
96*4882a593Smuzhiyun case PCI_BASE_ADDRESS_2:
97*4882a593Smuzhiyun case PCI_BASE_ADDRESS_3:
98*4882a593Smuzhiyun case PCI_BASE_ADDRESS_4:
99*4882a593Smuzhiyun case PCI_BASE_ADDRESS_5: {
100*4882a593Smuzhiyun int barnum;
101*4882a593Smuzhiyun u32 *bar, result;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun barnum = offset_to_barnum(offset);
104*4882a593Smuzhiyun bar = &plat->bar[barnum];
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun result = *bar;
107*4882a593Smuzhiyun if (*bar == 0xffffffff) {
108*4882a593Smuzhiyun if (barinfo[barnum].type) {
109*4882a593Smuzhiyun result = (~(barinfo[barnum].size - 1) &
110*4882a593Smuzhiyun PCI_BASE_ADDRESS_IO_MASK) |
111*4882a593Smuzhiyun PCI_BASE_ADDRESS_SPACE_IO;
112*4882a593Smuzhiyun } else {
113*4882a593Smuzhiyun result = (~(barinfo[barnum].size - 1) &
114*4882a593Smuzhiyun PCI_BASE_ADDRESS_MEM_MASK) |
115*4882a593Smuzhiyun PCI_BASE_ADDRESS_MEM_TYPE_32;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun debug("r bar %d=%x\n", barnum, result);
119*4882a593Smuzhiyun *valuep = result;
120*4882a593Smuzhiyun break;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun return 0;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
sandbox_swap_case_write_config(struct udevice * emul,uint offset,ulong value,enum pci_size_t size)127*4882a593Smuzhiyun static int sandbox_swap_case_write_config(struct udevice *emul, uint offset,
128*4882a593Smuzhiyun ulong value, enum pci_size_t size)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct swap_case_platdata *plat = dev_get_platdata(emul);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun switch (offset) {
133*4882a593Smuzhiyun case PCI_COMMAND:
134*4882a593Smuzhiyun plat->command = value;
135*4882a593Smuzhiyun break;
136*4882a593Smuzhiyun case PCI_BASE_ADDRESS_0:
137*4882a593Smuzhiyun case PCI_BASE_ADDRESS_1: {
138*4882a593Smuzhiyun int barnum;
139*4882a593Smuzhiyun u32 *bar;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun barnum = offset_to_barnum(offset);
142*4882a593Smuzhiyun bar = &plat->bar[barnum];
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun debug("w bar %d=%lx\n", barnum, value);
145*4882a593Smuzhiyun *bar = value;
146*4882a593Smuzhiyun break;
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun }
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun return 0;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
sandbox_swap_case_find_bar(struct udevice * emul,unsigned int addr,int * barnump,unsigned int * offsetp)153*4882a593Smuzhiyun static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr,
154*4882a593Smuzhiyun int *barnump, unsigned int *offsetp)
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun struct swap_case_platdata *plat = dev_get_platdata(emul);
157*4882a593Smuzhiyun int barnum;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) {
160*4882a593Smuzhiyun unsigned int size = barinfo[barnum].size;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (addr >= plat->bar[barnum] &&
163*4882a593Smuzhiyun addr < plat->bar[barnum] + size) {
164*4882a593Smuzhiyun *barnump = barnum;
165*4882a593Smuzhiyun *offsetp = addr - plat->bar[barnum];
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun *barnump = -1;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun return -ENOENT;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
sandbox_swap_case_do_op(enum swap_case_op op,char * str,int len)174*4882a593Smuzhiyun static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun for (; len > 0; len--, str++) {
177*4882a593Smuzhiyun switch (op) {
178*4882a593Smuzhiyun case OP_TO_UPPER:
179*4882a593Smuzhiyun *str = toupper(*str);
180*4882a593Smuzhiyun break;
181*4882a593Smuzhiyun case OP_TO_LOWER:
182*4882a593Smuzhiyun *str = tolower(*str);
183*4882a593Smuzhiyun break;
184*4882a593Smuzhiyun case OP_SWAP:
185*4882a593Smuzhiyun if (isupper(*str))
186*4882a593Smuzhiyun *str = tolower(*str);
187*4882a593Smuzhiyun else
188*4882a593Smuzhiyun *str = toupper(*str);
189*4882a593Smuzhiyun break;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
sandbox_swap_case_read_io(struct udevice * dev,unsigned int addr,ulong * valuep,enum pci_size_t size)194*4882a593Smuzhiyun int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr,
195*4882a593Smuzhiyun ulong *valuep, enum pci_size_t size)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun struct swap_case_priv *priv = dev_get_priv(dev);
198*4882a593Smuzhiyun unsigned int offset;
199*4882a593Smuzhiyun int barnum;
200*4882a593Smuzhiyun int ret;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
203*4882a593Smuzhiyun if (ret)
204*4882a593Smuzhiyun return ret;
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (barnum == 0 && offset == 0)
207*4882a593Smuzhiyun *valuep = (*valuep & ~0xff) | priv->op;
208*4882a593Smuzhiyun
209*4882a593Smuzhiyun return 0;
210*4882a593Smuzhiyun }
211*4882a593Smuzhiyun
sandbox_swap_case_write_io(struct udevice * dev,unsigned int addr,ulong value,enum pci_size_t size)212*4882a593Smuzhiyun int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr,
213*4882a593Smuzhiyun ulong value, enum pci_size_t size)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun struct swap_case_priv *priv = dev_get_priv(dev);
216*4882a593Smuzhiyun unsigned int offset;
217*4882a593Smuzhiyun int barnum;
218*4882a593Smuzhiyun int ret;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
221*4882a593Smuzhiyun if (ret)
222*4882a593Smuzhiyun return ret;
223*4882a593Smuzhiyun if (barnum == 0 && offset == 0)
224*4882a593Smuzhiyun priv->op = value;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
sandbox_swap_case_map_physmem(struct udevice * dev,phys_addr_t addr,unsigned long * lenp,void ** ptrp)229*4882a593Smuzhiyun static int sandbox_swap_case_map_physmem(struct udevice *dev,
230*4882a593Smuzhiyun phys_addr_t addr, unsigned long *lenp, void **ptrp)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun struct swap_case_priv *priv = dev_get_priv(dev);
233*4882a593Smuzhiyun unsigned int offset, avail;
234*4882a593Smuzhiyun int barnum;
235*4882a593Smuzhiyun int ret;
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset);
238*4882a593Smuzhiyun if (ret)
239*4882a593Smuzhiyun return ret;
240*4882a593Smuzhiyun if (barnum == 1) {
241*4882a593Smuzhiyun *ptrp = priv->mem_text + offset;
242*4882a593Smuzhiyun avail = barinfo[1].size - offset;
243*4882a593Smuzhiyun if (avail > barinfo[1].size)
244*4882a593Smuzhiyun *lenp = 0;
245*4882a593Smuzhiyun else
246*4882a593Smuzhiyun *lenp = min(*lenp, (ulong)avail);
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun return 0;
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun return -ENOENT;
252*4882a593Smuzhiyun }
253*4882a593Smuzhiyun
sandbox_swap_case_unmap_physmem(struct udevice * dev,const void * vaddr,unsigned long len)254*4882a593Smuzhiyun static int sandbox_swap_case_unmap_physmem(struct udevice *dev,
255*4882a593Smuzhiyun const void *vaddr, unsigned long len)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun struct swap_case_priv *priv = dev_get_priv(dev);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun sandbox_swap_case_do_op(priv->op, (void *)vaddr, len);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun return 0;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun struct dm_pci_emul_ops sandbox_swap_case_emul_ops = {
265*4882a593Smuzhiyun .get_devfn = sandbox_swap_case_get_devfn,
266*4882a593Smuzhiyun .read_config = sandbox_swap_case_read_config,
267*4882a593Smuzhiyun .write_config = sandbox_swap_case_write_config,
268*4882a593Smuzhiyun .read_io = sandbox_swap_case_read_io,
269*4882a593Smuzhiyun .write_io = sandbox_swap_case_write_io,
270*4882a593Smuzhiyun .map_physmem = sandbox_swap_case_map_physmem,
271*4882a593Smuzhiyun .unmap_physmem = sandbox_swap_case_unmap_physmem,
272*4882a593Smuzhiyun };
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun static const struct udevice_id sandbox_swap_case_ids[] = {
275*4882a593Smuzhiyun { .compatible = "sandbox,swap-case" },
276*4882a593Smuzhiyun { }
277*4882a593Smuzhiyun };
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun U_BOOT_DRIVER(sandbox_swap_case_emul) = {
280*4882a593Smuzhiyun .name = "sandbox_swap_case_emul",
281*4882a593Smuzhiyun .id = UCLASS_PCI_EMUL,
282*4882a593Smuzhiyun .of_match = sandbox_swap_case_ids,
283*4882a593Smuzhiyun .ops = &sandbox_swap_case_emul_ops,
284*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct swap_case_priv),
285*4882a593Smuzhiyun .platdata_auto_alloc_size = sizeof(struct swap_case_platdata),
286*4882a593Smuzhiyun };
287