xref: /openbmc/qemu/hw/usb/chipidea.c (revision 37677d7d)
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 
18 enum {
19     CHIPIDEA_USBx_DCIVERSION   = 0x000,
20     CHIPIDEA_USBx_DCCPARAMS    = 0x004,
21     CHIPIDEA_USBx_DCCPARAMS_HC = BIT(8),
22 };
23 
24 static uint64_t chipidea_read(void *opaque, hwaddr offset,
25                                unsigned size)
26 {
27     return 0;
28 }
29 
30 static void chipidea_write(void *opaque, hwaddr offset,
31                             uint64_t value, unsigned size)
32 {
33 }
34 
35 static 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 
52 static 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 
71 static void chipidea_dc_write(void *opaque, hwaddr offset,
72                               uint64_t value, unsigned size)
73 {
74 }
75 
76 static 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 
93 static 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 
148 static 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 
165 static 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 
173 static void chipidea_register_type(void)
174 {
175     type_register_static(&chipidea_info);
176 }
177 type_init(chipidea_register_type)
178