/* * Copyright (C) 2010 Tobias Klauser * Copyright (C) 2010 chysun2000@gmail.com * * This file is part of nios2sim-ng. * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include #include #include #include "nios2sim-ng.h" #include "device.h" #include "timer.h" struct timer { struct io_register regs[TIMER_REG_COUNT]; uint32_t set_period; uint32_t curr_count; }; static const uint32_t timer_valid_mask[TIMER_REG_COUNT] = { 0x3, 0xF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF }; static const uint32_t timer_read_only_mask[TIMER_REG_COUNT] = { 0x2, 0, 0, 0, 0, 0 }; static int timer_init(struct device *dev) { unsigned int i; struct timer *t = malloc(sizeof(struct timer)); struct io_register *regs; if (unlikely(t == NULL)) return -1; /* Initialite registers */ regs = t->regs; for (i = 0; i < TIMER_REG_COUNT; i++) { io_register_init(®s[i], TIMER_BASE + REG_TO_OFF(i), timer_valid_mask[i], timer_read_only_mask[i], 0); } t->set_period = 0; t->curr_count = 0; dev->priv = t; return 0; } static void update_period_reg(struct timer *t) { t->regs[TIMER_PERIODL_REG].value = t->curr_count & 0xFFFF; t->regs[TIMER_PERIODH_REG].value = (t->curr_count & 0xFFFF0000) >> 16; //copy_snapshot(); } #if 0 static void copy_snapshot(struct timer *t) { t->regs[TIMER_SNAPL_REG].value = t->regs[TIMER_PERIODL_REG].value; t->regs[TIMER_SNAPH_REG].value = t->regs[TIMER_PERIODH_REG].value; } static uint32_t timer_read(struct device *dev, uint32_t addr, uint32_t data_len) { struct timer *t = dev->priv; uint32_t ret_val = 0; uint32_t index = 0; index = (addr - dev->base) / 4; if (index >= TIMER_REG_COUNT) return 0; if (index == TIMER_SNAPL_REG || index == TIMER_SNAPH_REG) copy_snapshot(t); ret_val = io_register_read(&t->regs[index]); ret_val = io_read_data(ret_val, data_len); return ret_val; } static void timer_write(struct device *dev, uint32_t addr, uint32_t data, size_t count) { struct timer *t = dev->priv; struct io_register *regs; uint32_t index = 0; uint32_t temp = 0; uint32_t only_read_mask = 0; uint32_t valid_mask = 0; index = (addr - dev->base) / 4; if (index >= TIMER_REG_COUNT) return; temp = regs[index].value; valid_mask = regs[index].valid_mask; only_read_mask = regs[index].readonly_mask; regs[index].value = io_write_data_mask(temp, data, data_len, valid_mask, only_read_mask); if (index == TIMER_PERIODL_REG){ t->set_period &= 0xFFFF0000; t->set_period |= data & 0xFFFF; t->curr_count = t->set_period; update_period_reg(t); } else if (index == TIMER_PERIODH_REG) { hw.set_period = hw.set_period & 0xFFFF; hw.set_period = hw.set_period | ((data & 0xFFFF) <<16); hw.curr_count = hw.set_period; update_period_reg(t); } else if (index == TIMER_STATUS_REG) { if ((regs[index].value & STATUS_TO_MASK) == 0) clean_ipending(dev->irq_mask); } } #endif static bool timer_has_irq(struct device *dev) { struct timer *t = dev->priv; uint32_t ctrl_val = io_register_read(&t->regs[TIMER_CTRL_REG]); uint32_t status_val = io_register_read(&t->regs[TIMER_STATUS_REG]); if ((ctrl_val & TIMER_CTRL_ITO_MASK) == TIMER_CTRL_ITO_MASK) if ((status_val & TIMER_STATUS_TO_MASK) == TIMER_STATUS_TO_MASK) return true; return false; } static void decrease_counter(struct timer *t) { if (t->curr_count > 2) t->curr_count -= 2; else t->curr_count = 0; update_period_reg(t); } static bool timer_can_decrease(struct timer *t) { uint32_t ctrl_val = t->regs[TIMER_CTRL_REG].value; /* Timer started? */ if ((ctrl_val & TIMER_CTRL_START_MASK) == 0) return false; /* Timer stopped? */ if ((ctrl_val & TIMER_CTRL_STOP_MASK) != 0) return false; /* If timer is not in continuous mode and counter has reached period */ if ((ctrl_val & TIMER_CTRL_CONT_MASK) == 0 && t->set_period == t->curr_count) return false; return true; } static void update_status(struct timer *t) { if (t->curr_count <= 0) { t->regs[TIMER_CTRL_REG].value |= TIMER_CTRL_ITO_MASK; t->curr_count = t->set_period; update_period_reg(t); t->regs[TIMER_STATUS_REG].value |= TIMER_STATUS_TO_MASK; } } static void timer_simulate(struct device *dev) { struct timer *t = dev->priv; if (timer_can_decrease(t)) { decrease_counter(t); update_status(t); } } struct device timer_core = { .name = "Timer Core", .base = TIMER_BASE, .size = TIMER_SIZE, .irq_mask = IRQ_TO_MASK(TIMER_IRQ), .init = timer_init, .is_dev_addr = NULL, /* use generic */ .has_irq = timer_has_irq, .simulate = timer_simulate, };