/* 
 | 
 * PCI emulation device which swaps the case of text 
 | 
 * 
 | 
 * Copyright (c) 2014 Google, Inc 
 | 
 * Written by Simon Glass <sjg@chromium.org> 
 | 
 * 
 | 
 * SPDX-License-Identifier:    GPL-2.0+ 
 | 
 */ 
 | 
  
 | 
#include <common.h> 
 | 
#include <dm.h> 
 | 
#include <errno.h> 
 | 
#include <pci.h> 
 | 
#include <asm/test.h> 
 | 
#include <linux/ctype.h> 
 | 
  
 | 
/** 
 | 
 * struct swap_case_platdata - platform data for this device 
 | 
 * 
 | 
 * @command:    Current PCI command value 
 | 
 * @bar:    Current base address values 
 | 
 */ 
 | 
struct swap_case_platdata { 
 | 
    u16 command; 
 | 
    u32 bar[2]; 
 | 
}; 
 | 
  
 | 
#define offset_to_barnum(offset)    \ 
 | 
        (((offset) - PCI_BASE_ADDRESS_0) / sizeof(u32)) 
 | 
  
 | 
enum { 
 | 
    MEM_TEXT_SIZE    = 0x100, 
 | 
}; 
 | 
  
 | 
enum swap_case_op { 
 | 
    OP_TO_LOWER, 
 | 
    OP_TO_UPPER, 
 | 
    OP_SWAP, 
 | 
}; 
 | 
  
 | 
static struct pci_bar { 
 | 
    int type; 
 | 
    u32 size; 
 | 
} barinfo[] = { 
 | 
    { PCI_BASE_ADDRESS_SPACE_IO, 1 }, 
 | 
    { PCI_BASE_ADDRESS_MEM_TYPE_32, MEM_TEXT_SIZE }, 
 | 
    { 0, 0 }, 
 | 
    { 0, 0 }, 
 | 
    { 0, 0 }, 
 | 
    { 0, 0 }, 
 | 
}; 
 | 
  
 | 
struct swap_case_priv { 
 | 
    enum swap_case_op op; 
 | 
    char mem_text[MEM_TEXT_SIZE]; 
 | 
}; 
 | 
  
 | 
static int sandbox_swap_case_get_devfn(struct udevice *dev) 
 | 
{ 
 | 
    struct pci_child_platdata *plat = dev_get_parent_platdata(dev); 
 | 
  
 | 
    return plat->devfn; 
 | 
} 
 | 
  
 | 
static int sandbox_swap_case_read_config(struct udevice *emul, uint offset, 
 | 
                     ulong *valuep, enum pci_size_t size) 
 | 
{ 
 | 
    struct swap_case_platdata *plat = dev_get_platdata(emul); 
 | 
  
 | 
    switch (offset) { 
 | 
    case PCI_COMMAND: 
 | 
        *valuep = plat->command; 
 | 
        break; 
 | 
    case PCI_HEADER_TYPE: 
 | 
        *valuep = 0; 
 | 
        break; 
 | 
    case PCI_VENDOR_ID: 
 | 
        *valuep = SANDBOX_PCI_VENDOR_ID; 
 | 
        break; 
 | 
    case PCI_DEVICE_ID: 
 | 
        *valuep = SANDBOX_PCI_DEVICE_ID; 
 | 
        break; 
 | 
    case PCI_CLASS_DEVICE: 
 | 
        if (size == PCI_SIZE_8) { 
 | 
            *valuep = SANDBOX_PCI_CLASS_SUB_CODE; 
 | 
        } else { 
 | 
            *valuep = (SANDBOX_PCI_CLASS_CODE << 8) | 
 | 
                    SANDBOX_PCI_CLASS_SUB_CODE; 
 | 
        } 
 | 
        break; 
 | 
    case PCI_CLASS_CODE: 
 | 
        *valuep = SANDBOX_PCI_CLASS_CODE; 
 | 
        break; 
 | 
    case PCI_BASE_ADDRESS_0: 
 | 
    case PCI_BASE_ADDRESS_1: 
 | 
    case PCI_BASE_ADDRESS_2: 
 | 
    case PCI_BASE_ADDRESS_3: 
 | 
    case PCI_BASE_ADDRESS_4: 
 | 
    case PCI_BASE_ADDRESS_5: { 
 | 
        int barnum; 
 | 
        u32 *bar, result; 
 | 
  
 | 
        barnum = offset_to_barnum(offset); 
 | 
        bar = &plat->bar[barnum]; 
 | 
  
 | 
        result = *bar; 
 | 
        if (*bar == 0xffffffff) { 
 | 
            if (barinfo[barnum].type) { 
 | 
                result = (~(barinfo[barnum].size - 1) & 
 | 
                    PCI_BASE_ADDRESS_IO_MASK) | 
 | 
                    PCI_BASE_ADDRESS_SPACE_IO; 
 | 
            } else { 
 | 
                result = (~(barinfo[barnum].size - 1) & 
 | 
                    PCI_BASE_ADDRESS_MEM_MASK) | 
 | 
                    PCI_BASE_ADDRESS_MEM_TYPE_32; 
 | 
            } 
 | 
        } 
 | 
        debug("r bar %d=%x\n", barnum, result); 
 | 
        *valuep = result; 
 | 
        break; 
 | 
    } 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int sandbox_swap_case_write_config(struct udevice *emul, uint offset, 
 | 
                      ulong value, enum pci_size_t size) 
 | 
{ 
 | 
    struct swap_case_platdata *plat = dev_get_platdata(emul); 
 | 
  
 | 
    switch (offset) { 
 | 
    case PCI_COMMAND: 
 | 
        plat->command = value; 
 | 
        break; 
 | 
    case PCI_BASE_ADDRESS_0: 
 | 
    case PCI_BASE_ADDRESS_1: { 
 | 
        int barnum; 
 | 
        u32 *bar; 
 | 
  
 | 
        barnum = offset_to_barnum(offset); 
 | 
        bar = &plat->bar[barnum]; 
 | 
  
 | 
        debug("w bar %d=%lx\n", barnum, value); 
 | 
        *bar = value; 
 | 
        break; 
 | 
    } 
 | 
    } 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int sandbox_swap_case_find_bar(struct udevice *emul, unsigned int addr, 
 | 
                      int *barnump, unsigned int *offsetp) 
 | 
{ 
 | 
    struct swap_case_platdata *plat = dev_get_platdata(emul); 
 | 
    int barnum; 
 | 
  
 | 
    for (barnum = 0; barnum < ARRAY_SIZE(barinfo); barnum++) { 
 | 
        unsigned int size = barinfo[barnum].size; 
 | 
  
 | 
        if (addr >= plat->bar[barnum] && 
 | 
            addr < plat->bar[barnum] + size) { 
 | 
            *barnump = barnum; 
 | 
            *offsetp = addr - plat->bar[barnum]; 
 | 
            return 0; 
 | 
        } 
 | 
    } 
 | 
    *barnump = -1; 
 | 
  
 | 
    return -ENOENT; 
 | 
} 
 | 
  
 | 
static void sandbox_swap_case_do_op(enum swap_case_op op, char *str, int len) 
 | 
{ 
 | 
    for (; len > 0; len--, str++) { 
 | 
        switch (op) { 
 | 
        case OP_TO_UPPER: 
 | 
            *str = toupper(*str); 
 | 
            break; 
 | 
        case OP_TO_LOWER: 
 | 
            *str = tolower(*str); 
 | 
            break; 
 | 
        case OP_SWAP: 
 | 
            if (isupper(*str)) 
 | 
                *str = tolower(*str); 
 | 
            else 
 | 
                *str = toupper(*str); 
 | 
            break; 
 | 
        } 
 | 
    } 
 | 
} 
 | 
  
 | 
int sandbox_swap_case_read_io(struct udevice *dev, unsigned int addr, 
 | 
                  ulong *valuep, enum pci_size_t size) 
 | 
{ 
 | 
    struct swap_case_priv *priv = dev_get_priv(dev); 
 | 
    unsigned int offset; 
 | 
    int barnum; 
 | 
    int ret; 
 | 
  
 | 
    ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
  
 | 
    if (barnum == 0 && offset == 0) 
 | 
        *valuep = (*valuep & ~0xff) | priv->op; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
int sandbox_swap_case_write_io(struct udevice *dev, unsigned int addr, 
 | 
                   ulong value, enum pci_size_t size) 
 | 
{ 
 | 
    struct swap_case_priv *priv = dev_get_priv(dev); 
 | 
    unsigned int offset; 
 | 
    int barnum; 
 | 
    int ret; 
 | 
  
 | 
    ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
    if (barnum == 0 && offset == 0) 
 | 
        priv->op = value; 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
static int sandbox_swap_case_map_physmem(struct udevice *dev, 
 | 
        phys_addr_t addr, unsigned long *lenp, void **ptrp) 
 | 
{ 
 | 
    struct swap_case_priv *priv = dev_get_priv(dev); 
 | 
    unsigned int offset, avail; 
 | 
    int barnum; 
 | 
    int ret; 
 | 
  
 | 
    ret = sandbox_swap_case_find_bar(dev, addr, &barnum, &offset); 
 | 
    if (ret) 
 | 
        return ret; 
 | 
    if (barnum == 1) { 
 | 
        *ptrp = priv->mem_text + offset; 
 | 
        avail = barinfo[1].size - offset; 
 | 
        if (avail > barinfo[1].size) 
 | 
            *lenp = 0; 
 | 
        else 
 | 
            *lenp = min(*lenp, (ulong)avail); 
 | 
  
 | 
        return 0; 
 | 
    } 
 | 
  
 | 
    return -ENOENT; 
 | 
} 
 | 
  
 | 
static int sandbox_swap_case_unmap_physmem(struct udevice *dev, 
 | 
                       const void *vaddr, unsigned long len) 
 | 
{ 
 | 
    struct swap_case_priv *priv = dev_get_priv(dev); 
 | 
  
 | 
    sandbox_swap_case_do_op(priv->op, (void *)vaddr, len); 
 | 
  
 | 
    return 0; 
 | 
} 
 | 
  
 | 
struct dm_pci_emul_ops sandbox_swap_case_emul_ops = { 
 | 
    .get_devfn = sandbox_swap_case_get_devfn, 
 | 
    .read_config = sandbox_swap_case_read_config, 
 | 
    .write_config = sandbox_swap_case_write_config, 
 | 
    .read_io = sandbox_swap_case_read_io, 
 | 
    .write_io = sandbox_swap_case_write_io, 
 | 
    .map_physmem = sandbox_swap_case_map_physmem, 
 | 
    .unmap_physmem = sandbox_swap_case_unmap_physmem, 
 | 
}; 
 | 
  
 | 
static const struct udevice_id sandbox_swap_case_ids[] = { 
 | 
    { .compatible = "sandbox,swap-case" }, 
 | 
    { } 
 | 
}; 
 | 
  
 | 
U_BOOT_DRIVER(sandbox_swap_case_emul) = { 
 | 
    .name        = "sandbox_swap_case_emul", 
 | 
    .id        = UCLASS_PCI_EMUL, 
 | 
    .of_match    = sandbox_swap_case_ids, 
 | 
    .ops        = &sandbox_swap_case_emul_ops, 
 | 
    .priv_auto_alloc_size = sizeof(struct swap_case_priv), 
 | 
    .platdata_auto_alloc_size = sizeof(struct swap_case_platdata), 
 | 
}; 
 |