From 5e623f2bf1b4a43022c2fd31919c76ddb9556e17 Mon Sep 17 00:00:00 2001
From: John Wang <wangzhiqiang.bj@bytedance.com>
Date: Thu, 10 Dec 2020 12:11:03 +0100
Subject: [PATCH 1/5] hw/misc: add an EMC141{3,4} device model
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Largely inspired by the TMP421 temperature sensor, here is a model for
the EMC1413/EMC1414 temperature sensors.

Specs can be found here :
  http://ww1.microchip.com/downloads/en/DeviceDoc/20005274A.pdf

Signed-off-by: John Wang <wangzhiqiang.bj@bytedance.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20201122105134.671-1-wangzhiqiang.bj@bytedance.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/Kconfig                 |   1 +
 hw/misc/Kconfig                |   4 +
 hw/misc/emc141x.c              | 326 +++++++++++++++++++++++++++++++++
 hw/misc/meson.build            |   1 +
 include/hw/misc/emc141x_regs.h |  37 ++++
 tests/qtest/emc141x-test.c     |  81 ++++++++
 tests/qtest/meson.build        |   1 +
 7 files changed, 451 insertions(+)
 create mode 100644 hw/misc/emc141x.c
 create mode 100644 include/hw/misc/emc141x_regs.h
 create mode 100644 tests/qtest/emc141x-test.c

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index e69a9009cf..eb8a8844cf 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -407,6 +407,7 @@ config ASPEED_SOC
     select SSI_M25P80
     select TMP105
     select TMP421
+    select EMC141X
     select UNIMP
     select LED
 
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index dc44dc14f6..cf18ac08e6 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -13,6 +13,10 @@ config TMP421
     bool
     depends on I2C
 
+config EMC141X
+    bool
+    depends on I2C
+
 config ISA_DEBUG
     bool
     depends on ISA_BUS
diff --git a/hw/misc/emc141x.c b/hw/misc/emc141x.c
new file mode 100644
index 0000000000..f7c53d48a4
--- /dev/null
+++ b/hw/misc/emc141x.c
@@ -0,0 +1,326 @@
+/*
+ * SMSC EMC141X temperature sensor.
+ *
+ * Copyright (c) 2020 Bytedance Corporation
+ * Written by John Wang <wangzhiqiang.bj@bytedance.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 or
+ * (at your option) version 3 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/i2c/i2c.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "qemu/module.h"
+#include "qom/object.h"
+#include "hw/misc/emc141x_regs.h"
+
+#define SENSORS_COUNT_MAX    4
+
+struct EMC141XState {
+    I2CSlave parent_obj;
+    struct {
+        uint8_t raw_temp_min;
+        uint8_t raw_temp_current;
+        uint8_t raw_temp_max;
+    } sensor[SENSORS_COUNT_MAX];
+    uint8_t len;
+    uint8_t data;
+    uint8_t pointer;
+};
+
+struct EMC141XClass {
+    I2CSlaveClass parent_class;
+    uint8_t model;
+    unsigned sensors_count;
+};
+
+#define TYPE_EMC141X "emc141x"
+OBJECT_DECLARE_TYPE(EMC141XState, EMC141XClass, EMC141X)
+
+static void emc141x_get_temperature(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
+{
+    EMC141XState *s = EMC141X(obj);
+    EMC141XClass *sc = EMC141X_GET_CLASS(s);
+    int64_t value;
+    unsigned tempid;
+
+    if (sscanf(name, "temperature%u", &tempid) != 1) {
+        error_setg(errp, "error reading %s: %s", name, g_strerror(errno));
+        return;
+    }
+
+    if (tempid >= sc->sensors_count) {
+        error_setg(errp, "error reading %s", name);
+        return;
+    }
+
+    value = s->sensor[tempid].raw_temp_current * 1000;
+
+    visit_type_int(v, name, &value, errp);
+}
+
+static void emc141x_set_temperature(Object *obj, Visitor *v, const char *name,
+                                    void *opaque, Error **errp)
+{
+    EMC141XState *s = EMC141X(obj);
+    EMC141XClass *sc = EMC141X_GET_CLASS(s);
+    int64_t temp;
+    unsigned tempid;
+
+    if (!visit_type_int(v, name, &temp, errp)) {
+        return;
+    }
+
+    if (sscanf(name, "temperature%u", &tempid) != 1) {
+        error_setg(errp, "error reading %s: %s", name, g_strerror(errno));
+        return;
+    }
+
+    if (tempid >= sc->sensors_count) {
+        error_setg(errp, "error reading %s", name);
+        return;
+    }
+
+    s->sensor[tempid].raw_temp_current = temp / 1000;
+}
+
+static void emc141x_read(EMC141XState *s)
+{
+    EMC141XClass *sc = EMC141X_GET_CLASS(s);
+    switch (s->pointer) {
+    case EMC141X_DEVICE_ID:
+        s->data = sc->model;
+        break;
+    case EMC141X_MANUFACTURER_ID:
+        s->data = MANUFACTURER_ID;
+        break;
+    case EMC141X_REVISION:
+        s->data = REVISION;
+        break;
+    case EMC141X_TEMP_HIGH0:
+        s->data = s->sensor[0].raw_temp_current;
+        break;
+    case EMC141X_TEMP_HIGH1:
+        s->data = s->sensor[1].raw_temp_current;
+        break;
+    case EMC141X_TEMP_HIGH2:
+        s->data = s->sensor[2].raw_temp_current;
+        break;
+    case EMC141X_TEMP_HIGH3:
+        s->data = s->sensor[3].raw_temp_current;
+        break;
+    case EMC141X_TEMP_MAX_HIGH0:
+        s->data = s->sensor[0].raw_temp_max;
+        break;
+    case EMC141X_TEMP_MAX_HIGH1:
+        s->data = s->sensor[1].raw_temp_max;
+        break;
+    case EMC141X_TEMP_MAX_HIGH2:
+        s->data = s->sensor[2].raw_temp_max;
+        break;
+    case EMC141X_TEMP_MAX_HIGH3:
+        s->data = s->sensor[3].raw_temp_max;
+        break;
+    case EMC141X_TEMP_MIN_HIGH0:
+        s->data = s->sensor[0].raw_temp_min;
+        break;
+    case EMC141X_TEMP_MIN_HIGH1:
+        s->data = s->sensor[1].raw_temp_min;
+        break;
+    case EMC141X_TEMP_MIN_HIGH2:
+        s->data = s->sensor[2].raw_temp_min;
+        break;
+    case EMC141X_TEMP_MIN_HIGH3:
+        s->data = s->sensor[3].raw_temp_min;
+        break;
+    default:
+        s->data = 0;
+    }
+}
+
+static void emc141x_write(EMC141XState *s)
+{
+    switch (s->pointer) {
+    case EMC141X_TEMP_MAX_HIGH0:
+        s->sensor[0].raw_temp_max = s->data;
+        break;
+    case EMC141X_TEMP_MAX_HIGH1:
+        s->sensor[1].raw_temp_max = s->data;
+        break;
+    case EMC141X_TEMP_MAX_HIGH2:
+        s->sensor[2].raw_temp_max = s->data;
+        break;
+    case EMC141X_TEMP_MAX_HIGH3:
+        s->sensor[3].raw_temp_max = s->data;
+        break;
+    case EMC141X_TEMP_MIN_HIGH0:
+        s->sensor[0].raw_temp_min = s->data;
+        break;
+    case EMC141X_TEMP_MIN_HIGH1:
+        s->sensor[1].raw_temp_min = s->data;
+        break;
+    case EMC141X_TEMP_MIN_HIGH2:
+        s->sensor[2].raw_temp_min = s->data;
+        break;
+    case EMC141X_TEMP_MIN_HIGH3:
+        s->sensor[3].raw_temp_min = s->data;
+        break;
+    default:
+        s->data = 0;
+    }
+}
+
+static uint8_t emc141x_rx(I2CSlave *i2c)
+{
+    EMC141XState *s = EMC141X(i2c);
+
+    if (s->len == 0) {
+        s->len++;
+        return s->data;
+    } else {
+        return 0xff;
+    }
+}
+
+static int emc141x_tx(I2CSlave *i2c, uint8_t data)
+{
+    EMC141XState *s = EMC141X(i2c);
+
+    if (s->len == 0) {
+        /* first byte is the reg pointer */
+        s->pointer = data;
+        s->len++;
+    } else if (s->len == 1) {
+        s->data = data;
+        emc141x_write(s);
+    }
+
+    return 0;
+}
+
+static int emc141x_event(I2CSlave *i2c, enum i2c_event event)
+{
+    EMC141XState *s = EMC141X(i2c);
+
+    if (event == I2C_START_RECV) {
+        emc141x_read(s);
+    }
+
+    s->len = 0;
+    return 0;
+}
+
+static const VMStateDescription vmstate_emc141x = {
+    .name = "EMC141X",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT8(len, EMC141XState),
+        VMSTATE_UINT8(data, EMC141XState),
+        VMSTATE_UINT8(pointer, EMC141XState),
+        VMSTATE_I2C_SLAVE(parent_obj, EMC141XState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void emc141x_reset(DeviceState *dev)
+{
+    EMC141XState *s = EMC141X(dev);
+    int i;
+
+    for (i = 0; i < SENSORS_COUNT_MAX; i++) {
+        s->sensor[i].raw_temp_max = 0x55;
+    }
+    s->pointer = 0;
+    s->len = 0;
+}
+
+static void emc141x_initfn(Object *obj)
+{
+    object_property_add(obj, "temperature0", "int",
+                        emc141x_get_temperature,
+                        emc141x_set_temperature, NULL, NULL);
+    object_property_add(obj, "temperature1", "int",
+                        emc141x_get_temperature,
+                        emc141x_set_temperature, NULL, NULL);
+    object_property_add(obj, "temperature2", "int",
+                        emc141x_get_temperature,
+                        emc141x_set_temperature, NULL, NULL);
+    object_property_add(obj, "temperature3", "int",
+                        emc141x_get_temperature,
+                        emc141x_set_temperature, NULL, NULL);
+}
+
+static void emc141x_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+    dc->reset = emc141x_reset;
+    k->event = emc141x_event;
+    k->recv = emc141x_rx;
+    k->send = emc141x_tx;
+    dc->vmsd = &vmstate_emc141x;
+}
+
+static void emc1413_class_init(ObjectClass *klass, void *data)
+{
+    EMC141XClass *ec = EMC141X_CLASS(klass);
+
+    emc141x_class_init(klass, data);
+    ec->model = EMC1413_DEVICE_ID;
+    ec->sensors_count = 3;
+}
+
+static void emc1414_class_init(ObjectClass *klass, void *data)
+{
+    EMC141XClass *ec = EMC141X_CLASS(klass);
+
+    emc141x_class_init(klass, data);
+    ec->model = EMC1414_DEVICE_ID;
+    ec->sensors_count = 4;
+}
+
+static const TypeInfo emc141x_info = {
+    .name          = TYPE_EMC141X,
+    .parent        = TYPE_I2C_SLAVE,
+    .instance_size = sizeof(EMC141XState),
+    .class_size    = sizeof(EMC141XClass),
+    .instance_init = emc141x_initfn,
+    .abstract      = true,
+};
+
+static const TypeInfo emc1413_info = {
+    .name          = "emc1413",
+    .parent        = TYPE_EMC141X,
+    .class_init    = emc1413_class_init,
+};
+
+static const TypeInfo emc1414_info = {
+    .name          = "emc1414",
+    .parent        = TYPE_EMC141X,
+    .class_init    = emc1414_class_init,
+};
+
+static void emc141x_register_types(void)
+{
+    type_register_static(&emc141x_info);
+    type_register_static(&emc1413_info);
+    type_register_static(&emc1414_info);
+}
+
+type_init(emc141x_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 1cd48e8a0f..ce15ffceb9 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -9,6 +9,7 @@ softmmu_ss.add(when: 'CONFIG_PCI_TESTDEV', if_true: files('pci-testdev.c'))
 softmmu_ss.add(when: 'CONFIG_SGA', if_true: files('sga.c'))
 softmmu_ss.add(when: 'CONFIG_TMP105', if_true: files('tmp105.c'))
 softmmu_ss.add(when: 'CONFIG_TMP421', if_true: files('tmp421.c'))
+softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c'))
 softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
 softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
 softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
diff --git a/include/hw/misc/emc141x_regs.h b/include/hw/misc/emc141x_regs.h
new file mode 100644
index 0000000000..0560fb7c5c
--- /dev/null
+++ b/include/hw/misc/emc141x_regs.h
@@ -0,0 +1,37 @@
+/*
+ * SMSC EMC141X temperature sensor.
+ *
+ * Browse the data sheet:
+ *
+ *    http://ww1.microchip.com/downloads/en/DeviceDoc/20005274A.pdf
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * later. See the COPYING file in the top-level directory.
+ */
+
+#ifndef TMP105_REGS_H
+#define TMP105_REGS_H
+
+#define EMC1413_DEVICE_ID                0x21
+#define EMC1414_DEVICE_ID                0x25
+#define MANUFACTURER_ID                  0x5d
+#define REVISION                         0x04
+
+/* the EMC141X registers */
+#define EMC141X_TEMP_HIGH0               0x00
+#define EMC141X_TEMP_HIGH1               0x01
+#define EMC141X_TEMP_HIGH2               0x23
+#define EMC141X_TEMP_HIGH3               0x2a
+#define EMC141X_TEMP_MAX_HIGH0           0x05
+#define EMC141X_TEMP_MIN_HIGH0           0x06
+#define EMC141X_TEMP_MAX_HIGH1           0x07
+#define EMC141X_TEMP_MIN_HIGH1           0x08
+#define EMC141X_TEMP_MAX_HIGH2           0x15
+#define EMC141X_TEMP_MIN_HIGH2           0x16
+#define EMC141X_TEMP_MAX_HIGH3           0x2c
+#define EMC141X_TEMP_MIN_HIGH3           0x2d
+#define EMC141X_DEVICE_ID                0xfd
+#define EMC141X_MANUFACTURER_ID          0xfe
+#define EMC141X_REVISION                 0xff
+
+#endif
diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c
new file mode 100644
index 0000000000..714058806a
--- /dev/null
+++ b/tests/qtest/emc141x-test.c
@@ -0,0 +1,81 @@
+/*
+ * QTest testcase for the EMC141X temperature sensor
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+
+#include "libqtest-single.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+#include "qapi/qmp/qdict.h"
+#include "hw/misc/emc141x_regs.h"
+
+#define EMC1414_TEST_ID   "emc1414-test"
+
+static int qmp_emc1414_get_temperature(const char *id)
+{
+    QDict *response;
+    int ret;
+
+    response = qmp("{ 'execute': 'qom-get', 'arguments': { 'path': %s, "
+                   "'property': 'temperature0' } }", id);
+    g_assert(qdict_haskey(response, "return"));
+    ret = qdict_get_int(response, "return");
+    qobject_unref(response);
+    return ret;
+}
+
+static void qmp_emc1414_set_temperature(const char *id, int value)
+{
+    QDict *response;
+
+    response = qmp("{ 'execute': 'qom-set', 'arguments': { 'path': %s, "
+                   "'property': 'temperature0', 'value': %d } }", id, value);
+    g_assert(qdict_haskey(response, "return"));
+    qobject_unref(response);
+}
+
+static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
+{
+    uint16_t value;
+    QI2CDevice *i2cdev = (QI2CDevice *)obj;
+
+    value = qmp_emc1414_get_temperature(EMC1414_TEST_ID);
+    g_assert_cmpuint(value, ==, 0);
+
+    value = i2c_get8(i2cdev, EMC141X_TEMP_HIGH0);
+    g_assert_cmphex(value, ==, 0);
+
+    /* The default max value is 85C, 0x55=85 */
+    value = i2c_get8(i2cdev, EMC141X_TEMP_MAX_HIGH0);
+    g_assert_cmphex(value, ==, 0x55);
+
+    value = i2c_get8(i2cdev, EMC141X_TEMP_MIN_HIGH0);
+    g_assert_cmphex(value, ==, 0);
+
+    /* 3000mc = 30C */
+    qmp_emc1414_set_temperature(EMC1414_TEST_ID, 30000);
+    value = qmp_emc1414_get_temperature(EMC1414_TEST_ID);
+    g_assert_cmpuint(value, ==, 30000);
+
+    value = i2c_get8(i2cdev, EMC141X_TEMP_HIGH0);
+    g_assert_cmphex(value, ==, 30);
+
+}
+
+static void emc1414_register_nodes(void)
+{
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "id=" EMC1414_TEST_ID ",address=0x70"
+    };
+    add_qi2c_address(&opts, &(QI2CAddress) { 0x70 });
+
+    qos_node_create_driver("emc1414", i2c_device_create);
+    qos_node_consumes("emc1414", "i2c-bus", &opts);
+
+    qos_add_test("tx-rx", "emc1414", send_and_receive, NULL);
+}
+libqos_init(emc1414_register_nodes);
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c19f1c8503..d776befd6e 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -188,6 +188,7 @@ qos_test_ss.add(
   'sdhci-test.c',
   'spapr-phb-test.c',
   'tmp105-test.c',
+  'emc141x-test.c',
   'usb-hcd-ohci-test.c',
   'virtio-test.c',
   'virtio-blk-test.c',

From 95f068c83da98874f4f9617b96fa007e8e2a7e9f Mon Sep 17 00:00:00 2001
From: John Wang <wangzhiqiang.bj@bytedance.com>
Date: Thu, 10 Dec 2020 12:11:03 +0100
Subject: [PATCH 2/5] aspeed: Add support for the g220a-bmc board
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

G220A is a 2 socket x86 motherboard supported by OpenBMC.
Strapping configuration was obtained from hardware.

Signed-off-by: John Wang <wangzhiqiang.bj@bytedance.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Message-Id: <20201122105134.671-2-wangzhiqiang.bj@bytedance.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/aspeed.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 0ef3f6b412..aee00ba8d6 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -121,6 +121,20 @@ struct AspeedMachineState {
         SCU_AST2500_HW_STRAP_ACPI_ENABLE |                              \
         SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER))
 
+#define G220A_BMC_HW_STRAP1 (                                      \
+        SCU_AST2500_HW_STRAP_SPI_AUTOFETCH_ENABLE |                     \
+        SCU_AST2500_HW_STRAP_GPIO_STRAP_ENABLE |                        \
+        SCU_AST2500_HW_STRAP_UART_DEBUG |                               \
+        SCU_AST2500_HW_STRAP_RESERVED28 |                               \
+        SCU_AST2500_HW_STRAP_DDR4_ENABLE |                              \
+        SCU_HW_STRAP_2ND_BOOT_WDT |                                     \
+        SCU_HW_STRAP_VGA_CLASS_CODE |                                   \
+        SCU_HW_STRAP_LPC_RESET_PIN |                                    \
+        SCU_HW_STRAP_SPI_MODE(SCU_HW_STRAP_SPI_MASTER) |                \
+        SCU_AST2500_HW_STRAP_SET_AXI_AHB_RATIO(AXI_AHB_RATIO_2_1) |     \
+        SCU_HW_STRAP_VGA_SIZE_SET(VGA_64M_DRAM) |                       \
+        SCU_AST2500_HW_STRAP_RESERVED1)
+
 /* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */
 #define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1
 
@@ -579,6 +593,30 @@ static void witherspoon_bmc_i2c_init(AspeedMachineState *bmc)
     /* Bus 11: TODO ucd90160@64 */
 }
 
+static void g220a_bmc_i2c_init(AspeedMachineState *bmc)
+{
+    AspeedSoCState *soc = &bmc->soc;
+    DeviceState *dev;
+
+    dev = DEVICE(i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 3),
+                                         "emc1413", 0x4c));
+    object_property_set_int(OBJECT(dev), "temperature0", 31000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature1", 28000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature2", 20000, &error_abort);
+
+    dev = DEVICE(i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 12),
+                                         "emc1413", 0x4c));
+    object_property_set_int(OBJECT(dev), "temperature0", 31000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature1", 28000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature2", 20000, &error_abort);
+
+    dev = DEVICE(i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 13),
+                                         "emc1413", 0x4c));
+    object_property_set_int(OBJECT(dev), "temperature0", 31000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature1", 28000, &error_abort);
+    object_property_set_int(OBJECT(dev), "temperature2", 20000, &error_abort);
+}
+
 static bool aspeed_get_mmio_exec(Object *obj, Error **errp)
 {
     return ASPEED_MACHINE(obj)->mmio_exec;
@@ -818,6 +856,24 @@ static void aspeed_machine_tacoma_class_init(ObjectClass *oc, void *data)
         aspeed_soc_num_cpus(amc->soc_name);
 };
 
+static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+    AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc);
+
+    mc->desc       = "Bytedance G220A BMC (ARM1176)";
+    amc->soc_name  = "ast2500-a1";
+    amc->hw_strap1 = G220A_BMC_HW_STRAP1;
+    amc->fmc_model = "n25q512a";
+    amc->spi_model = "mx25l25635e";
+    amc->num_cs    = 2;
+    amc->macs_mask  = ASPEED_MAC1_ON | ASPEED_MAC2_ON;
+    amc->i2c_init  = g220a_bmc_i2c_init;
+    mc->default_ram_size = 1024 * MiB;
+    mc->default_cpus = mc->min_cpus = mc->max_cpus =
+        aspeed_soc_num_cpus(amc->soc_name);
+};
+
 static const TypeInfo aspeed_machine_types[] = {
     {
         .name          = MACHINE_TYPE_NAME("palmetto-bmc"),
@@ -855,6 +911,10 @@ static const TypeInfo aspeed_machine_types[] = {
         .name          = MACHINE_TYPE_NAME("tacoma-bmc"),
         .parent        = TYPE_ASPEED_MACHINE,
         .class_init    = aspeed_machine_tacoma_class_init,
+    }, {
+        .name          = MACHINE_TYPE_NAME("g220a-bmc"),
+        .parent        = TYPE_ASPEED_MACHINE,
+        .class_init    = aspeed_machine_g220a_class_init,
     }, {
         .name          = TYPE_ASPEED_MACHINE,
         .parent        = TYPE_MACHINE,

From e01b4d5b6ec9dbdd1ff31002fca0183f4ea2bf79 Mon Sep 17 00:00:00 2001
From: Joel Stanley <joel@jms.id.au>
Date: Thu, 10 Dec 2020 12:11:03 +0100
Subject: [PATCH 3/5] ast2600: SRAM is 89KB
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

On the AST2600A1, the SRAM size was increased to 89KB.

Fixes: 7582591ae745 ("aspeed: Support AST2600A1 silicon revision")
Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20201112012113.835858-1-joel@jms.id.au>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/aspeed_ast2600.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c
index 1450bde7cf..12e4a16d37 100644
--- a/hw/arm/aspeed_ast2600.c
+++ b/hw/arm/aspeed_ast2600.c
@@ -481,7 +481,7 @@ static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
     sc->name         = "ast2600-a1";
     sc->cpu_type     = ARM_CPU_TYPE_NAME("cortex-a7");
     sc->silicon_rev  = AST2600_A1_SILICON_REV;
-    sc->sram_size    = 0x10000;
+    sc->sram_size    = 0x16400;
     sc->spis_num     = 2;
     sc->ehcis_num    = 2;
     sc->wdts_num     = 4;

From af453a5ef58d21fa902aea9b6e4bc2312ac0467f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= <clg@kaod.org>
Date: Thu, 10 Dec 2020 12:11:03 +0100
Subject: [PATCH 4/5] aspeed/smc: Add support for address lane disablement
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The controller can be configured to disable or enable address and data
byte lanes when issuing commands. This is useful in read command mode
to send SPI NOR commands that don't have an address space, such as
RDID. It's a good way to have a unified read operation for registers
and flash contents accesses.

A new SPI driver proposed by Aspeed makes use of this feature. Add
support for address lanes to start with. We will do the same for the
data lanes if they are controlled one day.

Cc: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Message-Id: <20201120161547.740806-2-clg@kaod.org>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ssi/aspeed_smc.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 795784e5f3..e3d5e26058 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -71,6 +71,16 @@
 #define   INTR_CTRL_CMD_ABORT_EN          (1 << 2)
 #define   INTR_CTRL_WRITE_PROTECT_EN      (1 << 1)
 
+/* Command Control Register */
+#define R_CE_CMD_CTRL      (0x0C / 4)
+#define   CTRL_ADDR_BYTE0_DISABLE_SHIFT       4
+#define   CTRL_DATA_BYTE0_DISABLE_SHIFT       0
+
+#define aspeed_smc_addr_byte_enabled(s, i)                               \
+    (!((s)->regs[R_CE_CMD_CTRL] & (1 << (CTRL_ADDR_BYTE0_DISABLE_SHIFT + (i)))))
+#define aspeed_smc_data_byte_enabled(s, i)                               \
+    (!((s)->regs[R_CE_CMD_CTRL] & (1 << (CTRL_DATA_BYTE0_DISABLE_SHIFT + (i)))))
+
 /* CEx Control Register */
 #define R_CTRL0           (0x10 / 4)
 #define   CTRL_IO_QPI              (1 << 31)
@@ -702,19 +712,17 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr)
 {
     const AspeedSMCState *s = fl->controller;
     uint8_t cmd = aspeed_smc_flash_cmd(fl);
-    int i;
+    int i = aspeed_smc_flash_is_4byte(fl) ? 4 : 3;
 
     /* Flash access can not exceed CS segment */
     addr = aspeed_smc_check_segment_addr(fl, addr);
 
     ssi_transfer(s->spi, cmd);
-
-    if (aspeed_smc_flash_is_4byte(fl)) {
-        ssi_transfer(s->spi, (addr >> 24) & 0xff);
+    while (i--) {
+        if (aspeed_smc_addr_byte_enabled(s, i)) {
+            ssi_transfer(s->spi, (addr >> (i * 8)) & 0xff);
+        }
     }
-    ssi_transfer(s->spi, (addr >> 16) & 0xff);
-    ssi_transfer(s->spi, (addr >> 8) & 0xff);
-    ssi_transfer(s->spi, (addr & 0xff));
 
     /*
      * Use fake transfers to model dummy bytes. The value should
@@ -988,6 +996,7 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
         (addr >= s->r_timings &&
          addr < s->r_timings + s->ctrl->nregs_timings) ||
         addr == s->r_ce_ctrl ||
+        addr == R_CE_CMD_CTRL ||
         addr == R_INTR_CTRL ||
         addr == R_DUMMY_DATA ||
         (s->ctrl->has_dma && addr == R_DMA_CTRL) ||
@@ -1276,6 +1285,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
         if (value != s->regs[R_SEG_ADDR0 + cs]) {
             aspeed_smc_flash_set_segment(s, cs, value);
         }
+    } else if (addr == R_CE_CMD_CTRL) {
+        s->regs[addr] = value & 0xff;
     } else if (addr == R_DUMMY_DATA) {
         s->regs[addr] = value & 0xff;
     } else if (addr == R_INTR_CTRL) {

From 6f5f6507e49df4820207a94f3aeaaeab08092d32 Mon Sep 17 00:00:00 2001
From: John Wang <wangzhiqiang.bj@bytedance.com>
Date: Thu, 10 Dec 2020 12:11:03 +0100
Subject: [PATCH 5/5] aspeed: g220a-bmc: Add an FRU
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add an eeprom device and fill it with fru
information

$ ipmitool fru print 0
Product Manufacturer  : Bytedance
Product Name          : G220A

Signed-off-by: John Wang <wangzhiqiang.bj@bytedance.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Message-Id: <20201210103607.556-1-wangzhiqiang.bj@bytedance.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/aspeed.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index aee00ba8d6..bfe2051cfe 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -615,6 +615,18 @@ static void g220a_bmc_i2c_init(AspeedMachineState *bmc)
     object_property_set_int(OBJECT(dev), "temperature0", 31000, &error_abort);
     object_property_set_int(OBJECT(dev), "temperature1", 28000, &error_abort);
     object_property_set_int(OBJECT(dev), "temperature2", 20000, &error_abort);
+
+    static uint8_t eeprom_buf[2 * 1024] = {
+            0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xfe,
+            0x01, 0x06, 0x00, 0xc9, 0x42, 0x79, 0x74, 0x65,
+            0x64, 0x61, 0x6e, 0x63, 0x65, 0xc5, 0x47, 0x32,
+            0x32, 0x30, 0x41, 0xc4, 0x41, 0x41, 0x42, 0x42,
+            0xc4, 0x43, 0x43, 0x44, 0x44, 0xc4, 0x45, 0x45,
+            0x46, 0x46, 0xc4, 0x48, 0x48, 0x47, 0x47, 0xc1,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa7,
+    };
+    smbus_eeprom_init_one(aspeed_i2c_get_bus(&soc->i2c, 4), 0x57,
+                          eeprom_buf);
 }
 
 static bool aspeed_get_mmio_exec(Object *obj, Error **errp)