xref: /openbmc/u-boot/drivers/misc/swap_case.c (revision 3e79a4ab)
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 };
287