Merge tag 'pull-loongarch-20241016' of https://gitlab.com/gaosong/qemu into staging
[qemu/armbru.git] / hw / timer / renesas_cmt.c
blobcd59b08c876a9531aec82f74ee7b077e2d380062
1 /*
2 * Renesas 16bit Compare-match timer
4 * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
5 * (Rev.1.40 R01UH0033EJ0140)
7 * Copyright (c) 2019 Yoshinori Sato
9 * SPDX-License-Identifier: GPL-2.0-or-later
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms and conditions of the GNU General Public License,
13 * version 2 or later, as published by the Free Software Foundation.
15 * This program is distributed in the hope it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 * more details.
20 * You should have received a copy of the GNU General Public License along with
21 * this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "qemu/osdep.h"
25 #include "qemu/log.h"
26 #include "hw/irq.h"
27 #include "hw/registerfields.h"
28 #include "hw/qdev-properties.h"
29 #include "hw/timer/renesas_cmt.h"
30 #include "migration/vmstate.h"
33 * +0 CMSTR - common control
34 * +2 CMCR - ch0
35 * +4 CMCNT - ch0
36 * +6 CMCOR - ch0
37 * +8 CMCR - ch1
38 * +10 CMCNT - ch1
39 * +12 CMCOR - ch1
40 * If we think that the address of CH 0 has an offset of +2,
41 * we can treat it with the same address as CH 1, so define it like that.
43 REG16(CMSTR, 0)
44 FIELD(CMSTR, STR0, 0, 1)
45 FIELD(CMSTR, STR1, 1, 1)
46 FIELD(CMSTR, STR, 0, 2)
47 /* This addeess is channel offset */
48 REG16(CMCR, 0)
49 FIELD(CMCR, CKS, 0, 2)
50 FIELD(CMCR, CMIE, 6, 1)
51 REG16(CMCNT, 2)
52 REG16(CMCOR, 4)
54 static void update_events(RCMTState *cmt, int ch)
56 int64_t next_time;
58 if ((cmt->cmstr & (1 << ch)) == 0) {
59 /* count disable, so not happened next event. */
60 return;
62 next_time = cmt->cmcor[ch] - cmt->cmcnt[ch];
63 next_time *= NANOSECONDS_PER_SECOND;
64 next_time /= cmt->input_freq;
66 * CKS -> div rate
67 * 0 -> 8 (1 << 3)
68 * 1 -> 32 (1 << 5)
69 * 2 -> 128 (1 << 7)
70 * 3 -> 512 (1 << 9)
72 next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
73 next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
74 timer_mod(&cmt->timer[ch], next_time);
77 static int64_t read_cmcnt(RCMTState *cmt, int ch)
79 int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
81 if (cmt->cmstr & (1 << ch)) {
82 delta = (now - cmt->tick[ch]);
83 delta /= NANOSECONDS_PER_SECOND;
84 delta /= cmt->input_freq;
85 delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
86 cmt->tick[ch] = now;
87 return cmt->cmcnt[ch] + delta;
88 } else {
89 return cmt->cmcnt[ch];
93 static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size)
95 RCMTState *cmt = opaque;
96 int ch = offset / 0x08;
97 uint64_t ret;
99 if (offset == A_CMSTR) {
100 ret = 0;
101 ret = FIELD_DP16(ret, CMSTR, STR,
102 FIELD_EX16(cmt->cmstr, CMSTR, STR));
103 return ret;
104 } else {
105 offset &= 0x07;
106 if (ch == 0) {
107 offset -= 0x02;
109 switch (offset) {
110 case A_CMCR:
111 ret = 0;
112 ret = FIELD_DP16(ret, CMCR, CKS,
113 FIELD_EX16(cmt->cmstr, CMCR, CKS));
114 ret = FIELD_DP16(ret, CMCR, CMIE,
115 FIELD_EX16(cmt->cmstr, CMCR, CMIE));
116 return ret;
117 case A_CMCNT:
118 return read_cmcnt(cmt, ch);
119 case A_CMCOR:
120 return cmt->cmcor[ch];
123 qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
124 "not implemented\n",
125 offset);
126 return UINT64_MAX;
129 static void start_stop(RCMTState *cmt, int ch, int st)
131 if (st) {
132 update_events(cmt, ch);
133 } else {
134 timer_del(&cmt->timer[ch]);
138 static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
140 RCMTState *cmt = opaque;
141 int ch = offset / 0x08;
143 if (offset == A_CMSTR) {
144 cmt->cmstr = FIELD_EX16(val, CMSTR, STR);
145 start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0));
146 start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1));
147 } else {
148 offset &= 0x07;
149 if (ch == 0) {
150 offset -= 0x02;
152 switch (offset) {
153 case A_CMCR:
154 cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS,
155 FIELD_EX16(val, CMCR, CKS));
156 cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE,
157 FIELD_EX16(val, CMCR, CMIE));
158 break;
159 case 2:
160 cmt->cmcnt[ch] = val;
161 break;
162 case 4:
163 cmt->cmcor[ch] = val;
164 break;
165 default:
166 qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
167 "not implemented\n",
168 offset);
169 return;
171 if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) {
172 update_events(cmt, ch);
177 static const MemoryRegionOps cmt_ops = {
178 .write = cmt_write,
179 .read = cmt_read,
180 .endianness = DEVICE_NATIVE_ENDIAN,
181 .impl = {
182 .min_access_size = 2,
183 .max_access_size = 2,
185 .valid = {
186 .min_access_size = 2,
187 .max_access_size = 2,
191 static void timer_events(RCMTState *cmt, int ch)
193 cmt->cmcnt[ch] = 0;
194 cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
195 update_events(cmt, ch);
196 if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) {
197 qemu_irq_pulse(cmt->cmi[ch]);
201 static void timer_event0(void *opaque)
203 RCMTState *cmt = opaque;
205 timer_events(cmt, 0);
208 static void timer_event1(void *opaque)
210 RCMTState *cmt = opaque;
212 timer_events(cmt, 1);
215 static void rcmt_reset(DeviceState *dev)
217 RCMTState *cmt = RCMT(dev);
218 cmt->cmstr = 0;
219 cmt->cmcr[0] = cmt->cmcr[1] = 0;
220 cmt->cmcnt[0] = cmt->cmcnt[1] = 0;
221 cmt->cmcor[0] = cmt->cmcor[1] = 0xffff;
224 static void rcmt_init(Object *obj)
226 SysBusDevice *d = SYS_BUS_DEVICE(obj);
227 RCMTState *cmt = RCMT(obj);
228 int i;
230 memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops,
231 cmt, "renesas-cmt", 0x10);
232 sysbus_init_mmio(d, &cmt->memory);
234 for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) {
235 sysbus_init_irq(d, &cmt->cmi[i]);
237 timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
238 timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt);
241 static const VMStateDescription vmstate_rcmt = {
242 .name = "rx-cmt",
243 .version_id = 1,
244 .minimum_version_id = 1,
245 .fields = (const VMStateField[]) {
246 VMSTATE_UINT16(cmstr, RCMTState),
247 VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH),
248 VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH),
249 VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH),
250 VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH),
251 VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH),
252 VMSTATE_END_OF_LIST()
256 static Property rcmt_properties[] = {
257 DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0),
258 DEFINE_PROP_END_OF_LIST(),
261 static void rcmt_class_init(ObjectClass *klass, void *data)
263 DeviceClass *dc = DEVICE_CLASS(klass);
265 dc->vmsd = &vmstate_rcmt;
266 device_class_set_legacy_reset(dc, rcmt_reset);
267 device_class_set_props(dc, rcmt_properties);
270 static const TypeInfo rcmt_info = {
271 .name = TYPE_RENESAS_CMT,
272 .parent = TYPE_SYS_BUS_DEVICE,
273 .instance_size = sizeof(RCMTState),
274 .instance_init = rcmt_init,
275 .class_init = rcmt_class_init,
278 static void rcmt_register_types(void)
280 type_register_static(&rcmt_info);
283 type_init(rcmt_register_types)