diff mbox series

[12/14] hw/misc: Add intel-me

Message ID 20220627222737.1011989-1-pdel@fb.com
State New
Headers show
Series aspeed: Add I2C new register DMA slave mode support | expand

Commit Message

Peter Delevoryas June 27, 2022, 10:27 p.m. UTC
Signed-off-by: Peter Delevoryas <pdel@fb.com>
---
 hw/arm/aspeed.c     |   1 +
 hw/misc/intel_me.c  | 176 ++++++++++++++++++++++++++++++++++++++++++++
 hw/misc/meson.build |   3 +-
 3 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/intel_me.c

Comments

Cédric Le Goater June 28, 2022, 6:58 a.m. UTC | #1
On 6/28/22 00:27, Peter Delevoryas wrote:
> Signed-off-by: Peter Delevoryas <pdel@fb.com>

Intro ?

I would rather have 2 patches, one for the slave model and one adding
a device to the machine.

Please replace the printf with trace events.

Thanks,

C.
  

> ---
>   hw/arm/aspeed.c     |   1 +
>   hw/misc/intel_me.c  | 176 ++++++++++++++++++++++++++++++++++++++++++++
>   hw/misc/meson.build |   3 +-
>   3 files changed, 179 insertions(+), 1 deletion(-)
>   create mode 100644 hw/misc/intel_me.c
> 
> diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
> index 2b9c1600c6..88e9a47dc2 100644
> --- a/hw/arm/aspeed.c
> +++ b/hw/arm/aspeed.c
> @@ -1447,6 +1447,7 @@ static void oby35_cl_i2c_init(AspeedMachineState *bmc)
>       i2c_slave_create_simple(i2c[1], "tmp105", 0x4a);
>       i2c_slave_create_simple(i2c[1], "adm1272", 0x40);
>       i2c_slave_create_simple(i2c[1], "tmp421", 0x4c);
> +    i2c_slave_create_simple(i2c[2], "intel-me", 0x16);
>       i2c_slave_create_simple(i2c[4], "isl69259", 0x76);
>       i2c_slave_create_simple(i2c[4], "isl69259", 0x62);
>       i2c_slave_create_simple(i2c[4], "isl69259", 0x60);
> diff --git a/hw/misc/intel_me.c b/hw/misc/intel_me.c
> new file mode 100644
> index 0000000000..fdc9180c26
> --- /dev/null
> +++ b/hw/misc/intel_me.c
> @@ -0,0 +1,176 @@
> +/*
> + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.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 "qemu/osdep.h"
> +#include "qemu/main-loop.h"
> +#include "hw/i2c/i2c.h"
> +
> +#define TYPE_INTEL_ME "intel-me"
> +OBJECT_DECLARE_SIMPLE_TYPE(IntelMEState, INTEL_ME);
> +
> +#define printf(...)
> +
> +struct IntelMEState {
> +    I2CSlave parent_obj;
> +
> +    I2CBus *bus;
> +    QEMUBH *bh;
> +    int rx_len;
> +    int tx_len;
> +    int tx_pos;
> +    uint8_t rx_buf[512];
> +    uint8_t tx_buf[512];
> +};
> +
> +static void intel_me_bh(void *opaque)
> +{
> +    IntelMEState *s = opaque;
> +
> +    assert(s->bus->bh == s->bh);
> +
> +    if (s->tx_pos == 0) {
> +        if (i2c_start_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
> +            goto done;
> +        }
> +        return;
> +    }
> +
> +    if (s->tx_pos < s->tx_len) {
> +        if (i2c_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
> +            goto done;
> +        }
> +        return;
> +    }
> +
> +done:
> +    i2c_end_transfer(s->bus);
> +    i2c_bus_release(s->bus);
> +    s->tx_len = 0;
> +    s->tx_pos = 0;
> +    memset(s->tx_buf, 0, sizeof(s->tx_buf));
> +}
> +
> +static void intel_me_realize(DeviceState *dev, Error **errp)
> +{
> +    IntelMEState *s = INTEL_ME(dev);
> +
> +    s->bus = I2C_BUS(qdev_get_parent_bus(dev));
> +    s->bh = qemu_bh_new(intel_me_bh, s);
> +    s->rx_len = 0;
> +    s->tx_len = 0;
> +    s->tx_pos = 0;
> +    memset(s->rx_buf, 0, sizeof(s->rx_buf));
> +    memset(s->tx_buf, 0, sizeof(s->tx_buf));
> +}
> +
> +static uint8_t checksum(const uint8_t *ptr, int len)
> +{
> +    int sum = 0;
> +
> +    for (int i = 0; i < len; i++) {
> +        sum += ptr[i];
> +    }
> +
> +    return 256 - sum;
> +}
> +
> +static int intel_me_i2c_event(I2CSlave *i2c, enum i2c_event event)
> +{
> +    IntelMEState *s = INTEL_ME(i2c);
> +
> +    switch (event) {
> +    case I2C_START_RECV:
> +        break;
> +    case I2C_START_SEND:
> +        s->rx_len = 0;
> +        memset(s->rx_buf, 0, sizeof(s->rx_buf));
> +        break;
> +    case I2C_START_SEND_ASYNC:
> +        break;
> +    case I2C_FINISH:
> +        printf("IntelME rx: [");
> +        for (int i = 0; i < s->rx_len; i++) {
> +            if (i) {
> +                printf(", ");
> +            }
> +            printf("0x%02x", s->rx_buf[i]);
> +        }
> +        printf("]\n");
> +
> +        s->tx_len = 10;
> +        s->tx_pos = 0;
> +        s->tx_buf[0] = s->rx_buf[2];
> +        s->tx_buf[1] = ((s->rx_buf[0] >> 2) + 1) << 2;
> +        s->tx_buf[2] = 256 - s->tx_buf[0] - s->tx_buf[1];
> +        s->tx_buf[3] = i2c->address; // rsSA response Slave Address
> +        s->tx_buf[4] = (s->rx_buf[3] >> 2) << 2; // sequence number
> +        s->tx_buf[5] = s->rx_buf[4]; // Same command code
> +        s->tx_buf[6] = 0x00; // OK
> +        s->tx_buf[7] = 0x55; // NO_ERROR
> +        s->tx_buf[8] = 0x00;
> +        s->tx_buf[9] = checksum(s->tx_buf, s->tx_len - 1);
> +        s->tx_buf[0] >>= 1;
> +        i2c_bus_master(s->bus, s->bh);
> +        break;
> +    case I2C_NACK:
> +        break;
> +    }
> +
> +    return 0;
> +}
> +
> +static uint8_t intel_me_i2c_recv(I2CSlave *i2c)
> +{
> +    return 0xff;
> +}
> +
> +static int intel_me_i2c_send(I2CSlave *i2c, uint8_t data)
> +{
> +    IntelMEState *s = INTEL_ME(i2c);
> +
> +    assert(s->rx_len < sizeof(s->rx_buf));
> +    s->rx_buf[s->rx_len++] = data;
> +
> +    return 0;
> +}
> +
> +static void intel_me_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +    I2CSlaveClass *i2c = I2C_SLAVE_CLASS(oc);
> +
> +    dc->realize = intel_me_realize;
> +    i2c->event = intel_me_i2c_event;
> +    i2c->recv = intel_me_i2c_recv;
> +    i2c->send = intel_me_i2c_send;
> +}
> +
> +static const TypeInfo types[] = {
> +    {
> +        .name = TYPE_INTEL_ME,
> +        .parent = TYPE_I2C_SLAVE,
> +        .instance_size = sizeof(IntelMEState),
> +        .class_init = intel_me_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(types);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index 1edad44b6b..a2c75894a3 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -118,7 +118,8 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
>     'aspeed_sdmc.c',
>     'aspeed_xdma.c',
>     'aspeed_peci.c',
> -  'fby35_cpld.c'))
> +  'fby35_cpld.c',
> +  'intel_me.c'))
>   
>   softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c'))
>   softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c'))
Peter Delevoryas June 28, 2022, 7:17 a.m. UTC | #2
> On Jun 27, 2022, at 11:58 PM, Cédric Le Goater <clg@kaod.org> wrote:
> 
> On 6/28/22 00:27, Peter Delevoryas wrote:
>> Signed-off-by: Peter Delevoryas <pdel@fb.com>
> 
> Intro ?

Yep, will do

> 
> I would rather have 2 patches, one for the slave model and one adding
> a device to the machine.

Got it, I’ll split it.

> 
> Please replace the printf with trace events.

Yeah sorry about that

> 
> Thanks,
> 
> C.
> 
>> ---
>>  hw/arm/aspeed.c     |   1 +
>>  hw/misc/intel_me.c  | 176 ++++++++++++++++++++++++++++++++++++++++++++
>>  hw/misc/meson.build |   3 +-
>>  3 files changed, 179 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/misc/intel_me.c
>> diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
>> index 2b9c1600c6..88e9a47dc2 100644
>> --- a/hw/arm/aspeed.c
>> +++ b/hw/arm/aspeed.c
>> @@ -1447,6 +1447,7 @@ static void oby35_cl_i2c_init(AspeedMachineState *bmc)
>>      i2c_slave_create_simple(i2c[1], "tmp105", 0x4a);
>>      i2c_slave_create_simple(i2c[1], "adm1272", 0x40);
>>      i2c_slave_create_simple(i2c[1], "tmp421", 0x4c);
>> +    i2c_slave_create_simple(i2c[2], "intel-me", 0x16);
>>      i2c_slave_create_simple(i2c[4], "isl69259", 0x76);
>>      i2c_slave_create_simple(i2c[4], "isl69259", 0x62);
>>      i2c_slave_create_simple(i2c[4], "isl69259", 0x60);
>> diff --git a/hw/misc/intel_me.c b/hw/misc/intel_me.c
>> new file mode 100644
>> index 0000000000..fdc9180c26
>> --- /dev/null
>> +++ b/hw/misc/intel_me.c
>> @@ -0,0 +1,176 @@
>> +/*
>> + * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.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 "qemu/osdep.h"
>> +#include "qemu/main-loop.h"
>> +#include "hw/i2c/i2c.h"
>> +
>> +#define TYPE_INTEL_ME "intel-me"
>> +OBJECT_DECLARE_SIMPLE_TYPE(IntelMEState, INTEL_ME);
>> +
>> +#define printf(...)
>> +
>> +struct IntelMEState {
>> +    I2CSlave parent_obj;
>> +
>> +    I2CBus *bus;
>> +    QEMUBH *bh;
>> +    int rx_len;
>> +    int tx_len;
>> +    int tx_pos;
>> +    uint8_t rx_buf[512];
>> +    uint8_t tx_buf[512];
>> +};
>> +
>> +static void intel_me_bh(void *opaque)
>> +{
>> +    IntelMEState *s = opaque;
>> +
>> +    assert(s->bus->bh == s->bh);
>> +
>> +    if (s->tx_pos == 0) {
>> +        if (i2c_start_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
>> +            goto done;
>> +        }
>> +        return;
>> +    }
>> +
>> +    if (s->tx_pos < s->tx_len) {
>> +        if (i2c_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
>> +            goto done;
>> +        }
>> +        return;
>> +    }
>> +
>> +done:
>> +    i2c_end_transfer(s->bus);
>> +    i2c_bus_release(s->bus);
>> +    s->tx_len = 0;
>> +    s->tx_pos = 0;
>> +    memset(s->tx_buf, 0, sizeof(s->tx_buf));
>> +}
>> +
>> +static void intel_me_realize(DeviceState *dev, Error **errp)
>> +{
>> +    IntelMEState *s = INTEL_ME(dev);
>> +
>> +    s->bus = I2C_BUS(qdev_get_parent_bus(dev));
>> +    s->bh = qemu_bh_new(intel_me_bh, s);
>> +    s->rx_len = 0;
>> +    s->tx_len = 0;
>> +    s->tx_pos = 0;
>> +    memset(s->rx_buf, 0, sizeof(s->rx_buf));
>> +    memset(s->tx_buf, 0, sizeof(s->tx_buf));
>> +}
>> +
>> +static uint8_t checksum(const uint8_t *ptr, int len)
>> +{
>> +    int sum = 0;
>> +
>> +    for (int i = 0; i < len; i++) {
>> +        sum += ptr[i];
>> +    }
>> +
>> +    return 256 - sum;
>> +}
>> +
>> +static int intel_me_i2c_event(I2CSlave *i2c, enum i2c_event event)
>> +{
>> +    IntelMEState *s = INTEL_ME(i2c);
>> +
>> +    switch (event) {
>> +    case I2C_START_RECV:
>> +        break;
>> +    case I2C_START_SEND:
>> +        s->rx_len = 0;
>> +        memset(s->rx_buf, 0, sizeof(s->rx_buf));
>> +        break;
>> +    case I2C_START_SEND_ASYNC:
>> +        break;
>> +    case I2C_FINISH:
>> +        printf("IntelME rx: [");
>> +        for (int i = 0; i < s->rx_len; i++) {
>> +            if (i) {
>> +                printf(", ");
>> +            }
>> +            printf("0x%02x", s->rx_buf[i]);
>> +        }
>> +        printf("]\n");
>> +
>> +        s->tx_len = 10;
>> +        s->tx_pos = 0;
>> +        s->tx_buf[0] = s->rx_buf[2];
>> +        s->tx_buf[1] = ((s->rx_buf[0] >> 2) + 1) << 2;
>> +        s->tx_buf[2] = 256 - s->tx_buf[0] - s->tx_buf[1];
>> +        s->tx_buf[3] = i2c->address; // rsSA response Slave Address
>> +        s->tx_buf[4] = (s->rx_buf[3] >> 2) << 2; // sequence number
>> +        s->tx_buf[5] = s->rx_buf[4]; // Same command code
>> +        s->tx_buf[6] = 0x00; // OK
>> +        s->tx_buf[7] = 0x55; // NO_ERROR
>> +        s->tx_buf[8] = 0x00;
>> +        s->tx_buf[9] = checksum(s->tx_buf, s->tx_len - 1);
>> +        s->tx_buf[0] >>= 1;
>> +        i2c_bus_master(s->bus, s->bh);
>> +        break;
>> +    case I2C_NACK:
>> +        break;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>> +static uint8_t intel_me_i2c_recv(I2CSlave *i2c)
>> +{
>> +    return 0xff;
>> +}
>> +
>> +static int intel_me_i2c_send(I2CSlave *i2c, uint8_t data)
>> +{
>> +    IntelMEState *s = INTEL_ME(i2c);
>> +
>> +    assert(s->rx_len < sizeof(s->rx_buf));
>> +    s->rx_buf[s->rx_len++] = data;
>> +
>> +    return 0;
>> +}
>> +
>> +static void intel_me_class_init(ObjectClass *oc, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(oc);
>> +    I2CSlaveClass *i2c = I2C_SLAVE_CLASS(oc);
>> +
>> +    dc->realize = intel_me_realize;
>> +    i2c->event = intel_me_i2c_event;
>> +    i2c->recv = intel_me_i2c_recv;
>> +    i2c->send = intel_me_i2c_send;
>> +}
>> +
>> +static const TypeInfo types[] = {
>> +    {
>> +        .name = TYPE_INTEL_ME,
>> +        .parent = TYPE_I2C_SLAVE,
>> +        .instance_size = sizeof(IntelMEState),
>> +        .class_init = intel_me_class_init,
>> +    },
>> +};
>> +
>> +DEFINE_TYPES(types);
>> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
>> index 1edad44b6b..a2c75894a3 100644
>> --- a/hw/misc/meson.build
>> +++ b/hw/misc/meson.build
>> @@ -118,7 +118,8 @@ softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
>>    'aspeed_sdmc.c',
>>    'aspeed_xdma.c',
>>    'aspeed_peci.c',
>> -  'fby35_cpld.c'))
>> +  'fby35_cpld.c',
>> +  'intel_me.c'))
>>    softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c'))
>>  softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c'))
>
diff mbox series

Patch

diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 2b9c1600c6..88e9a47dc2 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -1447,6 +1447,7 @@  static void oby35_cl_i2c_init(AspeedMachineState *bmc)
     i2c_slave_create_simple(i2c[1], "tmp105", 0x4a);
     i2c_slave_create_simple(i2c[1], "adm1272", 0x40);
     i2c_slave_create_simple(i2c[1], "tmp421", 0x4c);
+    i2c_slave_create_simple(i2c[2], "intel-me", 0x16);
     i2c_slave_create_simple(i2c[4], "isl69259", 0x76);
     i2c_slave_create_simple(i2c[4], "isl69259", 0x62);
     i2c_slave_create_simple(i2c[4], "isl69259", 0x60);
diff --git a/hw/misc/intel_me.c b/hw/misc/intel_me.c
new file mode 100644
index 0000000000..fdc9180c26
--- /dev/null
+++ b/hw/misc/intel_me.c
@@ -0,0 +1,176 @@ 
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates. (http://www.meta.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 "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "hw/i2c/i2c.h"
+
+#define TYPE_INTEL_ME "intel-me"
+OBJECT_DECLARE_SIMPLE_TYPE(IntelMEState, INTEL_ME);
+
+#define printf(...)
+
+struct IntelMEState {
+    I2CSlave parent_obj;
+
+    I2CBus *bus;
+    QEMUBH *bh;
+    int rx_len;
+    int tx_len;
+    int tx_pos;
+    uint8_t rx_buf[512];
+    uint8_t tx_buf[512];
+};
+
+static void intel_me_bh(void *opaque)
+{
+    IntelMEState *s = opaque;
+
+    assert(s->bus->bh == s->bh);
+
+    if (s->tx_pos == 0) {
+        if (i2c_start_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
+            goto done;
+        }
+        return;
+    }
+
+    if (s->tx_pos < s->tx_len) {
+        if (i2c_send_async(s->bus, s->tx_buf[s->tx_pos++]) != 0) {
+            goto done;
+        }
+        return;
+    }
+
+done:
+    i2c_end_transfer(s->bus);
+    i2c_bus_release(s->bus);
+    s->tx_len = 0;
+    s->tx_pos = 0;
+    memset(s->tx_buf, 0, sizeof(s->tx_buf));
+}
+
+static void intel_me_realize(DeviceState *dev, Error **errp)
+{
+    IntelMEState *s = INTEL_ME(dev);
+
+    s->bus = I2C_BUS(qdev_get_parent_bus(dev));
+    s->bh = qemu_bh_new(intel_me_bh, s);
+    s->rx_len = 0;
+    s->tx_len = 0;
+    s->tx_pos = 0;
+    memset(s->rx_buf, 0, sizeof(s->rx_buf));
+    memset(s->tx_buf, 0, sizeof(s->tx_buf));
+}
+
+static uint8_t checksum(const uint8_t *ptr, int len)
+{
+    int sum = 0;
+
+    for (int i = 0; i < len; i++) {
+        sum += ptr[i];
+    }
+
+    return 256 - sum;
+}
+
+static int intel_me_i2c_event(I2CSlave *i2c, enum i2c_event event)
+{
+    IntelMEState *s = INTEL_ME(i2c);
+
+    switch (event) {
+    case I2C_START_RECV:
+        break;
+    case I2C_START_SEND:
+        s->rx_len = 0;
+        memset(s->rx_buf, 0, sizeof(s->rx_buf));
+        break;
+    case I2C_START_SEND_ASYNC:
+        break;
+    case I2C_FINISH:
+        printf("IntelME rx: [");
+        for (int i = 0; i < s->rx_len; i++) {
+            if (i) {
+                printf(", ");
+            }
+            printf("0x%02x", s->rx_buf[i]);
+        }
+        printf("]\n");
+
+        s->tx_len = 10;
+        s->tx_pos = 0;
+        s->tx_buf[0] = s->rx_buf[2];
+        s->tx_buf[1] = ((s->rx_buf[0] >> 2) + 1) << 2;
+        s->tx_buf[2] = 256 - s->tx_buf[0] - s->tx_buf[1];
+        s->tx_buf[3] = i2c->address; // rsSA response Slave Address
+        s->tx_buf[4] = (s->rx_buf[3] >> 2) << 2; // sequence number
+        s->tx_buf[5] = s->rx_buf[4]; // Same command code
+        s->tx_buf[6] = 0x00; // OK
+        s->tx_buf[7] = 0x55; // NO_ERROR
+        s->tx_buf[8] = 0x00;
+        s->tx_buf[9] = checksum(s->tx_buf, s->tx_len - 1);
+        s->tx_buf[0] >>= 1;
+        i2c_bus_master(s->bus, s->bh);
+        break;
+    case I2C_NACK:
+        break;
+    }
+
+    return 0;
+}
+
+static uint8_t intel_me_i2c_recv(I2CSlave *i2c)
+{
+    return 0xff;
+}
+
+static int intel_me_i2c_send(I2CSlave *i2c, uint8_t data)
+{
+    IntelMEState *s = INTEL_ME(i2c);
+
+    assert(s->rx_len < sizeof(s->rx_buf));
+    s->rx_buf[s->rx_len++] = data;
+
+    return 0;
+}
+
+static void intel_me_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    I2CSlaveClass *i2c = I2C_SLAVE_CLASS(oc);
+
+    dc->realize = intel_me_realize;
+    i2c->event = intel_me_i2c_event;
+    i2c->recv = intel_me_i2c_recv;
+    i2c->send = intel_me_i2c_send;
+}
+
+static const TypeInfo types[] = {
+    {
+        .name = TYPE_INTEL_ME,
+        .parent = TYPE_I2C_SLAVE,
+        .instance_size = sizeof(IntelMEState),
+        .class_init = intel_me_class_init,
+    },
+};
+
+DEFINE_TYPES(types);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 1edad44b6b..a2c75894a3 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -118,7 +118,8 @@  softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
   'aspeed_sdmc.c',
   'aspeed_xdma.c',
   'aspeed_peci.c',
-  'fby35_cpld.c'))
+  'fby35_cpld.c',
+  'intel_me.c'))
 
 softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c'))
 softmmu_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_rng.c'))