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