diff --git a/hw/ftrtc011.c b/hw/ftrtc011.c
new file mode 100644
index 0000000..466cbb6
--- /dev/null
+++ b/hw/ftrtc011.c
@@ -0,0 +1,308 @@
+/*
+ * QEMU model of the FTRTC011 RTC Timer
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Copyright (C) 2012 Dante Su <dantesu@faraday-tech.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/timer.h"
+
+/* Hardware registers */
+#define REG_SEC            0x00
+#define REG_MIN            0x04
+#define REG_HOUR        0x08
+#define REG_DAY            0x0C
+
+#define REG_ALARM_SEC    0x10
+#define REG_ALARM_MIN    0x14
+#define REG_ALARM_HOUR    0x18
+
+#define REG_CR            0x20
+#define REG_WSEC        0x24
+#define REG_WMIN        0x28
+#define REG_WHOUR        0x2C
+#define REG_WDAY        0x30
+#define REG_ISR            0x34
+
+#define REG_REV            0x3C
+#define REG_CURRENT        0x44
+
+enum ftrtc011_irqpin {
+    IRQ_ALARM_LEVEL = 0,
+    IRQ_ALARM_EDGE,
+    IRQ_SEC,
+    IRQ_MIN,
+    IRQ_HOUR,
+    IRQ_DAY,
+};
+
+typedef struct {
+    SysBusDevice busdev;
+    MemoryRegion mmio;
+
+    qemu_irq irq[6];
+
+    QEMUTimer *qtimer;
+    
+    uint8_t sec;
+    uint8_t min;
+    uint8_t hr;
+    uint32_t day;
+    
+    uint8_t alarm_sec;
+    uint8_t alarm_min;
+    uint8_t alarm_hr;
+    
+    uint32_t cr;
+    uint32_t isr;
+    
+} ftrtc011_state;
+
+/* Update interrupts.  */
+static inline void ftrtc011_update_irq(ftrtc011_state *s)
+{
+    uint32_t mask = ((s->cr >> 1) & 0x1f) & s->isr;
+    
+    qemu_set_irq(s->irq[IRQ_ALARM_LEVEL], (mask & 0x10) ? 1 : 0);
+    
+    if (mask) {
+        if (mask & 0x01)
+            qemu_irq_pulse(s->irq[IRQ_SEC]);
+        if (mask & 0x02)
+            qemu_irq_pulse(s->irq[IRQ_MIN]);
+        if (mask & 0x04)
+            qemu_irq_pulse(s->irq[IRQ_HOUR]);
+        if (mask & 0x08)
+            qemu_irq_pulse(s->irq[IRQ_DAY]);
+        if (mask & 0x10)
+            qemu_irq_pulse(s->irq[IRQ_ALARM_EDGE]);
+    }
+}
+
+static uint64_t ftrtc011_mem_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    ftrtc011_state *s = opaque;
+    uint32_t rc = 0;
+
+    switch (addr) {
+    case REG_SEC:
+        return s->sec;
+    case REG_MIN:
+        return s->min;
+    case REG_HOUR:
+        return s->hr;
+    case REG_DAY:
+        return s->day;
+    case REG_ALARM_SEC:
+        return s->alarm_sec;
+    case REG_ALARM_MIN:
+        return s->alarm_min;
+    case REG_ALARM_HOUR:
+        return s->alarm_hr;
+    case REG_CR:
+        return s->cr;
+    case REG_ISR:
+        return s->isr;
+    case REG_REV:
+        return 0x00010000;
+    case REG_CURRENT:
+        return (s->day << 17) | (s->hr << 12) | (s->min << 6) | (s->sec);
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+static void ftrtc011_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned int size)
+{
+    ftrtc011_state *s = opaque;
+    
+    switch (addr) {
+    case REG_ALARM_SEC:
+        s->alarm_sec = (uint32_t)val;
+        break;
+    case REG_ALARM_MIN:
+        s->alarm_min = (uint32_t)val;
+        break;
+    case REG_ALARM_HOUR:
+        s->alarm_hr = (uint32_t)val;
+        break;
+    case REG_WSEC:
+        s->sec = (uint32_t)val;
+        break;
+    case REG_WMIN:
+        s->min = (uint32_t)val;
+        break;
+    case REG_WHOUR:
+        s->hr = (uint32_t)val;
+        break;
+    case REG_WDAY:
+        s->day = (uint32_t)val;
+        break;
+    case REG_CR:
+        s->cr = (uint32_t)val;
+        if (s->cr & 0x01) {
+            qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec());
+        } else {
+            qemu_del_timer(s->qtimer);
+        }
+        break;
+    case REG_ISR:
+        s->isr &= ~((uint32_t)val);
+        ftrtc011_update_irq(s);
+        break;
+    default:
+        break;
+    }
+}
+
+static const MemoryRegionOps bus_ops = {
+    .read  = ftrtc011_mem_read,
+    .write = ftrtc011_mem_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+static void ftrtc011_timer_tick(void *opaque)
+{
+    ftrtc011_state *s = (ftrtc011_state *)opaque;
+    
+    s->sec += 1;
+    if (s->cr & (1 << 1))
+        s->isr |= (1 << 0);
+    
+    if (s->sec >= 60) {
+        s->sec = 0;
+        s->min += 1;
+        if (s->cr & (1 << 2))
+            s->isr |= (1 << 1);
+    
+        if (s->min >= 60) {
+            s->min = 0;
+            s->hr += 1;
+            if (s->cr & (1 << 3))
+                s->isr |= (1 << 2);
+            
+            if (s->hr >= 24) {
+                s->hr = 0;
+                s->day += 1;
+                if (s->cr & (1 << 4))
+                    s->isr |= (1 << 3);
+            }
+        }
+    }
+
+    /* alarm interrupt */
+    if (s->cr & (1 << 5)) {
+        if ((s->sec | (s->min << 8) | (s->hr << 16)) == 
+            (s->alarm_sec | (s->alarm_min << 8) | (s->alarm_hr << 16))) {
+            s->isr |= (1 << 4);
+        }
+    }
+    
+    /* send interrupt signal */
+    ftrtc011_update_irq(s);
+        
+    qemu_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + get_ticks_per_sec());
+}
+
+static void ftrtc011_reset(DeviceState *d)
+{
+    ftrtc011_state *s = DO_UPCAST(ftrtc011_state, busdev.qdev, d);
+    
+    s->sec = 0;
+    s->min = 0;
+    s->hr  = 0;
+    s->day = 0;
+    s->alarm_sec = 0;
+    s->alarm_min = 0;
+    s->alarm_hr  = 0;
+    
+    ftrtc011_update_irq(s);
+}
+
+static int ftrtc011_init(SysBusDevice *dev)
+{
+    ftrtc011_state *s = FROM_SYSBUS(typeof(*s), dev);
+    
+    s->qtimer = qemu_new_timer_ns(vm_clock, ftrtc011_timer_tick, s);
+
+    memory_region_init_io(&s->mmio, &bus_ops, s, "ftrtc011", 0x1000);
+    sysbus_init_mmio(dev, &s->mmio);
+    sysbus_init_irq(dev, &s->irq[IRQ_ALARM_LEVEL]);
+    sysbus_init_irq(dev, &s->irq[IRQ_ALARM_EDGE]);
+    sysbus_init_irq(dev, &s->irq[IRQ_SEC]);
+    sysbus_init_irq(dev, &s->irq[IRQ_MIN]);
+    sysbus_init_irq(dev, &s->irq[IRQ_HOUR]);
+    sysbus_init_irq(dev, &s->irq[IRQ_DAY]);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_ftrtc011 = {
+    .name = "ftrtc011",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(sec, ftrtc011_state),
+        VMSTATE_UINT8(min, ftrtc011_state),
+        VMSTATE_UINT8(hr, ftrtc011_state),
+        VMSTATE_UINT32(day, ftrtc011_state),
+        VMSTATE_UINT8(alarm_sec, ftrtc011_state),
+        VMSTATE_UINT8(alarm_min, ftrtc011_state),
+        VMSTATE_UINT8(alarm_hr, ftrtc011_state),
+        VMSTATE_UINT32(cr, ftrtc011_state),
+        VMSTATE_UINT32(isr, ftrtc011_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void ftrtc011_class_init(ObjectClass *klass, void *data)
+{
+    SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *k = DEVICE_CLASS(klass);    
+
+    sdc->init  = ftrtc011_init;
+    k->vmsd    = &vmstate_ftrtc011;
+    k->reset   = ftrtc011_reset;
+    k->no_user = 1;
+}
+
+static TypeInfo ftrtc011_info = {
+    .name           = "ftrtc011",
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(ftrtc011_state),
+    .class_init     = ftrtc011_class_init,
+};
+
+static void ftrtc011_register_types(void)
+{
+    type_register_static(&ftrtc011_info);
+}
+
+type_init(ftrtc011_register_types)
