qemu/hw/usb/chipidea.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2018, Impinj, Inc.
   3 *
   4 * Chipidea USB block emulation code
   5 *
   6 * Author: Andrey Smirnov <andrew.smirnov@gmail.com>
   7 *
   8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   9 * See the COPYING file in the top-level directory.
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "hw/usb/hcd-ehci.h"
  14#include "hw/usb/chipidea.h"
  15#include "qemu/log.h"
  16#include "qemu/module.h"
  17
  18enum {
  19    CHIPIDEA_USBx_DCIVERSION   = 0x000,
  20    CHIPIDEA_USBx_DCCPARAMS    = 0x004,
  21    CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8),
  22};
  23
  24static uint64_t chipidea_read(void *opaque, hwaddr offset,
  25                               unsigned size)
  26{
  27    return 0;
  28}
  29
  30static void chipidea_write(void *opaque, hwaddr offset,
  31                            uint64_t value, unsigned size)
  32{
  33}
  34
  35static const struct MemoryRegionOps chipidea_ops = {
  36    .read = chipidea_read,
  37    .write = chipidea_write,
  38    .endianness = DEVICE_NATIVE_ENDIAN,
  39    .impl = {
  40        /*
  41         * Our device would not work correctly if the guest was doing
  42         * unaligned access. This might not be a limitation on the
  43         * real device but in practice there is no reason for a guest
  44         * to access this device unaligned.
  45         */
  46        .min_access_size = 4,
  47        .max_access_size = 4,
  48        .unaligned = false,
  49    },
  50};
  51
  52static uint64_t chipidea_dc_read(void *opaque, hwaddr offset,
  53                                 unsigned size)
  54{
  55    switch (offset) {
  56    case CHIPIDEA_USBx_DCIVERSION:
  57        return 0x1;
  58    case CHIPIDEA_USBx_DCCPARAMS:
  59        /*
  60         * Real hardware (at least i.MX7) will also report the
  61         * controller as "Device Capable" (and 8 supported endpoints),
  62         * but there doesn't seem to be much point in doing so, since
  63         * we don't emulate that part.
  64         */
  65        return CHIPIDEA_USBx_DCCPARAMS_HC;
  66    }
  67
  68    return 0;
  69}
  70
  71static void chipidea_dc_write(void *opaque, hwaddr offset,
  72                              uint64_t value, unsigned size)
  73{
  74}
  75
  76static const struct MemoryRegionOps chipidea_dc_ops = {
  77    .read = chipidea_dc_read,
  78    .write = chipidea_dc_write,
  79    .endianness = DEVICE_NATIVE_ENDIAN,
  80    .impl = {
  81        /*
  82         * Our device would not work correctly if the guest was doing
  83         * unaligned access. This might not be a limitation on the real
  84         * device but in practice there is no reason for a guest to access
  85         * this device unaligned.
  86         */
  87        .min_access_size = 4,
  88        .max_access_size = 4,
  89        .unaligned = false,
  90    },
  91};
  92
  93static void chipidea_init(Object *obj)
  94{
  95    EHCIState *ehci = &SYS_BUS_EHCI(obj)->ehci;
  96    ChipideaState *ci = CHIPIDEA(obj);
  97    int i;
  98
  99    for (i = 0; i < ARRAY_SIZE(ci->iomem); i++) {
 100        const struct {
 101            const char *name;
 102            hwaddr offset;
 103            uint64_t size;
 104            const struct MemoryRegionOps *ops;
 105        } regions[ARRAY_SIZE(ci->iomem)] = {
 106            /*
 107             * Registers located between offsets 0x000 and 0xFC
 108             */
 109            {
 110                .name   = TYPE_CHIPIDEA ".misc",
 111                .offset = 0x000,
 112                .size   = 0x100,
 113                .ops    = &chipidea_ops,
 114            },
 115            /*
 116             * Registers located between offsets 0x1A4 and 0x1DC
 117             */
 118            {
 119                .name   = TYPE_CHIPIDEA ".endpoints",
 120                .offset = 0x1A4,
 121                .size   = 0x1DC - 0x1A4 + 4,
 122                .ops    = &chipidea_ops,
 123            },
 124            /*
 125             * USB_x_DCIVERSION and USB_x_DCCPARAMS
 126             */
 127            {
 128                .name   = TYPE_CHIPIDEA ".dc",
 129                .offset = 0x120,
 130                .size   = 8,
 131                .ops    = &chipidea_dc_ops,
 132            },
 133        };
 134
 135        memory_region_init_io(&ci->iomem[i],
 136                              obj,
 137                              regions[i].ops,
 138                              ci,
 139                              regions[i].name,
 140                              regions[i].size);
 141
 142        memory_region_add_subregion(&ehci->mem,
 143                                    regions[i].offset,
 144                                    &ci->iomem[i]);
 145    }
 146}
 147
 148static void chipidea_class_init(ObjectClass *klass, void *data)
 149{
 150    DeviceClass *dc = DEVICE_CLASS(klass);
 151    SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass);
 152
 153    /*
 154     * Offsets used were taken from i.MX7Dual Applications Processor
 155     * Reference Manual, Rev 0.1, p. 3177, Table 11-59
 156     */
 157    sec->capsbase   = 0x100;
 158    sec->opregbase  = 0x140;
 159    sec->portnr     = 1;
 160
 161    set_bit(DEVICE_CATEGORY_USB, dc->categories);
 162    dc->desc = "Chipidea USB Module";
 163}
 164
 165static const TypeInfo chipidea_info = {
 166    .name          = TYPE_CHIPIDEA,
 167    .parent        = TYPE_SYS_BUS_EHCI,
 168    .instance_size = sizeof(ChipideaState),
 169    .instance_init = chipidea_init,
 170    .class_init    = chipidea_class_init,
 171};
 172
 173static void chipidea_register_type(void)
 174{
 175    type_register_static(&chipidea_info);
 176}
 177type_init(chipidea_register_type)
 178