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