static void update_ctrl(struct etrax_timer *t, int tnum) { unsigned int op; unsigned int freq; unsigned int freq_hz; unsigned int div; uint32_t ctrl; ptimer_state *timer; if (tnum == 0) { ctrl = t->rw_tmr0_ctrl; div = t->rw_tmr0_div; timer = t->ptimer_t0; } else { ctrl = t->rw_tmr1_ctrl; div = t->rw_tmr1_div; timer = t->ptimer_t1; } op = ctrl & 3; freq = ctrl >> 2; freq_hz = 32000000; switch (freq) { case 0: case 1: D(printf ("extern or disabled timer clock?\n")); break; case 4: freq_hz = 29493000; break; case 5: freq_hz = 32000000; break; case 6: freq_hz = 32768000; break; case 7: freq_hz = 100000000; break; default: abort(); break; } D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); ptimer_set_freq(timer, freq_hz); ptimer_set_limit(timer, div, 0); switch (op) { case 0: /* Load. */ ptimer_set_limit(timer, div, 1); break; case 1: /* Hold. */ ptimer_stop(timer); break; case 2: /* Run. */ ptimer_run(timer, 0); break; default: abort(); break; } }
static void sysctl_write(void *opaque, target_phys_addr_t addr, uint64_t value, unsigned size) { MilkymistSysctlState *s = opaque; trace_milkymist_sysctl_memory_write(addr, value); addr >>= 2; switch (addr) { case R_GPIO_OUT: case R_GPIO_INTEN: case R_TIMER0_COUNTER: case R_TIMER1_COUNTER: s->regs[addr] = value; break; case R_TIMER0_COMPARE: ptimer_set_limit(s->ptimer0, value, 0); s->regs[addr] = value; break; case R_TIMER1_COMPARE: ptimer_set_limit(s->ptimer1, value, 0); s->regs[addr] = value; break; case R_TIMER0_CONTROL: s->regs[addr] = value; if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { trace_milkymist_sysctl_start_timer0(); ptimer_set_count(s->ptimer0, s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); ptimer_run(s->ptimer0, 0); } else { trace_milkymist_sysctl_stop_timer0(); ptimer_stop(s->ptimer0); } break; case R_TIMER1_CONTROL: s->regs[addr] = value; if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { trace_milkymist_sysctl_start_timer1(); ptimer_set_count(s->ptimer1, s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); ptimer_run(s->ptimer1, 0); } else { trace_milkymist_sysctl_stop_timer1(); ptimer_stop(s->ptimer1); } break; case R_ICAP: sysctl_icap_write(s, value); break; case R_SYSTEM_ID: qemu_system_reset_request(); break; case R_GPIO_IN: case R_CAPABILITIES: error_report("milkymist_sysctl: write to read-only register 0x" TARGET_FMT_plx, addr << 2); break; default: error_report("milkymist_sysctl: write access to unknown register 0x" TARGET_FMT_plx, addr << 2); break; } }
static void sh_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value) { sh_timer_state *s = (sh_timer_state *)opaque; int freq; switch (offset >> 2) { case OFFSET_TCOR: s->tcor = value; ptimer_set_limit(s->timer, s->tcor, 0); break; case OFFSET_TCNT: s->tcnt = value; ptimer_set_count(s->timer, s->tcnt); break; case OFFSET_TCR: if (s->enabled) { /* Pause the timer if it is running. This may cause some inaccuracy dure to rounding, but avoids a whole lot of other messyness. */ ptimer_stop(s->timer); } freq = s->freq; /* ??? Need to recalculate expiry time after changing divisor. */ switch (value & TIMER_TCR_TPSC) { case 0: freq >>= 2; break; case 1: freq >>= 4; break; case 2: freq >>= 6; break; case 3: freq >>= 8; break; case 4: freq >>= 10; break; case 6: case 7: if (s->feat & TIMER_FEAT_EXTCLK) break; default: hw_error("sh_timer_write: Reserved TPSC value\n"); break; } switch ((value & TIMER_TCR_CKEG) >> 3) { case 0: break; case 1: case 2: case 3: if (s->feat & TIMER_FEAT_EXTCLK) break; default: hw_error("sh_timer_write: Reserved CKEG value\n"); break; } switch ((value & TIMER_TCR_ICPE) >> 6) { case 0: break; case 2: case 3: if (s->feat & TIMER_FEAT_CAPT) break; default: hw_error("sh_timer_write: Reserved ICPE value\n"); break; } if ((value & TIMER_TCR_UNF) == 0) s->int_level = 0; value &= ~TIMER_TCR_UNF; if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) hw_error("sh_timer_write: Reserved ICPF value\n"); value &= ~TIMER_TCR_ICPF; /* capture not supported */ if (value & TIMER_TCR_RESERVED) hw_error("sh_timer_write: Reserved TCR bits set\n"); s->tcr = value; ptimer_set_limit(s->timer, s->tcor, 0); ptimer_set_freq(s->timer, freq); if (s->enabled) { /* Restart the timer if still enabled. */ ptimer_run(s->timer, 0); } break; case OFFSET_TCPR: if (s->feat & TIMER_FEAT_CAPT) { s->tcpr = value; break; } default: hw_error("sh_timer_write: Bad offset %x\n", (int)offset); } sh_timer_update(s); }
static void slavio_timer_mem_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { TimerContext *tc = opaque; SLAVIO_TIMERState *s = tc->s; uint32_t saddr; unsigned int timer_index = tc->timer_index; CPUTimerState *t = &s->cputimer[timer_index]; trace_slavio_timer_mem_writel(addr, val); saddr = addr >> 2; switch (saddr) { case TIMER_LIMIT: if (slavio_timer_is_user(tc)) { uint64_t count; // set user counter MSW, reset counter t->limit = TIMER_MAX_COUNT64; t->counthigh = val & (TIMER_MAX_COUNT64 >> 32); t->reached = 0; count = ((uint64_t)t->counthigh << 32) | t->count; trace_slavio_timer_mem_writel_limit(timer_index, count); ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); } else { // set limit, reset counter qemu_irq_lower(t->irq); t->limit = val & TIMER_MAX_COUNT32; if (t->timer) { if (t->limit == 0) { /* free-run */ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); } else { ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 1); } } } break; case TIMER_COUNTER: if (slavio_timer_is_user(tc)) { uint64_t count; // set user counter LSW, reset counter t->limit = TIMER_MAX_COUNT64; t->count = val & TIMER_MAX_COUNT64; t->reached = 0; count = ((uint64_t)t->counthigh) << 32 | t->count; trace_slavio_timer_mem_writel_limit(timer_index, count); ptimer_set_count(t->timer, LIMIT_TO_PERIODS(t->limit - count)); } else { trace_slavio_timer_mem_writel_counter_invalid(); } break; case TIMER_COUNTER_NORST: // set limit without resetting counter t->limit = val & TIMER_MAX_COUNT32; if (t->limit == 0) { /* free-run */ ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0); } else { ptimer_set_limit(t->timer, LIMIT_TO_PERIODS(t->limit), 0); } break; case TIMER_STATUS: if (slavio_timer_is_user(tc)) { // start/stop user counter if ((val & 1) && !t->running) { trace_slavio_timer_mem_writel_status_start(timer_index); ptimer_run(t->timer, 0); t->running = 1; } else if (!(val & 1) && t->running) { trace_slavio_timer_mem_writel_status_stop(timer_index); ptimer_stop(t->timer); t->running = 0; } } break; case TIMER_MODE: if (timer_index == 0) { unsigned int i; for (i = 0; i < s->num_cpus; i++) { unsigned int processor = 1 << i; CPUTimerState *curr_timer = &s->cputimer[i + 1]; // check for a change in timer mode for this processor if ((val & processor) != (s->cputimer_mode & processor)) { if (val & processor) { // counter -> user timer qemu_irq_lower(curr_timer->irq); // counters are always running ptimer_stop(curr_timer->timer); curr_timer->running = 0; // user timer limit is always the same curr_timer->limit = TIMER_MAX_COUNT64; ptimer_set_limit(curr_timer->timer, LIMIT_TO_PERIODS(curr_timer->limit), 1); // set this processors user timer bit in config // register s->cputimer_mode |= processor; trace_slavio_timer_mem_writel_mode_user(timer_index); } else { // user timer -> counter // stop the user timer if it is running if (curr_timer->running) { ptimer_stop(curr_timer->timer); } // start the counter ptimer_run(curr_timer->timer, 0); curr_timer->running = 1; // clear this processors user timer bit in config // register s->cputimer_mode &= ~processor; trace_slavio_timer_mem_writel_mode_counter(timer_index); } } } } else { trace_slavio_timer_mem_writel_mode_invalid(); } break; default: trace_slavio_timer_mem_writel_invalid(addr); break; }
static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { SLAVIO_TIMERState *s = opaque; uint32_t saddr; DPRINTF("write " TARGET_FMT_plx " %08x\n", addr, val); saddr = addr >> 2; switch (saddr) { case TIMER_LIMIT: if (slavio_timer_is_user(s)) { uint64_t count; // set user counter MSW, reset counter s->limit = TIMER_MAX_COUNT64; s->counthigh = val & (TIMER_MAX_COUNT64 >> 32); s->reached = 0; count = ((uint64_t)s->counthigh << 32) | s->count; DPRINTF("processor %d user timer set to %016llx\n", s->slave_index, count); if (s->timer) ptimer_set_count(s->timer, LIMIT_TO_PERIODS(s->limit - count)); } else { // set limit, reset counter qemu_irq_lower(s->irq); s->limit = val & TIMER_MAX_COUNT32; if (s->timer) { if (s->limit == 0) /* free-run */ ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 1); else ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(s->limit), 1); } } break; case TIMER_COUNTER: if (slavio_timer_is_user(s)) { uint64_t count; // set user counter LSW, reset counter s->limit = TIMER_MAX_COUNT64; s->count = val & TIMER_MAX_COUNT64; s->reached = 0; count = ((uint64_t)s->counthigh) << 32 | s->count; DPRINTF("processor %d user timer set to %016llx\n", s->slave_index, count); if (s->timer) ptimer_set_count(s->timer, LIMIT_TO_PERIODS(s->limit - count)); } else DPRINTF("not user timer\n"); break; case TIMER_COUNTER_NORST: // set limit without resetting counter s->limit = val & TIMER_MAX_COUNT32; if (s->timer) { if (s->limit == 0) /* free-run */ ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(TIMER_MAX_COUNT32), 0); else ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(s->limit), 0); } break; case TIMER_STATUS: if (slavio_timer_is_user(s)) { // start/stop user counter if ((val & 1) && !s->running) { DPRINTF("processor %d user timer started\n", s->slave_index); if (s->timer) ptimer_run(s->timer, 0); s->running = 1; } else if (!(val & 1) && s->running) { DPRINTF("processor %d user timer stopped\n", s->slave_index); if (s->timer) ptimer_stop(s->timer); s->running = 0; } } break; case TIMER_MODE: if (s->master == NULL) { unsigned int i; for (i = 0; i < s->num_slaves; i++) { unsigned int processor = 1 << i; // check for a change in timer mode for this processor if ((val & processor) != (s->slave_mode & processor)) { if (val & processor) { // counter -> user timer qemu_irq_lower(s->slave[i]->irq); // counters are always running ptimer_stop(s->slave[i]->timer); s->slave[i]->running = 0; // user timer limit is always the same s->slave[i]->limit = TIMER_MAX_COUNT64; ptimer_set_limit(s->slave[i]->timer, LIMIT_TO_PERIODS(s->slave[i]->limit), 1); // set this processors user timer bit in config // register s->slave_mode |= processor; DPRINTF("processor %d changed from counter to user " "timer\n", s->slave[i]->slave_index); } else { // user timer -> counter // stop the user timer if it is running if (s->slave[i]->running) ptimer_stop(s->slave[i]->timer); // start the counter ptimer_run(s->slave[i]->timer, 0); s->slave[i]->running = 1; // clear this processors user timer bit in config // register s->slave_mode &= ~processor; DPRINTF("processor %d changed from user timer to " "counter\n", s->slave[i]->slave_index); } } } } else DPRINTF("not system timer\n"); break; default: DPRINTF("invalid write address " TARGET_FMT_plx "\n", addr); break; }
static void imx_epit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXEPITState *s = IMX_EPIT(opaque); uint32_t reg = offset >> 2; uint64_t oldcr; DPRINTF("(%s, value = 0x%08x)\n", imx_epit_reg_name(reg), (uint32_t)value); switch (reg) { case 0: /* CR */ oldcr = s->cr; s->cr = value & 0x03ffffff; if (s->cr & CR_SWR) { /* handle the reset */ imx_epit_reset(DEVICE(s)); } else { imx_epit_set_freq(s); } if (s->freq && (s->cr & CR_EN) && !(oldcr & CR_EN)) { if (s->cr & CR_ENMOD) { if (s->cr & CR_RLD) { ptimer_set_limit(s->timer_reload, s->lr, 1); ptimer_set_limit(s->timer_cmp, s->lr, 1); } else { ptimer_set_limit(s->timer_reload, EPIT_TIMER_MAX, 1); ptimer_set_limit(s->timer_cmp, EPIT_TIMER_MAX, 1); } } imx_epit_reload_compare_timer(s); ptimer_run(s->timer_reload, 0); if (s->cr & CR_OCIEN) { ptimer_run(s->timer_cmp, 0); } else { ptimer_stop(s->timer_cmp); } } else if (!(s->cr & CR_EN)) { /* stop both timers */ ptimer_stop(s->timer_reload); ptimer_stop(s->timer_cmp); } else if (s->cr & CR_OCIEN) { if (!(oldcr & CR_OCIEN)) { imx_epit_reload_compare_timer(s); ptimer_run(s->timer_cmp, 0); } } else { ptimer_stop(s->timer_cmp); } break; case 1: /* SR - ACK*/ /* writing 1 to OCIF clear the OCIF bit */ if (value & 0x01) { s->sr = 0; imx_epit_update_int(s); } break; case 2: /* LR - set ticks */ s->lr = value; if (s->cr & CR_RLD) { /* Also set the limit if the LRD bit is set */ /* If IOVW bit is set then set the timer value */ ptimer_set_limit(s->timer_reload, s->lr, s->cr & CR_IOVW); ptimer_set_limit(s->timer_cmp, s->lr, 0); } else if (s->cr & CR_IOVW) { /* If IOVW bit is set then set the timer value */ ptimer_set_count(s->timer_reload, s->lr); } imx_epit_reload_compare_timer(s); break; case 3: /* CMP */ s->cmp = value; imx_epit_reload_compare_timer(s); break; default: IPRINTF("Bad offset %x\n", reg); break; } }
static void a10_pit_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { AwA10PITState *s = AW_A10_PIT(opaque); uint8_t index; switch (offset) { case AW_A10_PIT_TIMER_IRQ_EN: s->irq_enable = value; a10_pit_update_irq(s); break; case AW_A10_PIT_TIMER_IRQ_ST: s->irq_status &= ~value; a10_pit_update_irq(s); break; case AW_A10_PIT_TIMER_BASE ... AW_A10_PIT_TIMER_BASE_END: index = offset & 0xf0; index >>= 4; index -= 1; switch (offset & 0x0f) { case AW_A10_PIT_TIMER_CONTROL: s->control[index] = value; a10_pit_set_freq(s, index); if (s->control[index] & AW_A10_PIT_TIMER_RELOAD) { ptimer_set_count(s->timer[index], s->interval[index]); } if (s->control[index] & AW_A10_PIT_TIMER_EN) { int oneshot = 0; if (s->control[index] & AW_A10_PIT_TIMER_MODE) { oneshot = 1; } ptimer_run(s->timer[index], oneshot); } else { ptimer_stop(s->timer[index]); } break; case AW_A10_PIT_TIMER_INTERVAL: s->interval[index] = value; ptimer_set_limit(s->timer[index], s->interval[index], 1); break; case AW_A10_PIT_TIMER_COUNT: s->count[index] = value; break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); } break; case AW_A10_PIT_WDOG_CONTROL: s->watch_dog_control = value; break; case AW_A10_PIT_WDOG_MODE: s->watch_dog_mode = value; break; case AW_A10_PIT_COUNT_LO: s->count_lo = value; break; case AW_A10_PIT_COUNT_HI: s->count_hi = value; break; case AW_A10_PIT_COUNT_CTL: s->count_ctl = value; if (s->count_ctl & AW_A10_PIT_COUNT_RL_EN) { uint64_t tmp_count = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); s->count_lo = tmp_count; s->count_hi = tmp_count >> 32; s->count_ctl &= ~AW_A10_PIT_COUNT_RL_EN; } if (s->count_ctl & AW_A10_PIT_COUNT_CLR_EN) { s->count_lo = 0; s->count_hi = 0; s->count_ctl &= ~AW_A10_PIT_COUNT_CLR_EN; } break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%x\n", __func__, (int)offset); break; }
static void imx_gpt_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { IMXGPTState *s = IMX_GPT(opaque); uint32_t oldreg; uint32_t reg = offset >> 2; DPRINTF("(%s, value = 0x%08x)\n", imx_gpt_reg_name(reg), (uint32_t)value); switch (reg) { case 0: oldreg = s->cr; s->cr = value & ~0x7c14; if (s->cr & GPT_CR_SWR) { /* force reset */ /* handle the reset */ imx_gpt_reset(DEVICE(s)); } else { /* set our freq, as the source might have changed */ imx_gpt_set_freq(s); if ((oldreg ^ s->cr) & GPT_CR_EN) { if (s->cr & GPT_CR_EN) { if (s->cr & GPT_CR_ENMOD) { s->next_timeout = GPT_TIMER_MAX; ptimer_set_count(s->timer, GPT_TIMER_MAX); imx_gpt_compute_next_timeout(s, false); } ptimer_run(s->timer, 1); } else { /* stop timer */ ptimer_stop(s->timer); } } } break; case 1: /* Prescaler */ s->pr = value & 0xfff; imx_gpt_set_freq(s); break; case 2: /* SR */ s->sr &= ~(value & 0x3f); imx_gpt_update_int(s); break; case 3: /* IR -- interrupt register */ s->ir = value & 0x3f; imx_gpt_update_int(s); imx_gpt_compute_next_timeout(s, false); break; case 4: /* OCR1 -- output compare register */ s->ocr1 = value; /* In non-freerun mode, reset count when this register is written */ if (!(s->cr & GPT_CR_FRR)) { s->next_timeout = GPT_TIMER_MAX; ptimer_set_limit(s->timer, GPT_TIMER_MAX, 1); } /* compute the new timeout */ imx_gpt_compute_next_timeout(s, false); break; case 5: /* OCR2 -- output compare register */ s->ocr2 = value; /* compute the new timeout */ imx_gpt_compute_next_timeout(s, false); break; case 6: /* OCR3 -- output compare register */ s->ocr3 = value; /* compute the new timeout */ imx_gpt_compute_next_timeout(s, false); break; default: IPRINTF("Bad offset %x\n", reg); break; } }