196401badSSubbaraya Sundeep /* 296401badSSubbaraya Sundeep * Block model of System timer present in 396401badSSubbaraya Sundeep * Microsemi's SmartFusion2 and SmartFusion SoCs. 496401badSSubbaraya Sundeep * 596401badSSubbaraya Sundeep * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>. 696401badSSubbaraya Sundeep * 796401badSSubbaraya Sundeep * Permission is hereby granted, free of charge, to any person obtaining a copy 896401badSSubbaraya Sundeep * of this software and associated documentation files (the "Software"), to deal 996401badSSubbaraya Sundeep * in the Software without restriction, including without limitation the rights 1096401badSSubbaraya Sundeep * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1196401badSSubbaraya Sundeep * copies of the Software, and to permit persons to whom the Software is 1296401badSSubbaraya Sundeep * furnished to do so, subject to the following conditions: 1396401badSSubbaraya Sundeep * 1496401badSSubbaraya Sundeep * The above copyright notice and this permission notice shall be included in 1596401badSSubbaraya Sundeep * all copies or substantial portions of the Software. 1696401badSSubbaraya Sundeep * 1796401badSSubbaraya Sundeep * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1896401badSSubbaraya Sundeep * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1996401badSSubbaraya Sundeep * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2096401badSSubbaraya Sundeep * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 2196401badSSubbaraya Sundeep * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 2296401badSSubbaraya Sundeep * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 2396401badSSubbaraya Sundeep * THE SOFTWARE. 2496401badSSubbaraya Sundeep */ 2596401badSSubbaraya Sundeep 2696401badSSubbaraya Sundeep #include "qemu/osdep.h" 270b8fa32fSMarkus Armbruster #include "qemu/module.h" 2896401badSSubbaraya Sundeep #include "qemu/log.h" 2964552b6bSMarkus Armbruster #include "hw/irq.h" 30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h" 3196401badSSubbaraya Sundeep #include "hw/timer/mss-timer.h" 32d6454270SMarkus Armbruster #include "migration/vmstate.h" 3396401badSSubbaraya Sundeep 3496401badSSubbaraya Sundeep #ifndef MSS_TIMER_ERR_DEBUG 3596401badSSubbaraya Sundeep #define MSS_TIMER_ERR_DEBUG 0 3696401badSSubbaraya Sundeep #endif 3796401badSSubbaraya Sundeep 3896401badSSubbaraya Sundeep #define DB_PRINT_L(lvl, fmt, args...) do { \ 3996401badSSubbaraya Sundeep if (MSS_TIMER_ERR_DEBUG >= lvl) { \ 4096401badSSubbaraya Sundeep qemu_log("%s: " fmt "\n", __func__, ## args); \ 4196401badSSubbaraya Sundeep } \ 422562755eSEric Blake } while (0) 4396401badSSubbaraya Sundeep 4496401badSSubbaraya Sundeep #define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args) 4596401badSSubbaraya Sundeep 4696401badSSubbaraya Sundeep #define R_TIM_VAL 0 4796401badSSubbaraya Sundeep #define R_TIM_LOADVAL 1 4896401badSSubbaraya Sundeep #define R_TIM_BGLOADVAL 2 4996401badSSubbaraya Sundeep #define R_TIM_CTRL 3 5096401badSSubbaraya Sundeep #define R_TIM_RIS 4 5196401badSSubbaraya Sundeep #define R_TIM_MIS 5 5296401badSSubbaraya Sundeep 5396401badSSubbaraya Sundeep #define TIMER_CTRL_ENBL (1 << 0) 5496401badSSubbaraya Sundeep #define TIMER_CTRL_ONESHOT (1 << 1) 5596401badSSubbaraya Sundeep #define TIMER_CTRL_INTR (1 << 2) 5696401badSSubbaraya Sundeep #define TIMER_RIS_ACK (1 << 0) 5796401badSSubbaraya Sundeep #define TIMER_RST_CLR (1 << 6) 5896401badSSubbaraya Sundeep #define TIMER_MODE (1 << 0) 5996401badSSubbaraya Sundeep 6096401badSSubbaraya Sundeep static void timer_update_irq(struct Msf2Timer *st) 6196401badSSubbaraya Sundeep { 6296401badSSubbaraya Sundeep bool isr, ier; 6396401badSSubbaraya Sundeep 6496401badSSubbaraya Sundeep isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK); 6596401badSSubbaraya Sundeep ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR); 6696401badSSubbaraya Sundeep qemu_set_irq(st->irq, (ier && isr)); 6796401badSSubbaraya Sundeep } 6896401badSSubbaraya Sundeep 6900ee4b0fSPeter Maydell /* Must be called from within a ptimer_transaction_begin/commit block */ 7096401badSSubbaraya Sundeep static void timer_update(struct Msf2Timer *st) 7196401badSSubbaraya Sundeep { 7296401badSSubbaraya Sundeep uint64_t count; 7396401badSSubbaraya Sundeep 7496401badSSubbaraya Sundeep if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL)) { 7596401badSSubbaraya Sundeep ptimer_stop(st->ptimer); 7696401badSSubbaraya Sundeep return; 7796401badSSubbaraya Sundeep } 7896401badSSubbaraya Sundeep 7996401badSSubbaraya Sundeep count = st->regs[R_TIM_LOADVAL]; 8096401badSSubbaraya Sundeep ptimer_set_limit(st->ptimer, count, 1); 8196401badSSubbaraya Sundeep ptimer_run(st->ptimer, 1); 8296401badSSubbaraya Sundeep } 8396401badSSubbaraya Sundeep 8496401badSSubbaraya Sundeep static uint64_t 8596401badSSubbaraya Sundeep timer_read(void *opaque, hwaddr offset, unsigned int size) 8696401badSSubbaraya Sundeep { 8796401badSSubbaraya Sundeep MSSTimerState *t = opaque; 8896401badSSubbaraya Sundeep hwaddr addr; 8996401badSSubbaraya Sundeep struct Msf2Timer *st; 9096401badSSubbaraya Sundeep uint32_t ret = 0; 9196401badSSubbaraya Sundeep int timer = 0; 9296401badSSubbaraya Sundeep int isr; 9396401badSSubbaraya Sundeep int ier; 9496401badSSubbaraya Sundeep 9596401badSSubbaraya Sundeep addr = offset >> 2; 9696401badSSubbaraya Sundeep /* 9796401badSSubbaraya Sundeep * Two independent timers has same base address. 9896401badSSubbaraya Sundeep * Based on address passed figure out which timer is being used. 9996401badSSubbaraya Sundeep */ 10096401badSSubbaraya Sundeep if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) { 10196401badSSubbaraya Sundeep timer = 1; 10296401badSSubbaraya Sundeep addr -= R_TIM1_MAX; 10396401badSSubbaraya Sundeep } 10496401badSSubbaraya Sundeep 10596401badSSubbaraya Sundeep st = &t->timers[timer]; 10696401badSSubbaraya Sundeep 10796401badSSubbaraya Sundeep switch (addr) { 10896401badSSubbaraya Sundeep case R_TIM_VAL: 10996401badSSubbaraya Sundeep ret = ptimer_get_count(st->ptimer); 11096401badSSubbaraya Sundeep break; 11196401badSSubbaraya Sundeep 11296401badSSubbaraya Sundeep case R_TIM_MIS: 11396401badSSubbaraya Sundeep isr = !!(st->regs[R_TIM_RIS] & TIMER_RIS_ACK); 11496401badSSubbaraya Sundeep ier = !!(st->regs[R_TIM_CTRL] & TIMER_CTRL_INTR); 11596401badSSubbaraya Sundeep ret = ier & isr; 11696401badSSubbaraya Sundeep break; 11796401badSSubbaraya Sundeep 11896401badSSubbaraya Sundeep default: 11996401badSSubbaraya Sundeep if (addr < R_TIM1_MAX) { 12096401badSSubbaraya Sundeep ret = st->regs[addr]; 12196401badSSubbaraya Sundeep } else { 12296401badSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 12396401badSSubbaraya Sundeep TYPE_MSS_TIMER": 64-bit mode not supported\n"); 12496401badSSubbaraya Sundeep return ret; 12596401badSSubbaraya Sundeep } 12696401badSSubbaraya Sundeep break; 12796401badSSubbaraya Sundeep } 12896401badSSubbaraya Sundeep 12996401badSSubbaraya Sundeep DB_PRINT("timer=%d 0x%" HWADDR_PRIx "=0x%" PRIx32, timer, offset, 13096401badSSubbaraya Sundeep ret); 13196401badSSubbaraya Sundeep return ret; 13296401badSSubbaraya Sundeep } 13396401badSSubbaraya Sundeep 13496401badSSubbaraya Sundeep static void 13596401badSSubbaraya Sundeep timer_write(void *opaque, hwaddr offset, 13696401badSSubbaraya Sundeep uint64_t val64, unsigned int size) 13796401badSSubbaraya Sundeep { 13896401badSSubbaraya Sundeep MSSTimerState *t = opaque; 13996401badSSubbaraya Sundeep hwaddr addr; 14096401badSSubbaraya Sundeep struct Msf2Timer *st; 14196401badSSubbaraya Sundeep int timer = 0; 14296401badSSubbaraya Sundeep uint32_t value = val64; 14396401badSSubbaraya Sundeep 14496401badSSubbaraya Sundeep addr = offset >> 2; 14596401badSSubbaraya Sundeep /* 14696401badSSubbaraya Sundeep * Two independent timers has same base address. 14796401badSSubbaraya Sundeep * Based on addr passed figure out which timer is being used. 14896401badSSubbaraya Sundeep */ 14996401badSSubbaraya Sundeep if ((addr >= R_TIM1_MAX) && (addr < NUM_TIMERS * R_TIM1_MAX)) { 15096401badSSubbaraya Sundeep timer = 1; 15196401badSSubbaraya Sundeep addr -= R_TIM1_MAX; 15296401badSSubbaraya Sundeep } 15396401badSSubbaraya Sundeep 15496401badSSubbaraya Sundeep st = &t->timers[timer]; 15596401badSSubbaraya Sundeep 15696401badSSubbaraya Sundeep DB_PRINT("addr=0x%" HWADDR_PRIx " val=0x%" PRIx32 " (timer=%d)", offset, 15796401badSSubbaraya Sundeep value, timer); 15896401badSSubbaraya Sundeep 15996401badSSubbaraya Sundeep switch (addr) { 16096401badSSubbaraya Sundeep case R_TIM_CTRL: 16196401badSSubbaraya Sundeep st->regs[R_TIM_CTRL] = value; 16200ee4b0fSPeter Maydell ptimer_transaction_begin(st->ptimer); 16396401badSSubbaraya Sundeep timer_update(st); 16400ee4b0fSPeter Maydell ptimer_transaction_commit(st->ptimer); 16596401badSSubbaraya Sundeep break; 16696401badSSubbaraya Sundeep 16796401badSSubbaraya Sundeep case R_TIM_RIS: 16896401badSSubbaraya Sundeep if (value & TIMER_RIS_ACK) { 16996401badSSubbaraya Sundeep st->regs[R_TIM_RIS] &= ~TIMER_RIS_ACK; 17096401badSSubbaraya Sundeep } 17196401badSSubbaraya Sundeep break; 17296401badSSubbaraya Sundeep 17396401badSSubbaraya Sundeep case R_TIM_LOADVAL: 17496401badSSubbaraya Sundeep st->regs[R_TIM_LOADVAL] = value; 17596401badSSubbaraya Sundeep if (st->regs[R_TIM_CTRL] & TIMER_CTRL_ENBL) { 17600ee4b0fSPeter Maydell ptimer_transaction_begin(st->ptimer); 17796401badSSubbaraya Sundeep timer_update(st); 17800ee4b0fSPeter Maydell ptimer_transaction_commit(st->ptimer); 17996401badSSubbaraya Sundeep } 18096401badSSubbaraya Sundeep break; 18196401badSSubbaraya Sundeep 18296401badSSubbaraya Sundeep case R_TIM_BGLOADVAL: 18396401badSSubbaraya Sundeep st->regs[R_TIM_BGLOADVAL] = value; 18496401badSSubbaraya Sundeep st->regs[R_TIM_LOADVAL] = value; 18596401badSSubbaraya Sundeep break; 18696401badSSubbaraya Sundeep 18796401badSSubbaraya Sundeep case R_TIM_VAL: 18896401badSSubbaraya Sundeep case R_TIM_MIS: 18996401badSSubbaraya Sundeep break; 19096401badSSubbaraya Sundeep 19196401badSSubbaraya Sundeep default: 19296401badSSubbaraya Sundeep if (addr < R_TIM1_MAX) { 19396401badSSubbaraya Sundeep st->regs[addr] = value; 19496401badSSubbaraya Sundeep } else { 19596401badSSubbaraya Sundeep qemu_log_mask(LOG_GUEST_ERROR, 19696401badSSubbaraya Sundeep TYPE_MSS_TIMER": 64-bit mode not supported\n"); 19796401badSSubbaraya Sundeep return; 19896401badSSubbaraya Sundeep } 19996401badSSubbaraya Sundeep break; 20096401badSSubbaraya Sundeep } 20196401badSSubbaraya Sundeep timer_update_irq(st); 20296401badSSubbaraya Sundeep } 20396401badSSubbaraya Sundeep 20496401badSSubbaraya Sundeep static const MemoryRegionOps timer_ops = { 20596401badSSubbaraya Sundeep .read = timer_read, 20696401badSSubbaraya Sundeep .write = timer_write, 20796401badSSubbaraya Sundeep .endianness = DEVICE_NATIVE_ENDIAN, 20896401badSSubbaraya Sundeep .valid = { 20996401badSSubbaraya Sundeep .min_access_size = 1, 21096401badSSubbaraya Sundeep .max_access_size = 4 21196401badSSubbaraya Sundeep } 21296401badSSubbaraya Sundeep }; 21396401badSSubbaraya Sundeep 21496401badSSubbaraya Sundeep static void timer_hit(void *opaque) 21596401badSSubbaraya Sundeep { 21696401badSSubbaraya Sundeep struct Msf2Timer *st = opaque; 21796401badSSubbaraya Sundeep 21896401badSSubbaraya Sundeep st->regs[R_TIM_RIS] |= TIMER_RIS_ACK; 21996401badSSubbaraya Sundeep 22096401badSSubbaraya Sundeep if (!(st->regs[R_TIM_CTRL] & TIMER_CTRL_ONESHOT)) { 22196401badSSubbaraya Sundeep timer_update(st); 22296401badSSubbaraya Sundeep } 22396401badSSubbaraya Sundeep timer_update_irq(st); 22496401badSSubbaraya Sundeep } 22596401badSSubbaraya Sundeep 22696401badSSubbaraya Sundeep static void mss_timer_init(Object *obj) 22796401badSSubbaraya Sundeep { 22896401badSSubbaraya Sundeep MSSTimerState *t = MSS_TIMER(obj); 22996401badSSubbaraya Sundeep int i; 23096401badSSubbaraya Sundeep 23196401badSSubbaraya Sundeep /* Init all the ptimers. */ 23296401badSSubbaraya Sundeep for (i = 0; i < NUM_TIMERS; i++) { 23396401badSSubbaraya Sundeep struct Msf2Timer *st = &t->timers[i]; 23496401badSSubbaraya Sundeep 2359598c1bbSPeter Maydell st->ptimer = ptimer_init(timer_hit, st, PTIMER_POLICY_LEGACY); 23600ee4b0fSPeter Maydell ptimer_transaction_begin(st->ptimer); 23796401badSSubbaraya Sundeep ptimer_set_freq(st->ptimer, t->freq_hz); 23800ee4b0fSPeter Maydell ptimer_transaction_commit(st->ptimer); 23996401badSSubbaraya Sundeep sysbus_init_irq(SYS_BUS_DEVICE(obj), &st->irq); 24096401badSSubbaraya Sundeep } 24196401badSSubbaraya Sundeep 24296401badSSubbaraya Sundeep memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, TYPE_MSS_TIMER, 24396401badSSubbaraya Sundeep NUM_TIMERS * R_TIM1_MAX * 4); 24496401badSSubbaraya Sundeep sysbus_init_mmio(SYS_BUS_DEVICE(obj), &t->mmio); 24596401badSSubbaraya Sundeep } 24696401badSSubbaraya Sundeep 247e4940041SGan Qixin static void mss_timer_finalize(Object *obj) 248e4940041SGan Qixin { 249e4940041SGan Qixin MSSTimerState *t = MSS_TIMER(obj); 250e4940041SGan Qixin int i; 251e4940041SGan Qixin 252e4940041SGan Qixin for (i = 0; i < NUM_TIMERS; i++) { 253e4940041SGan Qixin struct Msf2Timer *st = &t->timers[i]; 254e4940041SGan Qixin 255e4940041SGan Qixin ptimer_free(st->ptimer); 256e4940041SGan Qixin } 257e4940041SGan Qixin } 258e4940041SGan Qixin 25996401badSSubbaraya Sundeep static const VMStateDescription vmstate_timers = { 26096401badSSubbaraya Sundeep .name = "mss-timer-block", 26196401badSSubbaraya Sundeep .version_id = 1, 26296401badSSubbaraya Sundeep .minimum_version_id = 1, 263ba324b3fSRichard Henderson .fields = (const VMStateField[]) { 26496401badSSubbaraya Sundeep VMSTATE_PTIMER(ptimer, struct Msf2Timer), 26596401badSSubbaraya Sundeep VMSTATE_UINT32_ARRAY(regs, struct Msf2Timer, R_TIM1_MAX), 26696401badSSubbaraya Sundeep VMSTATE_END_OF_LIST() 26796401badSSubbaraya Sundeep } 26896401badSSubbaraya Sundeep }; 26996401badSSubbaraya Sundeep 27096401badSSubbaraya Sundeep static const VMStateDescription vmstate_mss_timer = { 27196401badSSubbaraya Sundeep .name = TYPE_MSS_TIMER, 27296401badSSubbaraya Sundeep .version_id = 1, 27396401badSSubbaraya Sundeep .minimum_version_id = 1, 274ba324b3fSRichard Henderson .fields = (const VMStateField[]) { 27596401badSSubbaraya Sundeep VMSTATE_UINT32(freq_hz, MSSTimerState), 27696401badSSubbaraya Sundeep VMSTATE_STRUCT_ARRAY(timers, MSSTimerState, NUM_TIMERS, 0, 27796401badSSubbaraya Sundeep vmstate_timers, struct Msf2Timer), 27896401badSSubbaraya Sundeep VMSTATE_END_OF_LIST() 27996401badSSubbaraya Sundeep } 28096401badSSubbaraya Sundeep }; 28196401badSSubbaraya Sundeep 28274734e2bSRichard Henderson static const Property mss_timer_properties[] = { 28396401badSSubbaraya Sundeep /* Libero GUI shows 100Mhz as default for clocks */ 28496401badSSubbaraya Sundeep DEFINE_PROP_UINT32("clock-frequency", MSSTimerState, freq_hz, 28596401badSSubbaraya Sundeep 100 * 1000000), 28696401badSSubbaraya Sundeep }; 28796401badSSubbaraya Sundeep 288*12d1a768SPhilippe Mathieu-Daudé static void mss_timer_class_init(ObjectClass *klass, const void *data) 28996401badSSubbaraya Sundeep { 29096401badSSubbaraya Sundeep DeviceClass *dc = DEVICE_CLASS(klass); 29196401badSSubbaraya Sundeep 2924f67d30bSMarc-André Lureau device_class_set_props(dc, mss_timer_properties); 29396401badSSubbaraya Sundeep dc->vmsd = &vmstate_mss_timer; 29496401badSSubbaraya Sundeep } 29596401badSSubbaraya Sundeep 29696401badSSubbaraya Sundeep static const TypeInfo mss_timer_info = { 29796401badSSubbaraya Sundeep .name = TYPE_MSS_TIMER, 29896401badSSubbaraya Sundeep .parent = TYPE_SYS_BUS_DEVICE, 29996401badSSubbaraya Sundeep .instance_size = sizeof(MSSTimerState), 30096401badSSubbaraya Sundeep .instance_init = mss_timer_init, 301e4940041SGan Qixin .instance_finalize = mss_timer_finalize, 30296401badSSubbaraya Sundeep .class_init = mss_timer_class_init, 30396401badSSubbaraya Sundeep }; 30496401badSSubbaraya Sundeep 30596401badSSubbaraya Sundeep static void mss_timer_register_types(void) 30696401badSSubbaraya Sundeep { 30796401badSSubbaraya Sundeep type_register_static(&mss_timer_info); 30896401badSSubbaraya Sundeep } 30996401badSSubbaraya Sundeep 31096401badSSubbaraya Sundeep type_init(mss_timer_register_types) 311