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