From patchwork Wed Apr 10 20:02:49 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jean-Christophe Dubois X-Patchwork-Id: 235463 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 86DE72C00A6 for ; Thu, 11 Apr 2013 06:03:20 +1000 (EST) Received: from localhost ([::1]:47724 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UQ1Ek-0007ly-IQ for incoming@patchwork.ozlabs.org; Wed, 10 Apr 2013 16:03:18 -0400 Received: from eggs.gnu.org ([208.118.235.92]:52544) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UQ1ER-0007iK-BG for qemu-devel@nongnu.org; Wed, 10 Apr 2013 16:03:01 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UQ1EO-00089A-Pj for qemu-devel@nongnu.org; Wed, 10 Apr 2013 16:02:59 -0400 Received: from zose-mta13.web4all.fr ([178.33.204.91]:55909) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UQ1EO-00088Z-FU for qemu-devel@nongnu.org; Wed, 10 Apr 2013 16:02:56 -0400 Received: from localhost (localhost [127.0.0.1]) by zose-mta13.web4all.fr (Postfix) with ESMTP id A78636A0EE; Wed, 10 Apr 2013 22:02:54 +0200 (CEST) X-Virus-Scanned: amavisd-new at Received: from zose-mta13.web4all.fr ([127.0.0.1]) by localhost (zose-mta13.web4all.fr [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hMlzKFU85gP3; Wed, 10 Apr 2013 22:02:54 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zose-mta13.web4all.fr (Postfix) with ESMTP id EF1D76A0F0; Wed, 10 Apr 2013 22:02:53 +0200 (CEST) X-Virus-Scanned: amavisd-new at Received: from zose-mta13.web4all.fr ([127.0.0.1]) by localhost (zose-mta13.web4all.fr [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id OZlOEYnw-sQ7; Wed, 10 Apr 2013 22:02:53 +0200 (CEST) Received: from localhost.localdomain (smm49-1-78-235-240-156.fbx.proxad.net [78.235.240.156]) by zose-mta13.web4all.fr (Postfix) with ESMTPSA id E63906A0EE; Wed, 10 Apr 2013 22:02:52 +0200 (CEST) From: Jean-Christophe DUBOIS To: qemu-devel@nongnu.org Date: Wed, 10 Apr 2013 22:02:49 +0200 Message-Id: <1365624169-15451-1-git-send-email-jcd@tribudubois.net> X-Mailer: git-send-email 1.7.10.4 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 178.33.204.91 Cc: peter.maydell@linaro.org, peter.chubb@nicta.com.au, philipo@ok-labs.com, Jean-Christophe DUBOIS Subject: [Qemu-devel] [PATCH v2] i.MX: implement a more correct version of EPIT timer. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch is providing a complete version of the EPIT timer. Note, however that the GPT timer in the same file is still not complete. Signed-off-by: Jean-Christophe DUBOIS Change from v1: - bump up the version number on VMSTATE struct - fix comment Reviewed-by: Peter Chubb --- hw/timer/imx_timer.c | 253 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 193 insertions(+), 60 deletions(-) diff --git a/hw/timer/imx_timer.c b/hw/timer/imx_timer.c index 03197e3..514a907 100644 --- a/hw/timer/imx_timer.c +++ b/hw/timer/imx_timer.c @@ -95,6 +95,10 @@ typedef struct { uint32_t sr; uint32_t ir; uint32_t ocr1; + uint32_t ocr2; + uint32_t ocr3; + uint32_t icr1; + uint32_t icr2; uint32_t cnt; uint32_t waiting_rov; @@ -103,7 +107,7 @@ typedef struct { static const VMStateDescription vmstate_imx_timerg = { .name = "imx-timerg", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { @@ -112,6 +116,10 @@ static const VMStateDescription vmstate_imx_timerg = { VMSTATE_UINT32(sr, IMXTimerGState), VMSTATE_UINT32(ir, IMXTimerGState), VMSTATE_UINT32(ocr1, IMXTimerGState), + VMSTATE_UINT32(ocr2, IMXTimerGState), + VMSTATE_UINT32(ocr3, IMXTimerGState), + VMSTATE_UINT32(icr1, IMXTimerGState), + VMSTATE_UINT32(icr2, IMXTimerGState), VMSTATE_UINT32(cnt, IMXTimerGState), VMSTATE_UINT32(waiting_rov, IMXTimerGState), VMSTATE_PTIMER(timer, IMXTimerGState), @@ -156,7 +164,6 @@ static void imx_timerg_update(IMXTimerGState *s) s->ir & GPT_SR_ROV ? "ROV" : "", s->cr & GPT_CR_EN ? "CR_EN" : "Not Enabled"); - qemu_set_irq(s->irq, (s->cr & GPT_CR_EN) && flags); } @@ -221,6 +228,21 @@ static uint64_t imx_timerg_read(void *opaque, hwaddr offset, DPRINTF(" ocr1 = %x\n", s->ocr1); return s->ocr1; + case 5: /* Output Compare Register 2 */ + DPRINTF(" ocr2 = %x\n", s->ocr2); + return s->ocr2; + + case 6: /* Output Compare Register 3 */ + DPRINTF(" ocr3 = %x\n", s->ocr3); + return s->ocr3; + + case 7: /* input Capture Register 1 */ + DPRINTF(" icr1 = %x\n", s->icr1); + return s->icr1; + + case 8: /* input Capture Register 2 */ + DPRINTF(" icr2 = %x\n", s->icr2); + return s->icr2; case 9: /* cnt */ imx_timerg_update_counts(s); @@ -230,6 +252,7 @@ static uint64_t imx_timerg_read(void *opaque, hwaddr offset, IPRINTF("imx_timerg_read: Bad offset %x\n", (int)offset >> 2); + return 0; } @@ -240,14 +263,19 @@ static void imx_timerg_reset(DeviceState *dev) /* * Soft reset doesn't touch some bits; hard reset clears them */ - s->cr &= ~(GPT_CR_EN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); + s->cr &= ~(GPT_CR_EN|GPT_CR_ENMOD|GPT_CR_STOPEN|GPT_CR_DOZEN|GPT_CR_WAITEN|GPT_CR_DBGEN); s->sr = 0; s->pr = 0; s->ir = 0; s->cnt = 0; s->ocr1 = TIMER_MAX; + s->ocr2 = TIMER_MAX; + s->ocr3 = TIMER_MAX; + s->icr1 = 0; + s->icr2 = 0; ptimer_stop(s->timer); ptimer_set_limit(s->timer, TIMER_MAX, 1); + ptimer_set_count(s->timer, TIMER_MAX); imx_timerg_set_freq(s); } @@ -323,6 +351,8 @@ static void imx_timerg_write(void *opaque, hwaddr offset, s->ocr1 = value; return; + case 5: /* OCR2 -- output compare register */ + case 6: /* OCR3 -- output compare register */ default: IPRINTF("imx_timerg_write: Bad offset %x\n", (int)offset >> 2); @@ -411,7 +441,7 @@ static int imx_timerg_init(SysBusDevice *dev) #define CR_SWR (1 << 16) #define CR_IOVW (1 << 17) #define CR_DBGEN (1 << 18) -#define CR_EPIT (1 << 19) +#define CR_WAITEN (1 << 19) #define CR_DOZEN (1 << 20) #define CR_STOPEN (1 << 21) #define CR_CLKSRC_SHIFT (24) @@ -423,24 +453,26 @@ static int imx_timerg_init(SysBusDevice *dev) * These are typical. */ static const IMXClk imx_timerp_clocks[] = { - 0, /* disabled */ - IPG, /* ipg_clk, ~532MHz */ - IPG, /* ipg_clk_highfreq */ - CLK_32k, /* ipg_clk_32k -- ~32kHz */ + 0, /* 00 disabled */ + IPG, /* 01 ipg_clk, ~532MHz */ + IPG, /* 10 ipg_clk_highfreq */ + CLK_32k, /* 11 ipg_clk_32k -- ~32kHz */ }; typedef struct { SysBusDevice busdev; - ptimer_state *timer; + ptimer_state *timer_reload; + ptimer_state *timer_cmp; MemoryRegion iomem; DeviceState *ccm; uint32_t cr; + uint32_t sr; uint32_t lr; uint32_t cmp; + uint32_t cnt; uint32_t freq; - int int_level; qemu_irq irq; } IMXTimerPState; @@ -449,23 +481,63 @@ typedef struct { */ static void imx_timerp_update(IMXTimerPState *s) { - if (s->int_level && (s->cr & CR_OCIEN)) { + if (s->sr && (s->cr & CR_OCIEN)) { qemu_irq_raise(s->irq); } else { qemu_irq_lower(s->irq); } } +static void set_timerp_freq(IMXTimerPState *s) +{ + int clksrc; + unsigned prescaler; + uint32_t freq; + + clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT; + prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK); + freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler; + + s->freq = freq; + DPRINTF("Setting ptimer frequency to %u\n", freq); + + if (freq) { + ptimer_set_freq(s->timer_reload, freq); + ptimer_set_freq(s->timer_cmp, freq); + } +} + static void imx_timerp_reset(DeviceState *dev) { IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev); - s->cr = 0; + /* + * Soft reset doesn't touch some bits; hard reset clears them + */ + s->cr &= ~(CR_EN|CR_ENMOD|CR_STOPEN|CR_DOZEN|CR_WAITEN|CR_DBGEN); + s->sr = 0; s->lr = TIMER_MAX; - s->int_level = 0; s->cmp = 0; - ptimer_stop(s->timer); - ptimer_set_count(s->timer, TIMER_MAX); + s->cnt = 0; + /* stop both timers */ + ptimer_stop(s->timer_cmp); + ptimer_stop(s->timer_reload); + /* compute new frequency */ + set_timerp_freq(s); + /* init both timers to TIMER_MAX */ + ptimer_set_limit(s->timer_cmp, TIMER_MAX, 1); + ptimer_set_limit(s->timer_reload, TIMER_MAX, 1); + if (s->freq && (s->cr & CR_EN)) { + /* if the timer is still enabled, restart it */ + ptimer_run(s->timer_reload, 1); + } +} + +static uint32_t imx_timerp_update_counts(IMXTimerPState *s) +{ + s->cnt = ptimer_get_count(s->timer_reload); + + return s->cnt; } static uint64_t imx_timerp_read(void *opaque, hwaddr offset, @@ -480,8 +552,8 @@ static uint64_t imx_timerp_read(void *opaque, hwaddr offset, return s->cr; case 1: /* Status Register */ - DPRINTF("int_level %x\n", s->int_level); - return s->int_level; + DPRINTF("sr %x\n", s->sr); + return s->sr; case 2: /* LR - ticks*/ DPRINTF("lr %x\n", s->lr); @@ -492,28 +564,29 @@ static uint64_t imx_timerp_read(void *opaque, hwaddr offset, return s->cmp; case 4: /* CNT */ - return ptimer_get_count(s->timer); + imx_timerp_update_counts(s); + DPRINTF(" cnt = %x\n", s->cnt); + return s->cnt; } + IPRINTF("imx_timerp_read: Bad offset %x\n", (int)offset >> 2); return 0; } -static void set_timerp_freq(IMXTimerPState *s) +static void imx_reload_compare_timer(IMXTimerPState *s) { - int clksrc; - unsigned prescaler; - uint32_t freq; - - clksrc = (s->cr & CR_CLKSRC_MASK) >> CR_CLKSRC_SHIFT; - prescaler = 1 + ((s->cr >> CR_PRESCALE_SHIFT) & CR_PRESCALE_MASK); - freq = imx_clock_frequency(s->ccm, imx_timerp_clocks[clksrc]) / prescaler; - - s->freq = freq; - DPRINTF("Setting ptimer frequency to %u\n", freq); - - if (freq) { - ptimer_set_freq(s->timer, freq); + if ((s->cr & CR_OCIEN) && s->cmp) { + /* if the compare feature is on */ + uint32_t tmp = imx_timerp_update_counts(s); + if (tmp > s->cmp) { + /* reinit the cmp timer if required */ + ptimer_set_count(s->timer_cmp, tmp - s->cmp); + if ((s->cr & CR_EN)) { + /* Restart the cmp timer if required */ + ptimer_run(s->timer_cmp, 0); + } + } } } @@ -526,40 +599,62 @@ static void imx_timerp_write(void *opaque, hwaddr offset, switch (offset >> 2) { case 0: /* CR */ - if (value & CR_SWR) { + s->cr = value & 0x03ffffff; + if (s->cr & CR_SWR) { + /* handle the reset */ imx_timerp_reset(&s->busdev.qdev); - value &= ~CR_SWR; + } else { + set_timerp_freq(s); } - s->cr = value & 0x03ffffff; - set_timerp_freq(s); if (s->freq && (s->cr & CR_EN)) { - if (!(s->cr & CR_ENMOD)) { - ptimer_set_count(s->timer, s->lr); + if (s->cr & CR_ENMOD) { + if (s->cr & CR_RLD) { + ptimer_set_limit(s->timer_reload, s->lr, 1); + } else { + ptimer_set_limit(s->timer_reload, TIMER_MAX, 1); + } } - ptimer_run(s->timer, 0); + + imx_reload_compare_timer(s); + + ptimer_run(s->timer_reload, 1); } else { - ptimer_stop(s->timer); + /* stop both timers */ + ptimer_stop(s->timer_reload); + ptimer_stop(s->timer_cmp); } break; case 1: /* SR - ACK*/ - s->int_level = 0; - imx_timerp_update(s); + /* writing 1 to OCIF clear the OCIF bit */ + if (value & 0x01) { + s->sr = 0; + imx_timerp_update(s); + } break; case 2: /* LR - set ticks */ s->lr = value; - ptimer_set_limit(s->timer, value, !!(s->cr & CR_IOVW)); + + 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); + } 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_reload_compare_timer(s); + break; case 3: /* CMP */ s->cmp = value; - if (value) { - IPRINTF( - "Values for EPIT comparison other than zero not supported\n" - ); - } + + imx_reload_compare_timer(s); + break; default: @@ -568,16 +663,50 @@ static void imx_timerp_write(void *opaque, hwaddr offset, } } -static void imx_timerp_tick(void *opaque) +static void imx_timerp_reload(void *opaque) { IMXTimerPState *s = (IMXTimerPState *)opaque; - DPRINTF("imxp tick\n"); - if (!(s->cr & CR_RLD)) { - ptimer_set_count(s->timer, TIMER_MAX); + DPRINTF("imxp reload\n"); + + if (!(s->cr & CR_EN)) { + return; + } + + if (s->cr & CR_RLD) { + ptimer_set_limit(s->timer_reload, s->lr, 1); + } else { + ptimer_set_limit(s->timer_reload, TIMER_MAX, 1); + } + + if (s->cr & CR_OCIEN) { + /* if compare register is 0 then we handle the interrupt here */ + if (s->cmp == 0) { + s->sr = 1; + imx_timerp_update(s); + } else if (s->cmp <= s->lr) { + /* We should launch the compare register */ + ptimer_set_count(s->timer_cmp, s->lr - s->cmp); + ptimer_run(s->timer_cmp, 0); + } else { + IPRINTF("imxp reload: s->lr < s->cmp\n"); + } + } +} + +static void imx_timerp_cmp(void *opaque) +{ + IMXTimerPState *s = (IMXTimerPState *)opaque; + + DPRINTF("imxp compare\n"); + + ptimer_stop(s->timer_cmp); + + /* compare register is not 0 */ + if (s->cmp) { + s->sr = 1; + imx_timerp_update(s); } - s->int_level = 1; - imx_timerp_update(s); } void imx_timerp_create(const hwaddr addr, @@ -600,16 +729,18 @@ static const MemoryRegionOps imx_timerp_ops = { static const VMStateDescription vmstate_imx_timerp = { .name = "imx-timerp", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(cr, IMXTimerPState), + VMSTATE_UINT32(sr, IMXTimerPState), VMSTATE_UINT32(lr, IMXTimerPState), VMSTATE_UINT32(cmp, IMXTimerPState), + VMSTATE_UINT32(cnt, IMXTimerPState), VMSTATE_UINT32(freq, IMXTimerPState), - VMSTATE_INT32(int_level, IMXTimerPState), - VMSTATE_PTIMER(timer, IMXTimerPState), + VMSTATE_PTIMER(timer_reload, IMXTimerPState), + VMSTATE_PTIMER(timer_cmp, IMXTimerPState), VMSTATE_END_OF_LIST() } }; @@ -620,15 +751,17 @@ static int imx_timerp_init(SysBusDevice *dev) QEMUBH *bh; DPRINTF("imx_timerp_init\n"); - sysbus_init_irq(dev, &s->irq); memory_region_init_io(&s->iomem, &imx_timerp_ops, s, "imxp-timer", 0x00001000); sysbus_init_mmio(dev, &s->iomem); - bh = qemu_bh_new(imx_timerp_tick, s); - s->timer = ptimer_init(bh); + bh = qemu_bh_new(imx_timerp_reload, s); + s->timer_reload = ptimer_init(bh); + + bh = qemu_bh_new(imx_timerp_cmp, s); + s->timer_cmp = ptimer_init(bh); return 0; }