xref: /openbmc/qemu/hw/intc/imx_gpcv2.c (revision c00506aa)
1 /*
2  * Copyright (c) 2018, Impinj, Inc.
3  *
4  * i.MX7 GPCv2 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/intc/imx_gpcv2.h"
14 #include "migration/vmstate.h"
15 #include "qemu/log.h"
16 #include "qemu/module.h"
17 
18 #define GPC_PU_PGC_SW_PUP_REQ       0x0f8
19 #define GPC_PU_PGC_SW_PDN_REQ       0x104
20 
21 #define USB_HSIC_PHY_SW_Pxx_REQ     BIT(4)
22 #define USB_OTG2_PHY_SW_Pxx_REQ     BIT(3)
23 #define USB_OTG1_PHY_SW_Pxx_REQ     BIT(2)
24 #define PCIE_PHY_SW_Pxx_REQ         BIT(1)
25 #define MIPI_PHY_SW_Pxx_REQ         BIT(0)
26 
27 
28 static void imx_gpcv2_reset(DeviceState *dev)
29 {
30     IMXGPCv2State *s = IMX_GPCV2(dev);
31 
32     memset(s->regs, 0, sizeof(s->regs));
33 }
34 
35 static uint64_t imx_gpcv2_read(void *opaque, hwaddr offset,
36                                unsigned size)
37 {
38     IMXGPCv2State *s = opaque;
39 
40     return s->regs[offset / sizeof(uint32_t)];
41 }
42 
43 static void imx_gpcv2_write(void *opaque, hwaddr offset,
44                             uint64_t value, unsigned size)
45 {
46     IMXGPCv2State *s = opaque;
47     const size_t idx = offset / sizeof(uint32_t);
48 
49     s->regs[idx] = value;
50 
51     /*
52      * Real HW will clear those bits once as a way to indicate that
53      * power up request is complete
54      */
55     if (offset == GPC_PU_PGC_SW_PUP_REQ ||
56         offset == GPC_PU_PGC_SW_PDN_REQ) {
57         s->regs[idx] &= ~(USB_HSIC_PHY_SW_Pxx_REQ |
58                           USB_OTG2_PHY_SW_Pxx_REQ |
59                           USB_OTG1_PHY_SW_Pxx_REQ |
60                           PCIE_PHY_SW_Pxx_REQ     |
61                           MIPI_PHY_SW_Pxx_REQ);
62     }
63 }
64 
65 static const struct MemoryRegionOps imx_gpcv2_ops = {
66     .read = imx_gpcv2_read,
67     .write = imx_gpcv2_write,
68     .endianness = DEVICE_NATIVE_ENDIAN,
69     .impl = {
70         /*
71          * Our device would not work correctly if the guest was doing
72          * unaligned access. This might not be a limitation on the real
73          * device but in practice there is no reason for a guest to access
74          * this device unaligned.
75          */
76         .min_access_size = 4,
77         .max_access_size = 4,
78         .unaligned = false,
79     },
80 };
81 
82 static void imx_gpcv2_init(Object *obj)
83 {
84     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
85     IMXGPCv2State *s = IMX_GPCV2(obj);
86 
87     memory_region_init_io(&s->iomem,
88                           obj,
89                           &imx_gpcv2_ops,
90                           s,
91                           TYPE_IMX_GPCV2 ".iomem",
92                           sizeof(s->regs));
93     sysbus_init_mmio(sd, &s->iomem);
94 }
95 
96 static const VMStateDescription vmstate_imx_gpcv2 = {
97     .name = TYPE_IMX_GPCV2,
98     .version_id = 1,
99     .minimum_version_id = 1,
100     .fields = (VMStateField[]) {
101         VMSTATE_UINT32_ARRAY(regs, IMXGPCv2State, GPC_NUM),
102         VMSTATE_END_OF_LIST()
103     },
104 };
105 
106 static void imx_gpcv2_class_init(ObjectClass *klass, void *data)
107 {
108     DeviceClass *dc = DEVICE_CLASS(klass);
109 
110     dc->reset = imx_gpcv2_reset;
111     dc->vmsd  = &vmstate_imx_gpcv2;
112     dc->desc  = "i.MX GPCv2 Module";
113 }
114 
115 static const TypeInfo imx_gpcv2_info = {
116     .name          = TYPE_IMX_GPCV2,
117     .parent        = TYPE_SYS_BUS_DEVICE,
118     .instance_size = sizeof(IMXGPCv2State),
119     .instance_init = imx_gpcv2_init,
120     .class_init    = imx_gpcv2_class_init,
121 };
122 
123 static void imx_gpcv2_register_type(void)
124 {
125     type_register_static(&imx_gpcv2_info);
126 }
127 type_init(imx_gpcv2_register_type)
128