hw/arm/raspi: fix CPRMAN base address
[qemu.git] / hw / usb / chipidea.c
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)