xref: /openbmc/qemu/hw/misc/imx7_ccm.c (revision a050901d)
1 /*
2  * Copyright (c) 2018, Impinj, Inc.
3  *
4  * i.MX7 CCM, PMU and ANALOG IP blocks 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 "qemu/log.h"
14 #include "qemu/module.h"
15 
16 #include "hw/misc/imx7_ccm.h"
17 
18 static void imx7_analog_reset(DeviceState *dev)
19 {
20     IMX7AnalogState *s = IMX7_ANALOG(dev);
21 
22     memset(s->pmu, 0, sizeof(s->pmu));
23     memset(s->analog, 0, sizeof(s->analog));
24 
25     s->analog[ANALOG_PLL_ARM]         = 0x00002042;
26     s->analog[ANALOG_PLL_DDR]         = 0x0060302c;
27     s->analog[ANALOG_PLL_DDR_SS]      = 0x00000000;
28     s->analog[ANALOG_PLL_DDR_NUM]     = 0x06aaac4d;
29     s->analog[ANALOG_PLL_DDR_DENOM]   = 0x100003ec;
30     s->analog[ANALOG_PLL_480]         = 0x00002000;
31     s->analog[ANALOG_PLL_480A]        = 0x52605a56;
32     s->analog[ANALOG_PLL_480B]        = 0x52525216;
33     s->analog[ANALOG_PLL_ENET]        = 0x00001fc0;
34     s->analog[ANALOG_PLL_AUDIO]       = 0x0001301b;
35     s->analog[ANALOG_PLL_AUDIO_SS]    = 0x00000000;
36     s->analog[ANALOG_PLL_AUDIO_NUM]   = 0x05f5e100;
37     s->analog[ANALOG_PLL_AUDIO_DENOM] = 0x2964619c;
38     s->analog[ANALOG_PLL_VIDEO]       = 0x0008201b;
39     s->analog[ANALOG_PLL_VIDEO_SS]    = 0x00000000;
40     s->analog[ANALOG_PLL_VIDEO_NUM]   = 0x0000f699;
41     s->analog[ANALOG_PLL_VIDEO_DENOM] = 0x000f4240;
42     s->analog[ANALOG_PLL_MISC0]       = 0x00000000;
43 
44     /* all PLLs need to be locked */
45     s->analog[ANALOG_PLL_ARM]   |= ANALOG_PLL_LOCK;
46     s->analog[ANALOG_PLL_DDR]   |= ANALOG_PLL_LOCK;
47     s->analog[ANALOG_PLL_480]   |= ANALOG_PLL_LOCK;
48     s->analog[ANALOG_PLL_480A]  |= ANALOG_PLL_LOCK;
49     s->analog[ANALOG_PLL_480B]  |= ANALOG_PLL_LOCK;
50     s->analog[ANALOG_PLL_ENET]  |= ANALOG_PLL_LOCK;
51     s->analog[ANALOG_PLL_AUDIO] |= ANALOG_PLL_LOCK;
52     s->analog[ANALOG_PLL_VIDEO] |= ANALOG_PLL_LOCK;
53     s->analog[ANALOG_PLL_MISC0] |= ANALOG_PLL_LOCK;
54 
55     /*
56      * Since I couldn't find any info about this in the reference
57      * manual the value of this register is based strictly on matching
58      * what Linux kernel expects it to be.
59      */
60     s->analog[ANALOG_DIGPROG]  = 0x720000;
61     /*
62      * Set revision to be 1.0 (Arbitrary choice, no particular
63      * reason).
64      */
65     s->analog[ANALOG_DIGPROG] |= 0x000010;
66 }
67 
68 static void imx7_ccm_reset(DeviceState *dev)
69 {
70     IMX7CCMState *s = IMX7_CCM(dev);
71 
72     memset(s->ccm, 0, sizeof(s->ccm));
73 }
74 
75 #define CCM_INDEX(offset)   (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
76 #define CCM_BITOP(offset)   ((offset) & (hwaddr)0xF)
77 
78 enum {
79     CCM_BITOP_NONE = 0x00,
80     CCM_BITOP_SET  = 0x04,
81     CCM_BITOP_CLR  = 0x08,
82     CCM_BITOP_TOG  = 0x0C,
83 };
84 
85 static uint64_t imx7_set_clr_tog_read(void *opaque, hwaddr offset,
86                                       unsigned size)
87 {
88     const uint32_t *mmio = opaque;
89 
90     return mmio[CCM_INDEX(offset)];
91 }
92 
93 static void imx7_set_clr_tog_write(void *opaque, hwaddr offset,
94                                    uint64_t value, unsigned size)
95 {
96     const uint8_t  bitop = CCM_BITOP(offset);
97     const uint32_t index = CCM_INDEX(offset);
98     uint32_t *mmio = opaque;
99 
100     switch (bitop) {
101     case CCM_BITOP_NONE:
102         mmio[index]  = value;
103         break;
104     case CCM_BITOP_SET:
105         mmio[index] |= value;
106         break;
107     case CCM_BITOP_CLR:
108         mmio[index] &= ~value;
109         break;
110     case CCM_BITOP_TOG:
111         mmio[index] ^= value;
112         break;
113     };
114 }
115 
116 static const struct MemoryRegionOps imx7_set_clr_tog_ops = {
117     .read = imx7_set_clr_tog_read,
118     .write = imx7_set_clr_tog_write,
119     .endianness = DEVICE_NATIVE_ENDIAN,
120     .impl = {
121         /*
122          * Our device would not work correctly if the guest was doing
123          * unaligned access. This might not be a limitation on the real
124          * device but in practice there is no reason for a guest to access
125          * this device unaligned.
126          */
127         .min_access_size = 4,
128         .max_access_size = 4,
129         .unaligned = false,
130     },
131 };
132 
133 static const struct MemoryRegionOps imx7_digprog_ops = {
134     .read = imx7_set_clr_tog_read,
135     .endianness = DEVICE_NATIVE_ENDIAN,
136     .impl = {
137         .min_access_size = 4,
138         .max_access_size = 4,
139         .unaligned = false,
140     },
141 };
142 
143 static void imx7_ccm_init(Object *obj)
144 {
145     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
146     IMX7CCMState *s = IMX7_CCM(obj);
147 
148     memory_region_init_io(&s->iomem,
149                           obj,
150                           &imx7_set_clr_tog_ops,
151                           s->ccm,
152                           TYPE_IMX7_CCM ".ccm",
153                           sizeof(s->ccm));
154 
155     sysbus_init_mmio(sd, &s->iomem);
156 }
157 
158 static void imx7_analog_init(Object *obj)
159 {
160     SysBusDevice *sd = SYS_BUS_DEVICE(obj);
161     IMX7AnalogState *s = IMX7_ANALOG(obj);
162 
163     memory_region_init(&s->mmio.container, obj, TYPE_IMX7_ANALOG,
164                        0x10000);
165 
166     memory_region_init_io(&s->mmio.analog,
167                           obj,
168                           &imx7_set_clr_tog_ops,
169                           s->analog,
170                           TYPE_IMX7_ANALOG,
171                           sizeof(s->analog));
172 
173     memory_region_add_subregion(&s->mmio.container,
174                                 0x60, &s->mmio.analog);
175 
176     memory_region_init_io(&s->mmio.pmu,
177                           obj,
178                           &imx7_set_clr_tog_ops,
179                           s->pmu,
180                           TYPE_IMX7_ANALOG ".pmu",
181                           sizeof(s->pmu));
182 
183     memory_region_add_subregion(&s->mmio.container,
184                                 0x200, &s->mmio.pmu);
185 
186     memory_region_init_io(&s->mmio.digprog,
187                           obj,
188                           &imx7_digprog_ops,
189                           &s->analog[ANALOG_DIGPROG],
190                           TYPE_IMX7_ANALOG ".digprog",
191                           sizeof(uint32_t));
192 
193     memory_region_add_subregion_overlap(&s->mmio.container,
194                                         0x800, &s->mmio.digprog, 10);
195 
196 
197     sysbus_init_mmio(sd, &s->mmio.container);
198 }
199 
200 static const VMStateDescription vmstate_imx7_ccm = {
201     .name = TYPE_IMX7_CCM,
202     .version_id = 1,
203     .minimum_version_id = 1,
204     .fields = (VMStateField[]) {
205         VMSTATE_UINT32_ARRAY(ccm, IMX7CCMState, CCM_MAX),
206         VMSTATE_END_OF_LIST()
207     },
208 };
209 
210 static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
211 {
212     /*
213      * This function is "consumed" by GPT emulation code, however on
214      * i.MX7 each GPT block can have their own clock root. This means
215      * that this functions needs somehow to know requester's identity
216      * and the way to pass it: be it via additional IMXClk constants
217      * or by adding another argument to this method needs to be
218      * figured out
219      */
220     qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Not implemented\n",
221                   TYPE_IMX7_CCM, __func__);
222     return 0;
223 }
224 
225 static void imx7_ccm_class_init(ObjectClass *klass, void *data)
226 {
227     DeviceClass *dc = DEVICE_CLASS(klass);
228     IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
229 
230     dc->reset = imx7_ccm_reset;
231     dc->vmsd  = &vmstate_imx7_ccm;
232     dc->desc  = "i.MX7 Clock Control Module";
233 
234     ccm->get_clock_frequency = imx7_ccm_get_clock_frequency;
235 }
236 
237 static const TypeInfo imx7_ccm_info = {
238     .name          = TYPE_IMX7_CCM,
239     .parent        = TYPE_IMX_CCM,
240     .instance_size = sizeof(IMX7CCMState),
241     .instance_init = imx7_ccm_init,
242     .class_init    = imx7_ccm_class_init,
243 };
244 
245 static const VMStateDescription vmstate_imx7_analog = {
246     .name = TYPE_IMX7_ANALOG,
247     .version_id = 1,
248     .minimum_version_id = 1,
249     .fields = (VMStateField[]) {
250         VMSTATE_UINT32_ARRAY(analog, IMX7AnalogState, ANALOG_MAX),
251         VMSTATE_UINT32_ARRAY(pmu,    IMX7AnalogState, PMU_MAX),
252         VMSTATE_END_OF_LIST()
253     },
254 };
255 
256 static void imx7_analog_class_init(ObjectClass *klass, void *data)
257 {
258     DeviceClass *dc = DEVICE_CLASS(klass);
259 
260     dc->reset = imx7_analog_reset;
261     dc->vmsd  = &vmstate_imx7_analog;
262     dc->desc  = "i.MX7 Analog Module";
263 }
264 
265 static const TypeInfo imx7_analog_info = {
266     .name          = TYPE_IMX7_ANALOG,
267     .parent        = TYPE_SYS_BUS_DEVICE,
268     .instance_size = sizeof(IMX7AnalogState),
269     .instance_init = imx7_analog_init,
270     .class_init    = imx7_analog_class_init,
271 };
272 
273 static void imx7_ccm_register_type(void)
274 {
275     type_register_static(&imx7_ccm_info);
276     type_register_static(&imx7_analog_info);
277 }
278 type_init(imx7_ccm_register_type)
279