diff mbox

[v6,07/24] hw/arm: add Faraday FTWDT010 watchdog timer support

Message ID 1362554857-3896-8-git-send-email-dantesu@gmail.com
State New
Headers show

Commit Message

Kuo-Jung Su March 6, 2013, 7:27 a.m. UTC
The FTWDT010 is used to prevent system from infinite loop
while software gets trapped in the deadlock.

Under the normal operation, users should restart FTWDT010
at the regular intervals before counter counts down to 0.

If the counter does reach 0, FTWDT010 will try to reset
the system by generating one or a combination of signals,
system reset, system interrupt, and external interrupt.

Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
---
 hw/arm/Makefile.objs      |    1 +
 hw/arm/faraday_a369_soc.c |   23 +++++
 hw/arm/ftwdt010.c         |  212 +++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/ftwdt010.h         |   35 ++++++++
 4 files changed, 271 insertions(+)
 create mode 100644 hw/arm/ftwdt010.c
 create mode 100644 hw/arm/ftwdt010.h

Comments

Paolo Bonzini March 6, 2013, 8:22 a.m. UTC | #1
Il 06/03/2013 08:27, Kuo-Jung Su ha scritto:
> The FTWDT010 is used to prevent system from infinite loop
> while software gets trapped in the deadlock.
> 
> Under the normal operation, users should restart FTWDT010
> at the regular intervals before counter counts down to 0.
> 
> If the counter does reach 0, FTWDT010 will try to reset
> the system by generating one or a combination of signals,
> system reset, system interrupt, and external interrupt.
> 
> Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
> ---
>  hw/arm/Makefile.objs      |    1 +
>  hw/arm/faraday_a369_soc.c |   23 +++++
>  hw/arm/ftwdt010.c         |  212 +++++++++++++++++++++++++++++++++++++++++++++
>  hw/arm/ftwdt010.h         |   35 ++++++++
>  4 files changed, 271 insertions(+)
>  create mode 100644 hw/arm/ftwdt010.c
>  create mode 100644 hw/arm/ftwdt010.h
> 
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 2190edd..bc8e2de 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -41,3 +41,4 @@ obj-y += ftintc020.o
>  obj-y += ftahbc020.o
>  obj-y += ftddrii030.o
>  obj-y += ftpwmtmr010.o
> +obj-y += ftwdt010.o
> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
> index 66d9891..1bf64d4 100644
> --- a/hw/arm/faraday_a369_soc.c
> +++ b/hw/arm/faraday_a369_soc.c
> @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds)
>  }
>  
>  static void
> +a369soc_system_reset(void *opaque)
> +{
> +    FaradaySoCState *s = FARADAY_SOC(opaque);
> +
> +    if (s->scu) {
> +        device_reset(s->scu);
> +    }
> +    if (s->ddrc) {
> +        device_reset(s->ddrc);
> +    }
> +    if (s->ahbc) {
> +        device_reset(s->ahbc);
> +    }
> +    if (s->cpu) {
> +        cpu_reset(CPU(s->cpu));
> +    }
> +}

Why is this needed?  Aren't they called already by

    qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());

?

>  a369soc_device_init(FaradaySoCState *s)
>  {
>      DriveInfo *dinfo;
> @@ -166,6 +185,10 @@ a369soc_device_init(FaradaySoCState *s)
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]);
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]);
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]);
> +
> +    /* ftwdt010 */
> +    sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]);
> +    qemu_register_reset(a369soc_system_reset, s);
>  }
>  
>  static int a369soc_init(SysBusDevice *dev)
> diff --git a/hw/arm/ftwdt010.c b/hw/arm/ftwdt010.c
> new file mode 100644
> index 0000000..361f16e
> --- /dev/null
> +++ b/hw/arm/ftwdt010.c
> @@ -0,0 +1,212 @@
> +/*
> + * QEMU model of the FTWDT010 WatchDog Timer
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + */
> +
> +#include "hw/sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "qemu/timer.h"
> +
> +#include "faraday.h"
> +#include "ftwdt010.h"
> +
> +#define TYPE_FTWDT010   "ftwdt010"
> +
> +typedef struct Ftwdt010State {
> +    SysBusDevice busdev;
> +    MemoryRegion mmio;
> +
> +    qemu_irq irq;
> +
> +    QEMUTimer *qtimer;
> +
> +    uint64_t timeout;
> +    uint64_t freq;        /* desired source clock */
> +    uint64_t step;        /* get_ticks_per_sec() / freq */
> +    bool running;
> +
> +    /* HW register cache */
> +    uint32_t load;
> +    uint32_t cr;
> +    uint32_t sr;
> +} Ftwdt010State;
> +
> +#define FTWDT010(obj) \
> +    OBJECT_CHECK(Ftwdt010State, obj, TYPE_FTWDT010)
> +
> +static uint64_t
> +ftwdt010_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftwdt010State *s = FTWDT010(opaque);
> +    uint32_t ret = 0;
> +
> +    switch (addr) {
> +    case REG_COUNTER:
> +        if (s->cr & CR_EN) {
> +            ret = s->timeout - qemu_get_clock_ms(rt_clock);
> +            ret = MIN(s->load, ret * 1000000ULL / s->step);
> +        } else {
> +            ret = s->load;
> +        }
> +        break;
> +    case REG_LOAD:
> +        return s->load;
> +    case REG_CR:
> +        return s->cr;
> +    case REG_SR:
> +        return s->sr;
> +    case REG_REVR:
> +        return 0x00010601;  /* rev. 1.6.1 */
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void
> +ftwdt010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
> +{
> +    Ftwdt010State *s = FTWDT010(opaque);
> +
> +    switch (addr) {
> +    case REG_LOAD:
> +        s->load = (uint32_t)val;
> +        break;
> +    case REG_RESTART:
> +        if ((s->cr & CR_EN) && (val == WDT_MAGIC)) {
> +            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
> +            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
> +            qemu_mod_timer(s->qtimer, s->timeout);
> +        }
> +        break;
> +    case REG_CR:
> +        s->cr = (uint32_t)val;
> +        if (s->cr & CR_EN) {
> +            if (s->running) {
> +                break;
> +            }
> +            s->running = true;
> +            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
> +            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
> +            qemu_mod_timer(s->qtimer, s->timeout);
> +        } else {
> +            s->running = false;
> +            qemu_del_timer(s->qtimer);
> +        }
> +        break;
> +    case REG_SCR:
> +        s->sr &= ~(uint32_t)val;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftwdt010_mem_read,
> +    .write = ftwdt010_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftwdt010_timer_tick(void *opaque)
> +{
> +    Ftwdt010State *s = FTWDT010(opaque);
> +
> +    s->sr = SR_SRST;
> +
> +    /* send interrupt signal */
> +    qemu_set_irq(s->irq, (s->cr & CR_INTR) ? 1 : 0);
> +
> +    /* send system reset */
> +    if (s->cr & CR_SRST) {
> +        qemu_system_reset_request();

Please use watchdog_perform_action() instead.

Paolo

> +    }
> +}
> +
> +static void ftwdt010_reset(DeviceState *ds)
> +{
> +    Ftwdt010State *s = FTWDT010(SYS_BUS_DEVICE(ds));
> +
> +    s->cr      = 0;
> +    s->sr      = 0;
> +    s->load    = 0x3ef1480;
> +    s->timeout = 0;
> +}
> +
> +static int ftwdt010_init(SysBusDevice *dev)
> +{
> +    Ftwdt010State *s = FTWDT010(dev);
> +
> +    s->step = (uint64_t)get_ticks_per_sec() / s->freq;
> +    s->qtimer = qemu_new_timer_ms(rt_clock, ftwdt010_timer_tick, s);
> +
> +    memory_region_init_io(&s->mmio,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTWDT010,
> +                          0x1000);
> +    sysbus_init_mmio(dev, &s->mmio);
> +    sysbus_init_irq(dev, &s->irq);
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_ftwdt010 = {
> +    .name = TYPE_FTWDT010,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT64(timeout, Ftwdt010State),
> +        VMSTATE_UINT64(freq, Ftwdt010State),
> +        VMSTATE_UINT64(step, Ftwdt010State),
> +        VMSTATE_UINT32(load, Ftwdt010State),
> +        VMSTATE_UINT32(cr, Ftwdt010State),
> +        VMSTATE_UINT32(sr, Ftwdt010State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property ftwdt010_properties[] = {
> +    DEFINE_PROP_UINT64("freq", Ftwdt010State, freq, 66000000ULL),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void ftwdt010_class_init(ObjectClass *klass, void *data)
> +{
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    k->init     = ftwdt010_init;
> +    dc->vmsd    = &vmstate_ftwdt010;
> +    dc->props   = ftwdt010_properties;
> +    dc->reset   = ftwdt010_reset;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftwdt010_info = {
> +    .name           = TYPE_FTWDT010,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(Ftwdt010State),
> +    .class_init     = ftwdt010_class_init,
> +};
> +
> +static void ftwdt010_register_types(void)
> +{
> +    type_register_static(&ftwdt010_info);
> +}
> +
> +type_init(ftwdt010_register_types)
> diff --git a/hw/arm/ftwdt010.h b/hw/arm/ftwdt010.h
> new file mode 100644
> index 0000000..46c0871
> --- /dev/null
> +++ b/hw/arm/ftwdt010.h
> @@ -0,0 +1,35 @@
> +/*
> + * QEMU model of the FTWDT010 WatchDog Timer
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <dantesu@faraday-tech.com>
> + *
> + * This file is licensed under GNU GPL v2+.
> + */
> +
> +#ifndef HW_ARM_FTWDT010_H
> +#define HW_ARM_FTWDT010_H
> +
> +#include "qemu/bitops.h"
> +
> +/* Hardware registers */
> +#define REG_COUNTER     0x00    /* counter register */
> +#define REG_LOAD        0x04    /* (re)load register */
> +#define REG_RESTART     0x08    /* restart register */
> +#define REG_CR          0x0C    /* control register */
> +#define REG_SR          0x10    /* status register */
> +#define REG_SCR         0x14    /* status clear register */
> +#define REG_INTR_LEN    0x18    /* interrupt length register */
> +#define REG_REVR        0x1C    /* revision register */
> +
> +#define CR_CLKS         BIT(4)  /* clock source */
> +#define CR_ESIG         BIT(3)  /* external signal enabled */
> +#define CR_INTR         BIT(2)  /* system reset interrupt enabled */
> +#define CR_SRST         BIT(1)  /* system reset enabled */
> +#define CR_EN           BIT(0)  /* chip enabled */
> +
> +#define SR_SRST         BIT(1)  /* system reset */
> +
> +#define WDT_MAGIC       0x5ab9  /* magic for watchdog restart */
> +
> +#endif
>
Kuo-Jung Su March 6, 2013, 8:56 a.m. UTC | #2
2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
> Il 06/03/2013 08:27, Kuo-Jung Su ha scritto:
>> The FTWDT010 is used to prevent system from infinite loop
>> while software gets trapped in the deadlock.
>>
>> Under the normal operation, users should restart FTWDT010
>> at the regular intervals before counter counts down to 0.
>>
>> If the counter does reach 0, FTWDT010 will try to reset
>> the system by generating one or a combination of signals,
>> system reset, system interrupt, and external interrupt.
>>
>> Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
>> ---
>>  hw/arm/Makefile.objs      |    1 +
>>  hw/arm/faraday_a369_soc.c |   23 +++++
>>  hw/arm/ftwdt010.c         |  212 +++++++++++++++++++++++++++++++++++++++++++++
>>  hw/arm/ftwdt010.h         |   35 ++++++++
>>  4 files changed, 271 insertions(+)
>>  create mode 100644 hw/arm/ftwdt010.c
>>  create mode 100644 hw/arm/ftwdt010.h
>>
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 2190edd..bc8e2de 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -41,3 +41,4 @@ obj-y += ftintc020.o
>>  obj-y += ftahbc020.o
>>  obj-y += ftddrii030.o
>>  obj-y += ftpwmtmr010.o
>> +obj-y += ftwdt010.o
>> diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
>> index 66d9891..1bf64d4 100644
>> --- a/hw/arm/faraday_a369_soc.c
>> +++ b/hw/arm/faraday_a369_soc.c
>> @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds)
>>  }
>>
>>  static void
>> +a369soc_system_reset(void *opaque)
>> +{
>> +    FaradaySoCState *s = FARADAY_SOC(opaque);
>> +
>> +    if (s->scu) {
>> +        device_reset(s->scu);
>> +    }
>> +    if (s->ddrc) {
>> +        device_reset(s->ddrc);
>> +    }
>> +    if (s->ahbc) {
>> +        device_reset(s->ahbc);
>> +    }
>> +    if (s->cpu) {
>> +        cpu_reset(CPU(s->cpu));
>> +    }
>> +}
>
> Why is this needed?  Aren't they called already by
>
>     qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
>
> ?
>

It doesn't work while running under ROM mode. ( no -kernel )
Because Faraday SoC Platform usually designed to boot from ROM and
followed by an AHB remapping process (i.e. remap ROM/RAM address).

>>  a369soc_device_init(FaradaySoCState *s)
>>  {
>>      DriveInfo *dinfo;
>> @@ -166,6 +185,10 @@ a369soc_device_init(FaradaySoCState *s)
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]);
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]);
>>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]);
>> +
>> +    /* ftwdt010 */
>> +    sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]);
>> +    qemu_register_reset(a369soc_system_reset, s);
>>  }
>>
>>  static int a369soc_init(SysBusDevice *dev)
>> diff --git a/hw/arm/ftwdt010.c b/hw/arm/ftwdt010.c
>> new file mode 100644
>> index 0000000..361f16e
>> --- /dev/null
>> +++ b/hw/arm/ftwdt010.c
>> @@ -0,0 +1,212 @@
>> +/*
>> + * QEMU model of the FTWDT010 WatchDog Timer
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +#include "hw/sysbus.h"
>> +#include "sysemu/sysemu.h"
>> +#include "qemu/timer.h"
>> +
>> +#include "faraday.h"
>> +#include "ftwdt010.h"
>> +
>> +#define TYPE_FTWDT010   "ftwdt010"
>> +
>> +typedef struct Ftwdt010State {
>> +    SysBusDevice busdev;
>> +    MemoryRegion mmio;
>> +
>> +    qemu_irq irq;
>> +
>> +    QEMUTimer *qtimer;
>> +
>> +    uint64_t timeout;
>> +    uint64_t freq;        /* desired source clock */
>> +    uint64_t step;        /* get_ticks_per_sec() / freq */
>> +    bool running;
>> +
>> +    /* HW register cache */
>> +    uint32_t load;
>> +    uint32_t cr;
>> +    uint32_t sr;
>> +} Ftwdt010State;
>> +
>> +#define FTWDT010(obj) \
>> +    OBJECT_CHECK(Ftwdt010State, obj, TYPE_FTWDT010)
>> +
>> +static uint64_t
>> +ftwdt010_mem_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    Ftwdt010State *s = FTWDT010(opaque);
>> +    uint32_t ret = 0;
>> +
>> +    switch (addr) {
>> +    case REG_COUNTER:
>> +        if (s->cr & CR_EN) {
>> +            ret = s->timeout - qemu_get_clock_ms(rt_clock);
>> +            ret = MIN(s->load, ret * 1000000ULL / s->step);
>> +        } else {
>> +            ret = s->load;
>> +        }
>> +        break;
>> +    case REG_LOAD:
>> +        return s->load;
>> +    case REG_CR:
>> +        return s->cr;
>> +    case REG_SR:
>> +        return s->sr;
>> +    case REG_REVR:
>> +        return 0x00010601;  /* rev. 1.6.1 */
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void
>> +ftwdt010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
>> +{
>> +    Ftwdt010State *s = FTWDT010(opaque);
>> +
>> +    switch (addr) {
>> +    case REG_LOAD:
>> +        s->load = (uint32_t)val;
>> +        break;
>> +    case REG_RESTART:
>> +        if ((s->cr & CR_EN) && (val == WDT_MAGIC)) {
>> +            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
>> +            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
>> +            qemu_mod_timer(s->qtimer, s->timeout);
>> +        }
>> +        break;
>> +    case REG_CR:
>> +        s->cr = (uint32_t)val;
>> +        if (s->cr & CR_EN) {
>> +            if (s->running) {
>> +                break;
>> +            }
>> +            s->running = true;
>> +            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
>> +            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
>> +            qemu_mod_timer(s->qtimer, s->timeout);
>> +        } else {
>> +            s->running = false;
>> +            qemu_del_timer(s->qtimer);
>> +        }
>> +        break;
>> +    case REG_SCR:
>> +        s->sr &= ~(uint32_t)val;
>> +        break;
>> +    default:
>> +        qemu_log_mask(LOG_GUEST_ERROR,
>> +            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps mmio_ops = {
>> +    .read  = ftwdt010_mem_read,
>> +    .write = ftwdt010_mem_write,
>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 4,
>> +        .max_access_size = 4
>> +    }
>> +};
>> +
>> +static void ftwdt010_timer_tick(void *opaque)
>> +{
>> +    Ftwdt010State *s = FTWDT010(opaque);
>> +
>> +    s->sr = SR_SRST;
>> +
>> +    /* send interrupt signal */
>> +    qemu_set_irq(s->irq, (s->cr & CR_INTR) ? 1 : 0);
>> +
>> +    /* send system reset */
>> +    if (s->cr & CR_SRST) {
>> +        qemu_system_reset_request();
>
> Please use watchdog_perform_action() instead.
>
> Paolo
>

Got it, thanks

>> +    }
>> +}
>> +
>> +static void ftwdt010_reset(DeviceState *ds)
>> +{
>> +    Ftwdt010State *s = FTWDT010(SYS_BUS_DEVICE(ds));
>> +
>> +    s->cr      = 0;
>> +    s->sr      = 0;
>> +    s->load    = 0x3ef1480;
>> +    s->timeout = 0;
>> +}
>> +
>> +static int ftwdt010_init(SysBusDevice *dev)
>> +{
>> +    Ftwdt010State *s = FTWDT010(dev);
>> +
>> +    s->step = (uint64_t)get_ticks_per_sec() / s->freq;
>> +    s->qtimer = qemu_new_timer_ms(rt_clock, ftwdt010_timer_tick, s);
>> +
>> +    memory_region_init_io(&s->mmio,
>> +                          &mmio_ops,
>> +                          s,
>> +                          TYPE_FTWDT010,
>> +                          0x1000);
>> +    sysbus_init_mmio(dev, &s->mmio);
>> +    sysbus_init_irq(dev, &s->irq);
>> +
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_ftwdt010 = {
>> +    .name = TYPE_FTWDT010,
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .minimum_version_id_old = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT64(timeout, Ftwdt010State),
>> +        VMSTATE_UINT64(freq, Ftwdt010State),
>> +        VMSTATE_UINT64(step, Ftwdt010State),
>> +        VMSTATE_UINT32(load, Ftwdt010State),
>> +        VMSTATE_UINT32(cr, Ftwdt010State),
>> +        VMSTATE_UINT32(sr, Ftwdt010State),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property ftwdt010_properties[] = {
>> +    DEFINE_PROP_UINT64("freq", Ftwdt010State, freq, 66000000ULL),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void ftwdt010_class_init(ObjectClass *klass, void *data)
>> +{
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +
>> +    k->init     = ftwdt010_init;
>> +    dc->vmsd    = &vmstate_ftwdt010;
>> +    dc->props   = ftwdt010_properties;
>> +    dc->reset   = ftwdt010_reset;
>> +    dc->no_user = 1;
>> +}
>> +
>> +static const TypeInfo ftwdt010_info = {
>> +    .name           = TYPE_FTWDT010,
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(Ftwdt010State),
>> +    .class_init     = ftwdt010_class_init,
>> +};
>> +
>> +static void ftwdt010_register_types(void)
>> +{
>> +    type_register_static(&ftwdt010_info);
>> +}
>> +
>> +type_init(ftwdt010_register_types)
>> diff --git a/hw/arm/ftwdt010.h b/hw/arm/ftwdt010.h
>> new file mode 100644
>> index 0000000..46c0871
>> --- /dev/null
>> +++ b/hw/arm/ftwdt010.h
>> @@ -0,0 +1,35 @@
>> +/*
>> + * QEMU model of the FTWDT010 WatchDog Timer
>> + *
>> + * Copyright (C) 2012 Faraday Technology
>> + * Written by Dante Su <dantesu@faraday-tech.com>
>> + *
>> + * This file is licensed under GNU GPL v2+.
>> + */
>> +
>> +#ifndef HW_ARM_FTWDT010_H
>> +#define HW_ARM_FTWDT010_H
>> +
>> +#include "qemu/bitops.h"
>> +
>> +/* Hardware registers */
>> +#define REG_COUNTER     0x00    /* counter register */
>> +#define REG_LOAD        0x04    /* (re)load register */
>> +#define REG_RESTART     0x08    /* restart register */
>> +#define REG_CR          0x0C    /* control register */
>> +#define REG_SR          0x10    /* status register */
>> +#define REG_SCR         0x14    /* status clear register */
>> +#define REG_INTR_LEN    0x18    /* interrupt length register */
>> +#define REG_REVR        0x1C    /* revision register */
>> +
>> +#define CR_CLKS         BIT(4)  /* clock source */
>> +#define CR_ESIG         BIT(3)  /* external signal enabled */
>> +#define CR_INTR         BIT(2)  /* system reset interrupt enabled */
>> +#define CR_SRST         BIT(1)  /* system reset enabled */
>> +#define CR_EN           BIT(0)  /* chip enabled */
>> +
>> +#define SR_SRST         BIT(1)  /* system reset */
>> +
>> +#define WDT_MAGIC       0x5ab9  /* magic for watchdog restart */
>> +
>> +#endif
>>
>



--
Best wishes,
Kuo-Jung Su
Paolo Bonzini March 6, 2013, 10:46 a.m. UTC | #3
> > > It doesn't work while running under ROM mode. ( no -kernel )
> > > Because Faraday SoC Platform usually designed to boot from ROM and
> > > followed by an AHB remapping process (i.e. remap ROM/RAM address).
> >
> > What doesn't work exactly?  Why aren't these called?  Or are
> > you forcing a particular reset order?
> >
> > Paolo
> 
> While booting from ROM, the faraday soc usually remap the ROM / RAM
> before jumping into linux.
> 
> In other words,  the system mapping is:
> 
> 1. Power-On:
> 
>     ROM: 0x00000000
>     RAM: N/A
>     SRAM: 0xA0000000
> 
> 2. AHB Remap (u-boot/linux)
> 
>     ROM: 0x20000000
>     RAM: 0x00000000
> 
> So I have to register my own reset handler to
> 
> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
> 2. Reset CPU

----- Messaggio originale -----
> Da: "Kuo-Jung Su" <dantesu@gmail.com>
> A: "Paolo Bonzini" <pbonzini@redhat.com>
> Inviato: Mercoledì, 6 marzo 2013 11:00:49
> Oggetto: Re: [PATCH v6 07/24] hw/arm: add Faraday FTWDT010 watchdog timer support
> 
> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
> >
> >> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
> >> > Il 06/03/2013 08:27, Kuo-Jung Su ha scritto:
> >> >> The FTWDT010 is used to prevent system from infinite loop
> >> >> while software gets trapped in the deadlock.
> >> >>
> >> >> Under the normal operation, users should restart FTWDT010
> >> >> at the regular intervals before counter counts down to 0.
> >> >>
> >> >> If the counter does reach 0, FTWDT010 will try to reset
> >> >> the system by generating one or a combination of signals,
> >> >> system reset, system interrupt, and external interrupt.
> >> >>
> >> >> Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
> >> >> ---
> >> >>  hw/arm/Makefile.objs      |    1 +
> >> >>  hw/arm/faraday_a369_soc.c |   23 +++++
> >> >>  hw/arm/ftwdt010.c         |  212
> >> >>  +++++++++++++++++++++++++++++++++++++++++++++
> >> >>  hw/arm/ftwdt010.h         |   35 ++++++++
> >> >>  4 files changed, 271 insertions(+)
> >> >>  create mode 100644 hw/arm/ftwdt010.c
> >> >>  create mode 100644 hw/arm/ftwdt010.h
> >> >>
> >> >> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> >> >> index 2190edd..bc8e2de 100644
> >> >> --- a/hw/arm/Makefile.objs
> >> >> +++ b/hw/arm/Makefile.objs
> >> >> @@ -41,3 +41,4 @@ obj-y += ftintc020.o
> >> >>  obj-y += ftahbc020.o
> >> >>  obj-y += ftddrii030.o
> >> >>  obj-y += ftpwmtmr010.o
> >> >> +obj-y += ftwdt010.o
> >> >> diff --git a/hw/arm/faraday_a369_soc.c
> >> >> b/hw/arm/faraday_a369_soc.c
> >> >> index 66d9891..1bf64d4 100644
> >> >> --- a/hw/arm/faraday_a369_soc.c
> >> >> +++ b/hw/arm/faraday_a369_soc.c
> >> >> @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds)
> >> >>  }
> >> >>
> >> >>  static void
> >> >> +a369soc_system_reset(void *opaque)
> >> >> +{
> >> >> +    FaradaySoCState *s = FARADAY_SOC(opaque);
> >> >> +
> >> >> +    if (s->scu) {
> >> >> +        device_reset(s->scu);
> >> >> +    }
> >> >> +    if (s->ddrc) {
> >> >> +        device_reset(s->ddrc);
> >> >> +    }
> >> >> +    if (s->ahbc) {
> >> >> +        device_reset(s->ahbc);
> >> >> +    }
> >> >> +    if (s->cpu) {
> >> >> +        cpu_reset(CPU(s->cpu));
> >> >> +    }
> >> >> +}
> >> >
> >> > Why is this needed?  Aren't they called already by
> >> >
> >> >     qemu_register_reset(qbus_reset_all_fn,
> >> >     sysbus_get_default());
> >> >
> >> > ?
> >> >
> >>
> >> It doesn't work while running under ROM mode. ( no -kernel )
> >> Because Faraday SoC Platform usually designed to boot from ROM and
> >> followed by an AHB remapping process (i.e. remap ROM/RAM address).
> >
> > What doesn't work exactly?  Why aren't these called?  Or are
> > you forcing a particular reset order?
> >
> > Paolo
> 
> While booting from ROM, the faraday soc usually remap the ROM / RAM
> before jumping into linux.
> 
> In other words,  the system mapping is:
> 
> 1. Power-On:
> 
>     ROM: 0x00000000
>     RAM: N/A
>     SRAM: 0xA0000000
> 
> 2. AHB Remap (u-boot/linux)
> 
>     ROM: 0x20000000
>     RAM: 0x00000000
> 
> So I have to register my own reset handler to
> 
> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
> 2. Reset CPU

I understand that.  What I'm missing is, why these reset handlers aren't
called anyway when QEMU does qemu_devices_reset(), for example from
qemu_system_reset().

Also, I do not understand who performs this again:

+        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
+        /* 1. Remap RAM to base of ROM */
+        s->ram_base = s->ahb_slave[4] & 0xfff00000;
+        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
+        /* 2. Remap ROM to base of ROM + size of RAM */
+        s->rom_base = s->ram_base
+                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
+        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);

when you do a system_reset and the board was started in kernel mode.

Paolo
Kuo-Jung Su March 7, 2013, 2:44 a.m. UTC | #4
2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>> > > It doesn't work while running under ROM mode. ( no -kernel )
>> > > Because Faraday SoC Platform usually designed to boot from ROM and
>> > > followed by an AHB remapping process (i.e. remap ROM/RAM address).
>> >
>> > What doesn't work exactly?  Why aren't these called?  Or are
>> > you forcing a particular reset order?
>> >
>> > Paolo
>>
>> While booting from ROM, the faraday soc usually remap the ROM / RAM
>> before jumping into linux.
>>
>> In other words,  the system mapping is:
>>
>> 1. Power-On:
>>
>>     ROM: 0x00000000
>>     RAM: N/A
>>     SRAM: 0xA0000000
>>
>> 2. AHB Remap (u-boot/linux)
>>
>>     ROM: 0x20000000
>>     RAM: 0x00000000
>>
>> So I have to register my own reset handler to
>>
>> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
>> 2. Reset CPU
>
> ----- Messaggio originale -----
>> Da: "Kuo-Jung Su" <dantesu@gmail.com>
>> A: "Paolo Bonzini" <pbonzini@redhat.com>
>> Inviato: Mercoledì, 6 marzo 2013 11:00:49
>> Oggetto: Re: [PATCH v6 07/24] hw/arm: add Faraday FTWDT010 watchdog timer support
>>
>> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>> >
>> >> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>> >> > Il 06/03/2013 08:27, Kuo-Jung Su ha scritto:
>> >> >> The FTWDT010 is used to prevent system from infinite loop
>> >> >> while software gets trapped in the deadlock.
>> >> >>
>> >> >> Under the normal operation, users should restart FTWDT010
>> >> >> at the regular intervals before counter counts down to 0.
>> >> >>
>> >> >> If the counter does reach 0, FTWDT010 will try to reset
>> >> >> the system by generating one or a combination of signals,
>> >> >> system reset, system interrupt, and external interrupt.
>> >> >>
>> >> >> Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
>> >> >> ---
>> >> >>  hw/arm/Makefile.objs      |    1 +
>> >> >>  hw/arm/faraday_a369_soc.c |   23 +++++
>> >> >>  hw/arm/ftwdt010.c         |  212
>> >> >>  +++++++++++++++++++++++++++++++++++++++++++++
>> >> >>  hw/arm/ftwdt010.h         |   35 ++++++++
>> >> >>  4 files changed, 271 insertions(+)
>> >> >>  create mode 100644 hw/arm/ftwdt010.c
>> >> >>  create mode 100644 hw/arm/ftwdt010.h
>> >> >>
>> >> >> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> >> >> index 2190edd..bc8e2de 100644
>> >> >> --- a/hw/arm/Makefile.objs
>> >> >> +++ b/hw/arm/Makefile.objs
>> >> >> @@ -41,3 +41,4 @@ obj-y += ftintc020.o
>> >> >>  obj-y += ftahbc020.o
>> >> >>  obj-y += ftddrii030.o
>> >> >>  obj-y += ftpwmtmr010.o
>> >> >> +obj-y += ftwdt010.o
>> >> >> diff --git a/hw/arm/faraday_a369_soc.c
>> >> >> b/hw/arm/faraday_a369_soc.c
>> >> >> index 66d9891..1bf64d4 100644
>> >> >> --- a/hw/arm/faraday_a369_soc.c
>> >> >> +++ b/hw/arm/faraday_a369_soc.c
>> >> >> @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds)
>> >> >>  }
>> >> >>
>> >> >>  static void
>> >> >> +a369soc_system_reset(void *opaque)
>> >> >> +{
>> >> >> +    FaradaySoCState *s = FARADAY_SOC(opaque);
>> >> >> +
>> >> >> +    if (s->scu) {
>> >> >> +        device_reset(s->scu);
>> >> >> +    }
>> >> >> +    if (s->ddrc) {
>> >> >> +        device_reset(s->ddrc);
>> >> >> +    }
>> >> >> +    if (s->ahbc) {
>> >> >> +        device_reset(s->ahbc);
>> >> >> +    }
>> >> >> +    if (s->cpu) {
>> >> >> +        cpu_reset(CPU(s->cpu));
>> >> >> +    }
>> >> >> +}
>> >> >
>> >> > Why is this needed?  Aren't they called already by
>> >> >
>> >> >     qemu_register_reset(qbus_reset_all_fn,
>> >> >     sysbus_get_default());
>> >> >
>> >> > ?
>> >> >
>> >>
>> >> It doesn't work while running under ROM mode. ( no -kernel )
>> >> Because Faraday SoC Platform usually designed to boot from ROM and
>> >> followed by an AHB remapping process (i.e. remap ROM/RAM address).
>> >
>> > What doesn't work exactly?  Why aren't these called?  Or are
>> > you forcing a particular reset order?
>> >
>> > Paolo
>>
>> While booting from ROM, the faraday soc usually remap the ROM / RAM
>> before jumping into linux.
>>
>> In other words,  the system mapping is:
>>
>> 1. Power-On:
>>
>>     ROM: 0x00000000
>>     RAM: N/A
>>     SRAM: 0xA0000000
>>
>> 2. AHB Remap (u-boot/linux)
>>
>>     ROM: 0x20000000
>>     RAM: 0x00000000
>>
>> So I have to register my own reset handler to
>>
>> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
>> 2. Reset CPU
>
> I understand that.  What I'm missing is, why these reset handlers aren't
> called anyway when QEMU does qemu_devices_reset(), for example from
> qemu_system_reset().
>

1. arm_cpu_reset() is never be invoked from the default reset handler.

2. Undo ROM/RAM remap would make bus become unstable there is no
    guaranty when the bus would be stabilized.
    (It looks to me that it's true for both QEMU and real hardware)

3. In QEMU, my guess is without arm_cpu_reset(), when the AHB remap process
    activated upon watchdog counts down to zero.
    The PC (r15) could be anywhere, and so does the stack & bss.
    Which means the CPU would likely suffers from UNKNOWN INSTRUCTION,
    DATA ABORT... etc.

> Also, I do not understand who performs this again:
>

It's performed at my ROM code:

https://github.com/dantesu1218/virgil/blob/master/arch/mach-a369/board.c

line 200 ~ 220

#ifndef CONFIG_SKIP_SYSINIT
/* Skip AHB remap if the jump address is not inside SDRAM address space */
"ldr r4, =0x10000000\n"
"cmp %2, r4\n"
"movhs pc, %2\n" /* jump without AHB remapped */
/* AHB remap
* REG32(CONFIG_IOBASE_DDR + 0x10) &= 0x00FFFFFF
* REG32(CONFIG_IOBASE_AHB + 0x88)  = 0x00100F01
*/
"ldr r4, [%0, #0x10]\n"
"bic r4, #0xff000000\n"
"ldr r5, =0x00100f01\n"
".ALIGN 5\n" /* Make sure all the following codes to be fetched in a
single 32-bytes cache line */
"str r4, [%0, #0x10]\n" /* REG32(CONFIG_IOBASE_DDR + 0x10) &= 0x00FFFFFF */
"str r5, [%1, #0x88]\n" /* REG32(CONFIG_IOBASE_AHB + 0x88)  = 0x00100F01 */
"5:\n"
"ldr r4, [%2, #0]\n" /* while(magic != REG32(addr)) */
"teq r4, %3\n"
"bne 5b\n"
#endif
"mov pc, %2\n" /* jump */


> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
> +        /* 1. Remap RAM to base of ROM */
> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
> +        /* 2. Remap ROM to base of ROM + size of RAM */
> +        s->rom_base = s->ram_base
> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
>
> when you do a system_reset and the board was started in kernel mode.
>

The problem is not about the kernel mode. In my words, I call them:

1. RAM Mode Emulation (Launching QEMU with -kernel) :
    The emulator would assume that the SDRAM has been remapped to
    0x00000000.

2. ROM Mode Emulation (Without -kernel):
    The emulator would map the 6KB ROM (pflash) to 0x00000000, 32KB SRAM
    to 0xA0000000, but no SDRAM

P.S:

In A369, the the boot sequence of ROM mode is:

1. Power-on
2. ROM code execution on ROM @ 0x00000000
3. 2nd bootstrap on SRAM @ 0xA0000000
4. General software (Linux/U-boot) execution on SDRAM @ 0x00000000

In newer design, (from 2010, after my referenced ROM code is reay)
the the boot sequence of ROM mode becomes:

1. Power-on
2. ROM code execution on ROM @ 0x00000000
3. General software (Linux/U-boot) execution on SDRAM @ 0x00000000

Currently I have a kw9505 QEMU model ready for this,  and since it's a
customer ASIC,
it shall never be merged into open source.
But if you want, I could provide the kw9505.c with lots of coding style issues
for function test.

> Paolo



--
Best wishes,
Kuo-Jung Su
Kuo-Jung Su March 7, 2013, 5:53 a.m. UTC | #5
2013/3/7 Kuo-Jung Su <dantesu@gmail.com>:
> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>>> > > It doesn't work while running under ROM mode. ( no -kernel )
>>> > > Because Faraday SoC Platform usually designed to boot from ROM and
>>> > > followed by an AHB remapping process (i.e. remap ROM/RAM address).
>>> >
>>> > What doesn't work exactly?  Why aren't these called?  Or are
>>> > you forcing a particular reset order?
>>> >
>>> > Paolo
>>>
>>> While booting from ROM, the faraday soc usually remap the ROM / RAM
>>> before jumping into linux.
>>>
>>> In other words,  the system mapping is:
>>>
>>> 1. Power-On:
>>>
>>>     ROM: 0x00000000
>>>     RAM: N/A
>>>     SRAM: 0xA0000000
>>>
>>> 2. AHB Remap (u-boot/linux)
>>>
>>>     ROM: 0x20000000
>>>     RAM: 0x00000000
>>>
>>> So I have to register my own reset handler to
>>>
>>> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
>>> 2. Reset CPU
>>
>> ----- Messaggio originale -----
>>> Da: "Kuo-Jung Su" <dantesu@gmail.com>
>>> A: "Paolo Bonzini" <pbonzini@redhat.com>
>>> Inviato: Mercoledì, 6 marzo 2013 11:00:49
>>> Oggetto: Re: [PATCH v6 07/24] hw/arm: add Faraday FTWDT010 watchdog timer support
>>>
>>> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>>> >
>>> >> 2013/3/6 Paolo Bonzini <pbonzini@redhat.com>:
>>> >> > Il 06/03/2013 08:27, Kuo-Jung Su ha scritto:
>>> >> >> The FTWDT010 is used to prevent system from infinite loop
>>> >> >> while software gets trapped in the deadlock.
>>> >> >>
>>> >> >> Under the normal operation, users should restart FTWDT010
>>> >> >> at the regular intervals before counter counts down to 0.
>>> >> >>
>>> >> >> If the counter does reach 0, FTWDT010 will try to reset
>>> >> >> the system by generating one or a combination of signals,
>>> >> >> system reset, system interrupt, and external interrupt.
>>> >> >>
>>> >> >> Signed-off-by: Kuo-Jung Su <dantesu@gmail.com>
>>> >> >> ---
>>> >> >>  hw/arm/Makefile.objs      |    1 +
>>> >> >>  hw/arm/faraday_a369_soc.c |   23 +++++
>>> >> >>  hw/arm/ftwdt010.c         |  212
>>> >> >>  +++++++++++++++++++++++++++++++++++++++++++++
>>> >> >>  hw/arm/ftwdt010.h         |   35 ++++++++
>>> >> >>  4 files changed, 271 insertions(+)
>>> >> >>  create mode 100644 hw/arm/ftwdt010.c
>>> >> >>  create mode 100644 hw/arm/ftwdt010.h
>>> >> >>
>>> >> >> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>>> >> >> index 2190edd..bc8e2de 100644
>>> >> >> --- a/hw/arm/Makefile.objs
>>> >> >> +++ b/hw/arm/Makefile.objs
>>> >> >> @@ -41,3 +41,4 @@ obj-y += ftintc020.o
>>> >> >>  obj-y += ftahbc020.o
>>> >> >>  obj-y += ftddrii030.o
>>> >> >>  obj-y += ftpwmtmr010.o
>>> >> >> +obj-y += ftwdt010.o
>>> >> >> diff --git a/hw/arm/faraday_a369_soc.c
>>> >> >> b/hw/arm/faraday_a369_soc.c
>>> >> >> index 66d9891..1bf64d4 100644
>>> >> >> --- a/hw/arm/faraday_a369_soc.c
>>> >> >> +++ b/hw/arm/faraday_a369_soc.c
>>> >> >> @@ -68,6 +68,25 @@ static void a369soc_reset(DeviceState *ds)
>>> >> >>  }
>>> >> >>
>>> >> >>  static void
>>> >> >> +a369soc_system_reset(void *opaque)
>>> >> >> +{
>>> >> >> +    FaradaySoCState *s = FARADAY_SOC(opaque);
>>> >> >> +
>>> >> >> +    if (s->scu) {
>>> >> >> +        device_reset(s->scu);
>>> >> >> +    }
>>> >> >> +    if (s->ddrc) {
>>> >> >> +        device_reset(s->ddrc);
>>> >> >> +    }
>>> >> >> +    if (s->ahbc) {
>>> >> >> +        device_reset(s->ahbc);
>>> >> >> +    }
>>> >> >> +    if (s->cpu) {
>>> >> >> +        cpu_reset(CPU(s->cpu));
>>> >> >> +    }
>>> >> >> +}
>>> >> >
>>> >> > Why is this needed?  Aren't they called already by
>>> >> >
>>> >> >     qemu_register_reset(qbus_reset_all_fn,
>>> >> >     sysbus_get_default());
>>> >> >
>>> >> > ?
>>> >> >
>>> >>
>>> >> It doesn't work while running under ROM mode. ( no -kernel )
>>> >> Because Faraday SoC Platform usually designed to boot from ROM and
>>> >> followed by an AHB remapping process (i.e. remap ROM/RAM address).
>>> >
>>> > What doesn't work exactly?  Why aren't these called?  Or are
>>> > you forcing a particular reset order?
>>> >
>>> > Paolo
>>>
>>> While booting from ROM, the faraday soc usually remap the ROM / RAM
>>> before jumping into linux.
>>>
>>> In other words,  the system mapping is:
>>>
>>> 1. Power-On:
>>>
>>>     ROM: 0x00000000
>>>     RAM: N/A
>>>     SRAM: 0xA0000000
>>>
>>> 2. AHB Remap (u-boot/linux)
>>>
>>>     ROM: 0x20000000
>>>     RAM: 0x00000000
>>>
>>> So I have to register my own reset handler to
>>>
>>> 1. Undo the ROM/RAM remap (i.e. device_reset(s->ahbc))
>>> 2. Reset CPU
>>
>> I understand that.  What I'm missing is, why these reset handlers aren't
>> called anyway when QEMU does qemu_devices_reset(), for example from
>> qemu_system_reset().
>>
>
> 1. arm_cpu_reset() is never be invoked from the default reset handler.
>
> 2. Undo ROM/RAM remap would make bus become unstable there is no
>     guaranty when the bus would be stabilized.
>     (It looks to me that it's true for both QEMU and real hardware)
>
> 3. In QEMU, my guess is without arm_cpu_reset(), when the AHB remap process
>     activated upon watchdog counts down to zero.
>     The PC (r15) could be anywhere, and so does the stack & bss.
>     Which means the CPU would likely suffers from UNKNOWN INSTRUCTION,
>     DATA ABORT... etc.
>
>> Also, I do not understand who performs this again:
>>
>
> It's performed at my ROM code:
>
> https://github.com/dantesu1218/virgil/blob/master/arch/mach-a369/board.c
>
> line 200 ~ 220
>
> #ifndef CONFIG_SKIP_SYSINIT
> /* Skip AHB remap if the jump address is not inside SDRAM address space */
> "ldr r4, =0x10000000\n"
> "cmp %2, r4\n"
> "movhs pc, %2\n" /* jump without AHB remapped */
> /* AHB remap
> * REG32(CONFIG_IOBASE_DDR + 0x10) &= 0x00FFFFFF
> * REG32(CONFIG_IOBASE_AHB + 0x88)  = 0x00100F01
> */
> "ldr r4, [%0, #0x10]\n"
> "bic r4, #0xff000000\n"
> "ldr r5, =0x00100f01\n"
> ".ALIGN 5\n" /* Make sure all the following codes to be fetched in a
> single 32-bytes cache line */
> "str r4, [%0, #0x10]\n" /* REG32(CONFIG_IOBASE_DDR + 0x10) &= 0x00FFFFFF */
> "str r5, [%1, #0x88]\n" /* REG32(CONFIG_IOBASE_AHB + 0x88)  = 0x00100F01 */
> "5:\n"
> "ldr r4, [%2, #0]\n" /* while(magic != REG32(addr)) */
> "teq r4, %3\n"
> "bne 5b\n"
> #endif
> "mov pc, %2\n" /* jump */
>
>

Sorry, if I get you wrong, and what you're actually asking is:
Where or which module is responsible for SDRAM init and AHB remap?

The answer is:

1. ftahbc020: It's responsible for AHB remap, please check this:

   static void ftahbc020_mem_write()
   {
        ......
        if (!soc->ahb_remapped && (s->cr & CR_REMAP)) {
            /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
            /* 1. Remap RAM to base of ROM */
            soc->ram_base = soc->ahb_slave[4] & 0xfff00000;
            /* 2. Remap ROM to base of ROM + size of RAM */
            soc->rom_base = soc->ram_base
                          + ((1 << extract32(soc->ahb_slave[6], 16, 4)) << 20);
            /* 3. Update ROM memory map */
            sysbus_mmio_map(SYS_BUS_DEVICE(soc->rom), 0, soc->rom_base);
            /* 4. Update RAM memory map if it has been initialized. */
            if (soc->ddr_inited) {
                memory_region_del_subregion(soc->as, soc->ram);
                memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
            }
            soc->ahb_remapped = true;
        }
        ......
   }

2. ftddrii030: It's responsible for SDRAM init, please check this:

static void ftddrii030_mem_write()
{
    ......
        if (!soc->ddr_inited && (val & MSR_CMD_MRS)) {
            val &= ~MSR_CMD_MRS;
            val |= MSR_INIT_OK;
            memory_region_add_subregion(soc->as, soc->ram_base, soc->ram);
            soc->ddr_inited = true;
        }
    ......
}

>> +        /* Remap AHB slave 4 (ROM) & slave 6 (RAM) */
>> +        /* 1. Remap RAM to base of ROM */
>> +        s->ram_base = s->ahb_slave[4] & 0xfff00000;
>> +        s->ahb_slave[6] = s->ram_base | (s->ahb_slave[6] & 0x000f0000);
>> +        /* 2. Remap ROM to base of ROM + size of RAM */
>> +        s->rom_base = s->ram_base
>> +                    + ((1 << extract32(s->ahb_slave[6], 16, 4)) << 20);
>> +        s->ahb_slave[4] = s->rom_base | (s->ahb_slave[4] & 0x000f0000);
>>
>> when you do a system_reset and the board was started in kernel mode.
>>
>
> The problem is not about the kernel mode. In my words, I call them:
>
> 1. RAM Mode Emulation (Launching QEMU with -kernel) :
>     The emulator would assume that the SDRAM has been remapped to
>     0x00000000.
>
> 2. ROM Mode Emulation (Without -kernel):
>     The emulator would map the 6KB ROM (pflash) to 0x00000000, 32KB SRAM
>     to 0xA0000000, but no SDRAM
>
> P.S:
>
> In A369, the the boot sequence of ROM mode is:
>
> 1. Power-on
> 2. ROM code execution on ROM @ 0x00000000
> 3. 2nd bootstrap on SRAM @ 0xA0000000
> 4. General software (Linux/U-boot) execution on SDRAM @ 0x00000000
>
> In newer design, (from 2010, after my referenced ROM code is reay)
> the the boot sequence of ROM mode becomes:
>
> 1. Power-on
> 2. ROM code execution on ROM @ 0x00000000
> 3. General software (Linux/U-boot) execution on SDRAM @ 0x00000000
>
> Currently I have a kw9505 QEMU model ready for this,  and since it's a
> customer ASIC,
> it shall never be merged into open source.
> But if you want, I could provide the kw9505.c with lots of coding style issues
> for function test.
>
>> Paolo
>
>
>
> --
> Best wishes,
> Kuo-Jung Su



--
Best wishes,
Kuo-Jung Su
diff mbox

Patch

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2190edd..bc8e2de 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -41,3 +41,4 @@  obj-y += ftintc020.o
 obj-y += ftahbc020.o
 obj-y += ftddrii030.o
 obj-y += ftpwmtmr010.o
+obj-y += ftwdt010.o
diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
index 66d9891..1bf64d4 100644
--- a/hw/arm/faraday_a369_soc.c
+++ b/hw/arm/faraday_a369_soc.c
@@ -68,6 +68,25 @@  static void a369soc_reset(DeviceState *ds)
 }
 
 static void
+a369soc_system_reset(void *opaque)
+{
+    FaradaySoCState *s = FARADAY_SOC(opaque);
+
+    if (s->scu) {
+        device_reset(s->scu);
+    }
+    if (s->ddrc) {
+        device_reset(s->ddrc);
+    }
+    if (s->ahbc) {
+        device_reset(s->ahbc);
+    }
+    if (s->cpu) {
+        cpu_reset(CPU(s->cpu));
+    }
+}
+
+static void
 a369soc_device_init(FaradaySoCState *s)
 {
     DriveInfo *dinfo;
@@ -166,6 +185,10 @@  a369soc_device_init(FaradaySoCState *s)
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 1, s->pic[9]);
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 2, s->pic[10]);
     sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[11]);
+
+    /* ftwdt010 */
+    sysbus_create_simple("ftwdt010", 0x92200000, s->pic[46]);
+    qemu_register_reset(a369soc_system_reset, s);
 }
 
 static int a369soc_init(SysBusDevice *dev)
diff --git a/hw/arm/ftwdt010.c b/hw/arm/ftwdt010.c
new file mode 100644
index 0000000..361f16e
--- /dev/null
+++ b/hw/arm/ftwdt010.c
@@ -0,0 +1,212 @@ 
+/*
+ * QEMU model of the FTWDT010 WatchDog Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+
+#include "faraday.h"
+#include "ftwdt010.h"
+
+#define TYPE_FTWDT010   "ftwdt010"
+
+typedef struct Ftwdt010State {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+
+    QEMUTimer *qtimer;
+
+    uint64_t timeout;
+    uint64_t freq;        /* desired source clock */
+    uint64_t step;        /* get_ticks_per_sec() / freq */
+    bool running;
+
+    /* HW register cache */
+    uint32_t load;
+    uint32_t cr;
+    uint32_t sr;
+} Ftwdt010State;
+
+#define FTWDT010(obj) \
+    OBJECT_CHECK(Ftwdt010State, obj, TYPE_FTWDT010)
+
+static uint64_t
+ftwdt010_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+    uint32_t ret = 0;
+
+    switch (addr) {
+    case REG_COUNTER:
+        if (s->cr & CR_EN) {
+            ret = s->timeout - qemu_get_clock_ms(rt_clock);
+            ret = MIN(s->load, ret * 1000000ULL / s->step);
+        } else {
+            ret = s->load;
+        }
+        break;
+    case REG_LOAD:
+        return s->load;
+    case REG_CR:
+        return s->cr;
+    case REG_SR:
+        return s->sr;
+    case REG_REVR:
+        return 0x00010601;  /* rev. 1.6.1 */
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+
+    return ret;
+}
+
+static void
+ftwdt010_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+
+    switch (addr) {
+    case REG_LOAD:
+        s->load = (uint32_t)val;
+        break;
+    case REG_RESTART:
+        if ((s->cr & CR_EN) && (val == WDT_MAGIC)) {
+            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
+            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
+            qemu_mod_timer(s->qtimer, s->timeout);
+        }
+        break;
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        if (s->cr & CR_EN) {
+            if (s->running) {
+                break;
+            }
+            s->running = true;
+            s->timeout = (s->step * (uint64_t)s->load) / 1000000ULL;
+            s->timeout = qemu_get_clock_ms(rt_clock) + MAX(s->timeout, 1);
+            qemu_mod_timer(s->qtimer, s->timeout);
+        } else {
+            s->running = false;
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_SCR:
+        s->sr &= ~(uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftwdt010: undefined memory access@%#" HWADDR_PRIx "\n", addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps mmio_ops = {
+    .read  = ftwdt010_mem_read,
+    .write = ftwdt010_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftwdt010_timer_tick(void *opaque)
+{
+    Ftwdt010State *s = FTWDT010(opaque);
+
+    s->sr = SR_SRST;
+
+    /* send interrupt signal */
+    qemu_set_irq(s->irq, (s->cr & CR_INTR) ? 1 : 0);
+
+    /* send system reset */
+    if (s->cr & CR_SRST) {
+        qemu_system_reset_request();
+    }
+}
+
+static void ftwdt010_reset(DeviceState *ds)
+{
+    Ftwdt010State *s = FTWDT010(SYS_BUS_DEVICE(ds));
+
+    s->cr      = 0;
+    s->sr      = 0;
+    s->load    = 0x3ef1480;
+    s->timeout = 0;
+}
+
+static int ftwdt010_init(SysBusDevice *dev)
+{
+    Ftwdt010State *s = FTWDT010(dev);
+
+    s->step = (uint64_t)get_ticks_per_sec() / s->freq;
+    s->qtimer = qemu_new_timer_ms(rt_clock, ftwdt010_timer_tick, s);
+
+    memory_region_init_io(&s->mmio,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTWDT010,
+                          0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftwdt010 = {
+    .name = TYPE_FTWDT010,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT64(timeout, Ftwdt010State),
+        VMSTATE_UINT64(freq, Ftwdt010State),
+        VMSTATE_UINT64(step, Ftwdt010State),
+        VMSTATE_UINT32(load, Ftwdt010State),
+        VMSTATE_UINT32(cr, Ftwdt010State),
+        VMSTATE_UINT32(sr, Ftwdt010State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftwdt010_properties[] = {
+    DEFINE_PROP_UINT64("freq", Ftwdt010State, freq, 66000000ULL),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftwdt010_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init     = ftwdt010_init;
+    dc->vmsd    = &vmstate_ftwdt010;
+    dc->props   = ftwdt010_properties;
+    dc->reset   = ftwdt010_reset;
+    dc->no_user = 1;
+}
+
+static const TypeInfo ftwdt010_info = {
+    .name           = TYPE_FTWDT010,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(Ftwdt010State),
+    .class_init     = ftwdt010_class_init,
+};
+
+static void ftwdt010_register_types(void)
+{
+    type_register_static(&ftwdt010_info);
+}
+
+type_init(ftwdt010_register_types)
diff --git a/hw/arm/ftwdt010.h b/hw/arm/ftwdt010.h
new file mode 100644
index 0000000..46c0871
--- /dev/null
+++ b/hw/arm/ftwdt010.h
@@ -0,0 +1,35 @@ 
+/*
+ * QEMU model of the FTWDT010 WatchDog Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ */
+
+#ifndef HW_ARM_FTWDT010_H
+#define HW_ARM_FTWDT010_H
+
+#include "qemu/bitops.h"
+
+/* Hardware registers */
+#define REG_COUNTER     0x00    /* counter register */
+#define REG_LOAD        0x04    /* (re)load register */
+#define REG_RESTART     0x08    /* restart register */
+#define REG_CR          0x0C    /* control register */
+#define REG_SR          0x10    /* status register */
+#define REG_SCR         0x14    /* status clear register */
+#define REG_INTR_LEN    0x18    /* interrupt length register */
+#define REG_REVR        0x1C    /* revision register */
+
+#define CR_CLKS         BIT(4)  /* clock source */
+#define CR_ESIG         BIT(3)  /* external signal enabled */
+#define CR_INTR         BIT(2)  /* system reset interrupt enabled */
+#define CR_SRST         BIT(1)  /* system reset enabled */
+#define CR_EN           BIT(0)  /* chip enabled */
+
+#define SR_SRST         BIT(1)  /* system reset */
+
+#define WDT_MAGIC       0x5ab9  /* magic for watchdog restart */
+
+#endif