xref: /openbmc/qemu/hw/usb/chipidea.c (revision a24273bb)
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