1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
/*
* Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com>
*
* i.MX 8M Plus CCM IP block emulation code
*
* Based on hw/misc/imx7_ccm.c
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "hw/misc/imx8mp_ccm.h"
#include "migration/vmstate.h"
#include "trace.h"
#define CKIH_FREQ 16000000 /* 16MHz crystal input */
static void imx8mp_ccm_reset(DeviceState *dev)
{
IMX8MPCCMState *s = IMX8MP_CCM(dev);
memset(s->ccm, 0, sizeof(s->ccm));
}
#define CCM_INDEX(offset) (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
#define CCM_BITOP(offset) ((offset) & (hwaddr)0xF)
enum {
CCM_BITOP_NONE = 0x00,
CCM_BITOP_SET = 0x04,
CCM_BITOP_CLR = 0x08,
CCM_BITOP_TOG = 0x0C,
};
static uint64_t imx8mp_set_clr_tog_read(void *opaque, hwaddr offset,
unsigned size)
{
const uint32_t *mmio = opaque;
return mmio[CCM_INDEX(offset)];
}
static void imx8mp_set_clr_tog_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size)
{
const uint8_t bitop = CCM_BITOP(offset);
const uint32_t index = CCM_INDEX(offset);
uint32_t *mmio = opaque;
switch (bitop) {
case CCM_BITOP_NONE:
mmio[index] = value;
break;
case CCM_BITOP_SET:
mmio[index] |= value;
break;
case CCM_BITOP_CLR:
mmio[index] &= ~value;
break;
case CCM_BITOP_TOG:
mmio[index] ^= value;
break;
};
}
static const struct MemoryRegionOps imx8mp_set_clr_tog_ops = {
.read = imx8mp_set_clr_tog_read,
.write = imx8mp_set_clr_tog_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl = {
/*
* Our device would not work correctly if the guest was doing
* unaligned access. This might not be a limitation on the real
* device but in practice there is no reason for a guest to access
* this device unaligned.
*/
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void imx8mp_ccm_init(Object *obj)
{
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IMX8MPCCMState *s = IMX8MP_CCM(obj);
memory_region_init_io(&s->iomem,
obj,
&imx8mp_set_clr_tog_ops,
s->ccm,
TYPE_IMX8MP_CCM ".ccm",
sizeof(s->ccm));
sysbus_init_mmio(sd, &s->iomem);
}
static const VMStateDescription imx8mp_ccm_vmstate = {
.name = TYPE_IMX8MP_CCM,
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(ccm, IMX8MPCCMState, CCM_MAX),
VMSTATE_END_OF_LIST()
},
};
static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
{
/*
* This function is "consumed" by GPT emulation code. Some clocks
* have fixed frequencies and we can provide requested frequency
* easily. However for CCM provided clocks (like IPG) each GPT
* timer can have its own clock root.
* This means we need additional information when calling this
* function to know the requester's identity.
*/
uint32_t freq = 0;
switch (clock) {
case CLK_NONE:
break;
case CLK_32k:
freq = CKIL_FREQ;
break;
case CLK_HIGH:
freq = CKIH_FREQ;
break;
case CLK_IPG:
case CLK_IPG_HIGH:
/*
* For now we don't have a way to figure out the device this
* function is called for. Until then the IPG derived clocks
* are left unimplemented.
*/
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n",
TYPE_IMX8MP_CCM, __func__, clock);
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
TYPE_IMX8MP_CCM, __func__, clock);
break;
}
trace_ccm_clock_freq(clock, freq);
return freq;
}
static void imx8mp_ccm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
device_class_set_legacy_reset(dc, imx8mp_ccm_reset);
dc->vmsd = &imx8mp_ccm_vmstate;
dc->desc = "i.MX 8M Plus Clock Control Module";
ccm->get_clock_frequency = imx8mp_ccm_get_clock_frequency;
}
static const TypeInfo imx8mp_ccm_types[] = {
{
.name = TYPE_IMX8MP_CCM,
.parent = TYPE_IMX_CCM,
.instance_size = sizeof(IMX8MPCCMState),
.instance_init = imx8mp_ccm_init,
.class_init = imx8mp_ccm_class_init,
},
};
DEFINE_TYPES(imx8mp_ccm_types);
|