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: Jean-Christophe DUBOIS <jcd@tribudubois.net>
Message-id: 1365624982-15647-1-git-send-email-jcd@tribudubois.net
Reviewed-by: Peter Chubb <peter.chubb@nicta.com.au>
[PMM: wrapped an overly long line]
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Jean-Christophe DUBOIS 2013-05-03 18:21:02 +01:00 committed by Peter Maydell
parent 743bddb4b3
commit 462566fc5e
1 changed files with 222 additions and 88 deletions

View File

@ -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,56 +482,13 @@ 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 imx_timerp_reset(DeviceState *dev)
{
IMXTimerPState *s = container_of(dev, IMXTimerPState, busdev.qdev);
s->cr = 0;
s->lr = TIMER_MAX;
s->int_level = 0;
s->cmp = 0;
ptimer_stop(s->timer);
ptimer_set_count(s->timer, TIMER_MAX);
}
static uint64_t imx_timerp_read(void *opaque, hwaddr offset,
unsigned size)
{
IMXTimerPState *s = (IMXTimerPState *)opaque;
DPRINTF("p-read(offset=%x)", offset >> 2);
switch (offset >> 2) {
case 0: /* Control Register */
DPRINTF("cr %x\n", s->cr);
return s->cr;
case 1: /* Status Register */
DPRINTF("int_level %x\n", s->int_level);
return s->int_level;
case 2: /* LR - ticks*/
DPRINTF("lr %x\n", s->lr);
return s->lr;
case 3: /* CMP */
DPRINTF("cmp %x\n", s->cmp);
return s->cmp;
case 4: /* CNT */
return ptimer_get_count(s->timer);
}
IPRINTF("imx_timerp_read: Bad offset %x\n",
(int)offset >> 2);
return 0;
}
static void set_timerp_freq(IMXTimerPState *s)
{
int clksrc;
@ -513,7 +503,91 @@ static void set_timerp_freq(IMXTimerPState *s)
DPRINTF("Setting ptimer frequency to %u\n", freq);
if (freq) {
ptimer_set_freq(s->timer, 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);
/*
* 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->cmp = 0;
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,
unsigned size)
{
IMXTimerPState *s = (IMXTimerPState *)opaque;
DPRINTF("p-read(offset=%x)", offset >> 2);
switch (offset >> 2) {
case 0: /* Control Register */
DPRINTF("cr %x\n", s->cr);
return s->cr;
case 1: /* Status Register */
DPRINTF("sr %x\n", s->sr);
return s->sr;
case 2: /* LR - ticks*/
DPRINTF("lr %x\n", s->lr);
return s->lr;
case 3: /* CMP */
DPRINTF("cmp %x\n", s->cmp);
return s->cmp;
case 4: /* CNT */
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 imx_reload_compare_timer(IMXTimerPState *s)
{
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;
set_timerp_freq(s);
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;
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 +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;
}
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 +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;
}