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
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"
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
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.
44 FIELD(CMSTR
, STR0
, 0, 1)
45 FIELD(CMSTR
, STR1
, 1, 1)
46 FIELD(CMSTR
, STR
, 0, 2)
47 /* This addeess is channel offset */
49 FIELD(CMCR
, CKS
, 0, 2)
50 FIELD(CMCR
, CMIE
, 6, 1)
54 static void update_events(RCMTState
*cmt
, int ch
)
58 if ((cmt
->cmstr
& (1 << ch
)) == 0) {
59 /* count disable, so not happened next event. */
62 next_time
= cmt
->cmcor
[ch
] - cmt
->cmcnt
[ch
];
63 next_time
*= NANOSECONDS_PER_SECOND
;
64 next_time
/= cmt
->input_freq
;
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);
87 return cmt
->cmcnt
[ch
] + delta
;
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;
99 if (offset
== A_CMSTR
) {
101 ret
= FIELD_DP16(ret
, CMSTR
, STR
,
102 FIELD_EX16(cmt
->cmstr
, CMSTR
, STR
));
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
));
118 return read_cmcnt(cmt
, ch
);
120 return cmt
->cmcor
[ch
];
123 qemu_log_mask(LOG_UNIMP
, "renesas_cmt: Register 0x%" HWADDR_PRIX
" "
129 static void start_stop(RCMTState
*cmt
, int ch
, int st
)
132 update_events(cmt
, ch
);
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
));
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
));
160 cmt
->cmcnt
[ch
] = val
;
163 cmt
->cmcor
[ch
] = val
;
166 qemu_log_mask(LOG_UNIMP
, "renesas_cmt: Register 0x%" HWADDR_PRIX
" "
171 if (FIELD_EX16(cmt
->cmstr
, CMSTR
, STR
) & (1 << ch
)) {
172 update_events(cmt
, ch
);
177 static const MemoryRegionOps cmt_ops
= {
180 .endianness
= DEVICE_NATIVE_ENDIAN
,
182 .min_access_size
= 2,
183 .max_access_size
= 2,
186 .min_access_size
= 2,
187 .max_access_size
= 2,
191 static void timer_events(RCMTState
*cmt
, int ch
)
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
);
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
);
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
= {
244 .minimum_version_id
= 1,
245 .fields
= (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 dc
->reset
= 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
)