1*85fdd74fSHavard Skinnemoen /* 2*85fdd74fSHavard Skinnemoen * Nuvoton NPCM7xx Timer Controller 3*85fdd74fSHavard Skinnemoen * 4*85fdd74fSHavard Skinnemoen * Copyright 2020 Google LLC 5*85fdd74fSHavard Skinnemoen * 6*85fdd74fSHavard Skinnemoen * This program is free software; you can redistribute it and/or modify it 7*85fdd74fSHavard Skinnemoen * under the terms of the GNU General Public License as published by the 8*85fdd74fSHavard Skinnemoen * Free Software Foundation; either version 2 of the License, or 9*85fdd74fSHavard Skinnemoen * (at your option) any later version. 10*85fdd74fSHavard Skinnemoen * 11*85fdd74fSHavard Skinnemoen * This program is distributed in the hope that it will be useful, but WITHOUT 12*85fdd74fSHavard Skinnemoen * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13*85fdd74fSHavard Skinnemoen * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14*85fdd74fSHavard Skinnemoen * for more details. 15*85fdd74fSHavard Skinnemoen */ 16*85fdd74fSHavard Skinnemoen 17*85fdd74fSHavard Skinnemoen #include "qemu/osdep.h" 18*85fdd74fSHavard Skinnemoen 19*85fdd74fSHavard Skinnemoen #include "hw/irq.h" 20*85fdd74fSHavard Skinnemoen #include "hw/misc/npcm7xx_clk.h" 21*85fdd74fSHavard Skinnemoen #include "hw/timer/npcm7xx_timer.h" 22*85fdd74fSHavard Skinnemoen #include "migration/vmstate.h" 23*85fdd74fSHavard Skinnemoen #include "qemu/bitops.h" 24*85fdd74fSHavard Skinnemoen #include "qemu/error-report.h" 25*85fdd74fSHavard Skinnemoen #include "qemu/log.h" 26*85fdd74fSHavard Skinnemoen #include "qemu/module.h" 27*85fdd74fSHavard Skinnemoen #include "qemu/timer.h" 28*85fdd74fSHavard Skinnemoen #include "qemu/units.h" 29*85fdd74fSHavard Skinnemoen #include "trace.h" 30*85fdd74fSHavard Skinnemoen 31*85fdd74fSHavard Skinnemoen /* 32-bit register indices. */ 32*85fdd74fSHavard Skinnemoen enum NPCM7xxTimerRegisters { 33*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR0, 34*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR1, 35*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR0, 36*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR1, 37*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR0, 38*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR1, 39*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TISR, 40*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_WTCR, 41*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR2, 42*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR3, 43*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR2, 44*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR3, 45*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR2, 46*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR3, 47*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TCSR4 = 0x0040 / sizeof(uint32_t), 48*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TICR4 = 0x0048 / sizeof(uint32_t), 49*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_TDR4 = 0x0050 / sizeof(uint32_t), 50*85fdd74fSHavard Skinnemoen NPCM7XX_TIMER_REGS_END, 51*85fdd74fSHavard Skinnemoen }; 52*85fdd74fSHavard Skinnemoen 53*85fdd74fSHavard Skinnemoen /* Register field definitions. */ 54*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CEN BIT(30) 55*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_IE BIT(29) 56*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PERIODIC BIT(27) 57*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CRST BIT(26) 58*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_CACT BIT(25) 59*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_RSVD 0x01ffff00 60*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_START 0 61*85fdd74fSHavard Skinnemoen #define NPCM7XX_TCSR_PRESCALE_LEN 8 62*85fdd74fSHavard Skinnemoen 63*85fdd74fSHavard Skinnemoen /* 64*85fdd74fSHavard Skinnemoen * Returns the index of timer in the tc->timer array. This can be used to 65*85fdd74fSHavard Skinnemoen * locate the registers that belong to this timer. 66*85fdd74fSHavard Skinnemoen */ 67*85fdd74fSHavard Skinnemoen static int npcm7xx_timer_index(NPCM7xxTimerCtrlState *tc, NPCM7xxTimer *timer) 68*85fdd74fSHavard Skinnemoen { 69*85fdd74fSHavard Skinnemoen int index = timer - tc->timer; 70*85fdd74fSHavard Skinnemoen 71*85fdd74fSHavard Skinnemoen g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL); 72*85fdd74fSHavard Skinnemoen 73*85fdd74fSHavard Skinnemoen return index; 74*85fdd74fSHavard Skinnemoen } 75*85fdd74fSHavard Skinnemoen 76*85fdd74fSHavard Skinnemoen /* Return the value by which to divide the reference clock rate. */ 77*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr) 78*85fdd74fSHavard Skinnemoen { 79*85fdd74fSHavard Skinnemoen return extract32(tcsr, NPCM7XX_TCSR_PRESCALE_START, 80*85fdd74fSHavard Skinnemoen NPCM7XX_TCSR_PRESCALE_LEN) + 1; 81*85fdd74fSHavard Skinnemoen } 82*85fdd74fSHavard Skinnemoen 83*85fdd74fSHavard Skinnemoen /* Convert a timer cycle count to a time interval in nanoseconds. */ 84*85fdd74fSHavard Skinnemoen static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count) 85*85fdd74fSHavard Skinnemoen { 86*85fdd74fSHavard Skinnemoen int64_t ns = count; 87*85fdd74fSHavard Skinnemoen 88*85fdd74fSHavard Skinnemoen ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ; 89*85fdd74fSHavard Skinnemoen ns *= npcm7xx_tcsr_prescaler(t->tcsr); 90*85fdd74fSHavard Skinnemoen 91*85fdd74fSHavard Skinnemoen return ns; 92*85fdd74fSHavard Skinnemoen } 93*85fdd74fSHavard Skinnemoen 94*85fdd74fSHavard Skinnemoen /* Convert a time interval in nanoseconds to a timer cycle count. */ 95*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns) 96*85fdd74fSHavard Skinnemoen { 97*85fdd74fSHavard Skinnemoen int64_t count; 98*85fdd74fSHavard Skinnemoen 99*85fdd74fSHavard Skinnemoen count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ); 100*85fdd74fSHavard Skinnemoen count /= npcm7xx_tcsr_prescaler(t->tcsr); 101*85fdd74fSHavard Skinnemoen 102*85fdd74fSHavard Skinnemoen return count; 103*85fdd74fSHavard Skinnemoen } 104*85fdd74fSHavard Skinnemoen 105*85fdd74fSHavard Skinnemoen /* 106*85fdd74fSHavard Skinnemoen * Raise the interrupt line if there's a pending interrupt and interrupts are 107*85fdd74fSHavard Skinnemoen * enabled for this timer. If not, lower it. 108*85fdd74fSHavard Skinnemoen */ 109*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t) 110*85fdd74fSHavard Skinnemoen { 111*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *tc = t->ctrl; 112*85fdd74fSHavard Skinnemoen int index = npcm7xx_timer_index(tc, t); 113*85fdd74fSHavard Skinnemoen bool pending = (t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index)); 114*85fdd74fSHavard Skinnemoen 115*85fdd74fSHavard Skinnemoen qemu_set_irq(t->irq, pending); 116*85fdd74fSHavard Skinnemoen trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, pending); 117*85fdd74fSHavard Skinnemoen } 118*85fdd74fSHavard Skinnemoen 119*85fdd74fSHavard Skinnemoen /* Start or resume the timer. */ 120*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_start(NPCM7xxTimer *t) 121*85fdd74fSHavard Skinnemoen { 122*85fdd74fSHavard Skinnemoen int64_t now; 123*85fdd74fSHavard Skinnemoen 124*85fdd74fSHavard Skinnemoen now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 125*85fdd74fSHavard Skinnemoen t->expires_ns = now + t->remaining_ns; 126*85fdd74fSHavard Skinnemoen timer_mod(&t->qtimer, t->expires_ns); 127*85fdd74fSHavard Skinnemoen } 128*85fdd74fSHavard Skinnemoen 129*85fdd74fSHavard Skinnemoen /* 130*85fdd74fSHavard Skinnemoen * Called when the counter reaches zero. Sets the interrupt flag, and either 131*85fdd74fSHavard Skinnemoen * restarts or disables the timer. 132*85fdd74fSHavard Skinnemoen */ 133*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t) 134*85fdd74fSHavard Skinnemoen { 135*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *tc = t->ctrl; 136*85fdd74fSHavard Skinnemoen int index = npcm7xx_timer_index(tc, t); 137*85fdd74fSHavard Skinnemoen 138*85fdd74fSHavard Skinnemoen tc->tisr |= BIT(index); 139*85fdd74fSHavard Skinnemoen 140*85fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_PERIODIC) { 141*85fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); 142*85fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 143*85fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 144*85fdd74fSHavard Skinnemoen } 145*85fdd74fSHavard Skinnemoen } else { 146*85fdd74fSHavard Skinnemoen t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT); 147*85fdd74fSHavard Skinnemoen } 148*85fdd74fSHavard Skinnemoen 149*85fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(t); 150*85fdd74fSHavard Skinnemoen } 151*85fdd74fSHavard Skinnemoen 152*85fdd74fSHavard Skinnemoen /* Stop counting. Record the time remaining so we can continue later. */ 153*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_pause(NPCM7xxTimer *t) 154*85fdd74fSHavard Skinnemoen { 155*85fdd74fSHavard Skinnemoen int64_t now; 156*85fdd74fSHavard Skinnemoen 157*85fdd74fSHavard Skinnemoen timer_del(&t->qtimer); 158*85fdd74fSHavard Skinnemoen now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 159*85fdd74fSHavard Skinnemoen t->remaining_ns = t->expires_ns - now; 160*85fdd74fSHavard Skinnemoen if (t->remaining_ns <= 0) { 161*85fdd74fSHavard Skinnemoen npcm7xx_timer_reached_zero(t); 162*85fdd74fSHavard Skinnemoen } 163*85fdd74fSHavard Skinnemoen } 164*85fdd74fSHavard Skinnemoen 165*85fdd74fSHavard Skinnemoen /* 166*85fdd74fSHavard Skinnemoen * Restart the timer from its initial value. If the timer was enabled and stays 167*85fdd74fSHavard Skinnemoen * enabled, adjust the QEMU timer according to the new count. If the timer is 168*85fdd74fSHavard Skinnemoen * transitioning from disabled to enabled, the caller is expected to start the 169*85fdd74fSHavard Skinnemoen * timer later. 170*85fdd74fSHavard Skinnemoen */ 171*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr) 172*85fdd74fSHavard Skinnemoen { 173*85fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr); 174*85fdd74fSHavard Skinnemoen 175*85fdd74fSHavard Skinnemoen if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { 176*85fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 177*85fdd74fSHavard Skinnemoen } 178*85fdd74fSHavard Skinnemoen } 179*85fdd74fSHavard Skinnemoen 180*85fdd74fSHavard Skinnemoen /* Register read and write handlers */ 181*85fdd74fSHavard Skinnemoen 182*85fdd74fSHavard Skinnemoen static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t) 183*85fdd74fSHavard Skinnemoen { 184*85fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 185*85fdd74fSHavard Skinnemoen int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 186*85fdd74fSHavard Skinnemoen 187*85fdd74fSHavard Skinnemoen return npcm7xx_timer_ns_to_count(t, t->expires_ns - now); 188*85fdd74fSHavard Skinnemoen } 189*85fdd74fSHavard Skinnemoen 190*85fdd74fSHavard Skinnemoen return npcm7xx_timer_ns_to_count(t, t->remaining_ns); 191*85fdd74fSHavard Skinnemoen } 192*85fdd74fSHavard Skinnemoen 193*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr) 194*85fdd74fSHavard Skinnemoen { 195*85fdd74fSHavard Skinnemoen uint32_t old_tcsr = t->tcsr; 196*85fdd74fSHavard Skinnemoen uint32_t tdr; 197*85fdd74fSHavard Skinnemoen 198*85fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_RSVD) { 199*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n", 200*85fdd74fSHavard Skinnemoen __func__, new_tcsr); 201*85fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_RSVD; 202*85fdd74fSHavard Skinnemoen } 203*85fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CACT) { 204*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n", 205*85fdd74fSHavard Skinnemoen __func__, new_tcsr); 206*85fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_CACT; 207*85fdd74fSHavard Skinnemoen } 208*85fdd74fSHavard Skinnemoen if ((new_tcsr & NPCM7XX_TCSR_CRST) && (new_tcsr & NPCM7XX_TCSR_CEN)) { 209*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 210*85fdd74fSHavard Skinnemoen "%s: both CRST and CEN set; ignoring CEN.\n", 211*85fdd74fSHavard Skinnemoen __func__); 212*85fdd74fSHavard Skinnemoen new_tcsr &= ~NPCM7XX_TCSR_CEN; 213*85fdd74fSHavard Skinnemoen } 214*85fdd74fSHavard Skinnemoen 215*85fdd74fSHavard Skinnemoen /* Calculate the value of TDR before potentially changing the prescaler. */ 216*85fdd74fSHavard Skinnemoen tdr = npcm7xx_timer_read_tdr(t); 217*85fdd74fSHavard Skinnemoen 218*85fdd74fSHavard Skinnemoen t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr; 219*85fdd74fSHavard Skinnemoen 220*85fdd74fSHavard Skinnemoen if (npcm7xx_tcsr_prescaler(old_tcsr) != npcm7xx_tcsr_prescaler(new_tcsr)) { 221*85fdd74fSHavard Skinnemoen /* Recalculate time remaining based on the current TDR value. */ 222*85fdd74fSHavard Skinnemoen t->remaining_ns = npcm7xx_timer_count_to_ns(t, tdr); 223*85fdd74fSHavard Skinnemoen if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) { 224*85fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 225*85fdd74fSHavard Skinnemoen } 226*85fdd74fSHavard Skinnemoen } 227*85fdd74fSHavard Skinnemoen 228*85fdd74fSHavard Skinnemoen if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) { 229*85fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(t); 230*85fdd74fSHavard Skinnemoen } 231*85fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CRST) { 232*85fdd74fSHavard Skinnemoen npcm7xx_timer_restart(t, old_tcsr); 233*85fdd74fSHavard Skinnemoen t->tcsr &= ~NPCM7XX_TCSR_CRST; 234*85fdd74fSHavard Skinnemoen } 235*85fdd74fSHavard Skinnemoen if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) { 236*85fdd74fSHavard Skinnemoen if (new_tcsr & NPCM7XX_TCSR_CEN) { 237*85fdd74fSHavard Skinnemoen t->tcsr |= NPCM7XX_TCSR_CACT; 238*85fdd74fSHavard Skinnemoen npcm7xx_timer_start(t); 239*85fdd74fSHavard Skinnemoen } else { 240*85fdd74fSHavard Skinnemoen t->tcsr &= ~NPCM7XX_TCSR_CACT; 241*85fdd74fSHavard Skinnemoen npcm7xx_timer_pause(t); 242*85fdd74fSHavard Skinnemoen } 243*85fdd74fSHavard Skinnemoen } 244*85fdd74fSHavard Skinnemoen } 245*85fdd74fSHavard Skinnemoen 246*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr) 247*85fdd74fSHavard Skinnemoen { 248*85fdd74fSHavard Skinnemoen t->ticr = new_ticr; 249*85fdd74fSHavard Skinnemoen 250*85fdd74fSHavard Skinnemoen npcm7xx_timer_restart(t, t->tcsr); 251*85fdd74fSHavard Skinnemoen } 252*85fdd74fSHavard Skinnemoen 253*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write_tisr(NPCM7xxTimerCtrlState *s, uint32_t value) 254*85fdd74fSHavard Skinnemoen { 255*85fdd74fSHavard Skinnemoen int i; 256*85fdd74fSHavard Skinnemoen 257*85fdd74fSHavard Skinnemoen s->tisr &= ~value; 258*85fdd74fSHavard Skinnemoen for (i = 0; i < ARRAY_SIZE(s->timer); i++) { 259*85fdd74fSHavard Skinnemoen if (value & (1U << i)) { 260*85fdd74fSHavard Skinnemoen npcm7xx_timer_check_interrupt(&s->timer[i]); 261*85fdd74fSHavard Skinnemoen } 262*85fdd74fSHavard Skinnemoen } 263*85fdd74fSHavard Skinnemoen } 264*85fdd74fSHavard Skinnemoen 265*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tcsr_index(hwaddr reg) 266*85fdd74fSHavard Skinnemoen { 267*85fdd74fSHavard Skinnemoen switch (reg) { 268*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 269*85fdd74fSHavard Skinnemoen return 0; 270*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 271*85fdd74fSHavard Skinnemoen return 1; 272*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 273*85fdd74fSHavard Skinnemoen return 2; 274*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 275*85fdd74fSHavard Skinnemoen return 3; 276*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 277*85fdd74fSHavard Skinnemoen return 4; 278*85fdd74fSHavard Skinnemoen default: 279*85fdd74fSHavard Skinnemoen g_assert_not_reached(); 280*85fdd74fSHavard Skinnemoen } 281*85fdd74fSHavard Skinnemoen } 282*85fdd74fSHavard Skinnemoen 283*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_ticr_index(hwaddr reg) 284*85fdd74fSHavard Skinnemoen { 285*85fdd74fSHavard Skinnemoen switch (reg) { 286*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 287*85fdd74fSHavard Skinnemoen return 0; 288*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 289*85fdd74fSHavard Skinnemoen return 1; 290*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 291*85fdd74fSHavard Skinnemoen return 2; 292*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 293*85fdd74fSHavard Skinnemoen return 3; 294*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 295*85fdd74fSHavard Skinnemoen return 4; 296*85fdd74fSHavard Skinnemoen default: 297*85fdd74fSHavard Skinnemoen g_assert_not_reached(); 298*85fdd74fSHavard Skinnemoen } 299*85fdd74fSHavard Skinnemoen } 300*85fdd74fSHavard Skinnemoen 301*85fdd74fSHavard Skinnemoen static hwaddr npcm7xx_tdr_index(hwaddr reg) 302*85fdd74fSHavard Skinnemoen { 303*85fdd74fSHavard Skinnemoen switch (reg) { 304*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 305*85fdd74fSHavard Skinnemoen return 0; 306*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 307*85fdd74fSHavard Skinnemoen return 1; 308*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 309*85fdd74fSHavard Skinnemoen return 2; 310*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 311*85fdd74fSHavard Skinnemoen return 3; 312*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 313*85fdd74fSHavard Skinnemoen return 4; 314*85fdd74fSHavard Skinnemoen default: 315*85fdd74fSHavard Skinnemoen g_assert_not_reached(); 316*85fdd74fSHavard Skinnemoen } 317*85fdd74fSHavard Skinnemoen } 318*85fdd74fSHavard Skinnemoen 319*85fdd74fSHavard Skinnemoen static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size) 320*85fdd74fSHavard Skinnemoen { 321*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = opaque; 322*85fdd74fSHavard Skinnemoen uint64_t value = 0; 323*85fdd74fSHavard Skinnemoen hwaddr reg; 324*85fdd74fSHavard Skinnemoen 325*85fdd74fSHavard Skinnemoen reg = offset / sizeof(uint32_t); 326*85fdd74fSHavard Skinnemoen switch (reg) { 327*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 328*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 329*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 330*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 331*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 332*85fdd74fSHavard Skinnemoen value = s->timer[npcm7xx_tcsr_index(reg)].tcsr; 333*85fdd74fSHavard Skinnemoen break; 334*85fdd74fSHavard Skinnemoen 335*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 336*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 337*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 338*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 339*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 340*85fdd74fSHavard Skinnemoen value = s->timer[npcm7xx_ticr_index(reg)].ticr; 341*85fdd74fSHavard Skinnemoen break; 342*85fdd74fSHavard Skinnemoen 343*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 344*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 345*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 346*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 347*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 348*85fdd74fSHavard Skinnemoen value = npcm7xx_timer_read_tdr(&s->timer[npcm7xx_tdr_index(reg)]); 349*85fdd74fSHavard Skinnemoen break; 350*85fdd74fSHavard Skinnemoen 351*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TISR: 352*85fdd74fSHavard Skinnemoen value = s->tisr; 353*85fdd74fSHavard Skinnemoen break; 354*85fdd74fSHavard Skinnemoen 355*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_WTCR: 356*85fdd74fSHavard Skinnemoen value = s->wtcr; 357*85fdd74fSHavard Skinnemoen break; 358*85fdd74fSHavard Skinnemoen 359*85fdd74fSHavard Skinnemoen default: 360*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 361*85fdd74fSHavard Skinnemoen "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 362*85fdd74fSHavard Skinnemoen __func__, offset); 363*85fdd74fSHavard Skinnemoen break; 364*85fdd74fSHavard Skinnemoen } 365*85fdd74fSHavard Skinnemoen 366*85fdd74fSHavard Skinnemoen trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value); 367*85fdd74fSHavard Skinnemoen 368*85fdd74fSHavard Skinnemoen return value; 369*85fdd74fSHavard Skinnemoen } 370*85fdd74fSHavard Skinnemoen 371*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_write(void *opaque, hwaddr offset, 372*85fdd74fSHavard Skinnemoen uint64_t v, unsigned size) 373*85fdd74fSHavard Skinnemoen { 374*85fdd74fSHavard Skinnemoen uint32_t reg = offset / sizeof(uint32_t); 375*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = opaque; 376*85fdd74fSHavard Skinnemoen uint32_t value = v; 377*85fdd74fSHavard Skinnemoen 378*85fdd74fSHavard Skinnemoen trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value); 379*85fdd74fSHavard Skinnemoen 380*85fdd74fSHavard Skinnemoen switch (reg) { 381*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR0: 382*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR1: 383*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR2: 384*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR3: 385*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TCSR4: 386*85fdd74fSHavard Skinnemoen npcm7xx_timer_write_tcsr(&s->timer[npcm7xx_tcsr_index(reg)], value); 387*85fdd74fSHavard Skinnemoen return; 388*85fdd74fSHavard Skinnemoen 389*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR0: 390*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR1: 391*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR2: 392*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR3: 393*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TICR4: 394*85fdd74fSHavard Skinnemoen npcm7xx_timer_write_ticr(&s->timer[npcm7xx_ticr_index(reg)], value); 395*85fdd74fSHavard Skinnemoen return; 396*85fdd74fSHavard Skinnemoen 397*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR0: 398*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR1: 399*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR2: 400*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR3: 401*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TDR4: 402*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 403*85fdd74fSHavard Skinnemoen "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", 404*85fdd74fSHavard Skinnemoen __func__, offset); 405*85fdd74fSHavard Skinnemoen return; 406*85fdd74fSHavard Skinnemoen 407*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_TISR: 408*85fdd74fSHavard Skinnemoen npcm7xx_timer_write_tisr(s, value); 409*85fdd74fSHavard Skinnemoen return; 410*85fdd74fSHavard Skinnemoen 411*85fdd74fSHavard Skinnemoen case NPCM7XX_TIMER_WTCR: 412*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n", 413*85fdd74fSHavard Skinnemoen __func__, value); 414*85fdd74fSHavard Skinnemoen return; 415*85fdd74fSHavard Skinnemoen } 416*85fdd74fSHavard Skinnemoen 417*85fdd74fSHavard Skinnemoen qemu_log_mask(LOG_GUEST_ERROR, 418*85fdd74fSHavard Skinnemoen "%s: invalid offset 0x%04" HWADDR_PRIx "\n", 419*85fdd74fSHavard Skinnemoen __func__, offset); 420*85fdd74fSHavard Skinnemoen } 421*85fdd74fSHavard Skinnemoen 422*85fdd74fSHavard Skinnemoen static const struct MemoryRegionOps npcm7xx_timer_ops = { 423*85fdd74fSHavard Skinnemoen .read = npcm7xx_timer_read, 424*85fdd74fSHavard Skinnemoen .write = npcm7xx_timer_write, 425*85fdd74fSHavard Skinnemoen .endianness = DEVICE_LITTLE_ENDIAN, 426*85fdd74fSHavard Skinnemoen .valid = { 427*85fdd74fSHavard Skinnemoen .min_access_size = 4, 428*85fdd74fSHavard Skinnemoen .max_access_size = 4, 429*85fdd74fSHavard Skinnemoen .unaligned = false, 430*85fdd74fSHavard Skinnemoen }, 431*85fdd74fSHavard Skinnemoen }; 432*85fdd74fSHavard Skinnemoen 433*85fdd74fSHavard Skinnemoen /* Called when the QEMU timer expires. */ 434*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_expired(void *opaque) 435*85fdd74fSHavard Skinnemoen { 436*85fdd74fSHavard Skinnemoen NPCM7xxTimer *t = opaque; 437*85fdd74fSHavard Skinnemoen 438*85fdd74fSHavard Skinnemoen if (t->tcsr & NPCM7XX_TCSR_CEN) { 439*85fdd74fSHavard Skinnemoen npcm7xx_timer_reached_zero(t); 440*85fdd74fSHavard Skinnemoen } 441*85fdd74fSHavard Skinnemoen } 442*85fdd74fSHavard Skinnemoen 443*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_enter_reset(Object *obj, ResetType type) 444*85fdd74fSHavard Skinnemoen { 445*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); 446*85fdd74fSHavard Skinnemoen int i; 447*85fdd74fSHavard Skinnemoen 448*85fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 449*85fdd74fSHavard Skinnemoen NPCM7xxTimer *t = &s->timer[i]; 450*85fdd74fSHavard Skinnemoen 451*85fdd74fSHavard Skinnemoen timer_del(&t->qtimer); 452*85fdd74fSHavard Skinnemoen t->expires_ns = 0; 453*85fdd74fSHavard Skinnemoen t->remaining_ns = 0; 454*85fdd74fSHavard Skinnemoen t->tcsr = 0x00000005; 455*85fdd74fSHavard Skinnemoen t->ticr = 0x00000000; 456*85fdd74fSHavard Skinnemoen } 457*85fdd74fSHavard Skinnemoen 458*85fdd74fSHavard Skinnemoen s->tisr = 0x00000000; 459*85fdd74fSHavard Skinnemoen s->wtcr = 0x00000400; 460*85fdd74fSHavard Skinnemoen } 461*85fdd74fSHavard Skinnemoen 462*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_hold_reset(Object *obj) 463*85fdd74fSHavard Skinnemoen { 464*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj); 465*85fdd74fSHavard Skinnemoen int i; 466*85fdd74fSHavard Skinnemoen 467*85fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 468*85fdd74fSHavard Skinnemoen qemu_irq_lower(s->timer[i].irq); 469*85fdd74fSHavard Skinnemoen } 470*85fdd74fSHavard Skinnemoen } 471*85fdd74fSHavard Skinnemoen 472*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_realize(DeviceState *dev, Error **errp) 473*85fdd74fSHavard Skinnemoen { 474*85fdd74fSHavard Skinnemoen NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev); 475*85fdd74fSHavard Skinnemoen SysBusDevice *sbd = &s->parent; 476*85fdd74fSHavard Skinnemoen int i; 477*85fdd74fSHavard Skinnemoen 478*85fdd74fSHavard Skinnemoen for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) { 479*85fdd74fSHavard Skinnemoen NPCM7xxTimer *t = &s->timer[i]; 480*85fdd74fSHavard Skinnemoen t->ctrl = s; 481*85fdd74fSHavard Skinnemoen timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t); 482*85fdd74fSHavard Skinnemoen sysbus_init_irq(sbd, &t->irq); 483*85fdd74fSHavard Skinnemoen } 484*85fdd74fSHavard Skinnemoen 485*85fdd74fSHavard Skinnemoen memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s, 486*85fdd74fSHavard Skinnemoen TYPE_NPCM7XX_TIMER, 4 * KiB); 487*85fdd74fSHavard Skinnemoen sysbus_init_mmio(sbd, &s->iomem); 488*85fdd74fSHavard Skinnemoen } 489*85fdd74fSHavard Skinnemoen 490*85fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer = { 491*85fdd74fSHavard Skinnemoen .name = "npcm7xx-timer", 492*85fdd74fSHavard Skinnemoen .version_id = 0, 493*85fdd74fSHavard Skinnemoen .minimum_version_id = 0, 494*85fdd74fSHavard Skinnemoen .fields = (VMStateField[]) { 495*85fdd74fSHavard Skinnemoen VMSTATE_TIMER(qtimer, NPCM7xxTimer), 496*85fdd74fSHavard Skinnemoen VMSTATE_INT64(expires_ns, NPCM7xxTimer), 497*85fdd74fSHavard Skinnemoen VMSTATE_INT64(remaining_ns, NPCM7xxTimer), 498*85fdd74fSHavard Skinnemoen VMSTATE_UINT32(tcsr, NPCM7xxTimer), 499*85fdd74fSHavard Skinnemoen VMSTATE_UINT32(ticr, NPCM7xxTimer), 500*85fdd74fSHavard Skinnemoen VMSTATE_END_OF_LIST(), 501*85fdd74fSHavard Skinnemoen }, 502*85fdd74fSHavard Skinnemoen }; 503*85fdd74fSHavard Skinnemoen 504*85fdd74fSHavard Skinnemoen static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { 505*85fdd74fSHavard Skinnemoen .name = "npcm7xx-timer-ctrl", 506*85fdd74fSHavard Skinnemoen .version_id = 0, 507*85fdd74fSHavard Skinnemoen .minimum_version_id = 0, 508*85fdd74fSHavard Skinnemoen .fields = (VMStateField[]) { 509*85fdd74fSHavard Skinnemoen VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState), 510*85fdd74fSHavard Skinnemoen VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState), 511*85fdd74fSHavard Skinnemoen VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState, 512*85fdd74fSHavard Skinnemoen NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer, 513*85fdd74fSHavard Skinnemoen NPCM7xxTimer), 514*85fdd74fSHavard Skinnemoen VMSTATE_END_OF_LIST(), 515*85fdd74fSHavard Skinnemoen }, 516*85fdd74fSHavard Skinnemoen }; 517*85fdd74fSHavard Skinnemoen 518*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_class_init(ObjectClass *klass, void *data) 519*85fdd74fSHavard Skinnemoen { 520*85fdd74fSHavard Skinnemoen ResettableClass *rc = RESETTABLE_CLASS(klass); 521*85fdd74fSHavard Skinnemoen DeviceClass *dc = DEVICE_CLASS(klass); 522*85fdd74fSHavard Skinnemoen 523*85fdd74fSHavard Skinnemoen QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS); 524*85fdd74fSHavard Skinnemoen 525*85fdd74fSHavard Skinnemoen dc->desc = "NPCM7xx Timer Controller"; 526*85fdd74fSHavard Skinnemoen dc->realize = npcm7xx_timer_realize; 527*85fdd74fSHavard Skinnemoen dc->vmsd = &vmstate_npcm7xx_timer_ctrl; 528*85fdd74fSHavard Skinnemoen rc->phases.enter = npcm7xx_timer_enter_reset; 529*85fdd74fSHavard Skinnemoen rc->phases.hold = npcm7xx_timer_hold_reset; 530*85fdd74fSHavard Skinnemoen } 531*85fdd74fSHavard Skinnemoen 532*85fdd74fSHavard Skinnemoen static const TypeInfo npcm7xx_timer_info = { 533*85fdd74fSHavard Skinnemoen .name = TYPE_NPCM7XX_TIMER, 534*85fdd74fSHavard Skinnemoen .parent = TYPE_SYS_BUS_DEVICE, 535*85fdd74fSHavard Skinnemoen .instance_size = sizeof(NPCM7xxTimerCtrlState), 536*85fdd74fSHavard Skinnemoen .class_init = npcm7xx_timer_class_init, 537*85fdd74fSHavard Skinnemoen }; 538*85fdd74fSHavard Skinnemoen 539*85fdd74fSHavard Skinnemoen static void npcm7xx_timer_register_type(void) 540*85fdd74fSHavard Skinnemoen { 541*85fdd74fSHavard Skinnemoen type_register_static(&npcm7xx_timer_info); 542*85fdd74fSHavard Skinnemoen } 543*85fdd74fSHavard Skinnemoen type_init(npcm7xx_timer_register_type); 544