185fdd74fSHavard Skinnemoen /* 285fdd74fSHavard Skinnemoen * Nuvoton NPCM7xx Timer Controller 385fdd74fSHavard Skinnemoen * 485fdd74fSHavard Skinnemoen * Copyright 2020 Google LLC 585fdd74fSHavard Skinnemoen * 685fdd74fSHavard Skinnemoen * This program is free software; you can redistribute it and/or modify it 785fdd74fSHavard Skinnemoen * under the terms of the GNU General Public License as published by the 885fdd74fSHavard Skinnemoen * Free Software Foundation; either version 2 of the License, or 985fdd74fSHavard Skinnemoen * (at your option) any later version. 1085fdd74fSHavard Skinnemoen * 1185fdd74fSHavard Skinnemoen * This program is distributed in the hope that it will be useful, but WITHOUT 1285fdd74fSHavard Skinnemoen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1385fdd74fSHavard Skinnemoen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1485fdd74fSHavard Skinnemoen * for more details. 1585fdd74fSHavard Skinnemoen */ 1685fdd74fSHavard Skinnemoen 1785fdd74fSHavard Skinnemoen #include "qemu/osdep.h" 1885fdd74fSHavard Skinnemoen 1985fdd74fSHavard Skinnemoen #include "hw/irq.h" 2085fdd74fSHavard Skinnemoen #include "hw/misc/npcm7xx_clk.h" 2185fdd74fSHavard Skinnemoen #include "hw/timer/npcm7xx_timer.h" 2285fdd74fSHavard Skinnemoen #include "migration/vmstate.h" 2385fdd74fSHavard Skinnemoen #include "qemu/bitops.h" 2485fdd74fSHavard Skinnemoen #include "qemu/error-report.h" 2585fdd74fSHavard Skinnemoen #include "qemu/log.h" 2685fdd74fSHavard Skinnemoen #include "qemu/module.h" 2785fdd74fSHavard Skinnemoen #include "qemu/timer.h" 2885fdd74fSHavard Skinnemoen #include "qemu/units.h" 2985fdd74fSHavard Skinnemoen #include "trace.h" 3085fdd74fSHavard Skinnemoen 3185fdd74fSHavard Skinnemoen /* 32-bit register indices. */ 3285fdd74fSHavard Skinnemoen enum NPCM7xxTimerRegisters { 3385fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR0, 3485fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR1, 3585fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR0, 3685fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR1, 3785fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR0, 3885fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR1, 3985fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TISR, 4085fdd74fSHavard Skinnemoen NPCM7XX_TIMER_WTCR, 4185fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR2, 4285fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR3, 4385fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR2, 4485fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR3, 4585fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR2, 4685fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR3, 4785fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR4 = 0x0040 / sizeof(uint32_t), 4885fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR4 = 0x0048 / sizeof(uint32_t), 4985fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR4 = 0x0050 / sizeof(uint32_t), 5085fdd74fSHavard Skinnemoen NPCM7XX_TIMER_REGS_END, 5185fdd74fSHavard Skinnemoen }; 5285fdd74fSHavard Skinnemoen 5385fdd74fSHavard Skinnemoen /* Register field definitions. */ 5485fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CEN BIT(30) 5585fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_IE BIT(29) 5685fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PERIODIC BIT(27) 5785fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CRST BIT(26) 5885fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CACT BIT(25) 5985fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_RSVD 0x01ffff00 6085fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_START 0 6185fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_LEN 8 6285fdd74fSHavard Skinnemoen 6385fdd74fSHavard Skinnemoen /* 6485fdd74fSHavard Skinnemoen * Returns the index of timer in the tc->timer array. This can be used to 6585fdd74fSHavard Skinnemoen * locate the registers that belong to this timer. 6685fdd74fSHavard Skinnemoen */ 6785fdd74fSHavard Skinnemoen static int npcm7xx_timer_index(NPCM7xxTimerCtrlState *tc, NPCM7xxTimer *timer) 6885fdd74fSHavard Skinnemoen { 6985fdd74fSHavard Skinnemoen int index = timer - tc->timer; 7085fdd74fSHavard Skinnemoen 7185fdd74fSHavard Skinnemoen g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL); 7285fdd74fSHavard Skinnemoen 7385fdd74fSHavard Skinnemoen return index; 7485fdd74fSHavard Skinnemoen } 7585fdd74fSHavard Skinnemoen 7685fdd74fSHavard Skinnemoen /* Return the value by which to divide the reference clock rate. */ 7785fdd74fSHavard Skinnemoen static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr) 7885fdd74fSHavard Skinnemoen { 7985fdd74fSHavard Skinnemoen return extract32(tcsr, NPCM7XX_TCSR_PRESCALE_START, 8085fdd74fSHavard Skinnemoen NPCM7XX_TCSR_PRESCALE_LEN) + 1; 8185fdd74fSHavard Skinnemoen } 8285fdd74fSHavard Skinnemoen 8385fdd74fSHavard Skinnemoen /* Convert a timer cycle count to a time interval in nanoseconds. */ 8485fdd74fSHavard Skinnemoen static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count) 8585fdd74fSHavard Skinnemoen { 8685fdd74fSHavard Skinnemoen int64_t ns = count; 8785fdd74fSHavard Skinnemoen 8885fdd74fSHavard Skinnemoen ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ; 8985fdd74fSHavard Skinnemoen ns *= npcm7xx_tcsr_prescaler(t->tcsr); 9085fdd74fSHavard Skinnemoen 9185fdd74fSHavard Skinnemoen return ns; 9285fdd74fSHavard Skinnemoen } 9385fdd74fSHavard Skinnemoen 9485fdd74fSHavard Skinnemoen /* Convert a time interval in nanoseconds to a timer cycle count. */ 9585fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns) 9685fdd74fSHavard Skinnemoen { 9785fdd74fSHavard Skinnemoen int64_t count; 9885fdd74fSHavard Skinnemoen 9985fdd74fSHavard Skinnemoen count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ); 10085fdd74fSHavard Skinnemoen count /= npcm7xx_tcsr_prescaler(t->tcsr); 10185fdd74fSHavard Skinnemoen 10285fdd74fSHavard Skinnemoen return count; 10385fdd74fSHavard Skinnemoen } 10485fdd74fSHavard Skinnemoen 10585fdd74fSHavard Skinnemoen /* 10685fdd74fSHavard Skinnemoen * Raise the interrupt line if there's a pending interrupt and interrupts are 10785fdd74fSHavard Skinnemoen * enabled for this timer. If not, lower it. 10885fdd74fSHavard Skinnemoen */ 10985fdd74fSHavard Skinnemoen static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t) 11085fdd74fSHavard Skinnemoen { 11185fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *tc = t->ctrl; 11285fdd74fSHavard Skinnemoen int index = npcm7xx_timer_index(tc, t); 11385fdd74fSHavard Skinnemoen bool pending = (t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index)); 11485fdd74fSHavard Skinnemoen 11585fdd74fSHavard Skinnemoen qemu_set_irq(t->irq, pending); 11685fdd74fSHavard Skinnemoen trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending); 11785fdd74fSHavard Skinnemoen } 11885fdd74fSHavard Skinnemoen 11985fdd74fSHavard Skinnemoen /* Start or resume the timer. */ 12085fdd74fSHavard Skinnemoen static void npcm7xx_timer_start(NPCM7xxTimer *t) 12185fdd74fSHavard Skinnemoen { 12285fdd74fSHavard Skinnemoen int64_t now; 12385fdd74fSHavard Skinnemoen 12485fdd74fSHavard Skinnemoen now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 12585fdd74fSHavard Skinnemoen t->expires_ns = now + t->remaining_ns; 12685fdd74fSHavard Skinnemoen timer_mod(&t->qtimer, t->expires_ns); 12785fdd74fSHavard Skinnemoen } 12885fdd74fSHavard Skinnemoen 12985fdd74fSHavard Skinnemoen /* 13085fdd74fSHavard Skinnemoen * Called when the counter reaches zero. Sets the interrupt flag, and either 13185fdd74fSHavard Skinnemoen * restarts or disables the timer. 13285fdd74fSHavard Skinnemoen */ 13385fdd74fSHavard Skinnemoen static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t) 13485fdd74fSHavard Skinnemoen { 13585fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *tc = t->ctrl; 13685fdd74fSHavard Skinnemoen int index = npcm7xx_timer_index(tc, t); 13785fdd74fSHavard Skinnemoen 13885fdd74fSHavard Skinnemoen tc->tisr |= BIT(index); 13985fdd74fSHavard Skinnemoen 14085fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_PERIODIC) { 14185fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); 14285fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 14385fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 14485fdd74fSHavard Skinnemoen } 14585fdd74fSHavard Skinnemoen } else { 14685fdd74fSHavard Skinnemoen t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT); 14785fdd74fSHavard Skinnemoen } 14885fdd74fSHavard Skinnemoen 14985fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(t); 15085fdd74fSHavard Skinnemoen } 15185fdd74fSHavard Skinnemoen 15285fdd74fSHavard Skinnemoen /* Stop counting. Record the time remaining so we can continue later. */ 15385fdd74fSHavard Skinnemoen static void npcm7xx_timer_pause(NPCM7xxTimer *t) 15485fdd74fSHavard Skinnemoen { 15585fdd74fSHavard Skinnemoen int64_t now; 15685fdd74fSHavard Skinnemoen 15785fdd74fSHavard Skinnemoen timer_del(&t->qtimer); 15885fdd74fSHavard Skinnemoen now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 15985fdd74fSHavard Skinnemoen t->remaining_ns = t->expires_ns - now; 16085fdd74fSHavard Skinnemoen } 16185fdd74fSHavard Skinnemoen 16285fdd74fSHavard Skinnemoen /* 16385fdd74fSHavard Skinnemoen * Restart the timer from its initial value. If the timer was enabled and stays 16485fdd74fSHavard Skinnemoen * enabled, adjust the QEMU timer according to the new count. If the timer is 16585fdd74fSHavard Skinnemoen * transitioning from disabled to enabled, the caller is expected to start the 16685fdd74fSHavard Skinnemoen * timer later. 16785fdd74fSHavard Skinnemoen */ 16885fdd74fSHavard Skinnemoen static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr) 16985fdd74fSHavard Skinnemoen { 17085fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); 17185fdd74fSHavard Skinnemoen 17285fdd74fSHavard Skinnemoen if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { 17385fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 17485fdd74fSHavard Skinnemoen } 17585fdd74fSHavard Skinnemoen } 17685fdd74fSHavard Skinnemoen 17785fdd74fSHavard Skinnemoen /* Register read and write handlers */ 17885fdd74fSHavard Skinnemoen 17985fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t) 18085fdd74fSHavard Skinnemoen { 18185fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 18285fdd74fSHavard Skinnemoen int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 18385fdd74fSHavard Skinnemoen 18485fdd74fSHavard Skinnemoen return npcm7xx_timer_ns_to_count(t, t->expires_ns - now); 18585fdd74fSHavard Skinnemoen } 18685fdd74fSHavard Skinnemoen 18785fdd74fSHavard Skinnemoen return npcm7xx_timer_ns_to_count(t, t->remaining_ns); 18885fdd74fSHavard Skinnemoen } 18985fdd74fSHavard Skinnemoen 19085fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr) 19185fdd74fSHavard Skinnemoen { 19285fdd74fSHavard Skinnemoen uint32_t old_tcsr = t->tcsr; 19385fdd74fSHavard Skinnemoen uint32_t tdr; 19485fdd74fSHavard Skinnemoen 19585fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_RSVD) { 19685fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n", 19785fdd74fSHavard Skinnemoen __func__, new_tcsr); 19885fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_RSVD; 19985fdd74fSHavard Skinnemoen } 20085fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CACT) { 20185fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n", 20285fdd74fSHavard Skinnemoen __func__, new_tcsr); 20385fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_CACT; 20485fdd74fSHavard Skinnemoen } 20585fdd74fSHavard Skinnemoen if ((new_tcsr & NPCM7XX_TCSR_CRST) && (new_tcsr & NPCM7XX_TCSR_CEN)) { 20685fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 20785fdd74fSHavard Skinnemoen "%s: both CRST and CEN set; ignoring CEN.\n", 20885fdd74fSHavard Skinnemoen __func__); 20985fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_CEN; 21085fdd74fSHavard Skinnemoen } 21185fdd74fSHavard Skinnemoen 21285fdd74fSHavard Skinnemoen /* Calculate the value of TDR before potentially changing the prescaler. */ 21385fdd74fSHavard Skinnemoen tdr = npcm7xx_timer_read_tdr(t); 21485fdd74fSHavard Skinnemoen 21585fdd74fSHavard Skinnemoen t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr; 21685fdd74fSHavard Skinnemoen 21785fdd74fSHavard Skinnemoen if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) { 21885fdd74fSHavard Skinnemoen /* Recalculate time remaining based on the current TDR value. */ 21985fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr); 22085fdd74fSHavard Skinnemoen if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { 22185fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 22285fdd74fSHavard Skinnemoen } 22385fdd74fSHavard Skinnemoen } 22485fdd74fSHavard Skinnemoen 22585fdd74fSHavard Skinnemoen if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) { 22685fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(t); 22785fdd74fSHavard Skinnemoen } 22885fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CRST) { 22985fdd74fSHavard Skinnemoen npcm7xx_timer_restart(t, old_tcsr); 23085fdd74fSHavard Skinnemoen t->tcsr &= ~NPCM7XX_TCSR_CRST; 23185fdd74fSHavard Skinnemoen } 23285fdd74fSHavard Skinnemoen if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) { 23385fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CEN) { 23485fdd74fSHavard Skinnemoen t->tcsr |= NPCM7XX_TCSR_CACT; 23585fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 23685fdd74fSHavard Skinnemoen } else { 23785fdd74fSHavard Skinnemoen t->tcsr &= ~NPCM7XX_TCSR_CACT; 23885fdd74fSHavard Skinnemoen npcm7xx_timer_pause(t); 239*2ac88848SHavard Skinnemoen if (t->remaining_ns <= 0) { 240*2ac88848SHavard Skinnemoen npcm7xx_timer_reached_zero(t); 241*2ac88848SHavard Skinnemoen } 24285fdd74fSHavard Skinnemoen } 24385fdd74fSHavard Skinnemoen } 24485fdd74fSHavard Skinnemoen } 24585fdd74fSHavard Skinnemoen 24685fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr) 24785fdd74fSHavard Skinnemoen { 24885fdd74fSHavard Skinnemoen t->ticr = new_ticr; 24985fdd74fSHavard Skinnemoen 25085fdd74fSHavard Skinnemoen npcm7xx_timer_restart(t, t->tcsr); 25185fdd74fSHavard Skinnemoen } 25285fdd74fSHavard Skinnemoen 25385fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value) 25485fdd74fSHavard Skinnemoen { 25585fdd74fSHavard Skinnemoen int i; 25685fdd74fSHavard Skinnemoen 25785fdd74fSHavard Skinnemoen s->tisr &= ~value; 25885fdd74fSHavard Skinnemoen for (i = 0; i < ARRAY_SIZE(s->timer); i++) { 25985fdd74fSHavard Skinnemoen if (value & (1U << i)) { 26085fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(&s->timer[i]); 26185fdd74fSHavard Skinnemoen } 26285fdd74fSHavard Skinnemoen } 26385fdd74fSHavard Skinnemoen } 26485fdd74fSHavard Skinnemoen 26585fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tcsr_index(hwaddr reg) 26685fdd74fSHavard Skinnemoen { 26785fdd74fSHavard Skinnemoen switch (reg) { 26885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 26985fdd74fSHavard Skinnemoen return 0; 27085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 27185fdd74fSHavard Skinnemoen return 1; 27285fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 27385fdd74fSHavard Skinnemoen return 2; 27485fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 27585fdd74fSHavard Skinnemoen return 3; 27685fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 27785fdd74fSHavard Skinnemoen return 4; 27885fdd74fSHavard Skinnemoen default: 27985fdd74fSHavard Skinnemoen g_assert_not_reached(); 28085fdd74fSHavard Skinnemoen } 28185fdd74fSHavard Skinnemoen } 28285fdd74fSHavard Skinnemoen 28385fdd74fSHavard Skinnemoen static hwaddr npcm7xx_ticr_index(hwaddr reg) 28485fdd74fSHavard Skinnemoen { 28585fdd74fSHavard Skinnemoen switch (reg) { 28685fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 28785fdd74fSHavard Skinnemoen return 0; 28885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 28985fdd74fSHavard Skinnemoen return 1; 29085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 29185fdd74fSHavard Skinnemoen return 2; 29285fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 29385fdd74fSHavard Skinnemoen return 3; 29485fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 29585fdd74fSHavard Skinnemoen return 4; 29685fdd74fSHavard Skinnemoen default: 29785fdd74fSHavard Skinnemoen g_assert_not_reached(); 29885fdd74fSHavard Skinnemoen } 29985fdd74fSHavard Skinnemoen } 30085fdd74fSHavard Skinnemoen 30185fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tdr_index(hwaddr reg) 30285fdd74fSHavard Skinnemoen { 30385fdd74fSHavard Skinnemoen switch (reg) { 30485fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 30585fdd74fSHavard Skinnemoen return 0; 30685fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 30785fdd74fSHavard Skinnemoen return 1; 30885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 30985fdd74fSHavard Skinnemoen return 2; 31085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 31185fdd74fSHavard Skinnemoen return 3; 31285fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 31385fdd74fSHavard Skinnemoen return 4; 31485fdd74fSHavard Skinnemoen default: 31585fdd74fSHavard Skinnemoen g_assert_not_reached(); 31685fdd74fSHavard Skinnemoen } 31785fdd74fSHavard Skinnemoen } 31885fdd74fSHavard Skinnemoen 31985fdd74fSHavard Skinnemoen static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size) 32085fdd74fSHavard Skinnemoen { 32185fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = opaque; 32285fdd74fSHavard Skinnemoen uint64_t value = 0; 32385fdd74fSHavard Skinnemoen hwaddr reg; 32485fdd74fSHavard Skinnemoen 32585fdd74fSHavard Skinnemoen reg = offset / sizeof(uint32_t); 32685fdd74fSHavard Skinnemoen switch (reg) { 32785fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 32885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 32985fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 33085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 33185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 33285fdd74fSHavard Skinnemoen value = s->timer[npcm7xx_tcsr_index(reg)].tcsr; 33385fdd74fSHavard Skinnemoen break; 33485fdd74fSHavard Skinnemoen 33585fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 33685fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 33785fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 33885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 33985fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 34085fdd74fSHavard Skinnemoen value = s->timer[npcm7xx_ticr_index(reg)].ticr; 34185fdd74fSHavard Skinnemoen break; 34285fdd74fSHavard Skinnemoen 34385fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 34485fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 34585fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 34685fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 34785fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 34885fdd74fSHavard Skinnemoen value = npcm7xx_timer_read_tdr(&s->timer[npcm7xx_tdr_index(reg)]); 34985fdd74fSHavard Skinnemoen break; 35085fdd74fSHavard Skinnemoen 35185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TISR: 35285fdd74fSHavard Skinnemoen value = s->tisr; 35385fdd74fSHavard Skinnemoen break; 35485fdd74fSHavard Skinnemoen 35585fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_WTCR: 35685fdd74fSHavard Skinnemoen value = s->wtcr; 35785fdd74fSHavard Skinnemoen break; 35885fdd74fSHavard Skinnemoen 35985fdd74fSHavard Skinnemoen default: 36085fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 36185fdd74fSHavard Skinnemoen "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 36285fdd74fSHavard Skinnemoen __func__, offset); 36385fdd74fSHavard Skinnemoen break; 36485fdd74fSHavard Skinnemoen } 36585fdd74fSHavard Skinnemoen 36685fdd74fSHavard Skinnemoen trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value); 36785fdd74fSHavard Skinnemoen 36885fdd74fSHavard Skinnemoen return value; 36985fdd74fSHavard Skinnemoen } 37085fdd74fSHavard Skinnemoen 37185fdd74fSHavard Skinnemoen static void npcm7xx_timer_write(void *opaque, hwaddr offset, 37285fdd74fSHavard Skinnemoen uint64_t v, unsigned size) 37385fdd74fSHavard Skinnemoen { 37485fdd74fSHavard Skinnemoen uint32_t reg = offset / sizeof(uint32_t); 37585fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = opaque; 37685fdd74fSHavard Skinnemoen uint32_t value = v; 37785fdd74fSHavard Skinnemoen 37885fdd74fSHavard Skinnemoen trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value); 37985fdd74fSHavard Skinnemoen 38085fdd74fSHavard Skinnemoen switch (reg) { 38185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 38285fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 38385fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 38485fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 38585fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 38685fdd74fSHavard Skinnemoen npcm7xx_timer_write_tcsr(&s->timer[npcm7xx_tcsr_index(reg)], value); 38785fdd74fSHavard Skinnemoen return; 38885fdd74fSHavard Skinnemoen 38985fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 39085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 39185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 39285fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 39385fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 39485fdd74fSHavard Skinnemoen npcm7xx_timer_write_ticr(&s->timer[npcm7xx_ticr_index(reg)], value); 39585fdd74fSHavard Skinnemoen return; 39685fdd74fSHavard Skinnemoen 39785fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 39885fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 39985fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 40085fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 40185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 40285fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 40385fdd74fSHavard Skinnemoen "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 40485fdd74fSHavard Skinnemoen __func__, offset); 40585fdd74fSHavard Skinnemoen return; 40685fdd74fSHavard Skinnemoen 40785fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TISR: 40885fdd74fSHavard Skinnemoen npcm7xx_timer_write_tisr(s, value); 40985fdd74fSHavard Skinnemoen return; 41085fdd74fSHavard Skinnemoen 41185fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_WTCR: 41285fdd74fSHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n", 41385fdd74fSHavard Skinnemoen __func__, value); 41485fdd74fSHavard Skinnemoen return; 41585fdd74fSHavard Skinnemoen } 41685fdd74fSHavard Skinnemoen 41785fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 41885fdd74fSHavard Skinnemoen "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 41985fdd74fSHavard Skinnemoen __func__, offset); 42085fdd74fSHavard Skinnemoen } 42185fdd74fSHavard Skinnemoen 42285fdd74fSHavard Skinnemoen static const struct MemoryRegionOps npcm7xx_timer_ops = { 42385fdd74fSHavard Skinnemoen .read = npcm7xx_timer_read, 42485fdd74fSHavard Skinnemoen .write = npcm7xx_timer_write, 42585fdd74fSHavard Skinnemoen .endianness = DEVICE_LITTLE_ENDIAN, 42685fdd74fSHavard Skinnemoen .valid = { 42785fdd74fSHavard Skinnemoen .min_access_size = 4, 42885fdd74fSHavard Skinnemoen .max_access_size = 4, 42985fdd74fSHavard Skinnemoen .unaligned = false, 43085fdd74fSHavard Skinnemoen }, 43185fdd74fSHavard Skinnemoen }; 43285fdd74fSHavard Skinnemoen 43385fdd74fSHavard Skinnemoen /* Called when the QEMU timer expires. */ 43485fdd74fSHavard Skinnemoen static void npcm7xx_timer_expired(void *opaque) 43585fdd74fSHavard Skinnemoen { 43685fdd74fSHavard Skinnemoen NPCM7xxTimer *t = opaque; 43785fdd74fSHavard Skinnemoen 43885fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 43985fdd74fSHavard Skinnemoen npcm7xx_timer_reached_zero(t); 44085fdd74fSHavard Skinnemoen } 44185fdd74fSHavard Skinnemoen } 44285fdd74fSHavard Skinnemoen 44385fdd74fSHavard Skinnemoen static void npcm7xx_timer_enter_reset(Object *obj, ResetType type) 44485fdd74fSHavard Skinnemoen { 44585fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); 44685fdd74fSHavard Skinnemoen int i; 44785fdd74fSHavard Skinnemoen 44885fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 44985fdd74fSHavard Skinnemoen NPCM7xxTimer *t = &s->timer[i]; 45085fdd74fSHavard Skinnemoen 45185fdd74fSHavard Skinnemoen timer_del(&t->qtimer); 45285fdd74fSHavard Skinnemoen t->expires_ns = 0; 45385fdd74fSHavard Skinnemoen t->remaining_ns = 0; 45485fdd74fSHavard Skinnemoen t->tcsr = 0x00000005; 45585fdd74fSHavard Skinnemoen t->ticr = 0x00000000; 45685fdd74fSHavard Skinnemoen } 45785fdd74fSHavard Skinnemoen 45885fdd74fSHavard Skinnemoen s->tisr = 0x00000000; 45985fdd74fSHavard Skinnemoen s->wtcr = 0x00000400; 46085fdd74fSHavard Skinnemoen } 46185fdd74fSHavard Skinnemoen 46285fdd74fSHavard Skinnemoen static void npcm7xx_timer_hold_reset(Object *obj) 46385fdd74fSHavard Skinnemoen { 46485fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); 46585fdd74fSHavard Skinnemoen int i; 46685fdd74fSHavard Skinnemoen 46785fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 46885fdd74fSHavard Skinnemoen qemu_irq_lower(s->timer[i].irq); 46985fdd74fSHavard Skinnemoen } 47085fdd74fSHavard Skinnemoen } 47185fdd74fSHavard Skinnemoen 47285fdd74fSHavard Skinnemoen static void npcm7xx_timer_realize(DeviceState *dev, Error **errp) 47385fdd74fSHavard Skinnemoen { 47485fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev); 47585fdd74fSHavard Skinnemoen SysBusDevice *sbd = &s->parent; 47685fdd74fSHavard Skinnemoen int i; 47785fdd74fSHavard Skinnemoen 47885fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 47985fdd74fSHavard Skinnemoen NPCM7xxTimer *t = &s->timer[i]; 48085fdd74fSHavard Skinnemoen t->ctrl = s; 48185fdd74fSHavard Skinnemoen timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t); 48285fdd74fSHavard Skinnemoen sysbus_init_irq(sbd, &t->irq); 48385fdd74fSHavard Skinnemoen } 48485fdd74fSHavard Skinnemoen 48585fdd74fSHavard Skinnemoen memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s, 48685fdd74fSHavard Skinnemoen TYPE_NPCM7XX_TIMER, 4 * KiB); 48785fdd74fSHavard Skinnemoen sysbus_init_mmio(sbd, &s->iomem); 48885fdd74fSHavard Skinnemoen } 48985fdd74fSHavard Skinnemoen 49085fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer = { 49185fdd74fSHavard Skinnemoen .name = "npcm7xx-timer", 49285fdd74fSHavard Skinnemoen .version_id = 0, 49385fdd74fSHavard Skinnemoen .minimum_version_id = 0, 49485fdd74fSHavard Skinnemoen .fields = (VMStateField[]) { 49585fdd74fSHavard Skinnemoen VMSTATE_TIMER(qtimer, NPCM7xxTimer), 49685fdd74fSHavard Skinnemoen VMSTATE_INT64(expires_ns, NPCM7xxTimer), 49785fdd74fSHavard Skinnemoen VMSTATE_INT64(remaining_ns, NPCM7xxTimer), 49885fdd74fSHavard Skinnemoen VMSTATE_UINT32(tcsr, NPCM7xxTimer), 49985fdd74fSHavard Skinnemoen VMSTATE_UINT32(ticr, NPCM7xxTimer), 50085fdd74fSHavard Skinnemoen VMSTATE_END_OF_LIST(), 50185fdd74fSHavard Skinnemoen }, 50285fdd74fSHavard Skinnemoen }; 50385fdd74fSHavard Skinnemoen 50485fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { 50585fdd74fSHavard Skinnemoen .name = "npcm7xx-timer-ctrl", 50685fdd74fSHavard Skinnemoen .version_id = 0, 50785fdd74fSHavard Skinnemoen .minimum_version_id = 0, 50885fdd74fSHavard Skinnemoen .fields = (VMStateField[]) { 50985fdd74fSHavard Skinnemoen VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState), 51085fdd74fSHavard Skinnemoen VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState), 51185fdd74fSHavard Skinnemoen VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState, 51285fdd74fSHavard Skinnemoen NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer, 51385fdd74fSHavard Skinnemoen NPCM7xxTimer), 51485fdd74fSHavard Skinnemoen VMSTATE_END_OF_LIST(), 51585fdd74fSHavard Skinnemoen }, 51685fdd74fSHavard Skinnemoen }; 51785fdd74fSHavard Skinnemoen 51885fdd74fSHavard Skinnemoen static void npcm7xx_timer_class_init(ObjectClass *klass, void *data) 51985fdd74fSHavard Skinnemoen { 52085fdd74fSHavard Skinnemoen ResettableClass *rc = RESETTABLE_CLASS(klass); 52185fdd74fSHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 52285fdd74fSHavard Skinnemoen 52385fdd74fSHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS); 52485fdd74fSHavard Skinnemoen 52585fdd74fSHavard Skinnemoen dc->desc = "NPCM7xx Timer Controller"; 52685fdd74fSHavard Skinnemoen dc->realize = npcm7xx_timer_realize; 52785fdd74fSHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_timer_ctrl; 52885fdd74fSHavard Skinnemoen rc->phases.enter = npcm7xx_timer_enter_reset; 52985fdd74fSHavard Skinnemoen rc->phases.hold = npcm7xx_timer_hold_reset; 53085fdd74fSHavard Skinnemoen } 53185fdd74fSHavard Skinnemoen 53285fdd74fSHavard Skinnemoen static const TypeInfo npcm7xx_timer_info = { 53385fdd74fSHavard Skinnemoen .name = TYPE_NPCM7XX_TIMER, 53485fdd74fSHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 53585fdd74fSHavard Skinnemoen .instance_size = sizeof(NPCM7xxTimerCtrlState), 53685fdd74fSHavard Skinnemoen .class_init = npcm7xx_timer_class_init, 53785fdd74fSHavard Skinnemoen }; 53885fdd74fSHavard Skinnemoen 53985fdd74fSHavard Skinnemoen static void npcm7xx_timer_register_type(void) 54085fdd74fSHavard Skinnemoen { 54185fdd74fSHavard Skinnemoen type_register_static(&npcm7xx_timer_info); 54285fdd74fSHavard Skinnemoen } 54385fdd74fSHavard Skinnemoen type_init(npcm7xx_timer_register_type); 544