Commit 462566fc authored by Jean-Christophe DUBOIS's avatar Jean-Christophe DUBOIS Committed by Peter Maydell
Browse files

i.MX: implement a more correct version of EPIT timer.



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: default avatarJean-Christophe DUBOIS <jcd@tribudubois.net>
Message-id: 1365624982-15647-1-git-send-email-jcd@tribudubois.net
Reviewed-by: default avatarPeter Chubb <peter.chubb@nicta.com.au>
[PMM: wrapped an overly long line]
Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parent 743bddb4
Loading
Loading
Loading
Loading
+198 −64
Original line number Diff line number Diff line
@@ -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,15 +107,19 @@ typedef struct {

static const VMStateDescription vmstate_imx_timerg = {
    .name = "imx-timerg",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .version_id = 2,
    .minimum_version_id = 2,
    .minimum_version_id_old = 2,
    .fields      = (VMStateField[]) {
        VMSTATE_UINT32(cr, IMXTimerGState),
        VMSTATE_UINT32(pr, IMXTimerGState),
        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,20 @@ 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 +352,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 +442,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 +454,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 +482,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 +553,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 +565,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 +600,62 @@ static void imx_timerp_write(void *opaque, hwaddr offset,

    switch (offset >> 2) {
    case 0: /* CR */
        if (value & CR_SWR) {
            imx_timerp_reset(&s->busdev.qdev);
            value &= ~CR_SWR;
        }
        s->cr = value & 0x03ffffff;
        if (s->cr & CR_SWR) {
            /* handle the reset */
            imx_timerp_reset(&s->busdev.qdev);
        } else {
            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;
        /* 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 +664,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;
    }
    s->int_level = 1;

    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);
    }
}

void imx_timerp_create(const hwaddr addr,
@@ -600,16 +730,18 @@ static const MemoryRegionOps imx_timerp_ops = {

static const VMStateDescription vmstate_imx_timerp = {
    .name = "imx-timerp",
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
    .version_id = 2,
    .minimum_version_id = 2,
    .minimum_version_id_old = 2,
    .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 +752,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;
}