diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak
index 599b630..c654ae9 100644
--- a/default-configs/x86_64-softmmu.mak
+++ b/default-configs/x86_64-softmmu.mak
@@ -46,3 +46,4 @@ CONFIG_APIC=y
 CONFIG_IOAPIC=y
 CONFIG_ICC_BUS=y
 CONFIG_PVPANIC=y
+CONFIG_ACPI_EC=y
diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs
index a0b63b5..0936f17 100644
--- a/hw/acpi/Makefile.objs
+++ b/hw/acpi/Makefile.objs
@@ -1,2 +1,3 @@
 common-obj-$(CONFIG_ACPI) += core.o piix4.o ich9.o
+common-obj-$(CONFIG_ACPI_EC) += ec.o
 
diff --git a/hw/acpi/ec.c b/hw/acpi/ec.c
new file mode 100644
index 0000000..24c2d5a
--- /dev/null
+++ b/hw/acpi/ec.c
@@ -0,0 +1,192 @@
+#include "hw/acpi/ec.h"
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "sysemu/sysemu.h"
+
+#define TYPE_EC_DEV "ACPI Embedded Controller"
+#define EC_DEV(obj) \
+    OBJECT_CHECK(ECState, (obj), TYPE_EC_DEV)
+
+static uint8_t ec_acpi_space[EC_ACPI_SPACE_SIZE] = {0};
+static uint8_t sci_event;
+static bool ec_enabled = false;
+
+typedef struct ECState {
+    ISADevice parent_obj;
+
+    uint8_t cmd;
+    uint8_t status;
+    uint8_t data;
+    uint8_t buf;
+    qemu_irq irq;
+    MemoryRegion io;
+} ECState;
+
+static NotifierList ec_sci_notifiers =
+    NOTIFIER_LIST_INITIALIZER(ec_sci_notifiers);
+void qemu_register_ec_sci_notifier(Notifier *notifier)
+{
+    notifier_list_add(&ec_sci_notifiers, notifier);
+}
+
+bool qemu_ec_enabled(void)
+{
+    return ec_enabled;
+}
+
+void ec_dev_init(ISABus *isabus)
+{
+    isa_create_simple(isabus, TYPE_EC_DEV);
+}
+
+void ec_acpi_event(uint8_t evt)
+{
+    sci_event = evt;
+}
+
+static void ec_generate_sci(void)
+{
+    notifier_list_notify(&ec_sci_notifiers, NULL);
+}
+
+static void acpi_data_write(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned size)
+{
+    ECState *s = opaque;
+    uint8_t tmp = val & 0xff;
+
+    if (s->status & EC_ACPI_CMD) {
+        s->buf = tmp & 0xff;
+        s->status &= ~EC_ACPI_CMD;
+    } else {
+        switch (tmp) {
+        case EC_ACPI_CMD_READ:
+            if (tmp < EC_ACPI_SPACE_SIZE) {
+                s->data = ec_acpi_space[tmp];
+            }
+            s->status |= EC_ACPI_OBF;
+            break;
+        case EC_ACPI_CMD_WRITE:
+            if (tmp < EC_ACPI_SPACE_SIZE) {
+                ec_acpi_space[s->buf] = tmp;
+            }
+            break;
+        case EC_ACPI_CMD_QUERY:
+            s->data = sci_event;
+        default:
+            break;
+        }
+    }
+    ec_generate_sci();
+}
+
+static uint64_t acpi_data_read(void *opaque, hwaddr addr, unsigned size)
+{
+    ECState *s = opaque;
+
+    s->status &= ~(EC_ACPI_OBF | EC_ACPI_SCI);
+
+    return s->data;
+}
+
+static void acpi_cmd_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+    ECState *s = opaque;
+    bool is_cmd = false;
+
+    s->status |= EC_ACPI_CMD;
+
+    switch (val & 0xff) {
+    case EC_ACPI_CMD_READ:
+    case EC_ACPI_CMD_WRITE:
+        is_cmd = true;
+        break;
+    case EC_ACPI_BURST_EN:
+        s->status |= EC_ACPI_BST;
+        s->data = 0x90;
+        is_cmd = true;
+        break;
+    case EC_ACPI_BURST_DN:
+        s->status &= ~EC_ACPI_BST;
+        is_cmd = true;
+        break;
+    case EC_ACPI_CMD_QUERY:
+        is_cmd = true;
+    default:
+        break;
+    }
+    if (is_cmd) {
+        s->cmd = val & 0xff;
+        ec_generate_sci();
+        s->status |= EC_ACPI_SCI;
+    } else {
+        s->cmd = 0;
+    }
+}
+
+static uint64_t acpi_status_read(void *opaque, hwaddr addr, unsigned size)
+{
+    ECState *s = opaque;
+
+    return s->status;
+}
+
+static const MemoryRegionOps io62_io_ops = {
+    .write = acpi_data_write,
+    .read = acpi_data_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static const MemoryRegionOps io66_io_ops = {
+    .write = acpi_cmd_write,
+    .read = acpi_status_read,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 1,
+    },
+};
+
+static int ec_dev_initfn(ISADevice *dev)
+{
+    ISADevice *isadev = dev;
+    ECState *s = EC_DEV(dev);
+
+/*    isa_init_irq(isadev, &s->irq, 0xb); */
+
+    memory_region_init_io(&s->io, &io62_io_ops, NULL, "ec-acpi-data", 1);
+    isa_register_ioport(isadev, &s->io, 0x62);
+
+    memory_region_init_io(&s->io + 1, &io66_io_ops, NULL, "ec-acpi-cmd", 1);
+    isa_register_ioport(isadev, &s->io + 1, 0x66);
+
+    return 0;
+}
+
+static void ec_class_initfn(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    ISADeviceClass *ic = ISA_DEVICE_CLASS(oc);
+
+    dc->no_user = 1;
+    ic->init = ec_dev_initfn;
+    ec_enabled = true;
+}
+
+static TypeInfo acpi_ec_info = {
+    .name = TYPE_EC_DEV,
+    .parent = TYPE_ISA_DEVICE,
+    .class_init = ec_class_initfn,
+    .instance_size = sizeof(ECState),
+};
+
+static void ec_register_type(void)
+{
+    type_register_static(&acpi_ec_info);
+}
+
+type_init(ec_register_type);
diff --git a/include/hw/acpi/ec.h b/include/hw/acpi/ec.h
new file mode 100644
index 0000000..3556acd
--- /dev/null
+++ b/include/hw/acpi/ec.h
@@ -0,0 +1,29 @@
+#ifndef __EC_H
+#define __EC_H
+
+#include "hw/i386/pc.h"
+#include "qemu/notify.h"
+
+#define EC_ACPI_SPACE_SIZE 0x80
+
+#define EC_ACPI_CMD_PORT 0x66
+#define EC_ACPI_DATA_PORT 0x62
+
+#define EC_ACPI_OBF 0x1
+#define EC_ACPI_IBF 0x2
+#define EC_ACPI_CMD 0x8
+#define EC_ACPI_BST 0x10
+#define EC_ACPI_SCI 0x20
+
+#define EC_ACPI_CMD_READ 0x80
+#define EC_ACPI_CMD_WRITE 0x81
+#define EC_ACPI_BURST_EN 0x82
+#define EC_ACPI_BURST_DN 0x83
+#define EC_ACPI_CMD_QUERY 0x84
+
+void qemu_register_ec_sci_notifier(Notifier *notifier);
+bool qemu_ec_enabled(void);
+void ec_dev_init(ISABus *isabus);
+void ec_acpi_event(uint8_t evt);
+
+#endif
