xref: /openbmc/qemu/hw/usb/chipidea.c (revision 6c769690)
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 
17 enum {
18     CHIPIDEA_USBx_DCIVERSION   = 0x000,
19     CHIPIDEA_USBx_DCCPARAMS    = 0x004,
20     CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8),
21 };
22 
chipidea_read(void * opaque,hwaddr offset,unsigned size)23 static uint64_t chipidea_read(void *opaque, hwaddr offset,
24                                unsigned size)
25 {
26     return 0;
27 }
28 
chipidea_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)29 static void chipidea_write(void *opaque, hwaddr offset,
30                             uint64_t value, unsigned size)
31 {
32 }
33 
34 static 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 
chipidea_dc_read(void * opaque,hwaddr offset,unsigned size)51 static 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 
chipidea_dc_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)70 static void chipidea_dc_write(void *opaque, hwaddr offset,
71                               uint64_t value, unsigned size)
72 {
73 }
74 
75 static 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 
chipidea_init(Object * obj)92 static 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 
chipidea_class_init(ObjectClass * klass,void * data)147 static 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 
164 static 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 
chipidea_register_type(void)172 static void chipidea_register_type(void)
173 {
174     type_register_static(&chipidea_info);
175 }
176 type_init(chipidea_register_type)
177