1 /* 2 * Renesas 16bit Compare-match timer 3 * 4 * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware 5 * (Rev.1.40 R01UH0033EJ0140) 6 * 7 * Copyright (c) 2019 Yoshinori Sato 8 * 9 * SPDX-License-Identifier: GPL-2.0-or-later 10 * 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. 14 * 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. 19 * 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/>. 22 */ 23 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" 31 32 /* 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. 42 */ 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) 53 54 static void update_events(RCMTState *cmt, int ch) 55 { 56 int64_t next_time; 57 58 if ((cmt->cmstr & (1 << ch)) == 0) { 59 /* count disable, so not happened next event. */ 60 return; 61 } 62 next_time = cmt->cmcor[ch] - cmt->cmcnt[ch]; 63 next_time *= NANOSECONDS_PER_SECOND; 64 next_time /= cmt->input_freq; 65 /* 66 * CKS -> div rate 67 * 0 -> 8 (1 << 3) 68 * 1 -> 32 (1 << 5) 69 * 2 -> 128 (1 << 7) 70 * 3 -> 512 (1 << 9) 71 */ 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); 75 } 76 77 static int64_t read_cmcnt(RCMTState *cmt, int ch) 78 { 79 int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 80 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]; 90 } 91 } 92 93 static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size) 94 { 95 RCMTState *cmt = opaque; 96 int ch = offset / 0x08; 97 uint64_t ret; 98 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; 108 } 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]; 121 } 122 } 123 qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " " 124 "not implemented\n", 125 offset); 126 return UINT64_MAX; 127 } 128 129 static void start_stop(RCMTState *cmt, int ch, int st) 130 { 131 if (st) { 132 update_events(cmt, ch); 133 } else { 134 timer_del(&cmt->timer[ch]); 135 } 136 } 137 138 static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) 139 { 140 RCMTState *cmt = opaque; 141 int ch = offset / 0x08; 142 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; 151 } 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; 170 } 171 if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) { 172 update_events(cmt, ch); 173 } 174 } 175 } 176 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, 184 }, 185 .valid = { 186 .min_access_size = 2, 187 .max_access_size = 2, 188 }, 189 }; 190 191 static void timer_events(RCMTState *cmt, int ch) 192 { 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]); 198 } 199 } 200 201 static void timer_event0(void *opaque) 202 { 203 RCMTState *cmt = opaque; 204 205 timer_events(cmt, 0); 206 } 207 208 static void timer_event1(void *opaque) 209 { 210 RCMTState *cmt = opaque; 211 212 timer_events(cmt, 1); 213 } 214 215 static void rcmt_reset(DeviceState *dev) 216 { 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; 222 } 223 224 static void rcmt_init(Object *obj) 225 { 226 SysBusDevice *d = SYS_BUS_DEVICE(obj); 227 RCMTState *cmt = RCMT(obj); 228 int i; 229 230 memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops, 231 cmt, "renesas-cmt", 0x10); 232 sysbus_init_mmio(d, &cmt->memory); 233 234 for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) { 235 sysbus_init_irq(d, &cmt->cmi[i]); 236 } 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); 239 } 240 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() 253 } 254 }; 255 256 static Property rcmt_properties[] = { 257 DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), 258 DEFINE_PROP_END_OF_LIST(), 259 }; 260 261 static void rcmt_class_init(ObjectClass *klass, void *data) 262 { 263 DeviceClass *dc = DEVICE_CLASS(klass); 264 265 dc->vmsd = &vmstate_rcmt; 266 device_class_set_legacy_reset(dc, rcmt_reset); 267 device_class_set_props(dc, rcmt_properties); 268 } 269 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, 276 }; 277 278 static void rcmt_register_types(void) 279 { 280 type_register_static(&rcmt_info); 281 } 282 283 type_init(rcmt_register_types) 284