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