uboot/drivers/misc/swap_case.c
<<
>>
Prefs
   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 */
  23struct swap_case_plat {
  24        u16 command;
  25        u32 bar[6];
  26};
  27
  28enum {
  29        MEM_TEXT_SIZE   = 0x100,
  30};
  31
  32enum swap_case_op {
  33        OP_TO_LOWER,
  34        OP_TO_UPPER,
  35        OP_SWAP,
  36};
  37
  38static 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
  50struct swap_case_priv {
  51        enum swap_case_op op;
  52        char mem_text[MEM_TEXT_SIZE];
  53};
  54
  55static 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. */
  64static 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
  86static 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
  99static 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
 200static 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
 228static 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
 249static 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
 269static 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
 287static 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
 304static int pci_ea_bar2_magic = PCI_EA_BAR2_MAGIC;
 305static int pci_ea_bar4_magic = PCI_EA_BAR4_MAGIC;
 306
 307static 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
 365static 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
 375static 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
 384static const struct udevice_id sandbox_swap_case_ids[] = {
 385        { .compatible = "sandbox,swap-case" },
 386        { }
 387};
 388
 389U_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
 398static 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
 404U_BOOT_PCI_DEVICE(sandbox_swap_case_emul, sandbox_swap_case_supported);
 405