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 | |