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