All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH qemu 00/12] SMC and network support
@ 2016-05-29 21:19 Cédric Le Goater
  2016-05-29 21:19 ` [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI) Cédric Le Goater
                   ` (12 more replies)
  0 siblings, 13 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

Hello,

The following patchset adds to the qemu palmetto-bmc platform initial
support for the AST2400 SMC controllers and for the FTGMAC100 network
adapter. The device models don't implement all the features the HW
proposes but they are complete enough to run OpenBMC on a qemu guest.

To boot one, use the following command line :

  $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
	-mtdblock ./flash-palmetto-20160512040959  \
	-mtdblock ./palmetto.pnor 
	-net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
	
or, if you have a libvirt bridge, you can tie the nic to it :
	
	-net nic,macaddr=C0:FF:EE:00:00:02,model=ftgmac100 \
	-net bridge,id=net0,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0 

The first block device uses the file './flash-palmetto-20160512040959'
which will act as a SPI flash module for the BMC, handled by the
SMC/FMC controller. The second block device uses the file
'./palmetto.pnor' which is an OpenPower firmware image for a palmetto
OpenPower system. This one will be handled by the SMC/SPI controller.


Code is based on Andrew's qemu and the full tree can be found here

    https://github.com/legoater/qemu/commits/aspeed

Flash images can be grabbed here :

    https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
    https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor

Thanks,

Cédric Le Goater (12):
  ast2400: add SMC controllers (FMC and SPI)
  ast2400: add SPI flash slave object
  ast2400: create SPI flash slaves
  m25p80: add a get storage routine
  ast2400: handle SPI flash Command mode (read only)
  ast2400: use contents of first SPI flash as a rom
  m25p80: add RDCR instruction for Macronix chip
  m25p80: add mx25l25635f chip
  ast2400: use a mx25l25635f chip
  ast2400: add a BT device
  net: add FTGMAC100 support
  ast2400: add a FTGMAC100 nic

 default-configs/arm-softmmu.mak |   1 +
 hw/arm/ast2400.c                |  68 ++++
 hw/arm/palmetto-bmc.c           |  12 +
 hw/block/m25p80.c               |  21 +
 hw/misc/Makefile.objs           |   2 +-
 hw/misc/aspeed_bt.c             | 175 ++++++++
 hw/net/Makefile.objs            |   1 +
 hw/net/ftgmac100.c              | 873 ++++++++++++++++++++++++++++++++++++++++
 hw/ssi/Makefile.objs            |   1 +
 hw/ssi/aspeed_smc.c             | 444 ++++++++++++++++++++
 include/hw/arm/ast2400.h        |   7 +
 include/hw/block/flash.h        |   2 +
 include/hw/misc/aspeed_bt.h     |  31 ++
 include/hw/net/ftgmac100.h      |  62 +++
 include/hw/ssi/aspeed_smc.h     |  88 ++++
 15 files changed, 1787 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/aspeed_bt.c
 create mode 100644 hw/net/ftgmac100.c
 create mode 100644 hw/ssi/aspeed_smc.c
 create mode 100644 include/hw/misc/aspeed_bt.h
 create mode 100644 include/hw/net/ftgmac100.h
 create mode 100644 include/hw/ssi/aspeed_smc.h

-- 
2.1.4

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI)
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-06-07  2:08   ` Andrew Jeffery
  2016-05-29 21:19 ` [PATCH qemu 02/12] ast2400: add SPI flash slave object Cédric Le Goater
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

This patch provides a simple device model for the SMC controllers
available on the Aspeed AST2400 soc. Support is limited to the FMC and
the SPI controllers, and to SPI flash slaves.

Below is the initial framework : the sysbus object, MMIO for registers
configuration and controls. Each controller has a SPI bus and a
configurable number of CS lines for SPI flash slaves.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/ast2400.c            |  33 ++++++
 hw/ssi/Makefile.objs        |   1 +
 hw/ssi/aspeed_smc.c         | 260 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/ast2400.h    |   3 +
 include/hw/ssi/aspeed_smc.h |  68 ++++++++++++
 5 files changed, 365 insertions(+)
 create mode 100644 hw/ssi/aspeed_smc.c
 create mode 100644 include/hw/ssi/aspeed_smc.h

diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index 23d52bffcaa7..8f678983cee5 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -23,6 +23,9 @@
 #define AST2400_UART_5_BASE      0x00184000
 #define AST2400_IOMEM_SIZE       0x00200000
 #define AST2400_IOMEM_BASE       0x1E600000
+#define AST2400_SMC_BASE         AST2400_IOMEM_BASE /* Legacy SMC */
+#define AST2400_FMC_BASE         0X1E620000
+#define AST2400_SPI_BASE         0X1E630000
 #define AST2400_VIC_BASE         0x1E6C0000
 #define AST2400_SCU_BASE         0x1E6E2000
 #define AST2400_TIMER_BASE       0x1E782000
@@ -77,6 +80,14 @@ static void ast2400_init(Object *obj)
     object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
     object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
     qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
+
+    object_initialize(&s->smc, sizeof(s->smc), TYPE_ASPEED_SMC);
+    object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
+    qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
+
+    object_initialize(&s->spi, sizeof(s->spi), TYPE_ASPEED_SMC);
+    object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
+    qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
 }
 
 static void ast2400_realize(DeviceState *dev, Error **errp)
@@ -171,6 +182,28 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
     /* The palmetto platform expects a ds3231 RTC but a ds1338 is
      * enough to provide basic RTC features. Alarms will be missing */
     i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&s->i2c), 0), "ds1338", 0x68);
+
+    /* SMC */
+    object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
+    object_property_set_int(OBJECT(&s->spi), AspeedSMCFMC, "smc-type", &err);
+    object_property_set_bool(OBJECT(&s->smc), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
+                       qdev_get_gpio_in(DEVICE(&s->vic), 19));
+
+    /* SPI */
+    object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
+    object_property_set_int(OBJECT(&s->spi), AspeedSMCSPI, "smc-type", &err);
+    object_property_set_bool(OBJECT(&s->spi), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
 }
 
 static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index 9555825acad1..6b32bf22ce3b 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -2,5 +2,6 @@ common-obj-$(CONFIG_PL022) += pl022.o
 common-obj-$(CONFIG_SSI) += ssi.o
 common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
+common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
 
 obj-$(CONFIG_OMAP) += omap_spi.o
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
new file mode 100644
index 000000000000..780fcbbc9e55
--- /dev/null
+++ b/hw/ssi/aspeed_smc.c
@@ -0,0 +1,260 @@
+/*
+ * ASPEED AST2400 SMC Controller (SPI Flash Only)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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 "hw/sysbus.h"
+#include "sysemu/sysemu.h"
+#include "qemu/log.h"
+#include "include/qemu/error-report.h"
+#include "exec/address-spaces.h"
+
+#include "hw/ssi/aspeed_smc.h"
+
+/* CE Type Setting Register */
+#define R_CONF            (0x00 / 4)
+#define   CONF_LEGACY_DISABLE  (1 << 31)
+#define   CONF_ENABLE_W4       20
+#define   CONF_ENABLE_W3       19
+#define   CONF_ENABLE_W2       18
+#define   CONF_ENABLE_W1       17
+#define   CONF_ENABLE_W0       16
+#define   CONF_FLASH_TYPE4     9
+#define   CONF_FLASH_TYPE3     7
+#define   CONF_FLASH_TYPE2     5
+#define   CONF_FLASH_TYPE1     3
+#define   CONF_FLASH_TYPE0     1
+
+/* CE Control Register */
+#define R_CTRL            (0x04 / 4)
+#define   CRTL_EXTENDED4       4  /* 32 bit addressing for SPI */
+#define   CRTL_EXTENDED3       3  /* 32 bit addressing for SPI */
+#define   CRTL_EXTENDED2       2  /* 32 bit addressing for SPI */
+#define   CRTL_EXTENDED1       1  /* 32 bit addressing for SPI */
+#define   CRTL_EXTENDED0       0  /* 32 bit addressing for SPI */
+
+/* Interrupt Control and Status Register */
+#define R_INTR_CTRL       (0x08 / 4)
+
+/* CEx Control Register */
+#define R_CTRL0           (0x10 / 4)
+#define   CTRL_CE_STOP_ACTIVE      (1 << 2)
+#define   CTRL_USERMODE            0x3
+#define R_CTRL1           (0x14 / 4)
+#define R_CTRL2           (0x18 / 4)
+#define R_CTRL3           (0x1C / 4)
+#define R_CTRL4           (0x20 / 4)
+
+/* CEx Segment Address Register */
+#define R_SEG_ADDR0       (0x30 / 4)
+#define   SEG_SIZE_SHIFT       24   /* 8MB units */
+#define   SEG_SIZE_MASK        0x7f
+#define   SEG_START_SHIFT      16   /* address bit [A29-A23] */
+#define   SEG_START_MASK       0x7f
+#define R_SEG_ADDR1       (0x34 / 4)
+#define R_SEG_ADDR2       (0x38 / 4)
+#define R_SEG_ADDR3       (0x3C / 4)
+#define R_SEG_ADDR4       (0x40 / 4)
+
+/* Misc Control Register #1 */
+#define R_MISC_CRTL1      (0x50 / 4)
+
+/* Misc Control Register #2 */
+#define R_MISC_CRTL2      (0x54 / 4)
+
+
+/* SPI controller registers and bits (that we care about) */
+#define R_SPI_CONF        (0x00 / 4)
+#define   SPI_CONF_ENABLE_W0   0
+#define R_SPI_CTRL0       (0x4 / 4)
+#define R_SPI_MISC_CRTL   (0x10 / 4)
+#define R_SPI_TIMINGS     (0x14 / 4)
+
+static const int aspeed_smc_r_ctrl0[]   = { R_CTRL0, R_CTRL0, R_SPI_CTRL0 };
+static const int aspeed_smc_r_conf[]    = { R_CONF,  R_CONF,  R_SPI_CONF  };
+static const int aspeed_smc_conf_enable_w0[] = {
+    CONF_ENABLE_W0, CONF_ENABLE_W0, SPI_CONF_ENABLE_W0 };
+
+
+static bool aspeed_smc_is_ce_stop_active(AspeedSMCState *s, int cs)
+{
+    return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
+}
+
+static void aspeed_smc_update_cs(AspeedSMCState *s)
+{
+    int i;
+
+    for (i = 0; i < s->num_cs; ++i) {
+        if (aspeed_smc_is_ce_stop_active(s, i)) {
+            /* should reset command byte in control reg */
+        }
+
+        qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
+    }
+}
+
+static void aspeed_smc_reset(DeviceState *d)
+{
+    AspeedSMCState *s = ASPEED_SMC(d);
+    int i;
+
+    memset(s->regs, 0, sizeof s->regs);
+
+    for (i = 0; i < s->num_cs; ++i) {
+        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
+    }
+
+    aspeed_smc_update_cs(s);
+}
+
+static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    AspeedSMCState *s = ASPEED_SMC(opaque);
+    uint32_t r = 0;
+
+    addr >>= 2;
+    switch (addr) {
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            r = s->regs[addr];
+        }
+        break;
+    }
+
+    return r;
+}
+
+static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
+                             unsigned int size)
+{
+    AspeedSMCState *s = ASPEED_SMC(opaque);
+    uint32_t value = data;
+
+    addr >>= 2;
+    switch (addr) {
+    default:
+        if (addr < ARRAY_SIZE(s->regs)) {
+            s->regs[addr] = value;
+        }
+        break;
+    }
+
+    if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
+        aspeed_smc_update_cs(s);
+    }
+}
+
+static const MemoryRegionOps aspeed_smc_ops = {
+    .read = aspeed_smc_read,
+    .write = aspeed_smc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid.unaligned = true,
+};
+
+static const char * const aspeed_smc_types[] = { "smc", "fmc", "spi" };
+static const int aspeed_smc_max_slaves[] = { 5, 5, 1 };
+
+static int aspeed_smc_init(SysBusDevice *sbd)
+{
+    DeviceState *dev = DEVICE(sbd);
+    AspeedSMCState *s = ASPEED_SMC(dev);
+    int i;
+    char name[16];
+
+    s->spi = ssi_create_bus(dev, "spi");
+
+    /* Enforce some real HW limits */
+    if (s->num_cs > aspeed_smc_max_slaves[s->smc_type]) {
+        s->num_cs = aspeed_smc_max_slaves[s->smc_type];
+    }
+
+    /* Setup cs_lines for slaves */
+    sysbus_init_irq(sbd, &s->irq);
+    s->cs_lines = g_new0(qemu_irq, s->num_cs);
+    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
+
+    for (i = 0; i < s->num_cs; ++i) {
+        sysbus_init_irq(sbd, &s->cs_lines[i]);
+    }
+
+    /* There are some differences in the register numbers and bits
+     * between the FMC and SPI controller. Let's abstract these.
+     */
+    s->r_ctrl0 = aspeed_smc_r_ctrl0[s->smc_type];
+    s->r_conf = aspeed_smc_r_conf[s->smc_type];
+    s->conf_enable_w0 = aspeed_smc_conf_enable_w0[s->smc_type];
+
+    /* Unselect all slaves */
+    for (i = 0; i < s->num_cs; ++i) {
+        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
+    }
+
+    snprintf(name, sizeof(name), "aspeed.%s", aspeed_smc_types[s->smc_type]);
+    memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
+                          name, ASPEED_SMC_R_MAX * 4);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    return 0;
+}
+
+static const VMStateDescription vmstate_aspeed_smc = {
+    .name = "aspeed.smc",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property aspeed_smc_properties[] = {
+    DEFINE_PROP_UINT8("num-cs", AspeedSMCState, num_cs, 1),
+    DEFINE_PROP_UINT8("smc-type", AspeedSMCState, smc_type, AspeedSMCFMC),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_smc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = aspeed_smc_init;
+    dc->reset = aspeed_smc_reset;
+    dc->props = aspeed_smc_properties;
+    dc->vmsd = &vmstate_aspeed_smc;
+}
+
+static const TypeInfo aspeed_smc_info = {
+    .name           = TYPE_ASPEED_SMC,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(AspeedSMCState),
+    .class_init     = aspeed_smc_class_init,
+};
+
+static void aspeed_smc_register_types(void)
+{
+    type_register_static(&aspeed_smc_info);
+}
+
+type_init(aspeed_smc_register_types)
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
index e96e3db3fbea..9ba4245619c1 100644
--- a/include/hw/arm/ast2400.h
+++ b/include/hw/arm/ast2400.h
@@ -17,6 +17,7 @@
 #include "hw/misc/aspeed_scu.h"
 #include "hw/timer/aspeed_timer.h"
 #include "hw/i2c/aspeed_i2c.h"
+#include "hw/ssi/aspeed_smc.h"
 
 typedef struct AST2400State {
     /*< private >*/
@@ -30,6 +31,8 @@ typedef struct AST2400State {
     AspeedTimerCtrlState timerctrl;
     AspeedSCUState scu;
     AspeedI2CState i2c;
+    AspeedSMCState smc;
+    AspeedSMCState spi;
 } AST2400State;
 
 #define TYPE_AST2400 "ast2400"
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
new file mode 100644
index 000000000000..9b95fcee5da7
--- /dev/null
+++ b/include/hw/ssi/aspeed_smc.h
@@ -0,0 +1,68 @@
+/*
+ * ASPEED AST2400 SMC Controller (SPI Flash Only)
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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.
+ */
+
+#ifndef ASPEED_SMC_H
+#define ASPEED_SMC_H
+
+#include "hw/ssi/ssi.h"
+
+enum AspeedSMCType {
+    AspeedSMCLegacy,
+    AspeedSMCFMC,
+    AspeedSMCSPI,
+};
+
+#define TYPE_ASPEED_SMC "aspeed.smc"
+#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC)
+
+#define ASPEED_SMC_R_MAX        (0x100 / 4)
+
+typedef struct AspeedSMCState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    qemu_irq irq;
+    int irqline;
+
+    uint8_t num_cs;
+    qemu_irq *cs_lines;
+
+    SSIBus *spi;
+
+    uint32_t regs[ASPEED_SMC_R_MAX];
+
+    uint8_t smc_type;
+
+    /* depends on the controller type */
+    uint8_t r_conf;
+    uint8_t r_ctrl0;
+    uint8_t conf_enable_w0;
+} AspeedSMCState;
+
+#define TYPE_ASPEED_SPI "aspeed.spi"
+#define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
+
+
+#endif /* ASPEED_SMC_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 02/12] ast2400: add SPI flash slave object
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
  2016-05-29 21:19 ` [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI) Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-06-07  5:34   ` Andrew Jeffery
  2016-05-29 21:19 ` [PATCH qemu 03/12] ast2400: create SPI flash slaves Cédric Le Goater
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

Each SPI flash slave can operate in two modes: Command and User. When
in User mode, accesses to the memory segment of the slaves are
translated in SPI transfers. When in Command mode, the HW generates
the SPI commands automatically and the memory segment is accessed as
if doing a MMIO. Other SPI controllers call that mode linear
addressing mode.

The patch below provides an initial model for a SPI flash module,
gathering SPI slave properties and a MemoryRegion to handle the memory
accesses. Only the User mode is supported for now but the patch
prepares ground for the Command mode.

Using a sysbus object for this abstraction might be a bit complex for
the need. We could probably survive with a simple struct under
AspeedSMCState or we could extend the m25p80 object providing a model
for the SPI flash modules. To be discussed.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ssi/aspeed_smc.c         | 110 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ssi/aspeed_smc.h |  15 ++++++
 2 files changed, 125 insertions(+)

diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 780fcbbc9e55..43743628ba0c 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -258,3 +258,113 @@ static void aspeed_smc_register_types(void)
 }
 
 type_init(aspeed_smc_register_types)
+
+static inline bool aspeed_smc_is_usermode(AspeedSMCState *s, int cs)
+{
+    return (((s->regs[s->r_ctrl0 + cs] & CTRL_USERMODE) == CTRL_USERMODE) &&
+            !aspeed_smc_is_ce_stop_active(s, cs));
+}
+
+static inline bool aspeed_smc_is_writable(AspeedSMCState *s, int cs)
+{
+    return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs));
+}
+
+static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
+{
+    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
+    AspeedSMCState *s = fl->controller;
+    uint64_t ret = 0;
+    int i;
+
+    if (aspeed_smc_is_usermode(s, fl->id)) {
+        for (i = 0; i < size; i++) {
+            ret = (ret << 8) | ssi_transfer(s->spi, 0x0);
+        }
+    } else {
+        error_report("%s: flash not in usermode", __func__);
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
+                           unsigned size)
+{
+    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
+    AspeedSMCState *s = fl->controller;
+    int i;
+
+    if (!aspeed_smc_is_writable(s, fl->id)) {
+        error_report("%s: flash not in writable", __func__);
+        return;
+    }
+
+    if (!aspeed_smc_is_usermode(s, fl->id)) {
+        error_report("%s: flash not in usermode", __func__);
+        return;
+    }
+
+    for (i = 0; i < size; i++) {
+        ssi_transfer(s->spi, (data >> 8 * (size - 1 - i)) & 0xff);
+    }
+}
+
+static const MemoryRegionOps aspeed_smc_flash_ops = {
+    .read = aspeed_smc_flash_read,
+    .write = aspeed_smc_flash_write,
+    .endianness = DEVICE_BIG_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 4,
+    },
+};
+
+static void aspeed_smc_flash_reset(DeviceState *d)
+{
+    ;
+}
+
+static int aspeed_smc_flash_init(SysBusDevice *sbd)
+{
+    return 0;
+}
+
+static const VMStateDescription vmstate_aspeed_smc_flash = {
+    .name = "aspeed.smc_flash",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property aspeed_smc_flash_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init = aspeed_smc_flash_init;
+    dc->reset = aspeed_smc_flash_reset;
+    dc->props = aspeed_smc_flash_properties;
+    dc->vmsd = &vmstate_aspeed_smc_flash;
+}
+
+static const TypeInfo aspeed_smc_flash_info = {
+    .name           = TYPE_ASPEED_SMC_FLASH,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(AspeedSMCState),
+    .class_init     = aspeed_smc_flash_class_init,
+};
+
+static void aspeed_smc_flash_register_types(void)
+{
+    type_register_static(&aspeed_smc_flash_info);
+}
+
+type_init(aspeed_smc_flash_register_types)
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 9b95fcee5da7..6cea1313eabd 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -27,6 +27,21 @@
 
 #include "hw/ssi/ssi.h"
 
+#define TYPE_ASPEED_SMC_FLASH "aspeed.smc_flash"
+#define ASPEED_SMC_FLASH(obj) \
+    OBJECT_CHECK(AspeedSMCFlashState, (obj), TYPE_ASPEED_SMC_FLASH)
+
+struct AspeedSMCState;
+
+typedef struct AspeedSMCFlashState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+    int id;
+    struct AspeedSMCState *controller;
+    DeviceState *flash;
+} AspeedSMCFlashState;
+
 enum AspeedSMCType {
     AspeedSMCLegacy,
     AspeedSMCFMC,
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 03/12] ast2400: create SPI flash slaves
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
  2016-05-29 21:19 ` [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI) Cédric Le Goater
  2016-05-29 21:19 ` [PATCH qemu 02/12] ast2400: add SPI flash slave object Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-06-07  5:56   ` Andrew Jeffery
  2016-05-29 21:19 ` [PATCH qemu 04/12] m25p80: add a get storage routine Cédric Le Goater
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

This patch creates a number of SPI flash slaves of the same type under
the SMC/FMC and under the SMC/SPI controllers. We use a "n25q256a"
flash module type for the BMC and a "mx25l25635e" for the host. These
types are common in the OpenPower ecosystem.

The segment addresses used for the memory mappings are the defaults
provided by the specs. They can be changed with the Segment Address
Register but this is not supported in the current implementation.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/palmetto-bmc.c       |  3 +++
 hw/ssi/aspeed_smc.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/ssi/aspeed_smc.h |  3 +++
 3 files changed, 68 insertions(+)

diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
index 6367f978bc7b..c4099987d354 100644
--- a/hw/arm/palmetto-bmc.c
+++ b/hw/arm/palmetto-bmc.c
@@ -50,6 +50,9 @@ static void palmetto_bmc_init(MachineState *machine)
     object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
                              &error_abort);
 
+    aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
+    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
+
     palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
     palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
     palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 43743628ba0c..4175356141e7 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -215,6 +215,8 @@ static int aspeed_smc_init(SysBusDevice *sbd)
                           name, ASPEED_SMC_R_MAX * 4);
     sysbus_init_mmio(sbd, &s->mmio);
 
+    s->flashes = g_new0(AspeedSMCFlashState *, s->num_cs);
+
     return 0;
 }
 
@@ -368,3 +370,63 @@ static void aspeed_smc_flash_register_types(void)
 }
 
 type_init(aspeed_smc_flash_register_types)
+
+/*
+ * Default segments mappings and size for each slave
+ */
+typedef struct AspeedSegments {
+    hwaddr addr;
+    uint32_t size;
+} AspeedSegments;
+
+/* unused */
+static const AspeedSegments aspeed_segment_legacy[] = {
+    { 0x14000000, 32 * 1024 * 1024 },
+};
+
+static const AspeedSegments aspeed_segment_fmc[] = {
+    { 0x20000000, 64 * 1024 * 1024 },
+    { 0x24000000, 32 * 1024 * 1024 },
+    { 0x26000000, 32 * 1024 * 1024 },
+    { 0x28000000, 32 * 1024 * 1024 },
+    { 0x2A000000, 32 * 1024 * 1024 }
+};
+
+static const AspeedSegments aspeed_segment_spi[] = {
+    { 0x30000000, 64 * 1024 * 1024 },
+};
+
+static const AspeedSegments *aspeed_segments[] = {
+    aspeed_segment_legacy, aspeed_segment_fmc, aspeed_segment_spi
+};
+
+void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype)
+{
+    int i ;
+    char name[32];
+
+    for (i = 0; i < s->num_cs; ++i) {
+        Object *new = object_new(TYPE_ASPEED_SMC_FLASH);
+        AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(new);
+        qemu_irq cs_line;
+
+        s->flashes[i] = fl;
+
+        snprintf(name, sizeof(name), "aspeed.%s.%d",
+                 aspeed_smc_types[s->smc_type], i);
+
+        fl->id = i;
+        fl->controller = s;
+        fl->flash = ssi_create_slave(s->spi, flashtype);
+
+        cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
+        sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
+
+        memory_region_init_io(&fl->mmio, new, &aspeed_smc_flash_ops, fl, name,
+                              aspeed_segments[s->smc_type][i].size);
+        sysbus_init_mmio(SYS_BUS_DEVICE(new), &fl->mmio);
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(new), 0,
+                        aspeed_segments[s->smc_type][i].addr);
+    }
+}
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 6cea1313eabd..1625dfb76a63 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -74,10 +74,13 @@ typedef struct AspeedSMCState {
     uint8_t r_conf;
     uint8_t r_ctrl0;
     uint8_t conf_enable_w0;
+
+    AspeedSMCFlashState **flashes;
 } AspeedSMCState;
 
 #define TYPE_ASPEED_SPI "aspeed.spi"
 #define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
 
+extern void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype);
 
 #endif /* ASPEED_SMC_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 04/12] m25p80: add a get storage routine
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (2 preceding siblings ...)
  2016-05-29 21:19 ` [PATCH qemu 03/12] ast2400: create SPI flash slaves Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-05-29 21:19 ` [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only) Cédric Le Goater
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

Some SPI controllers, the Aspeed AST2400 for instance, have a mode in
which accessing the flash content is no different than doing an
MMIO. The controller generates all the necessary commands to load (or
store) data in memory.

To emulate such a behavior, we need to map the underlying storage of
the m25p80 flash model to a memory region. This is what this patch
proposes by adding a m25p80_get_storage() routine which can later be
used to handle IOs in a memory region.

It is sufficient to support read only accesses. Handling write
accesses would demand more changes to the m25p80 flash model.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/block/m25p80.c        | 9 +++++++++
 include/hw/block/flash.h | 2 ++
 2 files changed, 11 insertions(+)

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 906b71257ecf..c4aee22eae88 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -27,6 +27,7 @@
 #include "sysemu/blockdev.h"
 #include "hw/ssi/ssi.h"
 #include "qemu/bitops.h"
+#include "hw/block/flash.h"
 
 #ifndef M25P80_ERR_DEBUG
 #define M25P80_ERR_DEBUG 0
@@ -1003,3 +1004,11 @@ static void m25p80_register_types(void)
 }
 
 type_init(m25p80_register_types)
+
+uint8_t *m25p80_get_storage(DeviceState *dev, uint32_t *size)
+{
+    Flash *s = M25P80(dev);
+
+    *size = s->size;
+    return s->storage;
+}
diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h
index 50ccbbcf1352..845c4c9ac28f 100644
--- a/include/hw/block/flash.h
+++ b/include/hw/block/flash.h
@@ -61,4 +61,6 @@ uint8_t ecc_digest(ECCState *s, uint8_t sample);
 void ecc_reset(ECCState *s);
 extern VMStateDescription vmstate_ecc_state;
 
+extern uint8_t *m25p80_get_storage(DeviceState *dev, uint32_t *size);
+
 #endif
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only)
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (3 preceding siblings ...)
  2016-05-29 21:19 ` [PATCH qemu 04/12] m25p80: add a get storage routine Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-06-07  6:04   ` Andrew Jeffery
  2016-05-29 21:19 ` [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom Cédric Le Goater
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

We can now use directly the m25p80 flash storage to handle memory
accesses when the SPI flash slave is configured in Command mode. Only
read only accesses are handled. Command mode is only required by
U-Boot, Linux does not use it yet.

Handling write accesses would demand more changes to the m25p80 flash
model.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ssi/aspeed_smc.c         | 18 +++++++++++++++---
 include/hw/ssi/aspeed_smc.h |  2 ++
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 4175356141e7..159ab691051d 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -28,6 +28,7 @@
 #include "qemu/log.h"
 #include "include/qemu/error-report.h"
 #include "exec/address-spaces.h"
+#include "hw/block/flash.h"
 
 #include "hw/ssi/aspeed_smc.h"
 
@@ -284,8 +285,9 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
             ret = (ret << 8) | ssi_transfer(s->spi, 0x0);
         }
     } else {
-        error_report("%s: flash not in usermode", __func__);
-        ret = -1;
+        for (i = 0; i < size; i++) {
+            ret = (ret << 8) | fl->storage[addr + i];
+        }
     }
 
     return ret;
@@ -330,6 +332,11 @@ static void aspeed_smc_flash_reset(DeviceState *d)
 
 static int aspeed_smc_flash_init(SysBusDevice *sbd)
 {
+    DeviceState *dev = DEVICE(sbd);
+    AspeedSMCFlashState *s = ASPEED_SMC_FLASH(dev);
+
+    s->size = 0;
+    s->storage = NULL;
     return 0;
 }
 
@@ -422,8 +429,13 @@ void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype)
         cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
         sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
 
+        fl->storage = m25p80_get_storage(DEVICE(fl->flash), &fl->size);
+        if (fl->size > aspeed_segments[s->smc_type][i].size) {
+            fl->size = aspeed_segments[s->smc_type][i].size;
+        }
+
         memory_region_init_io(&fl->mmio, new, &aspeed_smc_flash_ops, fl, name,
-                              aspeed_segments[s->smc_type][i].size);
+                              fl->size);
         sysbus_init_mmio(SYS_BUS_DEVICE(new), &fl->mmio);
 
         sysbus_mmio_map(SYS_BUS_DEVICE(new), 0,
diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 1625dfb76a63..2615ce9fa8aa 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -38,6 +38,8 @@ typedef struct AspeedSMCFlashState {
 
     MemoryRegion mmio;
     int id;
+    uint32_t size;
+    uint8_t *storage;
     struct AspeedSMCState *controller;
     DeviceState *flash;
 } AspeedSMCFlashState;
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (4 preceding siblings ...)
  2016-05-29 21:19 ` [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only) Cédric Le Goater
@ 2016-05-29 21:19 ` Cédric Le Goater
  2016-06-07  6:24   ` Andrew Jeffery
  2016-05-29 21:20 ` [PATCH qemu 07/12] m25p80: add RDCR instruction for Macronix chip Cédric Le Goater
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:19 UTC (permalink / raw)
  To: openbmc

This provides support for U-Boot images which are loaded at 0x0.

A Palmetto BMC guest can now be simply booted with :

  $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
	-mtdblock ./flash-palmetto-20160512040959  \
	-mtdblock ./palmetto.pnor

The first block device uses the file "./flash-palmetto-20160512040959"
which will act as a SPI flash module for the BMC, handled by the
SMC/FMC controller. The second block device uses the file
"./palmetto.pnor" which is an OpenPower firmware image for a palmetto
OpenPower system. This one will be handled by the SMC/SPI controller.

The flash images can be grabbed here :

    https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
    https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor

We could add a second BMC SPI flash by changing the 'num-cs' property
of the controller and emulate a golden image flash module.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 That might seem a little too hacky ? 

 hw/arm/palmetto-bmc.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
index c4099987d354..50369c0331a6 100644
--- a/hw/arm/palmetto-bmc.c
+++ b/hw/arm/palmetto-bmc.c
@@ -17,6 +17,7 @@
 #include "hw/arm/arm.h"
 #include "hw/arm/ast2400.h"
 #include "hw/boards.h"
+#include "hw/loader.h"
 
 static struct arm_boot_info palmetto_bmc_binfo = {
     .loader_start = AST2400_SDRAM_BASE,
@@ -53,6 +54,14 @@ static void palmetto_bmc_init(MachineState *machine)
     aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
     aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
 
+    /*
+     * Install first SMC/FMC flash content as a rom.
+     */
+    if (bmc->soc.smc.flashes[0]) {
+        rom_add_blob_fixed("aspeed.smc.0", bmc->soc.smc.flashes[0]->storage,
+                           bmc->soc.smc.flashes[0]->size, 0);
+    }
+
     palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
     palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
     palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 07/12] m25p80: add RDCR instruction for Macronix chip
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (5 preceding siblings ...)
  2016-05-29 21:19 ` [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-05-29 21:20 ` [PATCH qemu 08/12] m25p80: add mx25l25635f chip Cédric Le Goater
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

Some Macronix chips, such as the MX25L25635F (0xc22019) used on
OpenPower boxes, have a Read Configuration Register (RDCR)
instruction. This patch adds this new command to the m25p80 object and
exposes the 4BYTE mode.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/block/m25p80.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index c4aee22eae88..02f77012c21b 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -252,6 +252,7 @@ typedef enum {
     WRDI = 0x4,
     RDSR = 0x5,
     WREN = 0x6,
+    RDCR = 0x15,
     JEDEC_READ = 0x9f,
     BULK_ERASE = 0xc7,
     READ_FSR = 0x70,
@@ -708,6 +709,13 @@ static void decode_new_cmd(Flash *s, uint32_t value)
         s->state = STATE_READING_DATA;
         break;
 
+    case RDCR:
+        s->data[0] = (!!s->four_bytes_address_mode) << 5;
+        s->pos = 0;
+        s->len = 1;
+        s->state = STATE_READING_DATA;
+        break;
+
     case READ_FSR:
         s->data[0] = FSR_FLASH_READY;
         if (s->four_bytes_address_mode) {
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 08/12] m25p80: add mx25l25635f chip
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (6 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 07/12] m25p80: add RDCR instruction for Macronix chip Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-05-29 21:20 ` [PATCH qemu 09/12] ast2400: use a " Cédric Le Goater
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

The Macronix chip mx25l25635f used on some OpenPower boxes is very
similar to the mx25l25635e. They share the same JEDEC identifier but
the WRSR instruction requires 2 bytes in the mx25l25635f case.

This patch introduce a new chip to prevent some warnings on guests
identifying JEDEC 0xc22019 as a MX25L25635F chip. OpenBMC is one them.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---

 This is may not be the best way to distinguish these chips. Adding a
 flag in the FlashPartInfo would be better ?

 hw/block/m25p80.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c
index 02f77012c21b..58607786c11c 100644
--- a/hw/block/m25p80.c
+++ b/hw/block/m25p80.c
@@ -157,6 +157,7 @@ static const FlashPartInfo known_devices[] = {
     { INFO("mx25l12805d", 0xc22018,      0,  64 << 10, 256, 0) },
     { INFO("mx25l12855e", 0xc22618,      0,  64 << 10, 256, 0) },
     { INFO("mx25l25635e", 0xc22019,      0,  64 << 10, 512, 0) },
+    { INFO("mx25l25635f", 0xc22019,      0,  64 << 10, 512, 0) },
     { INFO("mx25l25655e", 0xc22619,      0,  64 << 10, 512, 0) },
 
     /* Micron */
@@ -692,6 +693,9 @@ static void decode_new_cmd(Flash *s, uint32_t value)
             s->pos = 0;
             s->len = 0;
             s->state = STATE_COLLECTING_DATA;
+            if (!strcmp(s->pi->part_name, "mx25l25635f")) {
+                s->needed_bytes = 2;
+            }
         }
         break;
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 09/12] ast2400: use a mx25l25635f chip
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (7 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 08/12] m25p80: add mx25l25635f chip Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-06-07  6:29   ` Andrew Jeffery
  2016-05-29 21:20 ` [PATCH qemu 10/12] ast2400: add a BT device Cédric Le Goater
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

The pflash command on OpenBmc is used to update the flash and it
expects a mx25l25635f and not a mx25l25635e.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/palmetto-bmc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
index 50369c0331a6..a1db516baf9e 100644
--- a/hw/arm/palmetto-bmc.c
+++ b/hw/arm/palmetto-bmc.c
@@ -52,7 +52,7 @@ static void palmetto_bmc_init(MachineState *machine)
                              &error_abort);
 
     aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
-    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
+    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635f");
 
     /*
      * Install first SMC/FMC flash content as a rom.
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 10/12] ast2400: add a BT device
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (8 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 09/12] ast2400: use a " Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-06-07  7:15   ` Andrew Jeffery
  2016-05-29 21:20 ` [PATCH qemu 11/12] net: add FTGMAC100 support Cédric Le Goater
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

This is an dummy empty shell for the moment. The plan would be to plug
this device in the IPMI framework already available in qemu and work
on a backend to connect it to an external host (a powernv guest) or a
ipmi-host simulator.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/ast2400.c            |  17 +++++
 hw/misc/Makefile.objs       |   2 +-
 hw/misc/aspeed_bt.c         | 175 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/ast2400.h    |   2 +
 include/hw/misc/aspeed_bt.h |  31 ++++++++
 5 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/aspeed_bt.c
 create mode 100644 include/hw/misc/aspeed_bt.h

diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index 8f678983cee5..a4a7c10e4361 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -29,6 +29,8 @@
 #define AST2400_VIC_BASE         0x1E6C0000
 #define AST2400_SCU_BASE         0x1E6E2000
 #define AST2400_TIMER_BASE       0x1E782000
+#define AST2400_LPC_BASE         0x1E789000
+#define AST2400_IBT_BASE         (AST2400_LPC_BASE + 0x140)
 #define AST2400_I2C_BASE         0x1E78A000
 
 static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
@@ -88,6 +90,11 @@ static void ast2400_init(Object *obj)
     object_initialize(&s->spi, sizeof(s->spi), TYPE_ASPEED_SMC);
     object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
     qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
+     qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
+
+    object_initialize(&s->bt, sizeof(s->bt), TYPE_ASPEED_BT);
+    object_property_add_child(obj, "bt", OBJECT(&s->bt), NULL);
+    qdev_set_parent_bus(DEVICE(&s->bt), sysbus_get_default());
 }
 
 static void ast2400_realize(DeviceState *dev, Error **errp)
@@ -204,6 +211,16 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
         return;
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
+
+    /* iBT */
+    object_property_set_bool(OBJECT(&s->bt), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->bt), 0, AST2400_IBT_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->bt), 0,
+                       qdev_get_gpio_in(DEVICE(&s->vic), 10));
 }
 
 static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index e50596965b03..b2931f85f097 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -51,4 +51,4 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o
 obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_EDU) += edu.o
 obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
-obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_bt.o
diff --git a/hw/misc/aspeed_bt.c b/hw/misc/aspeed_bt.c
new file mode 100644
index 000000000000..496c9aa0be4a
--- /dev/null
+++ b/hw/misc/aspeed_bt.c
@@ -0,0 +1,175 @@
+/*
+ * ASPEED iBT Device
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include <inttypes.h>
+#include "hw/misc/aspeed_bt.h"
+#include "hw/qdev-properties.h"
+#include "qemu/bitops.h"
+#include "trace.h"
+
+#ifdef ASPEED_BT_DEBUG
+#define DB_PRINT(fmt, ...) do {         \
+        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
+    } while (0);
+#else
+    #define DB_PRINT(...) do {} while (0)
+#endif
+
+#define BT_IO_REGION_SIZE 0x1C
+
+#define TO_REG(o) (o >> 2)
+
+#define BT_CR0                0x0
+#define   BT_CR0_IO_BASE         16
+#define   BT_CR0_IRQ             12
+#define   BT_CR0_EN_CLR_SLV_RDP  0x8
+#define   BT_CR0_EN_CLR_SLV_WRP  0x4
+#define   BT_CR0_ENABLE_IBT      0x1
+#define BT_CR1                0x4
+#define   BT_CR1_IRQ_H2B         0x01
+#define   BT_CR1_IRQ_HBUSY       0x40
+#define BT_CR2                0x8
+#define   BT_CR2_IRQ_H2B         0x01
+#define   BT_CR2_IRQ_HBUSY       0x40
+#define BT_CR3                0xc
+#define BT_CTRL                  0x10
+#define   BT_CTRL_B_BUSY         0x80
+#define   BT_CTRL_H_BUSY         0x40
+#define   BT_CTRL_OEM0           0x20
+#define   BT_CTRL_SMS_ATN        0x10
+#define   BT_CTRL_B2H_ATN        0x08
+#define   BT_CTRL_H2B_ATN        0x04
+#define   BT_CTRL_CLR_RD_PTR     0x02
+#define   BT_CTRL_CLR_WR_PTR     0x01
+#define BT_BMC2HOST            0x14
+#define BT_INTMASK             0x18
+#define   BT_INTMASK_B2H_IRQEN   0x01
+#define   BT_INTMASK_B2H_IRQ     0x02
+#define   BT_INTMASK_BMC_HWRST   0x80
+
+static uint64_t aspeed_bt_read(void *opaque, hwaddr offset, unsigned size)
+{
+    AspeedBTState *s = ASPEED_BT(opaque);
+
+    DB_PRINT("To 0x%" HWADDR_PRIx " of size %u\n", offset, size);
+
+    if (TO_REG(offset) >= ARRAY_SIZE(s->regs)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return 0;
+    }
+
+    switch (offset) {
+    case 0x00 ... 0x18:
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: Read from uninitialised register 0x%"
+                      HWADDR_PRIx "\n", __func__, offset);
+        break;
+    }
+
+    return s->regs[TO_REG(offset)];
+}
+
+static void aspeed_bt_write(void *opaque, hwaddr offset, uint64_t data,
+                             unsigned size)
+{
+    AspeedBTState *s = ASPEED_BT(opaque);
+
+    DB_PRINT("To 0x%" HWADDR_PRIx " of size %u: 0x%" PRIx64"\n",
+             offset, size, data);
+
+    if (TO_REG(offset) >= ARRAY_SIZE(s->regs)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
+                      __func__, offset);
+        return;
+    }
+
+    switch (offset) {
+    case 0x00 ... 0x18:
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: Write to uninitialised register 0x%"
+                      HWADDR_PRIx "\n", __func__, offset);
+        break;
+    }
+
+    s->regs[TO_REG(offset)] = (uint32_t) data;
+}
+
+static const MemoryRegionOps aspeed_bt_ops = {
+    .read = aspeed_bt_read,
+    .write = aspeed_bt_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .valid.unaligned = false,
+};
+
+static void aspeed_bt_reset(DeviceState *dev)
+{
+    AspeedBTState *s = ASPEED_BT(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void aspeed_bt_realize(DeviceState *dev, Error **errp)
+{
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    AspeedBTState *s = ASPEED_BT(dev);
+
+    sysbus_init_irq(sbd, &s->irq);
+    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_bt_ops, s,
+                          TYPE_ASPEED_BT, BT_IO_REGION_SIZE);
+
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static Property aspeed_bt_props[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_aspeed_bt = {
+    .name = "aspeed.bt",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, AspeedBTState, ASPEED_BT_NR_REGS),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void aspeed_bt_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    dc->realize = aspeed_bt_realize;
+    dc->reset = aspeed_bt_reset;
+    dc->desc = "ASPEED iBT Device";
+    dc->vmsd = &vmstate_aspeed_bt;
+    dc->props = aspeed_bt_props;
+}
+
+static const TypeInfo aspeed_bt_info = {
+    .name = TYPE_ASPEED_BT,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(AspeedBTState),
+    .class_init = aspeed_bt_class_init,
+};
+
+static void aspeed_bt_register_types(void)
+{
+    type_register_static(&aspeed_bt_info);
+}
+
+type_init(aspeed_bt_register_types);
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
index 9ba4245619c1..2b517e89eeea 100644
--- a/include/hw/arm/ast2400.h
+++ b/include/hw/arm/ast2400.h
@@ -18,6 +18,7 @@
 #include "hw/timer/aspeed_timer.h"
 #include "hw/i2c/aspeed_i2c.h"
 #include "hw/ssi/aspeed_smc.h"
+#include "hw/misc/aspeed_bt.h"
 
 typedef struct AST2400State {
     /*< private >*/
@@ -33,6 +34,7 @@ typedef struct AST2400State {
     AspeedI2CState i2c;
     AspeedSMCState smc;
     AspeedSMCState spi;
+    AspeedBTState bt;
 } AST2400State;
 
 #define TYPE_AST2400 "ast2400"
diff --git a/include/hw/misc/aspeed_bt.h b/include/hw/misc/aspeed_bt.h
new file mode 100644
index 000000000000..1e9aa9510959
--- /dev/null
+++ b/include/hw/misc/aspeed_bt.h
@@ -0,0 +1,31 @@
+/*
+ * ASPEED iBT Device
+ *
+ * Copyright 2016 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+#ifndef ASPEED_BT_H
+#define ASPEED_BT_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ASPEED_BT "aspeed.bt"
+#define ASPEED_BT(obj) OBJECT_CHECK(AspeedBTState, (obj), TYPE_ASPEED_BT)
+
+#define ASPEED_BT_NR_REGS (0x1C >> 2)
+
+typedef struct AspeedBTState {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    qemu_irq irq;
+
+    uint32_t regs[ASPEED_BT_NR_REGS];
+
+} AspeedBTState;
+
+#endif /* ASPEED_BT_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 11/12] net: add FTGMAC100 support
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (9 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 10/12] ast2400: add a BT device Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-06-08  3:50   ` Andrew Jeffery
  2016-05-29 21:20 ` [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic Cédric Le Goater
  2016-06-23 17:00 ` [PATCH qemu 00/12] SMC and network support Cédric Le Goater
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

This patch adds to qemu initial support for the FTGMAC100 network
adapter. The code uses the Coldfire Fast Ethernet Controller as a base
and was tested in an Aspeed AST2400 guest with Linux and U-Boot.

The device is configured and controlled via a set of registers
available on AHB bus. Packets are transmitted and received using DMA
ring buffers.

The model is for the moment complete enough to satisfy U-Boot and the
Linux kernel but there are areas that need some more attention :

 - PHY support, this is a copy paste from the Coldfire model
 - Automatic receive/transmit polling, no timers are used to poll the
   ring buffers descriptors. We just expect the qemu nic backend to
   push packets when they arrive.
 - CRC

And some are still on the TODO list (not used today in the drivers) :

 - Ethernet Address Filtering
 - Multicast
 - High priority queues
 - VLAN
 - NCSI, may be we could add n/w filters
 - etc.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 default-configs/arm-softmmu.mak |   1 +
 hw/net/Makefile.objs            |   1 +
 hw/net/ftgmac100.c              | 873 ++++++++++++++++++++++++++++++++++++++++
 include/hw/net/ftgmac100.h      |  62 +++
 4 files changed, 937 insertions(+)
 create mode 100644 hw/net/ftgmac100.c
 create mode 100644 include/hw/net/ftgmac100.h

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index c63cdd073d35..7c3b6c07d291 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -29,6 +29,7 @@ CONFIG_LAN9118=y
 CONFIG_SMC91C111=y
 CONFIG_ALLWINNER_EMAC=y
 CONFIG_IMX_FEC=y
+CONFIG_FTGMAC100=y
 CONFIG_DS1338=y
 CONFIG_PFLASH_CFI01=y
 CONFIG_PFLASH_CFI02=y
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index 64d044923ce1..0b271d2fd569 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -24,6 +24,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
 common-obj-$(CONFIG_CADENCE) += cadence_gem.o
 common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
 common-obj-$(CONFIG_LANCE) += lance.o
+common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o
 
 obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o
 obj-$(CONFIG_COLDFIRE) += mcf_fec.o
diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
new file mode 100644
index 000000000000..c8f8724e1f17
--- /dev/null
+++ b/hw/net/ftgmac100.c
@@ -0,0 +1,873 @@
+/*
+ * Faraday FTGMAC100 Gigabit Ethernet
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * Based on Coldfire Fast Ethernet Controller emulation.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ *  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 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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/net/ftgmac100.h"
+#include "sysemu/dma.h"
+
+/* For crc32 */
+#include <zlib.h>
+
+#ifdef FTGMAC100_DEBUG
+#define DEBUG(fmt, ...) do {         \
+        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
+    } while (0);
+#else
+    #define DEBUG(...) do {} while (0)
+#endif
+
+#ifdef FTGMAC100_PHY_DEBUG
+#define PHY_DEBUG(fmt, ...) do {         \
+        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
+    } while (0);
+#else
+    #define PHY_DEBUG(...) do {} while (0)
+#endif
+
+/*
+ * IFTGMAC100 registers
+ */
+#define FTGMAC100_ISR             0x00
+#define FTGMAC100_IER             0x04
+#define FTGMAC100_MAC_MADR        0x08
+#define FTGMAC100_MAC_LADR        0x0c
+
+#define FTGMAC100_NPTXPD          0x18
+#define FTGMAC100_NPTXR_BADR      0x20
+#define FTGMAC100_RXR_BADR        0x24
+#define FTGMAC100_APTC            0x34
+#define FTGMAC100_RBSR            0x4c
+
+#define FTGMAC100_MACCR           0x50
+#define FTGMAC100_PHYCR           0x60
+#define FTGMAC100_PHYDATA         0x64
+
+/*
+ * Interrupt status register & interrupt enable register
+ */
+#define FTGMAC100_INT_RPKT_BUF    (1 << 0)
+#define FTGMAC100_INT_RPKT_FIFO   (1 << 1)
+#define FTGMAC100_INT_NO_RXBUF    (1 << 2)
+#define FTGMAC100_INT_RPKT_LOST   (1 << 3)
+#define FTGMAC100_INT_XPKT_ETH    (1 << 4)
+#define FTGMAC100_INT_XPKT_FIFO   (1 << 5)
+#define FTGMAC100_INT_NO_NPTXBUF  (1 << 6)
+#define FTGMAC100_INT_XPKT_LOST   (1 << 7)
+#define FTGMAC100_INT_AHB_ERR     (1 << 8)
+#define FTGMAC100_INT_PHYSTS_CHG  (1 << 9)
+#define FTGMAC100_INT_NO_HPTXBUF  (1 << 10)
+
+/*
+ * Automatic polling timer control register
+ */
+#define FTGMAC100_APTC_RXPOLL_CNT(x)        ((x) & 0xf)
+#define FTGMAC100_APTC_RXPOLL_TIME_SEL      (1 << 4)
+#define FTGMAC100_APTC_TXPOLL_CNT(x)        (((x) >> 8) & 0xf)
+#define FTGMAC100_APTC_TXPOLL_TIME_SEL      (1 << 12)
+
+/*
+ * PHY control register
+ */
+#define FTGMAC100_PHYCR_MIIRD               (1 << 26)
+#define FTGMAC100_PHYCR_MIIWR               (1 << 27)
+
+/*
+ * PHY data register
+ */
+#define FTGMAC100_PHYDATA_MIIWDATA(x)       ((x) & 0xffff)
+#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff)
+
+
+/*
+ * MAC control register
+ */
+#define FTGMAC100_MACCR_TXDMA_EN         (1 << 0)
+#define FTGMAC100_MACCR_RXDMA_EN         (1 << 1)
+#define FTGMAC100_MACCR_TXMAC_EN         (1 << 2)
+#define FTGMAC100_MACCR_RXMAC_EN         (1 << 3)
+#define FTGMAC100_MACCR_RM_VLAN          (1 << 4)
+#define FTGMAC100_MACCR_HPTXR_EN         (1 << 5)
+#define FTGMAC100_MACCR_LOOP_EN          (1 << 6)
+#define FTGMAC100_MACCR_ENRX_IN_HALFTX   (1 << 7)
+#define FTGMAC100_MACCR_FULLDUP          (1 << 8)
+#define FTGMAC100_MACCR_GIGA_MODE        (1 << 9)
+#define FTGMAC100_MACCR_CRC_APD          (1 << 10)
+#define FTGMAC100_MACCR_RX_RUNT          (1 << 12)
+#define FTGMAC100_MACCR_JUMBO_LF         (1 << 13)
+#define FTGMAC100_MACCR_RX_ALL           (1 << 14)
+#define FTGMAC100_MACCR_HT_MULTI_EN      (1 << 15)
+#define FTGMAC100_MACCR_RX_MULTIPKT      (1 << 16)
+#define FTGMAC100_MACCR_RX_BROADPKT      (1 << 17)
+#define FTGMAC100_MACCR_DISCARD_CRCERR   (1 << 18)
+#define FTGMAC100_MACCR_FAST_MODE        (1 << 19)
+#define FTGMAC100_MACCR_SW_RST           (1 << 31)
+
+/*
+ * Transmit descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_txdes {
+        unsigned int        txdes0;
+        unsigned int        txdes1;
+        unsigned int        txdes2;      /* not used by HW */
+        unsigned int        txdes3;      /* TXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_TXDES0_TXBUF_SIZE(x)   ((x) & 0x3fff)
+#define FTGMAC100_TXDES0_EDOTR           (1 << 15)
+#define FTGMAC100_TXDES0_CRC_ERR         (1 << 19)
+#define FTGMAC100_TXDES0_LTS             (1 << 28)
+#define FTGMAC100_TXDES0_FTS             (1 << 29)
+#define FTGMAC100_TXDES0_TXDMA_OWN       (1 << 31)
+
+#define FTGMAC100_TXDES1_VLANTAG_CI(x)   ((x) & 0xffff)
+#define FTGMAC100_TXDES1_INS_VLANTAG     (1 << 16)
+#define FTGMAC100_TXDES1_TCP_CHKSUM      (1 << 17)
+#define FTGMAC100_TXDES1_UDP_CHKSUM      (1 << 18)
+#define FTGMAC100_TXDES1_IP_CHKSUM       (1 << 19)
+#define FTGMAC100_TXDES1_LLC             (1 << 22)
+#define FTGMAC100_TXDES1_TX2FIC          (1 << 30)
+#define FTGMAC100_TXDES1_TXIC            (1 << 31)
+
+/*
+ * Receive descriptor, aligned to 16 bytes
+ */
+struct ftgmac100_rxdes {
+        unsigned int        rxdes0;
+        unsigned int        rxdes1;
+        unsigned int        rxdes2;      /* not used by HW */
+        unsigned int        rxdes3;      /* RXBUF_BADR */
+} __attribute__ ((aligned(16)));
+
+#define FTGMAC100_RXDES0_VDBC            0x3fff
+#define FTGMAC100_RXDES0_EDORR           (1 << 15)
+#define FTGMAC100_RXDES0_MULTICAST       (1 << 16)
+#define FTGMAC100_RXDES0_BROADCAST       (1 << 17)
+#define FTGMAC100_RXDES0_RX_ERR          (1 << 18)
+#define FTGMAC100_RXDES0_CRC_ERR         (1 << 19)
+#define FTGMAC100_RXDES0_FTL             (1 << 20)
+#define FTGMAC100_RXDES0_RUNT            (1 << 21)
+#define FTGMAC100_RXDES0_RX_ODD_NB       (1 << 22)
+#define FTGMAC100_RXDES0_FIFO_FULL       (1 << 23)
+#define FTGMAC100_RXDES0_PAUSE_OPCODE    (1 << 24)
+#define FTGMAC100_RXDES0_PAUSE_FRAME     (1 << 25)
+#define FTGMAC100_RXDES0_LRS             (1 << 28)
+#define FTGMAC100_RXDES0_FRS             (1 << 29)
+#define FTGMAC100_RXDES0_RXPKT_RDY       (1 << 31)
+
+#define FTGMAC100_RXDES1_VLANTAG_CI      0xffff
+#define FTGMAC100_RXDES1_PROT_MASK       (0x3 << 20)
+#define FTGMAC100_RXDES1_PROT_NONIP      (0x0 << 20)
+#define FTGMAC100_RXDES1_PROT_IP         (0x1 << 20)
+#define FTGMAC100_RXDES1_PROT_TCPIP      (0x2 << 20)
+#define FTGMAC100_RXDES1_PROT_UDPIP      (0x3 << 20)
+#define FTGMAC100_RXDES1_LLC             (1 << 22)
+#define FTGMAC100_RXDES1_DF              (1 << 23)
+#define FTGMAC100_RXDES1_VLANTAG_AVAIL   (1 << 24)
+#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR  (1 << 25)
+#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR  (1 << 26)
+#define FTGMAC100_RXDES1_IP_CHKSUM_ERR   (1 << 27)
+
+/*
+ * PHY values (to be defined elsewhere ...)
+ */
+#define PHY_INT_ENERGYON            (1 << 7)
+#define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
+#define PHY_INT_FAULT               (1 << 5)
+#define PHY_INT_DOWN                (1 << 4)
+#define PHY_INT_AUTONEG_LP          (1 << 3)
+#define PHY_INT_PARFAULT            (1 << 2)
+#define PHY_INT_AUTONEG_PAGE        (1 << 1)
+
+/* Common Buffer Descriptor  */
+typedef struct {
+    uint32_t        des0;
+    uint32_t        des1;
+    uint32_t        des2;        /* not used by HW */
+    uint32_t        des3;        /* TXBUF_BADR */
+} Ftgmac100Desc  __attribute__ ((aligned(16)));
+
+/* max frame size is :
+ *
+ *   9216 for Jumbo frames (+ 4 for VLAN)
+ *   1518 for other frames (+ 4 for VLAN)
+ */
+#define FTGMAC100_MAX_FRAME_SIZE(s)                             \
+    ((s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4)
+
+static void ftgmac100_update_irq(Ftgmac100State *s);
+
+/*
+ * The MII phy could raise a GPIO to the processor which in turn
+ * could be handled as an interrpt by the OS.
+ * For now we don't handle any GPIO/interrupt line, so the OS will
+ * have to poll for the PHY status.
+ */
+static void phy_update_irq(Ftgmac100State *s)
+{
+    ftgmac100_update_irq(s);
+}
+
+static void phy_update_link(Ftgmac100State *s)
+{
+    /* Autonegotiation status mirrors link status.  */
+    if (qemu_get_queue(s->nic)->link_down) {
+        PHY_DEBUG("link is down\n");
+        s->phy_status &= ~0x0024;
+        s->phy_int |= PHY_INT_DOWN;
+    } else {
+        PHY_DEBUG("link is up\n");
+        s->phy_status |= 0x0024;
+        s->phy_int |= PHY_INT_ENERGYON;
+        s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
+    }
+    phy_update_irq(s);
+}
+
+static void ftgmac100_set_link(NetClientState *nc)
+{
+    phy_update_link(FTGMAC100(qemu_get_nic_opaque(nc)));
+}
+
+static void phy_reset(Ftgmac100State *s)
+{
+    s->phy_status = 0x7809;
+    s->phy_control = 0x3000;
+    s->phy_advertise = 0x01e1;
+    s->phy_int_mask = 0;
+    s->phy_int = 0;
+    phy_update_link(s);
+}
+
+static uint32_t do_phy_read(Ftgmac100State *s, int reg)
+{
+    uint32_t val;
+
+    if (reg > 31) {
+        /* we only advertise one phy */
+        return 0;
+    }
+
+    switch (reg) {
+    case 0:     /* Basic Control */
+        val = s->phy_control;
+        break;
+    case 1:     /* Basic Status */
+        val = s->phy_status;
+        break;
+    case 2:     /* ID1 */
+        val = 0x0007;
+        break;
+    case 3:     /* ID2 */
+        val = 0xc0d1;
+        break;
+    case 4:     /* Auto-neg advertisement */
+        val = s->phy_advertise;
+        break;
+    case 5:     /* Auto-neg Link Partner Ability */
+        val = 0x0f71;
+        break;
+    case 6:     /* Auto-neg Expansion */
+        val = 1;
+        break;
+        val = 0x0800;
+        break;
+    case 29:    /* Interrupt source.  */
+        val = s->phy_int;
+        s->phy_int = 0;
+        phy_update_irq(s);
+        break;
+    case 30:    /* Interrupt mask */
+        val = s->phy_int_mask;
+        break;
+    case 0x0a: /* 1000BASE-T status  */
+    case 17:
+    case 18:
+    case 27:
+    case 31:
+        qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
+                      __func__, reg);
+        val = 0;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
+                      __func__, reg);
+        val = 0;
+        break;
+    }
+
+    PHY_DEBUG("PHY read 0x%04x @%d\n", val, reg);
+    return val;
+}
+
+static void do_phy_write(Ftgmac100State *s, int reg, uint32_t val)
+{
+    PHY_DEBUG("PHY: write 0x%04x @%d\n", val, reg);
+
+    if (reg > 31) {
+        /* we only advertise one phy */
+        return;
+    }
+
+    switch (reg) {
+    case 0:     /* Basic Control */
+        if (val & 0x8000) {
+            phy_reset(s);
+        } else {
+            s->phy_control = val & 0x7980;
+            /* Complete autonegotiation immediately.  */
+            if (val & 0x1000) {
+                s->phy_status |= 0x0020;
+            }
+        }
+        break;
+    case 4:     /* Auto-neg advertisement */
+        s->phy_advertise = (val & 0x2d7f) | 0x80;
+        break;
+    case 30:    /* Interrupt mask */
+        s->phy_int_mask = val & 0xff;
+        phy_update_irq(s);
+        break;
+    case 17:
+    case 18:
+    case 27:
+    case 31:
+        qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
+                      __func__, reg);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
+                      __func__, reg);
+        break;
+    }
+}
+
+static void ftgmac100_read_bd(Ftgmac100Desc *bd, dma_addr_t addr)
+{
+    dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void ftgmac100_write_bd(Ftgmac100Desc *bd, dma_addr_t addr)
+{
+    dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
+}
+
+static void ftgmac100_update_irq(Ftgmac100State *s)
+{
+    uint32_t active;
+    uint32_t changed;
+
+    active = s->isr & s->ier;
+    changed = active ^ s->irq_state;
+    if (changed) {
+        qemu_set_irq(s->irq, active);
+    }
+    s->irq_state = active;
+}
+
+/* Locate a possible first descriptor to transmit. When Linux resets
+ * the device, the indexes of ring buffers are cleared but the dma
+ * buffers are not, so we need to find a starting point.
+ */
+static uint32_t ftgmac100_find_txdes(Ftgmac100State *s, uint32_t addr)
+{
+    Ftgmac100Desc bd;
+
+    while (1) {
+        ftgmac100_read_bd(&bd, addr);
+        if (bd.des0 & (FTGMAC100_TXDES0_FTS | FTGMAC100_TXDES0_EDOTR)) {
+            break;
+        }
+        addr += sizeof(Ftgmac100Desc);
+    }
+    return addr;
+}
+
+static void ftgmac100_do_tx(Ftgmac100State *s)
+{
+    int frame_size = 0;
+    uint8_t frame[FTGMAC100_MAX_FRAME_SIZE(s)];
+    uint8_t *ptr = frame;
+    uint32_t addr;
+
+    addr = ftgmac100_find_txdes(s, s->tx_descriptor);
+
+    while (1) {
+        Ftgmac100Desc bd;
+        int len;
+
+        ftgmac100_read_bd(&bd, addr);
+        if ((bd.des0 & FTGMAC100_TXDES0_TXDMA_OWN) == 0) {
+            /* Run out of descriptors to transmit.  */
+            break;
+        }
+        len = bd.des0 & 0x3FFF;
+        if (frame_size + len > FTGMAC100_MAX_FRAME_SIZE(s)) {
+            len = FTGMAC100_MAX_FRAME_SIZE(s) - frame_size;
+            s->isr |= FTGMAC100_INT_NO_NPTXBUF;
+        }
+        dma_memory_read(&address_space_memory, bd.des3, ptr, len);
+        ptr += len;
+        frame_size += len;
+        if (bd.des0 & FTGMAC100_TXDES0_LTS) {
+            /* Last buffer in frame.  */
+            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
+            ptr = frame;
+            frame_size = 0;
+            if (bd.des1 & FTGMAC100_TXDES1_TX2FIC) {
+                s->isr |= FTGMAC100_INT_XPKT_FIFO;
+            }
+            if (bd.des1 & FTGMAC100_TXDES1_TXIC) {
+                s->isr |= FTGMAC100_INT_XPKT_ETH;
+            }
+        }
+        bd.des0 &= ~FTGMAC100_TXDES0_TXDMA_OWN;
+
+        /* Write back the modified descriptor.  */
+        ftgmac100_write_bd(&bd, addr);
+        /* Advance to the next descriptor.  */
+        if (bd.des0 & FTGMAC100_TXDES0_EDOTR) {
+            addr = s->tx_ring;
+        } else {
+            addr += sizeof(Ftgmac100Desc);
+        }
+    }
+
+    s->tx_descriptor = addr;
+
+    ftgmac100_update_irq(s);
+}
+
+static void ftgmac100_enable_rx(Ftgmac100State *s)
+{
+    Ftgmac100Desc bd;
+    uint32_t full;
+
+    /* Find an empty descriptor to use */
+    while (1) {
+        ftgmac100_read_bd(&bd, s->rx_descriptor);
+        full = (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY);
+        if (!full || bd.des0 & FTGMAC100_TXDES0_EDOTR) {
+            break;
+        }
+        s->rx_descriptor += sizeof(Ftgmac100Desc);
+    }
+
+    if (full) {
+        DEBUG("RX buffer full\n");
+    }
+
+    s->rx_enabled = (full == 0);
+    if (s->rx_enabled) {
+        qemu_flush_queued_packets(qemu_get_queue(s->nic));
+    }
+}
+
+/*
+ * This is purely informative. The HW can poll the RW (and RX) ring
+ * buffers for available descriptors but we don't need to trigger a
+ * timer for that in qemu.
+ */
+static void ftgmac100_rxpoll(Ftgmac100State *s)
+{
+    /* Polling times :
+     *
+     * Speed      TIME_SEL=0    TIME_SEL=1
+     *
+     *    10         51.2 ms      819.2 ms
+     *   100         5.12 ms      81.92 ms
+     *  1000        1.024 ms     16.384 ms
+     */
+    static const int div[] = { 20, 200, 1000 };
+
+    uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr);
+    uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0;
+    uint32_t period __attribute__ ((unused));
+
+    if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) {
+        cnt <<= 4;
+    }
+
+    if (s->maccr & FTGMAC100_MACCR_GIGA_MODE) {
+        speed = 2;
+    }
+
+    period = cnt / div[speed];
+
+    DEBUG("polling in %d ms\n", period);
+}
+
+static void ftgmac100_reset(DeviceState *d)
+{
+    Ftgmac100State *s = FTGMAC100(d);
+
+    /* Reset the FTGMAC100 */
+    s->isr = 0;
+    s->ier = 0;
+    s->rx_enabled = 0;
+    s->maccr = 0;
+    s->rx_ring = 0;
+    s->rx_descriptor = 0;
+    s->rbsr = 0x640; /* HW default according to u-boot driver */
+    s->tx_ring = 0;
+    s->tx_descriptor = 0;
+    s->phycr = 0;
+    s->phydata = 0;
+    s->aptcr = 0;
+
+    /* We also reset the PHY */
+    phy_reset(s);
+}
+
+static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+
+    DEBUG("reading from @ 0x%" HWADDR_PRIx "\n", addr);
+
+    switch (addr & 0xff) {
+    case FTGMAC100_ISR:
+        return s->isr;
+    case FTGMAC100_IER:
+        return s->ier;
+    case FTGMAC100_MAC_MADR:
+        return (s->conf.macaddr.a[0] << 8)  | s->conf.macaddr.a[1];
+    case FTGMAC100_MAC_LADR:
+        return (s->conf.macaddr.a[2] << 24) | (s->conf.macaddr.a[3] << 16) |
+               (s->conf.macaddr.a[4] << 8)  |  s->conf.macaddr.a[5];
+    case FTGMAC100_MACCR:
+        return s->maccr;
+    case FTGMAC100_PHYCR:
+        return s->phycr;
+    case FTGMAC100_PHYDATA:
+        return s->phydata;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_FTGMAC100, __func__, addr);
+        return 0;
+    }
+}
+
+static void ftgmac100_write(void *opaque, hwaddr addr,
+                          uint64_t value, unsigned size)
+{
+    Ftgmac100State *s = FTGMAC100(opaque);
+
+    DEBUG("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr);
+
+    switch (addr & 0xff) {
+    case FTGMAC100_ISR: /* Interrupt status */
+        s->isr &= ~value;
+        break;
+    case FTGMAC100_IER:  /* Interrupt control */
+        s->ier = value;
+        break;
+    case FTGMAC100_MAC_MADR: /* MAC */
+        s->conf.macaddr.a[0] = value >> 8;
+        s->conf.macaddr.a[1] = value;
+        break;
+    case FTGMAC100_MAC_LADR:
+        s->conf.macaddr.a[2] = value >> 24;
+        s->conf.macaddr.a[3] = value >> 16;
+        s->conf.macaddr.a[4] = value >> 8;
+        s->conf.macaddr.a[5] = value;
+        break;
+
+    case FTGMAC100_RXR_BADR:  /* Ring buffer address */
+        s->rx_ring = value;
+        s->rx_descriptor = s->rx_ring;
+        break;
+
+    case FTGMAC100_RBSR: /* DMA buffer size */
+        s->rbsr = value;
+        break;
+
+    case FTGMAC100_NPTXR_BADR:  /* Transmit buffer address */
+        s->tx_ring = value;
+        s->tx_descriptor = s->tx_ring;
+        break;
+
+    case FTGMAC100_NPTXPD: /* Trigger transmit */
+        if (s->maccr & FTGMAC100_MACCR_TXDMA_EN) {
+            ftgmac100_do_tx(s);
+        }
+        break;
+
+    case FTGMAC100_APTC: /* Automatic polling */
+        s->aptcr = value;
+
+        if (FTGMAC100_APTC_RXPOLL_CNT(s->aptcr)) {
+            ftgmac100_rxpoll(s);
+        }
+
+        if (FTGMAC100_APTC_TXPOLL_CNT(s->aptcr)) {
+            qemu_log_mask(LOG_UNIMP, "%s: no transmit polling\n", __func__);
+        }
+        break;
+
+    case FTGMAC100_MACCR: /* MAC Device control */
+        s->maccr = value;
+        if (value & FTGMAC100_MACCR_SW_RST) {
+            ftgmac100_reset(DEVICE(s));
+        }
+
+        if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) && !s->rx_enabled) {
+            ftgmac100_enable_rx(s);
+        }
+
+        if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) == 0) {
+            s->rx_enabled = 0;
+        }
+        break;
+
+    case FTGMAC100_PHYCR:  /* PHY Device control */
+        s->phycr = value;
+        if (value & FTGMAC100_PHYCR_MIIWR) {
+            do_phy_write(s, extract32(value, 21, 5), s->phydata & 0xffff);
+            s->phycr &= ~FTGMAC100_PHYCR_MIIWR;
+       } else {
+            s->phydata = do_phy_read(s, extract32(value, 21, 5)) << 16;
+            s->phycr &= ~FTGMAC100_PHYCR_MIIRD;
+       }
+        break;
+    case FTGMAC100_PHYDATA:
+        s->phydata = value;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
+                      HWADDR_PRIx "\n", TYPE_FTGMAC100, __func__, addr);
+        break;
+    }
+
+    ftgmac100_update_irq(s);
+}
+
+static bool packet_is_broadcast(const uint8_t *buf)
+{
+    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
+                                              0xff, 0xff, 0xff};
+    return memcmp(buf, sa_bcast, 6) == 0;
+}
+
+static int ftgmac100_can_receive(NetClientState *nc)
+{
+    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
+
+    return s->rx_enabled;
+}
+
+static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
+                               size_t len)
+{
+    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
+    Ftgmac100Desc bd;
+    uint32_t flags = 0;
+    uint32_t addr;
+    uint32_t crc;
+    uint32_t buf_addr;
+    uint8_t *crc_ptr;
+    unsigned int buf_len;
+    size_t size = len;
+    uint32_t first = FTGMAC100_RXDES0_FRS;
+
+    DEBUG("len %d\n", (int)size);
+
+    if (!s->rx_enabled) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unexpected packet\n", __func__);
+        return 0;
+    }
+
+    /* FIXME: Pad short packets.  */
+
+    /* 4 bytes for the CRC.  */
+    size += 4;
+    crc = cpu_to_be32(crc32(~0, buf, size));
+    crc_ptr = (uint8_t *) &crc;
+
+    /* Huge frames are truncted.  */
+    if (size > FTGMAC100_MAX_FRAME_SIZE(s)) {
+        size = FTGMAC100_MAX_FRAME_SIZE(s);
+        flags |= FTGMAC100_RXDES0_FTL | FTGMAC100_RXDES0_RX_ERR;
+    }
+
+    if (packet_is_broadcast(buf)) {
+        flags |= FTGMAC100_RXDES0_BROADCAST;
+    }
+
+    addr = s->rx_descriptor;
+    while (size > 0) {
+        ftgmac100_read_bd(&bd, addr);
+        if (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY) {
+            /* No descriptors available.  Bail out.  */
+            /*
+             * FIXME: This is wrong. We should probably either
+             * save the remainder for when more RX buffers are
+             * available, or flag an error.
+             */
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: Lost end of frame\n",
+                          __func__);
+            s->isr |= FTGMAC100_INT_NO_RXBUF;
+            break;
+        }
+        buf_len = (size <= s->rbsr) ? size : s->rbsr;
+        bd.des0 |= buf_len & 0x3fff;
+        size -= buf_len;
+
+        /* The last 4 bytes are the CRC.  */
+        if (size < 4) {
+            buf_len += size - 4;
+        }
+        buf_addr = bd.des3;
+        dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
+        buf += buf_len;
+        if (size < 4) {
+            dma_memory_write(&address_space_memory, buf_addr + buf_len,
+                             crc_ptr, 4 - size);
+            crc_ptr += 4 - size;
+        }
+
+        bd.des0 |= first | FTGMAC100_RXDES0_RXPKT_RDY;
+        first = 0;
+        if (size == 0) {
+            /* Last buffer in frame.  */
+            bd.des0 |= flags | FTGMAC100_RXDES0_LRS;
+            DEBUG("rx frame flags %04x\n", bd.des0);
+            s->isr |= FTGMAC100_INT_RPKT_BUF;
+        } else {
+            s->isr |= FTGMAC100_INT_RPKT_FIFO;
+        }
+        ftgmac100_write_bd(&bd, addr);
+        if (bd.des0 & FTGMAC100_RXDES0_EDORR) {
+            addr = s->rx_ring;
+        } else {
+            addr += sizeof(Ftgmac100Desc);
+        }
+    }
+    s->rx_descriptor = addr;
+
+    ftgmac100_enable_rx(s);
+    ftgmac100_update_irq(s);
+    return len;
+}
+
+static const MemoryRegionOps ftgmac100_ops = {
+    .read = ftgmac100_read,
+    .write = ftgmac100_write,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void ftgmac100_cleanup(NetClientState *nc)
+{
+    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
+
+    s->nic = NULL;
+}
+
+static NetClientInfo net_ftgmac100_info = {
+    .type = NET_CLIENT_OPTIONS_KIND_NIC,
+    .size = sizeof(NICState),
+    .can_receive = ftgmac100_can_receive,
+    .receive = ftgmac100_receive,
+    .cleanup = ftgmac100_cleanup,
+    .link_status_changed = ftgmac100_set_link,
+};
+
+static void ftgmac100_realize(DeviceState *dev, Error **errp)
+{
+    Ftgmac100State *s = FTGMAC100(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+    memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s,
+                          TYPE_FTGMAC100, 0x2000);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq);
+    qemu_macaddr_default_if_unset(&s->conf.macaddr);
+
+    s->conf.peers.ncs[0] = nd_table[0].netdev;
+
+    s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
+                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
+                          s);
+    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static const VMStateDescription vmstate_ftgmac100 = {
+    .name = TYPE_FTGMAC100,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(irq_state, Ftgmac100State),
+        VMSTATE_UINT32(isr, Ftgmac100State),
+        VMSTATE_UINT32(ier, Ftgmac100State),
+        VMSTATE_UINT32(rx_enabled, Ftgmac100State),
+        VMSTATE_UINT32(rx_ring, Ftgmac100State),
+        VMSTATE_UINT32(rbsr, Ftgmac100State),
+        VMSTATE_UINT32(tx_ring, Ftgmac100State),
+        VMSTATE_UINT32(rx_descriptor, Ftgmac100State),
+        VMSTATE_UINT32(tx_descriptor, Ftgmac100State),
+        VMSTATE_UINT32(maccr, Ftgmac100State),
+        VMSTATE_UINT32(phycr, Ftgmac100State),
+        VMSTATE_UINT32(phydata, Ftgmac100State),
+        VMSTATE_UINT32(aptcr, Ftgmac100State),
+
+        VMSTATE_UINT32(phy_status, Ftgmac100State),
+        VMSTATE_UINT32(phy_control, Ftgmac100State),
+        VMSTATE_UINT32(phy_advertise, Ftgmac100State),
+        VMSTATE_UINT32(phy_int, Ftgmac100State),
+        VMSTATE_UINT32(phy_int_mask, Ftgmac100State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static Property ftgmac100_properties[] = {
+    DEFINE_NIC_PROPERTIES(Ftgmac100State, conf),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void ftgmac100_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->vmsd = &vmstate_ftgmac100;
+    dc->reset = ftgmac100_reset;
+    dc->props = ftgmac100_properties;
+    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
+    dc->realize = ftgmac100_realize;
+    dc->desc = "Faraday FTGMAC100 Gigabit Ethernet emulation";
+}
+
+static const TypeInfo ftgmac100_info = {
+    .name = TYPE_FTGMAC100,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Ftgmac100State),
+    .class_init = ftgmac100_class_init,
+};
+
+static void ftgmac100_register_types(void)
+{
+    type_register_static(&ftgmac100_info);
+}
+
+type_init(ftgmac100_register_types)
diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h
new file mode 100644
index 000000000000..cc5cb4207a5c
--- /dev/null
+++ b/include/hw/net/ftgmac100.h
@@ -0,0 +1,62 @@
+/*
+ * Faraday FTGMAC100 Gigabit Ethernet
+ *
+ * Copyright (C) 2016 IBM Corp.
+ *
+ * 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
+ * of the License, or (at your option) any later version.
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef FTGMAC100_H
+#define FTGMAC100_H
+
+#define TYPE_FTGMAC100 "ftgmac100"
+#define FTGMAC100(obj) OBJECT_CHECK(Ftgmac100State, (obj), TYPE_FTGMAC100)
+
+#include "hw/sysbus.h"
+#include "net/net.h"
+
+typedef struct Ftgmac100State {
+    /*< private >*/
+    SysBusDevice parent_obj;
+
+    /*< public >*/
+    NICState *nic;
+    NICConf conf;
+    qemu_irq irq;
+    MemoryRegion iomem;
+
+    uint32_t irq_state;
+    uint32_t isr;
+    uint32_t ier;
+    uint32_t rx_enabled;
+    uint32_t rx_ring;
+    uint32_t rbsr;
+    uint32_t rx_descriptor;
+    uint32_t tx_ring;
+    uint32_t tx_descriptor;
+    uint32_t maccr;
+    uint32_t phycr;
+    uint32_t phydata;
+    uint32_t aptcr;
+
+    uint32_t phy_status;
+    uint32_t phy_control;
+    uint32_t phy_advertise;
+    uint32_t phy_int;
+    uint32_t phy_int_mask;
+
+} Ftgmac100State;
+
+#endif
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (10 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 11/12] net: add FTGMAC100 support Cédric Le Goater
@ 2016-05-29 21:20 ` Cédric Le Goater
  2016-06-07  7:26   ` Andrew Jeffery
  2016-06-23 17:00 ` [PATCH qemu 00/12] SMC and network support Cédric Le Goater
  12 siblings, 1 reply; 30+ messages in thread
From: Cédric Le Goater @ 2016-05-29 21:20 UTC (permalink / raw)
  To: openbmc

Only the first nic, but the second shouldn't be too complex to add if
needed.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/ast2400.c         | 18 ++++++++++++++++++
 include/hw/arm/ast2400.h |  2 ++
 2 files changed, 20 insertions(+)

diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
index a4a7c10e4361..67ae91f5f977 100644
--- a/hw/arm/ast2400.c
+++ b/hw/arm/ast2400.c
@@ -19,6 +19,7 @@
 #include "hw/char/serial.h"
 #include "hw/boards.h"
 #include "hw/i2c/aspeed_i2c.h"
+#include "net/net.h"
 
 #define AST2400_UART_5_BASE      0x00184000
 #define AST2400_IOMEM_SIZE       0x00200000
@@ -32,6 +33,8 @@
 #define AST2400_LPC_BASE         0x1E789000
 #define AST2400_IBT_BASE         (AST2400_LPC_BASE + 0x140)
 #define AST2400_I2C_BASE         0x1E78A000
+#define AST2400_ETH1_BASE        0x1E660000
+#define AST2400_ETH2_BASE        0x1E680000
 
 static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
 static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
@@ -95,6 +98,10 @@ static void ast2400_init(Object *obj)
     object_initialize(&s->bt, sizeof(s->bt), TYPE_ASPEED_BT);
     object_property_add_child(obj, "bt", OBJECT(&s->bt), NULL);
     qdev_set_parent_bus(DEVICE(&s->bt), sysbus_get_default());
+
+    object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100);
+    object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL);
+    qdev_set_parent_bus(DEVICE(&s->ftgmac100), sysbus_get_default());
 }
 
 static void ast2400_realize(DeviceState *dev, Error **errp)
@@ -221,6 +228,17 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->bt), 0, AST2400_IBT_BASE);
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->bt), 0,
                        qdev_get_gpio_in(DEVICE(&s->vic), 10));
+
+    /* Net */
+    qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
+    object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, AST2400_ETH1_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0,
+                       qdev_get_gpio_in(DEVICE(&s->vic), 2));
 }
 
 static void ast2400_class_init(ObjectClass *oc, void *data)
diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
index 2b517e89eeea..bb221734c7b1 100644
--- a/include/hw/arm/ast2400.h
+++ b/include/hw/arm/ast2400.h
@@ -19,6 +19,7 @@
 #include "hw/i2c/aspeed_i2c.h"
 #include "hw/ssi/aspeed_smc.h"
 #include "hw/misc/aspeed_bt.h"
+#include "hw/net/ftgmac100.h"
 
 typedef struct AST2400State {
     /*< private >*/
@@ -35,6 +36,7 @@ typedef struct AST2400State {
     AspeedSMCState smc;
     AspeedSMCState spi;
     AspeedBTState bt;
+    Ftgmac100State ftgmac100;
 } AST2400State;
 
 #define TYPE_AST2400 "ast2400"
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI)
  2016-05-29 21:19 ` [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI) Cédric Le Goater
@ 2016-06-07  2:08   ` Andrew Jeffery
  2016-06-08 11:53     ` Cédric Le Goater
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  2:08 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 18207 bytes --]

On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> This patch provides a simple device model for the SMC controllers
> available on the Aspeed AST2400 soc. Support is limited to the FMC and
> the SPI controllers, and to SPI flash slaves.
> 
> Below is the initial framework : the sysbus object, MMIO for registers
> configuration and controls. Each controller has a SPI bus and a
> configurable number of CS lines for SPI flash slaves.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/arm/ast2400.c            |  33 ++++++
>  hw/ssi/Makefile.objs        |   1 +
>  hw/ssi/aspeed_smc.c         | 260 ++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/arm/ast2400.h    |   3 +
>  include/hw/ssi/aspeed_smc.h |  68 ++++++++++++
>  5 files changed, 365 insertions(+)
>  create mode 100644 hw/ssi/aspeed_smc.c
>  create mode 100644 include/hw/ssi/aspeed_smc.h
> 
> diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
> index 23d52bffcaa7..8f678983cee5 100644
> --- a/hw/arm/ast2400.c
> +++ b/hw/arm/ast2400.c
> @@ -23,6 +23,9 @@
>  #define AST2400_UART_5_BASE      0x00184000
>  #define AST2400_IOMEM_SIZE       0x00200000
>  #define AST2400_IOMEM_BASE       0x1E600000
> +#define AST2400_SMC_BASE         AST2400_IOMEM_BASE /* Legacy SMC */
> +#define AST2400_FMC_BASE         0X1E620000
> +#define AST2400_SPI_BASE         0X1E630000
>  #define AST2400_VIC_BASE         0x1E6C0000
>  #define AST2400_SCU_BASE         0x1E6E2000
>  #define AST2400_TIMER_BASE       0x1E782000
> @@ -77,6 +80,14 @@ static void ast2400_init(Object *obj)
>      object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
>      object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
>      qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
> +
> +    object_initialize(&s->smc, sizeof(s->smc), TYPE_ASPEED_SMC);
> +    object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
> +    qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
> +
> +    object_initialize(&s->spi, sizeof(s->spi), TYPE_ASPEED_SMC);
> +    object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
> +    qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
>  }
>  
>  static void ast2400_realize(DeviceState *dev, Error **errp)
> @@ -171,6 +182,28 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
>      /* The palmetto platform expects a ds3231 RTC but a ds1338 is
>       * enough to provide basic RTC features. Alarms will be missing */
>      i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&s->i2c), 0), "ds1338", 0x68);
> +
> +    /* SMC */
> +    object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
> +    object_property_set_int(OBJECT(&s->spi), AspeedSMCFMC, "smc-type", &err);
> +    object_property_set_bool(OBJECT(&s->smc), true, "realized", &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->vic), 19));
> +
> +    /* SPI */
> +    object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
> +    object_property_set_int(OBJECT(&s->spi), AspeedSMCSPI, "smc-type", &err);
> +    object_property_set_bool(OBJECT(&s->spi), true, "realized", &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
>  }
>  
>  static void ast2400_class_init(ObjectClass *oc, void *data)
> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
> index 9555825acad1..6b32bf22ce3b 100644
> --- a/hw/ssi/Makefile.objs
> +++ b/hw/ssi/Makefile.objs
> @@ -2,5 +2,6 @@ common-obj-$(CONFIG_PL022) += pl022.o
>  common-obj-$(CONFIG_SSI) += ssi.o
>  common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>  common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
> +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
>  
>  obj-$(CONFIG_OMAP) += omap_spi.o
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> new file mode 100644
> index 000000000000..780fcbbc9e55
> --- /dev/null
> +++ b/hw/ssi/aspeed_smc.c
> @@ -0,0 +1,260 @@
> +/*
> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * 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 "hw/sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "qemu/log.h"
> +#include "include/qemu/error-report.h"
> +#include "exec/address-spaces.h"
> +
> +#include "hw/ssi/aspeed_smc.h"
> +
> +/* CE Type Setting Register */
> +#define R_CONF            (0x00 / 4)
> +#define   CONF_LEGACY_DISABLE  (1 << 31)
> +#define   CONF_ENABLE_W4       20
> +#define   CONF_ENABLE_W3       19
> +#define   CONF_ENABLE_W2       18
> +#define   CONF_ENABLE_W1       17
> +#define   CONF_ENABLE_W0       16
> +#define   CONF_FLASH_TYPE4     9
> +#define   CONF_FLASH_TYPE3     7
> +#define   CONF_FLASH_TYPE2     5
> +#define   CONF_FLASH_TYPE1     3
> +#define   CONF_FLASH_TYPE0     1
> +
> +/* CE Control Register */
> +#define R_CTRL            (0x04 / 4)
> +#define   CRTL_EXTENDED4       4  /* 32 bit addressing for SPI */
> +#define   CRTL_EXTENDED3       3  /* 32 bit addressing for SPI */
> +#define   CRTL_EXTENDED2       2  /* 32 bit addressing for SPI */
> +#define   CRTL_EXTENDED1       1  /* 32 bit addressing for SPI */
> +#define   CRTL_EXTENDED0       0  /* 32 bit addressing for SPI */
> +
> +/* Interrupt Control and Status Register */
> +#define R_INTR_CTRL       (0x08 / 4)
> +
> +/* CEx Control Register */
> +#define R_CTRL0           (0x10 / 4)
> +#define   CTRL_CE_STOP_ACTIVE      (1 << 2)
> +#define   CTRL_USERMODE            0x3
> +#define R_CTRL1           (0x14 / 4)
> +#define R_CTRL2           (0x18 / 4)
> +#define R_CTRL3           (0x1C / 4)
> +#define R_CTRL4           (0x20 / 4)
> +
> +/* CEx Segment Address Register */
> +#define R_SEG_ADDR0       (0x30 / 4)
> +#define   SEG_SIZE_SHIFT       24   /* 8MB units */
> +#define   SEG_SIZE_MASK        0x7f
> +#define   SEG_START_SHIFT      16   /* address bit [A29-A23] */
> +#define   SEG_START_MASK       0x7f
> +#define R_SEG_ADDR1       (0x34 / 4)
> +#define R_SEG_ADDR2       (0x38 / 4)
> +#define R_SEG_ADDR3       (0x3C / 4)
> +#define R_SEG_ADDR4       (0x40 / 4)
> +
> +/* Misc Control Register #1 */
> +#define R_MISC_CRTL1      (0x50 / 4)
> +
> +/* Misc Control Register #2 */
> +#define R_MISC_CRTL2      (0x54 / 4)
> +
> +
> +/* SPI controller registers and bits (that we care about) */
> +#define R_SPI_CONF        (0x00 / 4)
> +#define   SPI_CONF_ENABLE_W0   0
> +#define R_SPI_CTRL0       (0x4 / 4)
> +#define R_SPI_MISC_CRTL   (0x10 / 4)
> +#define R_SPI_TIMINGS     (0x14 / 4)
> +
> +static const int aspeed_smc_r_ctrl0[]   = { R_CTRL0, R_CTRL0, R_SPI_CTRL0 };
> +static const int aspeed_smc_r_conf[]    = { R_CONF,  R_CONF,  R_SPI_CONF  };
> +static const int aspeed_smc_conf_enable_w0[] = {
> +    CONF_ENABLE_W0, CONF_ENABLE_W0, SPI_CONF_ENABLE_W0 };
> +
> +
> +static bool aspeed_smc_is_ce_stop_active(AspeedSMCState *s, int cs)
> +{
> +    return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
> +}
> +
> +static void aspeed_smc_update_cs(AspeedSMCState *s)
> +{
> +    int i;
> +
> +    for (i = 0; i < s->num_cs; ++i) {
> +        if (aspeed_smc_is_ce_stop_active(s, i)) {
> +            /* should reset command byte in control reg */

Can you expand on why this hasn't been implemented? I assume there are
no side-effects?

> +        }
> +
> +        qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
> +    }
> +}
> +
> +static void aspeed_smc_reset(DeviceState *d)
> +{
> +    AspeedSMCState *s = ASPEED_SMC(d);
> +    int i;
> +
> +    memset(s->regs, 0, sizeof s->regs);
> +
> +    for (i = 0; i < s->num_cs; ++i) {
> +        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
> +    }
> +
> +    aspeed_smc_update_cs(s);
> +}
> +
> +static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    AspeedSMCState *s = ASPEED_SMC(opaque);
> +    uint32_t r = 0;
> +
> +    addr >>= 2;
> +    switch (addr) {
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            r = s->regs[addr];
> +        }
> +        break;
> +    }

Is there a good reason to use the switch here if there's only a default
case?

> +
> +    return r;
> +}
> +
> +static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
> +                             unsigned int size)
> +{
> +    AspeedSMCState *s = ASPEED_SMC(opaque);
> +    uint32_t value = data;
> +
> +    addr >>= 2;
> +    switch (addr) {
> +    default:
> +        if (addr < ARRAY_SIZE(s->regs)) {
> +            s->regs[addr] = value;
> +        }
> +        break;
> +    }

Similar to above, though with ranges maybe we could accommodate the
condition below?

> +
> +    if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
> +        aspeed_smc_update_cs(s);
> +    }
> +}
> +
> +static const MemoryRegionOps aspeed_smc_ops = {
> +    .read = aspeed_smc_read,
> +    .write = aspeed_smc_write,
> +    .endianness = DEVICE_NATIVE_ENDIAN,

I think I asked about this previously - should it be
DEVICE_LITTLE_ENDIAN?

> +    .valid.unaligned = true,
> +};
> +
> +static const char * const aspeed_smc_types[] = { "smc", "fmc", "spi" };
> +static const int aspeed_smc_max_slaves[] = { 5, 5, 1 };
> +
> +static int aspeed_smc_init(SysBusDevice *sbd)
> +{
> +    DeviceState *dev = DEVICE(sbd);
> +    AspeedSMCState *s = ASPEED_SMC(dev);
> +    int i;
> +    char name[16];
> +
> +    s->spi = ssi_create_bus(dev, "spi");
> +
> +    /* Enforce some real HW limits */
> +    if (s->num_cs > aspeed_smc_max_slaves[s->smc_type]) {
> +        s->num_cs = aspeed_smc_max_slaves[s->smc_type];
> +    }
> +
> +    /* Setup cs_lines for slaves */
> +    sysbus_init_irq(sbd, &s->irq);
> +    s->cs_lines = g_new0(qemu_irq, s->num_cs);
> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
> +
> +    for (i = 0; i < s->num_cs; ++i) {
> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
> +    }
> +
> +    /* There are some differences in the register numbers and bits
> +     * between the FMC and SPI controller. Let's abstract these.
> +     */
> +    s->r_ctrl0 = aspeed_smc_r_ctrl0[s->smc_type];
> +    s->r_conf = aspeed_smc_r_conf[s->smc_type];
> +    s->conf_enable_w0 = aspeed_smc_conf_enable_w0[s->smc_type];

A little hairy, but understandable in that we avoid two mostly similar
implementations.

> +
> +    /* Unselect all slaves */
> +    for (i = 0; i < s->num_cs; ++i) {
> +        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
> +    }
> +
> +    snprintf(name, sizeof(name), "aspeed.%s", aspeed_smc_types[s->smc_type]);
> +    memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
> +                          name, ASPEED_SMC_R_MAX * 4);
> +    sysbus_init_mmio(sbd, &s->mmio);
> +
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_aspeed_smc = {
> +    .name = "aspeed.smc",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property aspeed_smc_properties[] = {
> +    DEFINE_PROP_UINT8("num-cs", AspeedSMCState, num_cs, 1),
> +    DEFINE_PROP_UINT8("smc-type", AspeedSMCState, smc_type, AspeedSMCFMC),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void aspeed_smc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = aspeed_smc_init;
> +    dc->reset = aspeed_smc_reset;
> +    dc->props = aspeed_smc_properties;
> +    dc->vmsd = &vmstate_aspeed_smc;
> +}
> +
> +static const TypeInfo aspeed_smc_info = {
> +    .name           = TYPE_ASPEED_SMC,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(AspeedSMCState),
> +    .class_init     = aspeed_smc_class_init,
> +};
> +
> +static void aspeed_smc_register_types(void)
> +{
> +    type_register_static(&aspeed_smc_info);
> +}
> +
> +type_init(aspeed_smc_register_types)
> diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
> index e96e3db3fbea..9ba4245619c1 100644
> --- a/include/hw/arm/ast2400.h
> +++ b/include/hw/arm/ast2400.h
> @@ -17,6 +17,7 @@
>  #include "hw/misc/aspeed_scu.h"
>  #include "hw/timer/aspeed_timer.h"
>  #include "hw/i2c/aspeed_i2c.h"
> +#include "hw/ssi/aspeed_smc.h"
>  
>  typedef struct AST2400State {
>      /*< private >*/
> @@ -30,6 +31,8 @@ typedef struct AST2400State {
>      AspeedTimerCtrlState timerctrl;
>      AspeedSCUState scu;
>      AspeedI2CState i2c;
> +    AspeedSMCState smc;
> +    AspeedSMCState spi;
>  } AST2400State;
>  
>  #define TYPE_AST2400 "ast2400"
> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
> new file mode 100644
> index 000000000000..9b95fcee5da7
> --- /dev/null
> +++ b/include/hw/ssi/aspeed_smc.h
> @@ -0,0 +1,68 @@
> +/*
> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef ASPEED_SMC_H
> +#define ASPEED_SMC_H
> +
> +#include "hw/ssi/ssi.h"
> +
> +enum AspeedSMCType {
> +    AspeedSMCLegacy,
> +    AspeedSMCFMC,
> +    AspeedSMCSPI,
> +};
> +
> +#define TYPE_ASPEED_SMC "aspeed.smc"
> +#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC)
> +
> +#define ASPEED_SMC_R_MAX        (0x100 / 4)
> +
> +typedef struct AspeedSMCState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +
> +    qemu_irq irq;
> +    int irqline;
> +
> +    uint8_t num_cs;
> +    qemu_irq *cs_lines;
> +
> +    SSIBus *spi;
> +
> +    uint32_t regs[ASPEED_SMC_R_MAX];
> +
> +    uint8_t smc_type;
> +
> +    /* depends on the controller type */
> +    uint8_t r_conf;
> +    uint8_t r_ctrl0;
> +    uint8_t conf_enable_w0;
> +} AspeedSMCState;
> +
> +#define TYPE_ASPEED_SPI "aspeed.spi"
> +#define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
> +
> +
> +#endif /* ASPEED_SMC_H */

Otherwise, looks good to me.

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 02/12] ast2400: add SPI flash slave object
  2016-05-29 21:19 ` [PATCH qemu 02/12] ast2400: add SPI flash slave object Cédric Le Goater
@ 2016-06-07  5:34   ` Andrew Jeffery
  2016-06-08 12:42     ` Cédric Le Goater
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  5:34 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 6204 bytes --]

On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> Each SPI flash slave can operate in two modes: Command and User. When
> in User mode, accesses to the memory segment of the slaves are
> translated in SPI transfers. When in Command mode, the HW generates
> the SPI commands automatically and the memory segment is accessed as
> if doing a MMIO. Other SPI controllers call that mode linear
> addressing mode.
> 
> The patch below provides an initial model for a SPI flash module,
> gathering SPI slave properties and a MemoryRegion to handle the memory
> accesses. Only the User mode is supported for now but the patch
> prepares ground for the Command mode.
> 
> Using a sysbus object for this abstraction might be a bit complex for
> the need. We could probably survive with a simple struct under
> AspeedSMCState or we could extend the m25p80 object providing a model
> for the SPI flash modules. To be discussed.

The patch seems reasonable to me, though if we took the struct-under-
AspeedSMCState approach we would register the same MemoryRegions, but
via the AspeedSMCState's SysBusDevice? Can you expand on extending the
m25p80? Is that just doing the same we do here in aspeed_smc in m25p80?
Or something else?

Cheers,

Andrew

> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/ssi/aspeed_smc.c         | 110 ++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ssi/aspeed_smc.h |  15 ++++++
>  2 files changed, 125 insertions(+)
> 
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> index 780fcbbc9e55..43743628ba0c 100644
> --- a/hw/ssi/aspeed_smc.c
> +++ b/hw/ssi/aspeed_smc.c
> @@ -258,3 +258,113 @@ static void aspeed_smc_register_types(void)
>  }
>  
>  type_init(aspeed_smc_register_types)
> +
> +static inline bool aspeed_smc_is_usermode(AspeedSMCState *s, int cs)
> +{
> +    return (((s->regs[s->r_ctrl0 + cs] & CTRL_USERMODE) == CTRL_USERMODE) &&
> +            !aspeed_smc_is_ce_stop_active(s, cs));
> +}
> +
> +static inline bool aspeed_smc_is_writable(AspeedSMCState *s, int cs)
> +{
> +    return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs));
> +}
> +
> +static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
> +    AspeedSMCState *s = fl->controller;
> +    uint64_t ret = 0;
> +    int i;
> +
> +    if (aspeed_smc_is_usermode(s, fl->id)) {
> +        for (i = 0; i < size; i++) {
> +            ret = (ret << 8) | ssi_transfer(s->spi, 0x0);
> +        }
> +    } else {
> +        error_report("%s: flash not in usermode", __func__);
> +        ret = -1;
> +    }
> +
> +    return ret;
> +}
> +
> +static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
> +                           unsigned size)
> +{
> +    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
> +    AspeedSMCState *s = fl->controller;
> +    int i;
> +
> +    if (!aspeed_smc_is_writable(s, fl->id)) {
> +        error_report("%s: flash not in writable", __func__);
> +        return;
> +    }
> +
> +    if (!aspeed_smc_is_usermode(s, fl->id)) {
> +        error_report("%s: flash not in usermode", __func__);
> +        return;
> +    }
> +
> +    for (i = 0; i < size; i++) {
> +        ssi_transfer(s->spi, (data >> 8 * (size - 1 - i)) & 0xff);
> +    }
> +}
> +
> +static const MemoryRegionOps aspeed_smc_flash_ops = {
> +    .read = aspeed_smc_flash_read,
> +    .write = aspeed_smc_flash_write,
> +    .endianness = DEVICE_BIG_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void aspeed_smc_flash_reset(DeviceState *d)
> +{
> +    ;
> +}
> +
> +static int aspeed_smc_flash_init(SysBusDevice *sbd)
> +{
> +    return 0;
> +}
> +
> +static const VMStateDescription vmstate_aspeed_smc_flash = {
> +    .name = "aspeed.smc_flash",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property aspeed_smc_flash_properties[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> +    k->init = aspeed_smc_flash_init;
> +    dc->reset = aspeed_smc_flash_reset;
> +    dc->props = aspeed_smc_flash_properties;
> +    dc->vmsd = &vmstate_aspeed_smc_flash;
> +}
> +
> +static const TypeInfo aspeed_smc_flash_info = {
> +    .name           = TYPE_ASPEED_SMC_FLASH,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(AspeedSMCState),
> +    .class_init     = aspeed_smc_flash_class_init,
> +};
> +
> +static void aspeed_smc_flash_register_types(void)
> +{
> +    type_register_static(&aspeed_smc_flash_info);
> +}
> +
> +type_init(aspeed_smc_flash_register_types)
> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
> index 9b95fcee5da7..6cea1313eabd 100644
> --- a/include/hw/ssi/aspeed_smc.h
> +++ b/include/hw/ssi/aspeed_smc.h
> @@ -27,6 +27,21 @@
>  
>  #include "hw/ssi/ssi.h"
>  
> +#define TYPE_ASPEED_SMC_FLASH "aspeed.smc_flash"
> +#define ASPEED_SMC_FLASH(obj) \
> +    OBJECT_CHECK(AspeedSMCFlashState, (obj), TYPE_ASPEED_SMC_FLASH)
> +
> +struct AspeedSMCState;
> +
> +typedef struct AspeedSMCFlashState {
> +    SysBusDevice parent_obj;
> +
> +    MemoryRegion mmio;
> +    int id;
> +    struct AspeedSMCState *controller;
> +    DeviceState *flash;
> +} AspeedSMCFlashState;
> +
>  enum AspeedSMCType {
>      AspeedSMCLegacy,
>      AspeedSMCFMC,

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 03/12] ast2400: create SPI flash slaves
  2016-05-29 21:19 ` [PATCH qemu 03/12] ast2400: create SPI flash slaves Cédric Le Goater
@ 2016-06-07  5:56   ` Andrew Jeffery
  2016-06-08 12:46     ` Cédric Le Goater
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  5:56 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 5186 bytes --]

On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> This patch creates a number of SPI flash slaves of the same type under
> the SMC/FMC and under the SMC/SPI controllers. We use a "n25q256a"
> flash module type for the BMC and a "mx25l25635e" for the host. These
> types are common in the OpenPower ecosystem.
> 
> The segment addresses used for the memory mappings are the defaults
> provided by the specs. They can be changed with the Segment Address
> Register but this is not supported in the current implementation.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

> ---
>  hw/arm/palmetto-bmc.c       |  3 +++
>  hw/ssi/aspeed_smc.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/ssi/aspeed_smc.h |  3 +++
>  3 files changed, 68 insertions(+)
> 
> diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
> index 6367f978bc7b..c4099987d354 100644
> --- a/hw/arm/palmetto-bmc.c
> +++ b/hw/arm/palmetto-bmc.c
> @@ -50,6 +50,9 @@ static void palmetto_bmc_init(MachineState *machine)
>      object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
>                               &error_abort);
>  
> +    aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
> +    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
> +
>      palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
>      palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
>      palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> index 43743628ba0c..4175356141e7 100644
> --- a/hw/ssi/aspeed_smc.c
> +++ b/hw/ssi/aspeed_smc.c
> @@ -215,6 +215,8 @@ static int aspeed_smc_init(SysBusDevice *sbd)
>                            name, ASPEED_SMC_R_MAX * 4);
>      sysbus_init_mmio(sbd, &s->mmio);
>  
> +    s->flashes = g_new0(AspeedSMCFlashState *, s->num_cs);
> +
>      return 0;
>  }
>  
> @@ -368,3 +370,63 @@ static void aspeed_smc_flash_register_types(void)
>  }
>  
>  type_init(aspeed_smc_flash_register_types)
> +
> +/*
> + * Default segments mappings and size for each slave
> + */
> +typedef struct AspeedSegments {
> +    hwaddr addr;
> +    uint32_t size;
> +} AspeedSegments;
> +
> +/* unused */
> +static const AspeedSegments aspeed_segment_legacy[] = {
> +    { 0x14000000, 32 * 1024 * 1024 },
> +};
> +
> +static const AspeedSegments aspeed_segment_fmc[] = {
> +    { 0x20000000, 64 * 1024 * 1024 },
> +    { 0x24000000, 32 * 1024 * 1024 },
> +    { 0x26000000, 32 * 1024 * 1024 },
> +    { 0x28000000, 32 * 1024 * 1024 },
> +    { 0x2A000000, 32 * 1024 * 1024 }
> +};
> +
> +static const AspeedSegments aspeed_segment_spi[] = {
> +    { 0x30000000, 64 * 1024 * 1024 },
> +};
> +
> +static const AspeedSegments *aspeed_segments[] = {
> +    aspeed_segment_legacy, aspeed_segment_fmc, aspeed_segment_spi
> +};
> +
> +void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype)
> +{
> +    int i ;
> +    char name[32];
> +
> +    for (i = 0; i < s->num_cs; ++i) {
> +        Object *new = object_new(TYPE_ASPEED_SMC_FLASH);
> +        AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(new);
> +        qemu_irq cs_line;
> +
> +        s->flashes[i] = fl;
> +
> +        snprintf(name, sizeof(name), "aspeed.%s.%d",
> +                 aspeed_smc_types[s->smc_type], i);
> +
> +        fl->id = i;
> +        fl->controller = s;
> +        fl->flash = ssi_create_slave(s->spi, flashtype);
> +
> +        cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
> +        sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
> +
> +        memory_region_init_io(&fl->mmio, new, &aspeed_smc_flash_ops, fl, name,
> +                              aspeed_segments[s->smc_type][i].size);
> +        sysbus_init_mmio(SYS_BUS_DEVICE(new), &fl->mmio);
> +
> +        sysbus_mmio_map(SYS_BUS_DEVICE(new), 0,
> +                        aspeed_segments[s->smc_type][i].addr);
> +    }
> +}
> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
> index 6cea1313eabd..1625dfb76a63 100644
> --- a/include/hw/ssi/aspeed_smc.h
> +++ b/include/hw/ssi/aspeed_smc.h
> @@ -74,10 +74,13 @@ typedef struct AspeedSMCState {
>      uint8_t r_conf;
>      uint8_t r_ctrl0;
>      uint8_t conf_enable_w0;
> +
> +    AspeedSMCFlashState **flashes;
>  } AspeedSMCState;
>  
>  #define TYPE_ASPEED_SPI "aspeed.spi"
>  #define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
>  
> +extern void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype);

extern seems superfluous here? Doesn't really matter though.

Cheers,

Andrew

>  
>  #endif /* ASPEED_SMC_H */

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only)
  2016-05-29 21:19 ` [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only) Cédric Le Goater
@ 2016-06-07  6:04   ` Andrew Jeffery
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  6:04 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 3286 bytes --]

On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> We can now use directly the m25p80 flash storage to handle memory
> accesses when the SPI flash slave is configured in Command mode. Only
> read only accesses are handled. Command mode is only required by
> U-Boot, Linux does not use it yet.
> 
> Handling write accesses would demand more changes to the m25p80 flash
> model.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Assuming the m25p80_get_storage() is acceptable upstream,

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

> ---
>  hw/ssi/aspeed_smc.c         | 18 +++++++++++++++---
>  include/hw/ssi/aspeed_smc.h |  2 ++
>  2 files changed, 17 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> index 4175356141e7..159ab691051d 100644
> --- a/hw/ssi/aspeed_smc.c
> +++ b/hw/ssi/aspeed_smc.c
> @@ -28,6 +28,7 @@
>  #include "qemu/log.h"
>  #include "include/qemu/error-report.h"
>  #include "exec/address-spaces.h"
> +#include "hw/block/flash.h"
>  
>  #include "hw/ssi/aspeed_smc.h"
>  
> @@ -284,8 +285,9 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
>              ret = (ret << 8) | ssi_transfer(s->spi, 0x0);
>          }
>      } else {
> -        error_report("%s: flash not in usermode", __func__);
> -        ret = -1;
> +        for (i = 0; i < size; i++) {
> +            ret = (ret << 8) | fl->storage[addr + i];
> +        }
>      }
>  
>      return ret;
> @@ -330,6 +332,11 @@ static void aspeed_smc_flash_reset(DeviceState *d)
>  
>  static int aspeed_smc_flash_init(SysBusDevice *sbd)
>  {
> +    DeviceState *dev = DEVICE(sbd);
> +    AspeedSMCFlashState *s = ASPEED_SMC_FLASH(dev);
> +
> +    s->size = 0;
> +    s->storage = NULL;
>      return 0;
>  }
>  
> @@ -422,8 +429,13 @@ void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype)
>          cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
>          sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
>  
> +        fl->storage = m25p80_get_storage(DEVICE(fl->flash), &fl->size);
> +        if (fl->size > aspeed_segments[s->smc_type][i].size) {
> +            fl->size = aspeed_segments[s->smc_type][i].size;
> +        }
> +
>          memory_region_init_io(&fl->mmio, new, &aspeed_smc_flash_ops, fl, name,
> -                              aspeed_segments[s->smc_type][i].size);
> +                              fl->size);
>          sysbus_init_mmio(SYS_BUS_DEVICE(new), &fl->mmio);
>  
>          sysbus_mmio_map(SYS_BUS_DEVICE(new), 0,
> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
> index 1625dfb76a63..2615ce9fa8aa 100644
> --- a/include/hw/ssi/aspeed_smc.h
> +++ b/include/hw/ssi/aspeed_smc.h
> @@ -38,6 +38,8 @@ typedef struct AspeedSMCFlashState {
>  
>      MemoryRegion mmio;
>      int id;
> +    uint32_t size;
> +    uint8_t *storage;
>      struct AspeedSMCState *controller;
>      DeviceState *flash;
>  } AspeedSMCFlashState;

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom
  2016-05-29 21:19 ` [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom Cédric Le Goater
@ 2016-06-07  6:24   ` Andrew Jeffery
  2016-06-09 10:42     ` Cédric Le Goater
  0 siblings, 1 reply; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  6:24 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 2452 bytes --]

On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> This provides support for U-Boot images which are loaded at 0x0.
> 
> A Palmetto BMC guest can now be simply booted with :
> 
>   $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
> 	-mtdblock ./flash-palmetto-20160512040959  \
> 	-mtdblock ./palmetto.pnor
> 
> The first block device uses the file "./flash-palmetto-20160512040959"
> which will act as a SPI flash module for the BMC, handled by the
> SMC/FMC controller. The second block device uses the file
> "./palmetto.pnor" which is an OpenPower firmware image for a palmetto
> OpenPower system. This one will be handled by the SMC/SPI controller.
> 
> The flash images can be grabbed here :
> 
>     https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
>     https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor
> 
> We could add a second BMC SPI flash by changing the 'num-cs' property
> of the controller and emulate a golden image flash module.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
> 
>  That might seem a little too hacky ? 
> 
>  hw/arm/palmetto-bmc.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
> index c4099987d354..50369c0331a6 100644
> --- a/hw/arm/palmetto-bmc.c
> +++ b/hw/arm/palmetto-bmc.c
> @@ -17,6 +17,7 @@
>  #include "hw/arm/arm.h"
>  #include "hw/arm/ast2400.h"
>  #include "hw/boards.h"
> +#include "hw/loader.h"
>  
>  static struct arm_boot_info palmetto_bmc_binfo = {
>      .loader_start = AST2400_SDRAM_BASE,
> @@ -53,6 +54,14 @@ static void palmetto_bmc_init(MachineState *machine)
>      aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
>      aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
>  
> +    /*
> +     * Install first SMC/FMC flash content as a rom.
> +     */
> +    if (bmc->soc.smc.flashes[0]) {
> +        rom_add_blob_fixed("aspeed.smc.0", bmc->soc.smc.flashes[0]->storage,
> +                           bmc->soc.smc.flashes[0]->size, 0);
> +    }
> +

As mentioned on IRC it's probably best we investigate alternative
approaches and use this as a last resort.

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 09/12] ast2400: use a mx25l25635f chip
  2016-05-29 21:20 ` [PATCH qemu 09/12] ast2400: use a " Cédric Le Goater
@ 2016-06-07  6:29   ` Andrew Jeffery
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  6:29 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 1030 bytes --]

On Sun, 2016-05-29 at 23:20 +0200, Cédric Le Goater wrote:
> The pflash command on OpenBmc is used to update the flash and it
> expects a mx25l25635f and not a mx25l25635e.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Assuming the m25p80 changes are fine,

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

> ---
>  hw/arm/palmetto-bmc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
> index 50369c0331a6..a1db516baf9e 100644
> --- a/hw/arm/palmetto-bmc.c
> +++ b/hw/arm/palmetto-bmc.c
> @@ -52,7 +52,7 @@ static void palmetto_bmc_init(MachineState
> *machine)
>                               &error_abort);
>  
>      aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
> -    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
> +    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635f");
>  
>      /*
>       * Install first SMC/FMC flash content as a rom.

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 10/12] ast2400: add a BT device
  2016-05-29 21:20 ` [PATCH qemu 10/12] ast2400: add a BT device Cédric Le Goater
@ 2016-06-07  7:15   ` Andrew Jeffery
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  7:15 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 11404 bytes --]

On Sun, 2016-05-29 at 23:20 +0200, Cédric Le Goater wrote:
> This is an dummy empty shell for the moment. The plan would be to plug
> this device in the IPMI framework already available in qemu and work
> on a backend to connect it to an external host (a powernv guest) or a
> ipmi-host simulator.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  hw/arm/ast2400.c            |  17 +++++
>  hw/misc/Makefile.objs       |   2 +-
>  hw/misc/aspeed_bt.c         | 175 ++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/arm/ast2400.h    |   2 +
>  include/hw/misc/aspeed_bt.h |  31 ++++++++
>  5 files changed, 226 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/aspeed_bt.c
>  create mode 100644 include/hw/misc/aspeed_bt.h
> 
> diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
> index 8f678983cee5..a4a7c10e4361 100644
> --- a/hw/arm/ast2400.c
> +++ b/hw/arm/ast2400.c
> @@ -29,6 +29,8 @@
>  #define AST2400_VIC_BASE         0x1E6C0000
>  #define AST2400_SCU_BASE         0x1E6E2000
>  #define AST2400_TIMER_BASE       0x1E782000
> +#define AST2400_LPC_BASE         0x1E789000
> +#define AST2400_IBT_BASE         (AST2400_LPC_BASE + 0x140)
>  #define AST2400_I2C_BASE         0x1E78A000
>  
>  static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
> @@ -88,6 +90,11 @@ static void ast2400_init(Object *obj)
>      object_initialize(&s->spi, sizeof(s->spi), TYPE_ASPEED_SMC);
>      object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
>      qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
> +     qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
> +
> +    object_initialize(&s->bt, sizeof(s->bt), TYPE_ASPEED_BT);
> +    object_property_add_child(obj, "bt", OBJECT(&s->bt), NULL);
> +    qdev_set_parent_bus(DEVICE(&s->bt), sysbus_get_default());
>  }
>  
>  static void ast2400_realize(DeviceState *dev, Error **errp)
> @@ -204,6 +211,16 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
>          return;
>      }
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
> +
> +    /* iBT */
> +    object_property_set_bool(OBJECT(&s->bt), true, "realized", &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->bt), 0, AST2400_IBT_BASE);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->bt), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->vic), 10));
>  }
>  
>  static void ast2400_class_init(ObjectClass *oc, void *data)
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index e50596965b03..b2931f85f097 100644
> --- a/hw/misc/Makefile.objs
> +++ b/hw/misc/Makefile.objs
> @@ -51,4 +51,4 @@ obj-$(CONFIG_MIPS_ITU) += mips_itu.o
>  obj-$(CONFIG_PVPANIC) += pvpanic.o
>  obj-$(CONFIG_EDU) += edu.o
>  obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
> -obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o
> +obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_bt.o
> diff --git a/hw/misc/aspeed_bt.c b/hw/misc/aspeed_bt.c
> new file mode 100644
> index 000000000000..496c9aa0be4a
> --- /dev/null
> +++ b/hw/misc/aspeed_bt.c
> @@ -0,0 +1,175 @@
> +/*
> + * ASPEED iBT Device
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * This code is licensed under the GPL version 2 or later.  See
> + * the COPYING file in the top-level directory.
> + */
> +
> +#include "qemu/osdep.h"
> +#include 
> +#include "hw/misc/aspeed_bt.h"
> +#include "hw/qdev-properties.h"
> +#include "qemu/bitops.h"
> +#include "trace.h"
> +
> +#ifdef ASPEED_BT_DEBUG
> +#define DB_PRINT(fmt, ...) do {         \
> +        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
> +    } while (0);
> +#else
> +    #define DB_PRINT(...) do {} while (0)
> +#endif

If they're worth keeping should we switch to trace events?

> +
> +#define BT_IO_REGION_SIZE 0x1C
> +
> +#define TO_REG(o) (o >> 2)
> +
> +#define BT_CR0                0x0
> +#define   BT_CR0_IO_BASE         16
> +#define   BT_CR0_IRQ             12

How were you planning to use BT_CR0_IO_BASE and BT_CR0_IRQ? They don't
seem to correspond to obvious fields in CR0. They don't appear to be
used by the code though.

> +#define   BT_CR0_EN_CLR_SLV_RDP  0x8
> +#define   BT_CR0_EN_CLR_SLV_WRP  0x4
> +#define   BT_CR0_ENABLE_IBT      0x1
> +#define BT_CR1                0x4
> +#define   BT_CR1_IRQ_H2B         0x01
> +#define   BT_CR1_IRQ_HBUSY       0x40
> +#define BT_CR2                0x8
> +#define   BT_CR2_IRQ_H2B         0x01
> +#define   BT_CR2_IRQ_HBUSY       0x40
> +#define BT_CR3                0xc
> +#define BT_CTRL                  0x10

The alignment of the value is a bit misleading - indented too far?

> +#define   BT_CTRL_B_BUSY         0x80
> +#define   BT_CTRL_H_BUSY         0x40
> +#define   BT_CTRL_OEM0           0x20
> +#define   BT_CTRL_SMS_ATN        0x10
> +#define   BT_CTRL_B2H_ATN        0x08
> +#define   BT_CTRL_H2B_ATN        0x04
> +#define   BT_CTRL_CLR_RD_PTR     0x02
> +#define   BT_CTRL_CLR_WR_PTR     0x01
> +#define BT_BMC2HOST            0x14
> +#define BT_INTMASK             0x18
> +#define   BT_INTMASK_B2H_IRQEN   0x01
> +#define   BT_INTMASK_B2H_IRQ     0x02
> +#define   BT_INTMASK_BMC_HWRST   0x80
> +
> +static uint64_t aspeed_bt_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    AspeedBTState *s = ASPEED_BT(opaque);
> +
> +    DB_PRINT("To 0x%" HWADDR_PRIx " of size %u\n", offset, size);
> +
> +    if (TO_REG(offset) >= ARRAY_SIZE(s->regs)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n",
> +                      __func__, offset);
> +        return 0;
> +    }
> +
> +    switch (offset) {
> +    case 0x00 ... 0x18:
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP,
> +                      "%s: Read from uninitialised register 0x%"
> +                      HWADDR_PRIx "\n", __func__, offset);
> +        break;
> +    }
> +
> +    return s->regs[TO_REG(offset)];
> +}
> +
> +static void aspeed_bt_write(void *opaque, hwaddr offset, uint64_t data,
> +                             unsigned size)
> +{
> +    AspeedBTState *s = ASPEED_BT(opaque);
> +
> +    DB_PRINT("To 0x%" HWADDR_PRIx " of size %u: 0x%" PRIx64"\n",
> +             offset, size, data);
> +
> +    if (TO_REG(offset) >= ARRAY_SIZE(s->regs)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n",
> +                      __func__, offset);
> +        return;
> +    }
> +
> +    switch (offset) {
> +    case 0x00 ... 0x18:
> +        break;
> +    default:
> +        qemu_log_mask(LOG_UNIMP,
> +                      "%s: Write to uninitialised register 0x%"
> +                      HWADDR_PRIx "\n", __func__, offset);
> +        break;
> +    }
> +
> +    s->regs[TO_REG(offset)] = (uint32_t) data;
> +}
> +
> +static const MemoryRegionOps aspeed_bt_ops = {
> +    .read = aspeed_bt_read,
> +    .write = aspeed_bt_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 4,
> +    .valid.unaligned = false,
> +};
> +
> +static void aspeed_bt_reset(DeviceState *dev)
> +{
> +    AspeedBTState *s = ASPEED_BT(dev);
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +}
> +
> +static void aspeed_bt_realize(DeviceState *dev, Error **errp)
> +{
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    AspeedBTState *s = ASPEED_BT(dev);
> +
> +    sysbus_init_irq(sbd, &s->irq);
> +    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_bt_ops, s,
> +                          TYPE_ASPEED_BT, BT_IO_REGION_SIZE);
> +
> +    sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static Property aspeed_bt_props[] = {
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static const VMStateDescription vmstate_aspeed_bt = {
> +    .name = "aspeed.bt",
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, AspeedBTState, ASPEED_BT_NR_REGS),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void aspeed_bt_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    dc->realize = aspeed_bt_realize;
> +    dc->reset = aspeed_bt_reset;
> +    dc->desc = "ASPEED iBT Device";
> +    dc->vmsd = &vmstate_aspeed_bt;
> +    dc->props = aspeed_bt_props;
> +}
> +
> +static const TypeInfo aspeed_bt_info = {
> +    .name = TYPE_ASPEED_BT,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(AspeedBTState),
> +    .class_init = aspeed_bt_class_init,
> +};
> +
> +static void aspeed_bt_register_types(void)
> +{
> +    type_register_static(&aspeed_bt_info);
> +}
> +
> +type_init(aspeed_bt_register_types);
> diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
> index 9ba4245619c1..2b517e89eeea 100644
> --- a/include/hw/arm/ast2400.h
> +++ b/include/hw/arm/ast2400.h
> @@ -18,6 +18,7 @@
>  #include "hw/timer/aspeed_timer.h"
>  #include "hw/i2c/aspeed_i2c.h"
>  #include "hw/ssi/aspeed_smc.h"
> +#include "hw/misc/aspeed_bt.h"
>  
>  typedef struct AST2400State {
>      /*< private >*/
> @@ -33,6 +34,7 @@ typedef struct AST2400State {
>      AspeedI2CState i2c;
>      AspeedSMCState smc;
>      AspeedSMCState spi;
> +    AspeedBTState bt;
>  } AST2400State;
>  
>  #define TYPE_AST2400 "ast2400"
> diff --git a/include/hw/misc/aspeed_bt.h b/include/hw/misc/aspeed_bt.h
> new file mode 100644
> index 000000000000..1e9aa9510959
> --- /dev/null
> +++ b/include/hw/misc/aspeed_bt.h
> @@ -0,0 +1,31 @@
> +/*
> + * ASPEED iBT Device
> + *
> + * Copyright 2016 IBM Corp.
> + *
> + * This code is licensed under the GPL version 2 or later.  See
> + * the COPYING file in the top-level directory.
> + */
> +#ifndef ASPEED_BT_H
> +#define ASPEED_BT_H
> +
> +#include "hw/sysbus.h"
> +
> +#define TYPE_ASPEED_BT "aspeed.bt"
> +#define ASPEED_BT(obj) OBJECT_CHECK(AspeedBTState, (obj), TYPE_ASPEED_BT)
> +
> +#define ASPEED_BT_NR_REGS (0x1C >> 2)
> +
> +typedef struct AspeedBTState {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    qemu_irq irq;
> +
> +    uint32_t regs[ASPEED_BT_NR_REGS];
> +
> +} AspeedBTState;
> +
> +#endif /* ASPEED_BT_H */

Cheers,

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic
  2016-05-29 21:20 ` [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic Cédric Le Goater
@ 2016-06-07  7:26   ` Andrew Jeffery
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-07  7:26 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 3739 bytes --]

On Sun, 2016-05-29 at 23:20 +0200, Cédric Le Goater wrote:
> Only the first nic, but the second shouldn't be too complex to add if
> needed.

What was this patch based on? The GEM NIC is in the openbmc/qemu repo,
but this patch doesn't remove it - Was it based on upstream?


> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>

Regardless, looks good to me, we can massage it to apply wherever we
need, eg. by first backing out the GEM NIC as a separate commit.

Reviewed-by: Andrew Jeffery <andrew@aj.id.au>

> ---
>  hw/arm/ast2400.c         | 18 ++++++++++++++++++
>  include/hw/arm/ast2400.h |  2 ++
>  2 files changed, 20 insertions(+)
> 
> diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
> index a4a7c10e4361..67ae91f5f977 100644
> --- a/hw/arm/ast2400.c
> +++ b/hw/arm/ast2400.c
> @@ -19,6 +19,7 @@
>  #include "hw/char/serial.h"
>  #include "hw/boards.h"
>  #include "hw/i2c/aspeed_i2c.h"
> +#include "net/net.h"
>  
>  #define AST2400_UART_5_BASE      0x00184000
>  #define AST2400_IOMEM_SIZE       0x00200000
> @@ -32,6 +33,8 @@
>  #define AST2400_LPC_BASE         0x1E789000
>  #define AST2400_IBT_BASE         (AST2400_LPC_BASE + 0x140)
>  #define AST2400_I2C_BASE         0x1E78A000
> +#define AST2400_ETH1_BASE        0x1E660000
> +#define AST2400_ETH2_BASE        0x1E680000
>  
>  static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
>  static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
> @@ -95,6 +98,10 @@ static void ast2400_init(Object *obj)
>      object_initialize(&s->bt, sizeof(s->bt), TYPE_ASPEED_BT);
>      object_property_add_child(obj, "bt", OBJECT(&s->bt), NULL);
>      qdev_set_parent_bus(DEVICE(&s->bt), sysbus_get_default());
> +
> +    object_initialize(&s->ftgmac100, sizeof(s->ftgmac100), TYPE_FTGMAC100);
> +    object_property_add_child(obj, "ftgmac100", OBJECT(&s->ftgmac100), NULL);
> +    qdev_set_parent_bus(DEVICE(&s->ftgmac100), sysbus_get_default());
>  }
>  
>  static void ast2400_realize(DeviceState *dev, Error **errp)
> @@ -221,6 +228,17 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->bt), 0, AST2400_IBT_BASE);
>      sysbus_connect_irq(SYS_BUS_DEVICE(&s->bt), 0,
>                         qdev_get_gpio_in(DEVICE(&s->vic), 10));
> +
> +    /* Net */
> +    qdev_set_nic_properties(DEVICE(&s->ftgmac100), &nd_table[0]);
> +    object_property_set_bool(OBJECT(&s->ftgmac100), true, "realized", &err);
> +    if (err) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ftgmac100), 0, AST2400_ETH1_BASE);
> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->ftgmac100), 0,
> +                       qdev_get_gpio_in(DEVICE(&s->vic), 2));
>  }
>  
>  static void ast2400_class_init(ObjectClass *oc, void *data)
> diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
> index 2b517e89eeea..bb221734c7b1 100644
> --- a/include/hw/arm/ast2400.h
> +++ b/include/hw/arm/ast2400.h
> @@ -19,6 +19,7 @@
>  #include "hw/i2c/aspeed_i2c.h"
>  #include "hw/ssi/aspeed_smc.h"
>  #include "hw/misc/aspeed_bt.h"
> +#include "hw/net/ftgmac100.h"
>  
>  typedef struct AST2400State {
>      /*< private >*/
> @@ -35,6 +36,7 @@ typedef struct AST2400State {
>      AspeedSMCState smc;
>      AspeedSMCState spi;
>      AspeedBTState bt;
> +    Ftgmac100State ftgmac100;
>  } AST2400State;
>  
>  #define TYPE_AST2400 "ast2400"

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 11/12] net: add FTGMAC100 support
  2016-05-29 21:20 ` [PATCH qemu 11/12] net: add FTGMAC100 support Cédric Le Goater
@ 2016-06-08  3:50   ` Andrew Jeffery
  0 siblings, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-08  3:50 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 39085 bytes --]

On Sun, 2016-05-29 at 23:20 +0200, Cédric Le Goater wrote:
> This patch adds to qemu initial support for the FTGMAC100 network
> adapter. The code uses the Coldfire Fast Ethernet Controller as a base

Have you said this because imx_fec.c says the same? It seemed the most
similar to this patch and I've reviewed the patch by applying and
diff'ing it against imx_fec. If that's the case maybe we should
reference imx_fec here?

> and was tested in an Aspeed AST2400 guest with Linux and U-Boot.
> 
> The device is configured and controlled via a set of registers
> available on AHB bus. Packets are transmitted and received using DMA
> ring buffers.
> 
> The model is for the moment complete enough to satisfy U-Boot and the
> Linux kernel but there are areas that need some more attention :
> 
>  - PHY support, this is a copy paste from the Coldfire model
>  - Automatic receive/transmit polling, no timers are used to poll the
>    ring buffers descriptors. We just expect the qemu nic backend to
>    push packets when they arrive.
>  - CRC
> 
> And some are still on the TODO list (not used today in the drivers) :
> 
>  - Ethernet Address Filtering
>  - Multicast
>  - High priority queues
>  - VLAN
>  - NCSI, may be we could add n/w filters
>  - etc.
> 
> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> ---
>  default-configs/arm-softmmu.mak |   1 +
>  hw/net/Makefile.objs            |   1 +
>  hw/net/ftgmac100.c              | 873 ++++++++++++++++++++++++++++++++++++++++
>  include/hw/net/ftgmac100.h      |  62 +++
>  4 files changed, 937 insertions(+)
>  create mode 100644 hw/net/ftgmac100.c
>  create mode 100644 include/hw/net/ftgmac100.h
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index c63cdd073d35..7c3b6c07d291 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -29,6 +29,7 @@ CONFIG_LAN9118=y
>  CONFIG_SMC91C111=y
>  CONFIG_ALLWINNER_EMAC=y
>  CONFIG_IMX_FEC=y
> +CONFIG_FTGMAC100=y
>  CONFIG_DS1338=y
>  CONFIG_PFLASH_CFI01=y
>  CONFIG_PFLASH_CFI02=y
> diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
> index 64d044923ce1..0b271d2fd569 100644
> --- a/hw/net/Makefile.objs
> +++ b/hw/net/Makefile.objs
> @@ -24,6 +24,7 @@ common-obj-$(CONFIG_IMX_FEC) += imx_fec.o
>  common-obj-$(CONFIG_CADENCE) += cadence_gem.o
>  common-obj-$(CONFIG_STELLARIS_ENET) += stellaris_enet.o
>  common-obj-$(CONFIG_LANCE) += lance.o
> +common-obj-$(CONFIG_FTGMAC100) += ftgmac100.o
>  
>  obj-$(CONFIG_ETRAXFS) += etraxfs_eth.o
>  obj-$(CONFIG_COLDFIRE) += mcf_fec.o
> diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c
> new file mode 100644
> index 000000000000..c8f8724e1f17
> --- /dev/null
> +++ b/hw/net/ftgmac100.c
> @@ -0,0 +1,873 @@
> +/*
> + * Faraday FTGMAC100 Gigabit Ethernet
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * Based on Coldfire Fast Ethernet Controller emulation.
> + *
> + * Copyright (c) 2007 CodeSourcery.
> + *
> + *  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 of the License, or
> + *  (at your option) any later version.
> + *
> + *  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/net/ftgmac100.h"
> +#include "sysemu/dma.h"
> +
> +/* For crc32 */
> +#include 
> +
> +#ifdef FTGMAC100_DEBUG
> +#define DEBUG(fmt, ...) do {         \
> +        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
> +    } while (0);
> +#else
> +    #define DEBUG(...) do {} while (0)
> +#endif
> +
> +#ifdef FTGMAC100_PHY_DEBUG
> +#define PHY_DEBUG(fmt, ...) do {         \
> +        fprintf(stderr,  "%s: " fmt, __func__, ## __VA_ARGS__);  \
> +    } while (0);
> +#else
> +    #define PHY_DEBUG(...) do {} while (0)
> +#endif
> +
> +/*
> + * IFTGMAC100 registers
> + */
> +#define FTGMAC100_ISR             0x00
> +#define FTGMAC100_IER             0x04
> +#define FTGMAC100_MAC_MADR        0x08
> +#define FTGMAC100_MAC_LADR        0x0c
> +
> +#define FTGMAC100_NPTXPD          0x18
> +#define FTGMAC100_NPTXR_BADR      0x20
> +#define FTGMAC100_RXR_BADR        0x24
> +#define FTGMAC100_APTC            0x34
> +#define FTGMAC100_RBSR            0x4c
> +
> +#define FTGMAC100_MACCR           0x50
> +#define FTGMAC100_PHYCR           0x60
> +#define FTGMAC100_PHYDATA         0x64
> +
> +/*
> + * Interrupt status register & interrupt enable register
> + */
> +#define FTGMAC100_INT_RPKT_BUF    (1 << 0)
> +#define FTGMAC100_INT_RPKT_FIFO   (1 << 1)
> +#define FTGMAC100_INT_NO_RXBUF    (1 << 2)
> +#define FTGMAC100_INT_RPKT_LOST   (1 << 3)
> +#define FTGMAC100_INT_XPKT_ETH    (1 << 4)
> +#define FTGMAC100_INT_XPKT_FIFO   (1 << 5)
> +#define FTGMAC100_INT_NO_NPTXBUF  (1 << 6)
> +#define FTGMAC100_INT_XPKT_LOST   (1 << 7)
> +#define FTGMAC100_INT_AHB_ERR     (1 << 8)
> +#define FTGMAC100_INT_PHYSTS_CHG  (1 << 9)
> +#define FTGMAC100_INT_NO_HPTXBUF  (1 << 10)
> +
> +/*
> + * Automatic polling timer control register
> + */
> +#define FTGMAC100_APTC_RXPOLL_CNT(x)        ((x) & 0xf)
> +#define FTGMAC100_APTC_RXPOLL_TIME_SEL      (1 << 4)
> +#define FTGMAC100_APTC_TXPOLL_CNT(x)        (((x) >> 8) & 0xf)
> +#define FTGMAC100_APTC_TXPOLL_TIME_SEL      (1 << 12)
> +
> +/*
> + * PHY control register
> + */
> +#define FTGMAC100_PHYCR_MIIRD               (1 << 26)
> +#define FTGMAC100_PHYCR_MIIWR               (1 << 27)
> +
> +/*
> + * PHY data register
> + */
> +#define FTGMAC100_PHYDATA_MIIWDATA(x)       ((x) & 0xffff)
> +#define FTGMAC100_PHYDATA_MIIRDATA(phydata) (((phydata) >> 16) & 0xffff)

The AST2500 u-boot uses the new MII interface - have you done anything
to handle that?

> +
> +
> +/*
> + * MAC control register
> + */
> +#define FTGMAC100_MACCR_TXDMA_EN         (1 << 0)
> +#define FTGMAC100_MACCR_RXDMA_EN         (1 << 1)
> +#define FTGMAC100_MACCR_TXMAC_EN         (1 << 2)
> +#define FTGMAC100_MACCR_RXMAC_EN         (1 << 3)
> +#define FTGMAC100_MACCR_RM_VLAN          (1 << 4)
> +#define FTGMAC100_MACCR_HPTXR_EN         (1 << 5)
> +#define FTGMAC100_MACCR_LOOP_EN          (1 << 6)

LOOP_EN? Datasheet suggests PHY link status detection, which sounds
different to me.

> +#define FTGMAC100_MACCR_ENRX_IN_HALFTX   (1 << 7)
> +#define FTGMAC100_MACCR_FULLDUP          (1 << 8)
> +#define FTGMAC100_MACCR_GIGA_MODE        (1 << 9)
> +#define FTGMAC100_MACCR_CRC_APD          (1 << 10)
> +#define FTGMAC100_MACCR_RX_RUNT          (1 << 12)
> +#define FTGMAC100_MACCR_JUMBO_LF         (1 << 13)
> +#define FTGMAC100_MACCR_RX_ALL           (1 << 14)
> +#define FTGMAC100_MACCR_HT_MULTI_EN      (1 << 15)
> +#define FTGMAC100_MACCR_RX_MULTIPKT      (1 << 16)
> +#define FTGMAC100_MACCR_RX_BROADPKT      (1 << 17)
> +#define FTGMAC100_MACCR_DISCARD_CRCERR   (1 << 18)
> +#define FTGMAC100_MACCR_FAST_MODE        (1 << 19)
> +#define FTGMAC100_MACCR_SW_RST           (1 << 31)
> +
> +/*
> + * Transmit descriptor, aligned to 16 bytes
> + */
> +struct ftgmac100_txdes {
> +        unsigned int        txdes0;
> +        unsigned int        txdes1;
> +        unsigned int        txdes2;      /* not used by HW */

... In the AST2400. But maybe leave all of the AST2500 bits until
later? Might be worth qualifying the comment though, here and for the
Rx code.

> +        unsigned int        txdes3;      /* TXBUF_BADR */
> +} __attribute__ ((aligned(16)));
> +
> +#define FTGMAC100_TXDES0_TXBUF_SIZE(x)   ((x) & 0x3fff)
> +#define FTGMAC100_TXDES0_EDOTR           (1 << 15)

Maybe a comment here too (and for Rx) regarding the change in behaviour
in the AST2500. And maybe that some revisions of the AST2400 datasheet
say bit 30 like the 2500 datasheet, but still use bit 15?

> +#define FTGMAC100_TXDES0_CRC_ERR         (1 << 19)
> +#define FTGMAC100_TXDES0_LTS             (1 << 28)
> +#define FTGMAC100_TXDES0_FTS             (1 << 29)
> +#define FTGMAC100_TXDES0_TXDMA_OWN       (1 << 31)
> +
> +#define FTGMAC100_TXDES1_VLANTAG_CI(x)   ((x) & 0xffff)
> +#define FTGMAC100_TXDES1_INS_VLANTAG     (1 << 16)
> +#define FTGMAC100_TXDES1_TCP_CHKSUM      (1 << 17)
> +#define FTGMAC100_TXDES1_UDP_CHKSUM      (1 << 18)
> +#define FTGMAC100_TXDES1_IP_CHKSUM       (1 << 19)

These _CHKSUM bits are marked as reserved in the AST2400 datasheet, but
are exposed in the AST2500 datasheet. Maybe a comment here regarding
that also?

> +#define FTGMAC100_TXDES1_LLC             (1 << 22)
> +#define FTGMAC100_TXDES1_TX2FIC          (1 << 30)
> +#define FTGMAC100_TXDES1_TXIC            (1 << 31)
> +
> +/*
> + * Receive descriptor, aligned to 16 bytes
> + */
> +struct ftgmac100_rxdes {
> +        unsigned int        rxdes0;
> +        unsigned int        rxdes1;
> +        unsigned int        rxdes2;      /* not used by HW */
> +        unsigned int        rxdes3;      /* RXBUF_BADR */
> +} __attribute__ ((aligned(16)));
> +
> +#define FTGMAC100_RXDES0_VDBC            0x3fff
> +#define FTGMAC100_RXDES0_EDORR           (1 << 15)
> +#define FTGMAC100_RXDES0_MULTICAST       (1 << 16)
> +#define FTGMAC100_RXDES0_BROADCAST       (1 << 17)
> +#define FTGMAC100_RXDES0_RX_ERR          (1 << 18)
> +#define FTGMAC100_RXDES0_CRC_ERR         (1 << 19)
> +#define FTGMAC100_RXDES0_FTL             (1 << 20)
> +#define FTGMAC100_RXDES0_RUNT            (1 << 21)
> +#define FTGMAC100_RXDES0_RX_ODD_NB       (1 << 22)
> +#define FTGMAC100_RXDES0_FIFO_FULL       (1 << 23)
> +#define FTGMAC100_RXDES0_PAUSE_OPCODE    (1 << 24)
> +#define FTGMAC100_RXDES0_PAUSE_FRAME     (1 << 25)
> +#define FTGMAC100_RXDES0_LRS             (1 << 28)
> +#define FTGMAC100_RXDES0_FRS             (1 << 29)
> +#define FTGMAC100_RXDES0_RXPKT_RDY       (1 << 31)
> +
> +#define FTGMAC100_RXDES1_VLANTAG_CI      0xffff
> +#define FTGMAC100_RXDES1_PROT_MASK       (0x3 << 20)
> +#define FTGMAC100_RXDES1_PROT_NONIP      (0x0 << 20)
> +#define FTGMAC100_RXDES1_PROT_IP         (0x1 << 20)
> +#define FTGMAC100_RXDES1_PROT_TCPIP      (0x2 << 20)
> +#define FTGMAC100_RXDES1_PROT_UDPIP      (0x3 << 20)
> +#define FTGMAC100_RXDES1_LLC             (1 << 22)
> +#define FTGMAC100_RXDES1_DF              (1 << 23)
> +#define FTGMAC100_RXDES1_VLANTAG_AVAIL   (1 << 24)
> +#define FTGMAC100_RXDES1_TCP_CHKSUM_ERR  (1 << 25)
> +#define FTGMAC100_RXDES1_UDP_CHKSUM_ERR  (1 << 26)
> +#define FTGMAC100_RXDES1_IP_CHKSUM_ERR   (1 << 27)
> +
> +/*
> + * PHY values (to be defined elsewhere ...)
> + */
> +#define PHY_INT_ENERGYON            (1 << 7)
> +#define PHY_INT_AUTONEG_COMPLETE    (1 << 6)
> +#define PHY_INT_FAULT               (1 << 5)
> +#define PHY_INT_DOWN                (1 << 4)
> +#define PHY_INT_AUTONEG_LP          (1 << 3)
> +#define PHY_INT_PARFAULT            (1 << 2)
> +#define PHY_INT_AUTONEG_PAGE        (1 << 1)
> +
> +/* Common Buffer Descriptor  */
> +typedef struct {
> +    uint32_t        des0;
> +    uint32_t        des1;
> +    uint32_t        des2;        /* not used by HW */
> +    uint32_t        des3;        /* TXBUF_BADR */
> +} Ftgmac100Desc  __attribute__ ((aligned(16)));
> +
> +/* max frame size is :
> + *
> + *   9216 for Jumbo frames (+ 4 for VLAN)
> + *   1518 for other frames (+ 4 for VLAN)
> + */
> +#define FTGMAC100_MAX_FRAME_SIZE(s)                             \
> +    ((s->maccr & FTGMAC100_MACCR_JUMBO_LF ? 9216 : 1518) + 4)
> +
> +static void ftgmac100_update_irq(Ftgmac100State *s);
> +
> +/*
> + * The MII phy could raise a GPIO to the processor which in turn
> + * could be handled as an interrpt by the OS.
> + * For now we don't handle any GPIO/interrupt line, so the OS will
> + * have to poll for the PHY status.
> + */
> +static void phy_update_irq(Ftgmac100State *s)
> +{
> +    ftgmac100_update_irq(s);
> +}
> +
> +static void phy_update_link(Ftgmac100State *s)
> +{
> +    /* Autonegotiation status mirrors link status.  */
> +    if (qemu_get_queue(s->nic)->link_down) {
> +        PHY_DEBUG("link is down\n");
> +        s->phy_status &= ~0x0024;
> +        s->phy_int |= PHY_INT_DOWN;
> +    } else {
> +        PHY_DEBUG("link is up\n");
> +        s->phy_status |= 0x0024;
> +        s->phy_int |= PHY_INT_ENERGYON;
> +        s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
> +    }
> +    phy_update_irq(s);
> +}
> +
> +static void ftgmac100_set_link(NetClientState *nc)
> +{
> +    phy_update_link(FTGMAC100(qemu_get_nic_opaque(nc)));
> +}
> +
> +static void phy_reset(Ftgmac100State *s)
> +{
> +    s->phy_status = 0x7809;
> +    s->phy_control = 0x3000;
> +    s->phy_advertise = 0x01e1;
> +    s->phy_int_mask = 0;
> +    s->phy_int = 0;
> +    phy_update_link(s);
> +}
> +
> +static uint32_t do_phy_read(Ftgmac100State *s, int reg)
> +{
> +    uint32_t val;
> +
> +    if (reg > 31) {
> +        /* we only advertise one phy */
> +        return 0;
> +    }
> +
> +    switch (reg) {
> +    case 0:     /* Basic Control */
> +        val = s->phy_control;
> +        break;
> +    case 1:     /* Basic Status */
> +        val = s->phy_status;
> +        break;
> +    case 2:     /* ID1 */
> +        val = 0x0007;
> +        break;
> +    case 3:     /* ID2 */
> +        val = 0xc0d1;
> +        break;
> +    case 4:     /* Auto-neg advertisement */
> +        val = s->phy_advertise;
> +        break;
> +    case 5:     /* Auto-neg Link Partner Ability */
> +        val = 0x0f71;
> +        break;
> +    case 6:     /* Auto-neg Expansion */
> +        val = 1;
> +        break;
> +        val = 0x0800;
> +        break;
> +    case 29:    /* Interrupt source.  */
> +        val = s->phy_int;
> +        s->phy_int = 0;
> +        phy_update_irq(s);
> +        break;
> +    case 30:    /* Interrupt mask */
> +        val = s->phy_int_mask;
> +        break;
> +    case 0x0a: /* 1000BASE-T status  */
> +    case 17:
> +    case 18:
> +    case 27:
> +    case 31:
> +        qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
> +                      __func__, reg);
> +        val = 0;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
> +                      __func__, reg);
> +        val = 0;
> +        break;
> +    }
> +
> +    PHY_DEBUG("PHY read 0x%04x @%d\n", val, reg);
> +    return val;
> +}
> +
> +static void do_phy_write(Ftgmac100State *s, int reg, uint32_t val)
> +{
> +    PHY_DEBUG("PHY: write 0x%04x @%d\n", val, reg);
> +
> +    if (reg > 31) {
> +        /* we only advertise one phy */
> +        return;
> +    }
> +
> +    switch (reg) {
> +    case 0:     /* Basic Control */
> +        if (val & 0x8000) {
> +            phy_reset(s);
> +        } else {
> +            s->phy_control = val & 0x7980;
> +            /* Complete autonegotiation immediately.  */
> +            if (val & 0x1000) {
> +                s->phy_status |= 0x0020;
> +            }
> +        }
> +        break;
> +    case 4:     /* Auto-neg advertisement */
> +        s->phy_advertise = (val & 0x2d7f) | 0x80;
> +        break;
> +    case 30:    /* Interrupt mask */
> +        s->phy_int_mask = val & 0xff;
> +        phy_update_irq(s);
> +        break;
> +    case 17:
> +    case 18:
> +    case 27:
> +    case 31:
> +        qemu_log_mask(LOG_UNIMP, "%s: reg %d not implemented\n",
> +                      __func__, reg);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address at offset %d\n",
> +                      __func__, reg);
> +        break;
> +    }
> +}
> +
> +static void ftgmac100_read_bd(Ftgmac100Desc *bd, dma_addr_t addr)
> +{
> +    dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void ftgmac100_write_bd(Ftgmac100Desc *bd, dma_addr_t addr)
> +{
> +    dma_memory_write(&address_space_memory, addr, bd, sizeof(*bd));
> +}
> +
> +static void ftgmac100_update_irq(Ftgmac100State *s)
> +{
> +    uint32_t active;
> +    uint32_t changed;
> +
> +    active = s->isr & s->ier;
> +    changed = active ^ s->irq_state;
> +    if (changed) {
> +        qemu_set_irq(s->irq, active);
> +    }
> +    s->irq_state = active;
> +}
> +
> +/* Locate a possible first descriptor to transmit. When Linux resets
> + * the device, the indexes of ring buffers are cleared but the dma
> + * buffers are not, so we need to find a starting point.
> + */
> +static uint32_t ftgmac100_find_txdes(Ftgmac100State *s, uint32_t addr)
> +{
> +    Ftgmac100Desc bd;
> +
> +    while (1) {
> +        ftgmac100_read_bd(&bd, addr);
> +        if (bd.des0 & (FTGMAC100_TXDES0_FTS | FTGMAC100_TXDES0_EDOTR)) {
> +            break;
> +        }
> +        addr += sizeof(Ftgmac100Desc);
> +    }
> +    return addr;
> +}
> +
> +static void ftgmac100_do_tx(Ftgmac100State *s)
> +{
> +    int frame_size = 0;
> +    uint8_t frame[FTGMAC100_MAX_FRAME_SIZE(s)];
> +    uint8_t *ptr = frame;
> +    uint32_t addr;
> +
> +    addr = ftgmac100_find_txdes(s, s->tx_descriptor);
> +
> +    while (1) {
> +        Ftgmac100Desc bd;
> +        int len;
> +
> +        ftgmac100_read_bd(&bd, addr);
> +        if ((bd.des0 & FTGMAC100_TXDES0_TXDMA_OWN) == 0) {
> +            /* Run out of descriptors to transmit.  */
> +            break;
> +        }
> +        len = bd.des0 & 0x3FFF;

Perhaps use our FTGMAC100_TXDES0_TXBUF_SIZE() macro here?

> +        if (frame_size + len > FTGMAC100_MAX_FRAME_SIZE(s)) {
> +            len = FTGMAC100_MAX_FRAME_SIZE(s) - frame_size;
> +            s->isr |= FTGMAC100_INT_NO_NPTXBUF;
> +        }
> +        dma_memory_read(&address_space_memory, bd.des3, ptr, len);
> +        ptr += len;
> +        frame_size += len;
> +        if (bd.des0 & FTGMAC100_TXDES0_LTS) {
> +            /* Last buffer in frame.  */
> +            qemu_send_packet(qemu_get_queue(s->nic), frame, len);
> +            ptr = frame;
> +            frame_size = 0;
> +            if (bd.des1 & FTGMAC100_TXDES1_TX2FIC) {
> +                s->isr |= FTGMAC100_INT_XPKT_FIFO;
> +            }
> +            if (bd.des1 & FTGMAC100_TXDES1_TXIC) {
> +                s->isr |= FTGMAC100_INT_XPKT_ETH;
> +            }
> +        }
> +        bd.des0 &= ~FTGMAC100_TXDES0_TXDMA_OWN;
> +
> +        /* Write back the modified descriptor.  */
> +        ftgmac100_write_bd(&bd, addr);
> +        /* Advance to the next descriptor.  */
> +        if (bd.des0 & FTGMAC100_TXDES0_EDOTR) {
> +            addr = s->tx_ring;
> +        } else {
> +            addr += sizeof(Ftgmac100Desc);
> +        }
> +    }
> +
> +    s->tx_descriptor = addr;
> +
> +    ftgmac100_update_irq(s);
> +}
> +
> +static void ftgmac100_enable_rx(Ftgmac100State *s)
> +{
> +    Ftgmac100Desc bd;
> +    uint32_t full;
> +
> +    /* Find an empty descriptor to use */
> +    while (1) {
> +        ftgmac100_read_bd(&bd, s->rx_descriptor);
> +        full = (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY);
> +        if (!full || bd.des0 & FTGMAC100_TXDES0_EDOTR) {

This happens to work because FTGMAC100_TXDES0_EDOTR
== FTGMAC100_RXDES0_EDORR, but this should use FTGMAC100_RXDES0_EDORR

> +            break;
> +        }
> +        s->rx_descriptor += sizeof(Ftgmac100Desc);
> +    }
> +
> +    if (full) {
> +        DEBUG("RX buffer full\n");
> +    }
> +
> +    s->rx_enabled = (full == 0);
> +    if (s->rx_enabled) {
> +        qemu_flush_queued_packets(qemu_get_queue(s->nic));
> +    }
> +}
> +
> +/*
> + * This is purely informative. The HW can poll the RW (and RX) ring
> + * buffers for available descriptors but we don't need to trigger a
> + * timer for that in qemu.
> + */
> +static void ftgmac100_rxpoll(Ftgmac100State *s)
> +{
> +    /* Polling times :
> +     *
> +     * Speed      TIME_SEL=0    TIME_SEL=1
> +     *
> +     *    10         51.2 ms      819.2 ms
> +     *   100         5.12 ms      81.92 ms
> +     *  1000        1.024 ms     16.384 ms
> +     */
> +    static const int div[] = { 20, 200, 1000 };
> +
> +    uint32_t cnt = 1024 * FTGMAC100_APTC_RXPOLL_CNT(s->aptcr);
> +    uint32_t speed = (s->maccr & FTGMAC100_MACCR_FAST_MODE) ? 1 : 0;
> +    uint32_t period __attribute__ ((unused));
> +
> +    if (s->aptcr & FTGMAC100_APTC_RXPOLL_TIME_SEL) {
> +        cnt <<= 4;
> +    }
> +
> +    if (s->maccr & FTGMAC100_MACCR_GIGA_MODE) {
> +        speed = 2;
> +    }
> +
> +    period = cnt / div[speed];
> +
> +    DEBUG("polling in %d ms\n", period);
> +}
> +
> +static void ftgmac100_reset(DeviceState *d)
> +{
> +    Ftgmac100State *s = FTGMAC100(d);
> +
> +    /* Reset the FTGMAC100 */
> +    s->isr = 0;
> +    s->ier = 0;
> +    s->rx_enabled = 0;
> +    s->maccr = 0;
> +    s->rx_ring = 0;
> +    s->rx_descriptor = 0;
> +    s->rbsr = 0x640; /* HW default according to u-boot driver */

0x640 is in the datasheet too, probably doesn't need the comment to
justify it.

> +    s->tx_ring = 0;
> +    s->tx_descriptor = 0;
> +    s->phycr = 0;
> +    s->phydata = 0;
> +    s->aptcr = 0;
> +
> +    /* We also reset the PHY */
> +    phy_reset(s);
> +}
> +
> +static uint64_t ftgmac100_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftgmac100State *s = FTGMAC100(opaque);
> +
> +    DEBUG("reading from @ 0x%" HWADDR_PRIx "\n", addr);
> +
> +    switch (addr & 0xff) {
> +    case FTGMAC100_ISR:
> +        return s->isr;
> +    case FTGMAC100_IER:
> +        return s->ier;
> +    case FTGMAC100_MAC_MADR:
> +        return (s->conf.macaddr.a[0] << 8)  | s->conf.macaddr.a[1];
> +    case FTGMAC100_MAC_LADR:
> +        return (s->conf.macaddr.a[2] << 24) | (s->conf.macaddr.a[3] << 16) |
> +               (s->conf.macaddr.a[4] << 8)  |  s->conf.macaddr.a[5];
> +    case FTGMAC100_MACCR:
> +        return s->maccr;
> +    case FTGMAC100_PHYCR:
> +        return s->phycr;
> +    case FTGMAC100_PHYDATA:
> +        return s->phydata;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> +                      HWADDR_PRIx "\n", TYPE_FTGMAC100, __func__, addr);
> +        return 0;
> +    }
> +}
> +
> +static void ftgmac100_write(void *opaque, hwaddr addr,
> +                          uint64_t value, unsigned size)
> +{
> +    Ftgmac100State *s = FTGMAC100(opaque);
> +
> +    DEBUG("writing 0x%08x @ 0x%" HWADDR_PRIx "\n", (int)value, addr);
> +
> +    switch (addr & 0xff) {
> +    case FTGMAC100_ISR: /* Interrupt status */
> +        s->isr &= ~value;
> +        break;
> +    case FTGMAC100_IER:  /* Interrupt control */
> +        s->ier = value;
> +        break;
> +    case FTGMAC100_MAC_MADR: /* MAC */
> +        s->conf.macaddr.a[0] = value >> 8;
> +        s->conf.macaddr.a[1] = value;
> +        break;
> +    case FTGMAC100_MAC_LADR:
> +        s->conf.macaddr.a[2] = value >> 24;
> +        s->conf.macaddr.a[3] = value >> 16;
> +        s->conf.macaddr.a[4] = value >> 8;
> +        s->conf.macaddr.a[5] = value;
> +        break;
> +
> +    case FTGMAC100_RXR_BADR:  /* Ring buffer address */
> +        s->rx_ring = value;
> +        s->rx_descriptor = s->rx_ring;
> +        break;
> +
> +    case FTGMAC100_RBSR: /* DMA buffer size */
> +        s->rbsr = value;
> +        break;
> +
> +    case FTGMAC100_NPTXR_BADR:  /* Transmit buffer address */
> +        s->tx_ring = value;
> +        s->tx_descriptor = s->tx_ring;
> +        break;
> +
> +    case FTGMAC100_NPTXPD: /* Trigger transmit */
> +        if (s->maccr & FTGMAC100_MACCR_TXDMA_EN) {
> +            ftgmac100_do_tx(s);
> +        }
> +        break;
> +
> +    case FTGMAC100_APTC: /* Automatic polling */
> +        s->aptcr = value;
> +
> +        if (FTGMAC100_APTC_RXPOLL_CNT(s->aptcr)) {
> +            ftgmac100_rxpoll(s);
> +        }
> +
> +        if (FTGMAC100_APTC_TXPOLL_CNT(s->aptcr)) {
> +            qemu_log_mask(LOG_UNIMP, "%s: no transmit polling\n", __func__);
> +        }
> +        break;
> +
> +    case FTGMAC100_MACCR: /* MAC Device control */
> +        s->maccr = value;
> +        if (value & FTGMAC100_MACCR_SW_RST) {
> +            ftgmac100_reset(DEVICE(s));
> +        }
> +
> +        if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) && !s->rx_enabled) {
> +            ftgmac100_enable_rx(s);
> +        }
> +
> +        if ((s->maccr & FTGMAC100_MACCR_RXDMA_EN) == 0) {
> +            s->rx_enabled = 0;
> +        }
> +        break;
> +
> +    case FTGMAC100_PHYCR:  /* PHY Device control */
> +        s->phycr = value;
> +        if (value & FTGMAC100_PHYCR_MIIWR) {
> +            do_phy_write(s, extract32(value, 21, 5), s->phydata & 0xffff);
> +            s->phycr &= ~FTGMAC100_PHYCR_MIIWR;
> +       } else {
> +            s->phydata = do_phy_read(s, extract32(value, 21, 5)) << 16;
> +            s->phycr &= ~FTGMAC100_PHYCR_MIIRD;
> +       }
> +        break;
> +    case FTGMAC100_PHYDATA:
> +        s->phydata = value;
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%"
> +                      HWADDR_PRIx "\n", TYPE_FTGMAC100, __func__, addr);
> +        break;
> +    }
> +
> +    ftgmac100_update_irq(s);
> +}
> +
> +static bool packet_is_broadcast(const uint8_t *buf)
> +{
> +    static const unsigned char sa_bcast[6] = {0xff, 0xff, 0xff,
> +                                              0xff, 0xff, 0xff};
> +    return memcmp(buf, sa_bcast, 6) == 0;
> +}
> +
> +static int ftgmac100_can_receive(NetClientState *nc)
> +{
> +    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
> +
> +    return s->rx_enabled;
> +}
> +
> +static ssize_t ftgmac100_receive(NetClientState *nc, const uint8_t *buf,
> +                               size_t len)
> +{
> +    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
> +    Ftgmac100Desc bd;
> +    uint32_t flags = 0;
> +    uint32_t addr;
> +    uint32_t crc;
> +    uint32_t buf_addr;
> +    uint8_t *crc_ptr;
> +    unsigned int buf_len;
> +    size_t size = len;
> +    uint32_t first = FTGMAC100_RXDES0_FRS;
> +
> +    DEBUG("len %d\n", (int)size);
> +
> +    if (!s->rx_enabled) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: Unexpected packet\n", __func__);
> +        return 0;
> +    }
> +
> +    /* FIXME: Pad short packets.  */
> +
> +    /* 4 bytes for the CRC.  */
> +    size += 4;
> +    crc = cpu_to_be32(crc32(~0, buf, size));
> +    crc_ptr = (uint8_t *) &crc;
> +
> +    /* Huge frames are truncted.  */
> +    if (size > FTGMAC100_MAX_FRAME_SIZE(s)) {
> +        size = FTGMAC100_MAX_FRAME_SIZE(s);
> +        flags |= FTGMAC100_RXDES0_FTL | FTGMAC100_RXDES0_RX_ERR;
> +    }
> +
> +    if (packet_is_broadcast(buf)) {
> +        flags |= FTGMAC100_RXDES0_BROADCAST;
> +    }
> +
> +    addr = s->rx_descriptor;
> +    while (size > 0) {
> +        ftgmac100_read_bd(&bd, addr);
> +        if (bd.des0 & FTGMAC100_RXDES0_RXPKT_RDY) {
> +            /* No descriptors available.  Bail out.  */
> +            /*
> +             * FIXME: This is wrong. We should probably either
> +             * save the remainder for when more RX buffers are
> +             * available, or flag an error.
> +             */
> +            qemu_log_mask(LOG_GUEST_ERROR, "%s: Lost end of frame\n",
> +                          __func__);
> +            s->isr |= FTGMAC100_INT_NO_RXBUF;
> +            break;
> +        }
> +        buf_len = (size <= s->rbsr) ? size : s->rbsr;
> +        bd.des0 |= buf_len & 0x3fff;

Use FTGMAC100_RXDES0_VDBC?

> +        size -= buf_len;
> +
> +        /* The last 4 bytes are the CRC.  */
> +        if (size < 4) {
> +            buf_len += size - 4;
> +        }
> +        buf_addr = bd.des3;
> +        dma_memory_write(&address_space_memory, buf_addr, buf, buf_len);
> +        buf += buf_len;
> +        if (size < 4) {
> +            dma_memory_write(&address_space_memory, buf_addr + buf_len,
> +                             crc_ptr, 4 - size);
> +            crc_ptr += 4 - size;
> +        }
> +
> +        bd.des0 |= first | FTGMAC100_RXDES0_RXPKT_RDY;
> +        first = 0;
> +        if (size == 0) {
> +            /* Last buffer in frame.  */
> +            bd.des0 |= flags | FTGMAC100_RXDES0_LRS;
> +            DEBUG("rx frame flags %04x\n", bd.des0);
> +            s->isr |= FTGMAC100_INT_RPKT_BUF;
> +        } else {
> +            s->isr |= FTGMAC100_INT_RPKT_FIFO;
> +        }
> +        ftgmac100_write_bd(&bd, addr);
> +        if (bd.des0 & FTGMAC100_RXDES0_EDORR) {
> +            addr = s->rx_ring;
> +        } else {
> +            addr += sizeof(Ftgmac100Desc);
> +        }
> +    }
> +    s->rx_descriptor = addr;
> +
> +    ftgmac100_enable_rx(s);
> +    ftgmac100_update_irq(s);
> +    return len;
> +}
> +
> +static const MemoryRegionOps ftgmac100_ops = {
> +    .read = ftgmac100_read,
> +    .write = ftgmac100_write,
> +    .valid.min_access_size = 4,
> +    .valid.max_access_size = 4,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +};
> +
> +static void ftgmac100_cleanup(NetClientState *nc)
> +{
> +    Ftgmac100State *s = FTGMAC100(qemu_get_nic_opaque(nc));
> +
> +    s->nic = NULL;
> +}
> +
> +static NetClientInfo net_ftgmac100_info = {
> +    .type = NET_CLIENT_OPTIONS_KIND_NIC,
> +    .size = sizeof(NICState),
> +    .can_receive = ftgmac100_can_receive,
> +    .receive = ftgmac100_receive,
> +    .cleanup = ftgmac100_cleanup,
> +    .link_status_changed = ftgmac100_set_link,
> +};
> +
> +static void ftgmac100_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftgmac100State *s = FTGMAC100(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +
> +    memory_region_init_io(&s->iomem, OBJECT(dev), &ftgmac100_ops, s,
> +                          TYPE_FTGMAC100, 0x2000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq);
> +    qemu_macaddr_default_if_unset(&s->conf.macaddr);
> +
> +    s->conf.peers.ncs[0] = nd_table[0].netdev;
> +
> +    s->nic = qemu_new_nic(&net_ftgmac100_info, &s->conf,
> +                          object_get_typename(OBJECT(dev)), DEVICE(dev)->id,
> +                          s);
> +    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
> +}
> +
> +static const VMStateDescription vmstate_ftgmac100 = {
> +    .name = TYPE_FTGMAC100,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(irq_state, Ftgmac100State),
> +        VMSTATE_UINT32(isr, Ftgmac100State),
> +        VMSTATE_UINT32(ier, Ftgmac100State),
> +        VMSTATE_UINT32(rx_enabled, Ftgmac100State),
> +        VMSTATE_UINT32(rx_ring, Ftgmac100State),
> +        VMSTATE_UINT32(rbsr, Ftgmac100State),
> +        VMSTATE_UINT32(tx_ring, Ftgmac100State),
> +        VMSTATE_UINT32(rx_descriptor, Ftgmac100State),
> +        VMSTATE_UINT32(tx_descriptor, Ftgmac100State),
> +        VMSTATE_UINT32(maccr, Ftgmac100State),
> +        VMSTATE_UINT32(phycr, Ftgmac100State),
> +        VMSTATE_UINT32(phydata, Ftgmac100State),
> +        VMSTATE_UINT32(aptcr, Ftgmac100State),
> +
> +        VMSTATE_UINT32(phy_status, Ftgmac100State),
> +        VMSTATE_UINT32(phy_control, Ftgmac100State),
> +        VMSTATE_UINT32(phy_advertise, Ftgmac100State),
> +        VMSTATE_UINT32(phy_int, Ftgmac100State),
> +        VMSTATE_UINT32(phy_int_mask, Ftgmac100State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static Property ftgmac100_properties[] = {
> +    DEFINE_NIC_PROPERTIES(Ftgmac100State, conf),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void ftgmac100_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd = &vmstate_ftgmac100;
> +    dc->reset = ftgmac100_reset;
> +    dc->props = ftgmac100_properties;
> +    set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
> +    dc->realize = ftgmac100_realize;
> +    dc->desc = "Faraday FTGMAC100 Gigabit Ethernet emulation";
> +}
> +
> +static const TypeInfo ftgmac100_info = {
> +    .name = TYPE_FTGMAC100,
> +    .parent = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(Ftgmac100State),
> +    .class_init = ftgmac100_class_init,
> +};
> +
> +static void ftgmac100_register_types(void)
> +{
> +    type_register_static(&ftgmac100_info);
> +}
> +
> +type_init(ftgmac100_register_types)
> diff --git a/include/hw/net/ftgmac100.h b/include/hw/net/ftgmac100.h
> new file mode 100644
> index 000000000000..cc5cb4207a5c
> --- /dev/null
> +++ b/include/hw/net/ftgmac100.h
> @@ -0,0 +1,62 @@
> +/*
> + * Faraday FTGMAC100 Gigabit Ethernet
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * 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
> + * of the License, or (at your option) any later version.
> + *
> + * 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 .
> + *
> + */
> +
> +#ifndef FTGMAC100_H
> +#define FTGMAC100_H
> +
> +#define TYPE_FTGMAC100 "ftgmac100"
> +#define FTGMAC100(obj) OBJECT_CHECK(Ftgmac100State, (obj), TYPE_FTGMAC100)
> +
> +#include "hw/sysbus.h"
> +#include "net/net.h"
> +
> +typedef struct Ftgmac100State {
> +    /*< private >*/
> +    SysBusDevice parent_obj;
> +
> +    /*< public >*/
> +    NICState *nic;
> +    NICConf conf;
> +    qemu_irq irq;
> +    MemoryRegion iomem;
> +
> +    uint32_t irq_state;
> +    uint32_t isr;
> +    uint32_t ier;
> +    uint32_t rx_enabled;
> +    uint32_t rx_ring;
> +    uint32_t rbsr;
> +    uint32_t rx_descriptor;
> +    uint32_t tx_ring;
> +    uint32_t tx_descriptor;
> +    uint32_t maccr;
> +    uint32_t phycr;
> +    uint32_t phydata;
> +    uint32_t aptcr;
> +
> +    uint32_t phy_status;
> +    uint32_t phy_control;
> +    uint32_t phy_advertise;
> +    uint32_t phy_int;
> +    uint32_t phy_int_mask;
> +
> +} Ftgmac100State;
> +
> +#endif

Cheers,

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI)
  2016-06-07  2:08   ` Andrew Jeffery
@ 2016-06-08 11:53     ` Cédric Le Goater
  0 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-08 11:53 UTC (permalink / raw)
  To: andrew, openbmc

On 06/07/2016 04:08 AM, Andrew Jeffery wrote:
> On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
>> This patch provides a simple device model for the SMC controllers
>> available on the Aspeed AST2400 soc. Support is limited to the FMC and
>> the SPI controllers, and to SPI flash slaves.
>>
>> Below is the initial framework : the sysbus object, MMIO for registers
>> configuration and controls. Each controller has a SPI bus and a
>> configurable number of CS lines for SPI flash slaves.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/arm/ast2400.c            |  33 ++++++
>>  hw/ssi/Makefile.objs        |   1 +
>>  hw/ssi/aspeed_smc.c         | 260 ++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/arm/ast2400.h    |   3 +
>>  include/hw/ssi/aspeed_smc.h |  68 ++++++++++++
>>  5 files changed, 365 insertions(+)
>>  create mode 100644 hw/ssi/aspeed_smc.c
>>  create mode 100644 include/hw/ssi/aspeed_smc.h
>>
>> diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
>> index 23d52bffcaa7..8f678983cee5 100644
>> --- a/hw/arm/ast2400.c
>> +++ b/hw/arm/ast2400.c
>> @@ -23,6 +23,9 @@
>>  #define AST2400_UART_5_BASE      0x00184000
>>  #define AST2400_IOMEM_SIZE       0x00200000
>>  #define AST2400_IOMEM_BASE       0x1E600000
>> +#define AST2400_SMC_BASE         AST2400_IOMEM_BASE /* Legacy SMC */
>> +#define AST2400_FMC_BASE         0X1E620000
>> +#define AST2400_SPI_BASE         0X1E630000
>>  #define AST2400_VIC_BASE         0x1E6C0000
>>  #define AST2400_SCU_BASE         0x1E6E2000
>>  #define AST2400_TIMER_BASE       0x1E782000
>> @@ -77,6 +80,14 @@ static void ast2400_init(Object *obj)
>>      object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
>>      object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
>>      qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
>> +
>> +    object_initialize(&s->smc, sizeof(s->smc), TYPE_ASPEED_SMC);
>> +    object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
>> +    qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
>> +
>> +    object_initialize(&s->spi, sizeof(s->spi), TYPE_ASPEED_SMC);
>> +    object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
>> +    qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
>>  }
>>  
>>  static void ast2400_realize(DeviceState *dev, Error **errp)
>> @@ -171,6 +182,28 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
>>      /* The palmetto platform expects a ds3231 RTC but a ds1338 is
>>       * enough to provide basic RTC features. Alarms will be missing */
>>      i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&s->i2c), 0), "ds1338", 0x68);
>> +
>> +    /* SMC */
>> +    object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
>> +    object_property_set_int(OBJECT(&s->spi), AspeedSMCFMC, "smc-type", &err);
>> +    object_property_set_bool(OBJECT(&s->smc), true, "realized", &err);
>> +    if (err) {
>> +        error_propagate(errp, err);
>> +        return;
>> +    }
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
>> +    sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
>> +                       qdev_get_gpio_in(DEVICE(&s->vic), 19));
>> +
>> +    /* SPI */
>> +    object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
>> +    object_property_set_int(OBJECT(&s->spi), AspeedSMCSPI, "smc-type", &err);
>> +    object_property_set_bool(OBJECT(&s->spi), true, "realized", &err);
>> +    if (err) {
>> +        error_propagate(errp, err);
>> +        return;
>> +    }
>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
>>  }
>>  
>>  static void ast2400_class_init(ObjectClass *oc, void *data)
>> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
>> index 9555825acad1..6b32bf22ce3b 100644
>> --- a/hw/ssi/Makefile.objs
>> +++ b/hw/ssi/Makefile.objs
>> @@ -2,5 +2,6 @@ common-obj-$(CONFIG_PL022) += pl022.o
>>  common-obj-$(CONFIG_SSI) += ssi.o
>>  common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>>  common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
>> +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
>>  
>>  obj-$(CONFIG_OMAP) += omap_spi.o
>> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
>> new file mode 100644
>> index 000000000000..780fcbbc9e55
>> --- /dev/null
>> +++ b/hw/ssi/aspeed_smc.c
>> @@ -0,0 +1,260 @@
>> +/*
>> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
>> + *
>> + * Copyright (C) 2016 IBM Corp.
>> + *
>> + * 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 "hw/sysbus.h"
>> +#include "sysemu/sysemu.h"
>> +#include "qemu/log.h"
>> +#include "include/qemu/error-report.h"
>> +#include "exec/address-spaces.h"
>> +
>> +#include "hw/ssi/aspeed_smc.h"
>> +
>> +/* CE Type Setting Register */
>> +#define R_CONF            (0x00 / 4)
>> +#define   CONF_LEGACY_DISABLE  (1 << 31)
>> +#define   CONF_ENABLE_W4       20
>> +#define   CONF_ENABLE_W3       19
>> +#define   CONF_ENABLE_W2       18
>> +#define   CONF_ENABLE_W1       17
>> +#define   CONF_ENABLE_W0       16
>> +#define   CONF_FLASH_TYPE4     9
>> +#define   CONF_FLASH_TYPE3     7
>> +#define   CONF_FLASH_TYPE2     5
>> +#define   CONF_FLASH_TYPE1     3
>> +#define   CONF_FLASH_TYPE0     1
>> +
>> +/* CE Control Register */
>> +#define R_CTRL            (0x04 / 4)
>> +#define   CRTL_EXTENDED4       4  /* 32 bit addressing for SPI */
>> +#define   CRTL_EXTENDED3       3  /* 32 bit addressing for SPI */
>> +#define   CRTL_EXTENDED2       2  /* 32 bit addressing for SPI */
>> +#define   CRTL_EXTENDED1       1  /* 32 bit addressing for SPI */
>> +#define   CRTL_EXTENDED0       0  /* 32 bit addressing for SPI */
>> +
>> +/* Interrupt Control and Status Register */
>> +#define R_INTR_CTRL       (0x08 / 4)
>> +
>> +/* CEx Control Register */
>> +#define R_CTRL0           (0x10 / 4)
>> +#define   CTRL_CE_STOP_ACTIVE      (1 << 2)
>> +#define   CTRL_USERMODE            0x3
>> +#define R_CTRL1           (0x14 / 4)
>> +#define R_CTRL2           (0x18 / 4)
>> +#define R_CTRL3           (0x1C / 4)
>> +#define R_CTRL4           (0x20 / 4)
>> +
>> +/* CEx Segment Address Register */
>> +#define R_SEG_ADDR0       (0x30 / 4)
>> +#define   SEG_SIZE_SHIFT       24   /* 8MB units */
>> +#define   SEG_SIZE_MASK        0x7f
>> +#define   SEG_START_SHIFT      16   /* address bit [A29-A23] */
>> +#define   SEG_START_MASK       0x7f
>> +#define R_SEG_ADDR1       (0x34 / 4)
>> +#define R_SEG_ADDR2       (0x38 / 4)
>> +#define R_SEG_ADDR3       (0x3C / 4)
>> +#define R_SEG_ADDR4       (0x40 / 4)
>> +
>> +/* Misc Control Register #1 */
>> +#define R_MISC_CRTL1      (0x50 / 4)
>> +
>> +/* Misc Control Register #2 */
>> +#define R_MISC_CRTL2      (0x54 / 4)
>> +
>> +
>> +/* SPI controller registers and bits (that we care about) */
>> +#define R_SPI_CONF        (0x00 / 4)
>> +#define   SPI_CONF_ENABLE_W0   0
>> +#define R_SPI_CTRL0       (0x4 / 4)
>> +#define R_SPI_MISC_CRTL   (0x10 / 4)
>> +#define R_SPI_TIMINGS     (0x14 / 4)
>> +
>> +static const int aspeed_smc_r_ctrl0[]   = { R_CTRL0, R_CTRL0, R_SPI_CTRL0 };
>> +static const int aspeed_smc_r_conf[]    = { R_CONF,  R_CONF,  R_SPI_CONF  };
>> +static const int aspeed_smc_conf_enable_w0[] = {
>> +    CONF_ENABLE_W0, CONF_ENABLE_W0, SPI_CONF_ENABLE_W0 };
>> +
>> +
>> +static bool aspeed_smc_is_ce_stop_active(AspeedSMCState *s, int cs)
>> +{
>> +    return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
>> +}
>> +
>> +static void aspeed_smc_update_cs(AspeedSMCState *s)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        if (aspeed_smc_is_ce_stop_active(s, i)) {
>> +            /* should reset command byte in control reg */
> 
> Can you expand on why this hasn't been implemented? I assume there are
> no side-effects?

This comes from an initial implementation in which the command in bits [23:16] 
of the control register were used to generate SPI transfers when the flash was 
accessed in command mode. But it was not efficient.

I would like to keep it around for the sake of the model. I will see what
I can do even it is not really useful. 

>> +        }
>> +
>> +        qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
>> +    }
>> +}
>> +
>> +static void aspeed_smc_reset(DeviceState *d)
>> +{
>> +    AspeedSMCState *s = ASPEED_SMC(d);
>> +    int i;
>> +
>> +    memset(s->regs, 0, sizeof s->regs);
>> +
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
>> +    }
>> +
>> +    aspeed_smc_update_cs(s);
>> +}
>> +
>> +static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
>> +{
>> +    AspeedSMCState *s = ASPEED_SMC(opaque);
>> +    uint32_t r = 0;
>> +
>> +    addr >>= 2;
>> +    switch (addr) {
>> +    default:
>> +        if (addr < ARRAY_SIZE(s->regs)) {
>> +            r = s->regs[addr];
>> +        }
>> +        break;
>> +    }
> 
> Is there a good reason to use the switch here if there's only a default
> case?

I can not even say laziness :) I will add some cases.

>> +
>> +    return r;
>> +}
>> +
>> +static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
>> +                             unsigned int size)
>> +{
>> +    AspeedSMCState *s = ASPEED_SMC(opaque);
>> +    uint32_t value = data;
>> +
>> +    addr >>= 2;
>> +    switch (addr) {
>> +    default:
>> +        if (addr < ARRAY_SIZE(s->regs)) {
>> +            s->regs[addr] = value;
>> +        }
>> +        break;
>> +    }
> 
> Similar to above, though with ranges maybe we could accommodate the
> condition below?

yes. some enforcement on which registers can be handled is better.

>> +
>> +    if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
>> +        aspeed_smc_update_cs(s);
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps aspeed_smc_ops = {
>> +    .read = aspeed_smc_read,
>> +    .write = aspeed_smc_write,
>> +    .endianness = DEVICE_NATIVE_ENDIAN,
> 
> I think I asked about this previously - should it be DEVICE_LITTLE_ENDIAN? 

yes.

>> +    .valid.unaligned = true,
>> +};
>> +
>> +static const char * const aspeed_smc_types[] = { "smc", "fmc", "spi" };
>> +static const int aspeed_smc_max_slaves[] = { 5, 5, 1 };
>> +
>> +static int aspeed_smc_init(SysBusDevice *sbd)
>> +{
>> +    DeviceState *dev = DEVICE(sbd);
>> +    AspeedSMCState *s = ASPEED_SMC(dev);
>> +    int i;
>> +    char name[16];
>> +
>> +    s->spi = ssi_create_bus(dev, "spi");
>> +
>> +    /* Enforce some real HW limits */
>> +    if (s->num_cs > aspeed_smc_max_slaves[s->smc_type]) {
>> +        s->num_cs = aspeed_smc_max_slaves[s->smc_type];
>> +    }
>> +
>> +    /* Setup cs_lines for slaves */
>> +    sysbus_init_irq(sbd, &s->irq);
>> +    s->cs_lines = g_new0(qemu_irq, s->num_cs);
>> +    ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
>> +
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
>> +    }
>> +
>> +    /* There are some differences in the register numbers and bits
>> +     * between the FMC and SPI controller. Let's abstract these.
>> +     */
>> +    s->r_ctrl0 = aspeed_smc_r_ctrl0[s->smc_type];
>> +    s->r_conf = aspeed_smc_r_conf[s->smc_type];
>> +    s->conf_enable_w0 = aspeed_smc_conf_enable_w0[s->smc_type];
> 
> A little hairy, but understandable in that we avoid two mostly similar
> implementations.

yeah. Well, that seem the best approach for the rest of the code. See 
below :
	s->regs[s->r_ctrl0 + i] 

I like an array of registers values: 

	s->regs[s->regs_value[CRTL0] + 1] 

or some macro : 

	#define REG_CRTL0(s) aspeed_smc_r_ctrl0[(s)->smc_type];

	s->regs[RET_CRTL0(s) + i]

I am open to any suggestions.

>> +
>> +    /* Unselect all slaves */
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
>> +    }
>> +
>> +    snprintf(name, sizeof(name), "aspeed.%s", aspeed_smc_types[s->smc_type]);
>> +    memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
>> +                          name, ASPEED_SMC_R_MAX * 4);
>> +    sysbus_init_mmio(sbd, &s->mmio);
>> +
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_aspeed_smc = {
>> +    .name = "aspeed.smc",
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property aspeed_smc_properties[] = {
>> +    DEFINE_PROP_UINT8("num-cs", AspeedSMCState, num_cs, 1),
>> +    DEFINE_PROP_UINT8("smc-type", AspeedSMCState, smc_type, AspeedSMCFMC),
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void aspeed_smc_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init = aspeed_smc_init;
>> +    dc->reset = aspeed_smc_reset;
>> +    dc->props = aspeed_smc_properties;
>> +    dc->vmsd = &vmstate_aspeed_smc;
>> +}
>> +
>> +static const TypeInfo aspeed_smc_info = {
>> +    .name           = TYPE_ASPEED_SMC,
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(AspeedSMCState),
>> +    .class_init     = aspeed_smc_class_init,
>> +};
>> +
>> +static void aspeed_smc_register_types(void)
>> +{
>> +    type_register_static(&aspeed_smc_info);
>> +}
>> +
>> +type_init(aspeed_smc_register_types)
>> diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
>> index e96e3db3fbea..9ba4245619c1 100644
>> --- a/include/hw/arm/ast2400.h
>> +++ b/include/hw/arm/ast2400.h
>> @@ -17,6 +17,7 @@
>>  #include "hw/misc/aspeed_scu.h"
>>  #include "hw/timer/aspeed_timer.h"
>>  #include "hw/i2c/aspeed_i2c.h"
>> +#include "hw/ssi/aspeed_smc.h"
>>  
>>  typedef struct AST2400State {
>>      /*< private >*/
>> @@ -30,6 +31,8 @@ typedef struct AST2400State {
>>      AspeedTimerCtrlState timerctrl;
>>      AspeedSCUState scu;
>>      AspeedI2CState i2c;
>> +    AspeedSMCState smc;
>> +    AspeedSMCState spi;
>>  } AST2400State;
>>  
>>  #define TYPE_AST2400 "ast2400"
>> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
>> new file mode 100644
>> index 000000000000..9b95fcee5da7
>> --- /dev/null
>> +++ b/include/hw/ssi/aspeed_smc.h
>> @@ -0,0 +1,68 @@
>> +/*
>> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
>> + *
>> + * Copyright (C) 2016 IBM Corp.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef ASPEED_SMC_H
>> +#define ASPEED_SMC_H
>> +
>> +#include "hw/ssi/ssi.h"
>> +
>> +enum AspeedSMCType {
>> +    AspeedSMCLegacy,
>> +    AspeedSMCFMC,
>> +    AspeedSMCSPI,
>> +};
>> +
>> +#define TYPE_ASPEED_SMC "aspeed.smc"
>> +#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC)
>> +
>> +#define ASPEED_SMC_R_MAX        (0x100 / 4)
>> +
>> +typedef struct AspeedSMCState {
>> +    SysBusDevice parent_obj;
>> +
>> +    MemoryRegion mmio;
>> +
>> +    qemu_irq irq;
>> +    int irqline;
>> +
>> +    uint8_t num_cs;
>> +    qemu_irq *cs_lines;
>> +
>> +    SSIBus *spi;
>> +
>> +    uint32_t regs[ASPEED_SMC_R_MAX];
>> +
>> +    uint8_t smc_type;
>> +
>> +    /* depends on the controller type */
>> +    uint8_t r_conf;
>> +    uint8_t r_ctrl0;
>> +    uint8_t conf_enable_w0;
>> +} AspeedSMCState;
>> +
>> +#define TYPE_ASPEED_SPI "aspeed.spi"
>> +#define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
>> +
>> +
>> +#endif /* ASPEED_SMC_H */
> 
> Otherwise, looks good to me.
> 
> Andrew
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 02/12] ast2400: add SPI flash slave object
  2016-06-07  5:34   ` Andrew Jeffery
@ 2016-06-08 12:42     ` Cédric Le Goater
  0 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-08 12:42 UTC (permalink / raw)
  To: andrew, openbmc

On 06/07/2016 07:34 AM, Andrew Jeffery wrote:
> On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
>> Each SPI flash slave can operate in two modes: Command and User. When
>> in User mode, accesses to the memory segment of the slaves are
>> translated in SPI transfers. When in Command mode, the HW generates
>> the SPI commands automatically and the memory segment is accessed as
>> if doing a MMIO. Other SPI controllers call that mode linear
>> addressing mode.
>>
>> The patch below provides an initial model for a SPI flash module,
>> gathering SPI slave properties and a MemoryRegion to handle the memory
>> accesses. Only the User mode is supported for now but the patch
>> prepares ground for the Command mode.
>>
>> Using a sysbus object for this abstraction might be a bit complex for
>> the need. We could probably survive with a simple struct under
>> AspeedSMCState or we could extend the m25p80 object providing a model
>> for the SPI flash modules. To be discussed.
> 
> The patch seems reasonable to me, though if we took the struct-under-
> AspeedSMCState approach we would register the same MemoryRegions, but
> via the AspeedSMCState's SysBusDevice? 

yes. That might be better for the model to use the controller SysBusDevice. 
I am not sure on that option. 

> Can you expand on extending the m25p80? 

well, we would extend the m25p80 object with a Memory region and do the 
mmio handling there.  

> Is that just doing the same we do here in aspeed_smc in m25p80? 
> Or something else?

looking at it closer, the mmio handling depends on the controller. So 
that won't work.

I think we can advance in steps. This is the first one, which adds some 
API to expose the flash storage backend to handle reads in the SMC model. 
The second would be to modify a bit more m25p80 to handle writes, and that 
might be just externalizing flash_write8().

We will see what mainline wants but I will rephrase the last paragraph of 
the changelog

Thanks,

C.


> Cheers,
> 
> Andrew
> 
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>  hw/ssi/aspeed_smc.c         | 110 ++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ssi/aspeed_smc.h |  15 ++++++
>>  2 files changed, 125 insertions(+)
>>
>> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
>> index 780fcbbc9e55..43743628ba0c 100644
>> --- a/hw/ssi/aspeed_smc.c
>> +++ b/hw/ssi/aspeed_smc.c
>> @@ -258,3 +258,113 @@ static void aspeed_smc_register_types(void)
>>  }
>>  
>>  type_init(aspeed_smc_register_types)
>> +
>> +static inline bool aspeed_smc_is_usermode(AspeedSMCState *s, int cs)
>> +{
>> +    return (((s->regs[s->r_ctrl0 + cs] & CTRL_USERMODE) == CTRL_USERMODE) &&
>> +            !aspeed_smc_is_ce_stop_active(s, cs));
>> +}
>> +
>> +static inline bool aspeed_smc_is_writable(AspeedSMCState *s, int cs)
>> +{
>> +    return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs));
>> +}
>> +
>> +static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size)
>> +{
>> +    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
>> +    AspeedSMCState *s = fl->controller;
>> +    uint64_t ret = 0;
>> +    int i;
>> +
>> +    if (aspeed_smc_is_usermode(s, fl->id)) {
>> +        for (i = 0; i < size; i++) {
>> +            ret = (ret << 8) | ssi_transfer(s->spi, 0x0);
>> +        }
>> +    } else {
>> +        error_report("%s: flash not in usermode", __func__);
>> +        ret = -1;
>> +    }
>> +
>> +    return ret;
>> +}
>> +
>> +static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data,
>> +                           unsigned size)
>> +{
>> +    AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(opaque);
>> +    AspeedSMCState *s = fl->controller;
>> +    int i;
>> +
>> +    if (!aspeed_smc_is_writable(s, fl->id)) {
>> +        error_report("%s: flash not in writable", __func__);
>> +        return;
>> +    }
>> +
>> +    if (!aspeed_smc_is_usermode(s, fl->id)) {
>> +        error_report("%s: flash not in usermode", __func__);
>> +        return;
>> +    }
>> +
>> +    for (i = 0; i < size; i++) {
>> +        ssi_transfer(s->spi, (data >> 8 * (size - 1 - i)) & 0xff);
>> +    }
>> +}
>> +
>> +static const MemoryRegionOps aspeed_smc_flash_ops = {
>> +    .read = aspeed_smc_flash_read,
>> +    .write = aspeed_smc_flash_write,
>> +    .endianness = DEVICE_BIG_ENDIAN,
>> +    .valid = {
>> +        .min_access_size = 1,
>> +        .max_access_size = 4,
>> +    },
>> +};
>> +
>> +static void aspeed_smc_flash_reset(DeviceState *d)
>> +{
>> +    ;
>> +}
>> +
>> +static int aspeed_smc_flash_init(SysBusDevice *sbd)
>> +{
>> +    return 0;
>> +}
>> +
>> +static const VMStateDescription vmstate_aspeed_smc_flash = {
>> +    .name = "aspeed.smc_flash",
>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (VMStateField[]) {
>> +        VMSTATE_END_OF_LIST()
>> +    }
>> +};
>> +
>> +static Property aspeed_smc_flash_properties[] = {
>> +    DEFINE_PROP_END_OF_LIST(),
>> +};
>> +
>> +static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data)
>> +{
>> +    DeviceClass *dc = DEVICE_CLASS(klass);
>> +    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
>> +
>> +    k->init = aspeed_smc_flash_init;
>> +    dc->reset = aspeed_smc_flash_reset;
>> +    dc->props = aspeed_smc_flash_properties;
>> +    dc->vmsd = &vmstate_aspeed_smc_flash;
>> +}
>> +
>> +static const TypeInfo aspeed_smc_flash_info = {
>> +    .name           = TYPE_ASPEED_SMC_FLASH,
>> +    .parent         = TYPE_SYS_BUS_DEVICE,
>> +    .instance_size  = sizeof(AspeedSMCState),
>> +    .class_init     = aspeed_smc_flash_class_init,
>> +};
>> +
>> +static void aspeed_smc_flash_register_types(void)
>> +{
>> +    type_register_static(&aspeed_smc_flash_info);
>> +}
>> +
>> +type_init(aspeed_smc_flash_register_types)
>> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
>> index 9b95fcee5da7..6cea1313eabd 100644
>> --- a/include/hw/ssi/aspeed_smc.h
>> +++ b/include/hw/ssi/aspeed_smc.h
>> @@ -27,6 +27,21 @@
>>  
>>  #include "hw/ssi/ssi.h"
>>  
>> +#define TYPE_ASPEED_SMC_FLASH "aspeed.smc_flash"
>> +#define ASPEED_SMC_FLASH(obj) \
>> +    OBJECT_CHECK(AspeedSMCFlashState, (obj), TYPE_ASPEED_SMC_FLASH)
>> +
>> +struct AspeedSMCState;
>> +
>> +typedef struct AspeedSMCFlashState {
>> +    SysBusDevice parent_obj;
>> +
>> +    MemoryRegion mmio;
>> +    int id;
>> +    struct AspeedSMCState *controller;
>> +    DeviceState *flash;
>> +} AspeedSMCFlashState;
>> +
>>  enum AspeedSMCType {
>>      AspeedSMCLegacy,
>>      AspeedSMCFMC,

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 03/12] ast2400: create SPI flash slaves
  2016-06-07  5:56   ` Andrew Jeffery
@ 2016-06-08 12:46     ` Cédric Le Goater
  0 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-08 12:46 UTC (permalink / raw)
  To: andrew, openbmc

On 06/07/2016 07:56 AM, Andrew Jeffery wrote:
> On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
>> This patch creates a number of SPI flash slaves of the same type under
>> the SMC/FMC and under the SMC/SPI controllers. We use a "n25q256a"
>> flash module type for the BMC and a "mx25l25635e" for the host. These
>> types are common in the OpenPower ecosystem.
>>
>> The segment addresses used for the memory mappings are the defaults
>> provided by the specs. They can be changed with the Segment Address
>> Register but this is not supported in the current implementation.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
> 
> Reviewed-by: Andrew Jeffery <andrew@aj.id.au>
> 
>> ---
>>  hw/arm/palmetto-bmc.c       |  3 +++
>>  hw/ssi/aspeed_smc.c         | 62 +++++++++++++++++++++++++++++++++++++++++++++
>>  include/hw/ssi/aspeed_smc.h |  3 +++
>>  3 files changed, 68 insertions(+)
>>
>> diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
>> index 6367f978bc7b..c4099987d354 100644
>> --- a/hw/arm/palmetto-bmc.c
>> +++ b/hw/arm/palmetto-bmc.c
>> @@ -50,6 +50,9 @@ static void palmetto_bmc_init(MachineState *machine)
>>      object_property_set_bool(OBJECT(&bmc->soc), true, "realized",
>>                               &error_abort);
>>  
>> +    aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
>> +    aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
>> +
>>      palmetto_bmc_binfo.kernel_filename = machine->kernel_filename;
>>      palmetto_bmc_binfo.initrd_filename = machine->initrd_filename;
>>      palmetto_bmc_binfo.kernel_cmdline = machine->kernel_cmdline;
>> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
>> index 43743628ba0c..4175356141e7 100644
>> --- a/hw/ssi/aspeed_smc.c
>> +++ b/hw/ssi/aspeed_smc.c
>> @@ -215,6 +215,8 @@ static int aspeed_smc_init(SysBusDevice *sbd)
>>                            name, ASPEED_SMC_R_MAX * 4);
>>      sysbus_init_mmio(sbd, &s->mmio);
>>  
>> +    s->flashes = g_new0(AspeedSMCFlashState *, s->num_cs);
>> +
>>      return 0;
>>  }
>>  
>> @@ -368,3 +370,63 @@ static void aspeed_smc_flash_register_types(void)
>>  }
>>  
>>  type_init(aspeed_smc_flash_register_types)
>> +
>> +/*
>> + * Default segments mappings and size for each slave
>> + */
>> +typedef struct AspeedSegments {
>> +    hwaddr addr;
>> +    uint32_t size;
>> +} AspeedSegments;
>> +
>> +/* unused */
>> +static const AspeedSegments aspeed_segment_legacy[] = {
>> +    { 0x14000000, 32 * 1024 * 1024 },
>> +};
>> +
>> +static const AspeedSegments aspeed_segment_fmc[] = {
>> +    { 0x20000000, 64 * 1024 * 1024 },
>> +    { 0x24000000, 32 * 1024 * 1024 },
>> +    { 0x26000000, 32 * 1024 * 1024 },
>> +    { 0x28000000, 32 * 1024 * 1024 },
>> +    { 0x2A000000, 32 * 1024 * 1024 }
>> +};
>> +
>> +static const AspeedSegments aspeed_segment_spi[] = {
>> +    { 0x30000000, 64 * 1024 * 1024 },
>> +};
>> +
>> +static const AspeedSegments *aspeed_segments[] = {
>> +    aspeed_segment_legacy, aspeed_segment_fmc, aspeed_segment_spi
>> +};
>> +
>> +void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype)
>> +{
>> +    int i ;
>> +    char name[32];
>> +
>> +    for (i = 0; i < s->num_cs; ++i) {
>> +        Object *new = object_new(TYPE_ASPEED_SMC_FLASH);
>> +        AspeedSMCFlashState *fl = ASPEED_SMC_FLASH(new);
>> +        qemu_irq cs_line;
>> +
>> +        s->flashes[i] = fl;
>> +
>> +        snprintf(name, sizeof(name), "aspeed.%s.%d",
>> +                 aspeed_smc_types[s->smc_type], i);
>> +
>> +        fl->id = i;
>> +        fl->controller = s;
>> +        fl->flash = ssi_create_slave(s->spi, flashtype);
>> +
>> +        cs_line = qdev_get_gpio_in_named(fl->flash, SSI_GPIO_CS, 0);
>> +        sysbus_connect_irq(SYS_BUS_DEVICE(s), i + 1, cs_line);
>> +
>> +        memory_region_init_io(&fl->mmio, new, &aspeed_smc_flash_ops, fl, name,
>> +                              aspeed_segments[s->smc_type][i].size);
>> +        sysbus_init_mmio(SYS_BUS_DEVICE(new), &fl->mmio);
>> +
>> +        sysbus_mmio_map(SYS_BUS_DEVICE(new), 0,
>> +                        aspeed_segments[s->smc_type][i].addr);
>> +    }
>> +}
>> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
>> index 6cea1313eabd..1625dfb76a63 100644
>> --- a/include/hw/ssi/aspeed_smc.h
>> +++ b/include/hw/ssi/aspeed_smc.h
>> @@ -74,10 +74,13 @@ typedef struct AspeedSMCState {
>>      uint8_t r_conf;
>>      uint8_t r_ctrl0;
>>      uint8_t conf_enable_w0;
>> +
>> +    AspeedSMCFlashState **flashes;
>>  } AspeedSMCState;
>>  
>>  #define TYPE_ASPEED_SPI "aspeed.spi"
>>  #define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
>>  
>> +extern void aspeed_smc_init_flashes(AspeedSMCState *s, const char *flashtype);
> 
> extern seems superfluous here? Doesn't really matter though.

script/checkpatch.pl did not complain but that might not be a reason. I will check
the coding styles.

Thanks,

C. 

> Cheers,
> 
> Andrew
> 
>>  
>>  #endif /* ASPEED_SMC_H */

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom
  2016-06-07  6:24   ` Andrew Jeffery
@ 2016-06-09 10:42     ` Cédric Le Goater
  0 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-09 10:42 UTC (permalink / raw)
  To: andrew, openbmc

On 06/07/2016 08:24 AM, Andrew Jeffery wrote:
> On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
>> This provides support for U-Boot images which are loaded at 0x0.
>>
>> A Palmetto BMC guest can now be simply booted with :
>>
>>   $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
>> 	-mtdblock ./flash-palmetto-20160512040959  \
>> 	-mtdblock ./palmetto.pnor
>>
>> The first block device uses the file "./flash-palmetto-20160512040959"
>> which will act as a SPI flash module for the BMC, handled by the
>> SMC/FMC controller. The second block device uses the file
>> "./palmetto.pnor" which is an OpenPower firmware image for a palmetto
>> OpenPower system. This one will be handled by the SMC/SPI controller.
>>
>> The flash images can be grabbed here :
>>
>>     https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
>>     https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor
>>
>> We could add a second BMC SPI flash by changing the 'num-cs' property
>> of the controller and emulate a golden image flash module.
>>
>> Signed-off-by: Cédric Le Goater <clg@kaod.org>
>> ---
>>
>>  That might seem a little too hacky ? 
>>
>>  hw/arm/palmetto-bmc.c | 9 +++++++++
>>  1 file changed, 9 insertions(+)
>>
>> diff --git a/hw/arm/palmetto-bmc.c b/hw/arm/palmetto-bmc.c
>> index c4099987d354..50369c0331a6 100644
>> --- a/hw/arm/palmetto-bmc.c
>> +++ b/hw/arm/palmetto-bmc.c
>> @@ -17,6 +17,7 @@
>>  #include "hw/arm/arm.h"
>>  #include "hw/arm/ast2400.h"
>>  #include "hw/boards.h"
>> +#include "hw/loader.h"
>>  
>>  static struct arm_boot_info palmetto_bmc_binfo = {
>>      .loader_start = AST2400_SDRAM_BASE,
>> @@ -53,6 +54,14 @@ static void palmetto_bmc_init(MachineState *machine)
>>      aspeed_smc_init_flashes(&bmc->soc.smc, "n25q256a");
>>      aspeed_smc_init_flashes(&bmc->soc.spi, "mx25l25635e");
>>  
>> +    /*
>> +     * Install first SMC/FMC flash content as a rom.
>> +     */
>> +    if (bmc->soc.smc.flashes[0]) {
>> +        rom_add_blob_fixed("aspeed.smc.0", bmc->soc.smc.flashes[0]->storage,
>> +                           bmc->soc.smc.flashes[0]->size, 0);
>> +    }
>> +
> 
> As mentioned on IRC it's probably best we investigate alternative
> approaches and use this as a last resort.

I agree. Ideally, we should add a memory region alias of bmc->soc.smc.flashes[0] 
at address 0x0.

To get it right, we would need a memory region of type rom backing 
the SPI flash object. The m25p80 object we use is initialized all 
at once today: it gets a disk and allocates storage. The init 
sequence needs a change to allow a late-init method (there are a 
couple of FIXMEs on that topic) and to set a preferred storage, 
like a rom memory region. Hopefully, there are no impacts on the 
SSI framework.

I quickly validated the idea with some hacks. The SMC model seems 
fine, we only have to make sure the rom mode is deactivated when 
not in user mode :

    memory_region_rom_device_set_romd(&s->flashes[i]->mmio, !usermode);

I need to study the m25p80 init question deeper now. 

Thanks,

C.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 00/12] SMC and network support
  2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
                   ` (11 preceding siblings ...)
  2016-05-29 21:20 ` [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic Cédric Le Goater
@ 2016-06-23 17:00 ` Cédric Le Goater
  2016-06-24  7:06   ` Joel Stanley
  2016-06-27 16:09   ` Andrew Jeffery
  12 siblings, 2 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-23 17:00 UTC (permalink / raw)
  To: openbmc

Hello,


On 05/29/2016 11:19 PM, Cédric Le Goater wrote:
> 
> The following patchset adds to the qemu palmetto-bmc platform initial
> support for the AST2400 SMC controllers and for the FTGMAC100 network
> adapter. The device models don't implement all the features the HW
> proposes but they are complete enough to run OpenBMC on a qemu guest.
> 
> To boot one, use the following command line :
> 
>   $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
> 	-mtdblock ./flash-palmetto-20160512040959  \
> 	-mtdblock ./palmetto.pnor 
> 	-net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
> 	
> or, if you have a libvirt bridge, you can tie the nic to it :
> 	
> 	-net nic,macaddr=C0:FF:EE:00:00:02,model=ftgmac100 \
> 	-net bridge,id=net0,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0 
> 
> The first block device uses the file './flash-palmetto-20160512040959'
> which will act as a SPI flash module for the BMC, handled by the
> SMC/FMC controller. The second block device uses the file
> './palmetto.pnor' which is an OpenPower firmware image for a palmetto
> OpenPower system. This one will be handled by the SMC/SPI controller.
> 
> 
> Code is based on Andrew's qemu and the full tree can be found here
> 
>     https://github.com/legoater/qemu/commits/aspeed
> 
> Flash images can be grabbed here :
> 
>     https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
>     https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor

Here is a quick update as I just did a rebase of my branch on mainline qemu 
(commit c7288767523f) :

	https://github.com/legoater/qemu/commits/aspeed

A couple of patches, that have been sent and discussed on qemu-devel@ but 
not merged yet, are included : 

	a828f92a1807 hw/misc: Add a model for the ASPEED System Control Unit
	1181e14788a9 ast2400: Integrate the SCU model and set silicon revision
	e17aa27fa6b8 palmetto-bmc: Configure the SCU's hardware strapping register
	26582428d17c ssi: change ssi_slave_init to be a realize ops
	7b50ed94582f m25p80: qdev-ify drive property
	9d058cd9dd81 ast2400: add SMC controllers (FMC and SPI)
	1f92166a189d ast2400: add SPI flash slave object
	a1fff8b8776f ast2400: create SPI flash slaves
	b93fe85af7b8 tests: add a m25p80 test

plus these for booting from the rom. There are some changes due to the rework 
of the previous and "fixes" for the block layer ... Looks stable now and mostly
ok.

	2f0dabacf3b9 m25p80: use synchronous writes to update the block backend
	0e7345a2d430 m25p80: add a m25p80_set_rom_storage() routine
	3f02fba0c02f ast2400: handle SPI flash Command mode (read only)
	8325fc4b1ad9 ast2400: use contents of first SPI flash as a rom

These are devices enhancements, nothing major :

	85f49de1c31e m25p80: add RDCR instruction for Macronix chip
	c519cdde94c6 m25p80: add mx25l25635f chip
	a520956879b1 ast2400: use a mx25l25635f chip
	31871f768618 hw/misc: add a TMP42{1,2,3} device model
	b09b8a5088a2 ast2400: add a temp sensor device on I2C bus 3
	8750ab3e2400 ast2400: add a rtc device on I2C bus 0

and to finish, the network device which has not changed but should with 
the recent findings on the ast2500 :

	4e57f68ccaf5 net: add FTGMAC100 support
	ba7a28c66ec3 ast2400: add a FTGMAC100 nic


To give it a try, use :

	$ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
		-drive file=flash-palmetto,format=raw,if=mtd \
		-drive file=palmetto.pnor,format=raw,if=mtd \ 
		-net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
		-serial stdio

and if you have a libvirt bridge :

	 	-net nic,macaddr=C0:FF:EE:00:00:02,model=ftgmac100 \
		-net bridge,id=net0,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0 


I could boot the OpenBMC 1.0 image with it. Comments welcome !

Cheers,

C.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 00/12] SMC and network support
  2016-06-23 17:00 ` [PATCH qemu 00/12] SMC and network support Cédric Le Goater
@ 2016-06-24  7:06   ` Joel Stanley
  2016-06-24  7:18     ` Cédric Le Goater
  2016-06-27 16:09   ` Andrew Jeffery
  1 sibling, 1 reply; 30+ messages in thread
From: Joel Stanley @ 2016-06-24  7:06 UTC (permalink / raw)
  To: Cédric Le Goater; +Cc: OpenBMC Maillist, Andrew Jeffery

Hi Cedric,

On Fri, Jun 24, 2016 at 2:30 AM, Cédric Le Goater <clg@kaod.org> wrote:
> Here is a quick update as I just did a rebase of my branch on mainline qemu
> (commit c7288767523f) :
>
>         https://github.com/legoater/qemu/commits/aspeed
>

Nice work! Good to see the long list of patches on their way upstream.

> To give it a try, use :
>
>         $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
>                 -drive file=flash-palmetto,format=raw,if=mtd \
>                 -drive file=palmetto.pnor,format=raw,if=mtd \
>                 -net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
>                 -serial stdio

I gave this a go and got:

qemu-system-arm: Initialization of device mx25l25635f failed: failed
to read the initial flash content

HEAD is ba7a28c66ec35d0a021b2182613e2cb26706d8a4.

Cheers,

Joel

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 00/12] SMC and network support
  2016-06-24  7:06   ` Joel Stanley
@ 2016-06-24  7:18     ` Cédric Le Goater
  0 siblings, 0 replies; 30+ messages in thread
From: Cédric Le Goater @ 2016-06-24  7:18 UTC (permalink / raw)
  To: Joel Stanley; +Cc: OpenBMC Maillist, Andrew Jeffery

On 06/24/2016 09:06 AM, Joel Stanley wrote:
> Hi Cedric,
> 
> On Fri, Jun 24, 2016 at 2:30 AM, Cédric Le Goater <clg@kaod.org> wrote:
>> Here is a quick update as I just did a rebase of my branch on mainline qemu
>> (commit c7288767523f) :
>>
>>         https://github.com/legoater/qemu/commits/aspeed
>>
> 
> Nice work! Good to see the long list of patches on their way upstream.
> 
>> To give it a try, use :
>>
>>         $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
>>                 -drive file=flash-palmetto,format=raw,if=mtd \
>>                 -drive file=palmetto.pnor,format=raw,if=mtd \
>>                 -net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
>>                 -serial stdio
> 
> I gave this a go and got:
> 
> qemu-system-arm: Initialization of device mx25l25635f failed: failed
> to read the initial flash content

This is an issue with the flash image. Is the path correct ? the permissions ? 

> HEAD is ba7a28c66ec35d0a021b2182613e2cb26706d8a4.

Looks good.

C. 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH qemu 00/12] SMC and network support
  2016-06-23 17:00 ` [PATCH qemu 00/12] SMC and network support Cédric Le Goater
  2016-06-24  7:06   ` Joel Stanley
@ 2016-06-27 16:09   ` Andrew Jeffery
  1 sibling, 0 replies; 30+ messages in thread
From: Andrew Jeffery @ 2016-06-27 16:09 UTC (permalink / raw)
  To: Cédric Le Goater, openbmc

[-- Attachment #1: Type: text/plain, Size: 4471 bytes --]

On Thu, 2016-06-23 at 19:00 +0200, Cédric Le Goater wrote:
> Hello,
> 
> 
> On 05/29/2016 11:19 PM, Cédric Le Goater wrote:
> > 
> > 
> > The following patchset adds to the qemu palmetto-bmc platform initial
> > support for the AST2400 SMC controllers and for the FTGMAC100 network
> > adapter. The device models don't implement all the features the HW
> > proposes but they are complete enough to run OpenBMC on a qemu guest.
> > 
> > To boot one, use the following command line :
> > 
> >   $ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
> > 	-mtdblock ./flash-palmetto-20160512040959  \
> > 	-mtdblock ./palmetto.pnor 
> > 	-net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
> > 	
> > or, if you have a libvirt bridge, you can tie the nic to it :
> > 	
> > 	-net nic,macaddr=C0:FF:EE:00:00:02,model=ftgmac100 \
> > 	-net bridge,id=net0,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0 
> > 
> > The first block device uses the file './flash-palmetto-20160512040959'
> > which will act as a SPI flash module for the BMC, handled by the
> > SMC/FMC controller. The second block device uses the file
> > './palmetto.pnor' which is an OpenPower firmware image for a palmetto
> > OpenPower system. This one will be handled by the SMC/SPI controller.
> > 
> > 
> > Code is based on Andrew's qemu and the full tree can be found here
> > 
> >     https://github.com/legoater/qemu/commits/aspeed
> > 
> > Flash images can be grabbed here :
> > 
> >     https://openpower.xyz/job/openbmc-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto/flash-palmetto
> >     https://openpower.xyz/job/openpower-op-build/distro=ubuntu,target=palmetto/lastSuccessfulBuild/artifact/images/palmetto.pnor
> Here is a quick update as I just did a rebase of my branch on mainline qemu 
> (commit c7288767523f) :
> 
> 	https://github.com/legoater/qemu/commits/aspeed
> 
> A couple of patches, that have been sent and discussed on qemu-devel@ but 
> not merged yet, are included : 
> 
> 	a828f92a1807 hw/misc: Add a model for the ASPEED System Control Unit
> 	1181e14788a9 ast2400: Integrate the SCU model and set silicon revision
> 	e17aa27fa6b8 palmetto-bmc: Configure the SCU's hardware strapping register
> 	26582428d17c ssi: change ssi_slave_init to be a realize ops
> 	7b50ed94582f m25p80: qdev-ify drive property
> 	9d058cd9dd81 ast2400: add SMC controllers (FMC and SPI)
> 	1f92166a189d ast2400: add SPI flash slave object
> 	a1fff8b8776f ast2400: create SPI flash slaves
> 	b93fe85af7b8 tests: add a m25p80 test
> 
> plus these for booting from the rom. There are some changes due to the rework 
> of the previous and "fixes" for the block layer ... Looks stable now and mostly
> ok.
> 
> 	2f0dabacf3b9 m25p80: use synchronous writes to update the block backend
> 	0e7345a2d430 m25p80: add a m25p80_set_rom_storage() routine
> 	3f02fba0c02f ast2400: handle SPI flash Command mode (read only)
> 	8325fc4b1ad9 ast2400: use contents of first SPI flash as a rom
> 
> These are devices enhancements, nothing major :
> 
> 	85f49de1c31e m25p80: add RDCR instruction for Macronix chip
> 	c519cdde94c6 m25p80: add mx25l25635f chip
> 	a520956879b1 ast2400: use a mx25l25635f chip
> 	31871f768618 hw/misc: add a TMP42{1,2,3} device model
> 	b09b8a5088a2 ast2400: add a temp sensor device on I2C bus 3
> 	8750ab3e2400 ast2400: add a rtc device on I2C bus 0
> 
> and to finish, the network device which has not changed but should with 
> the recent findings on the ast2500 :
> 
> 	4e57f68ccaf5 net: add FTGMAC100 support
> 	ba7a28c66ec3 ast2400: add a FTGMAC100 nic
> 
> 
> To give it a try, use :
> 
> 	$ qemu-system-arm -m 256 -M palmetto-bmc -nographic -nodefaults  \
> 		-drive file=flash-palmetto,format=raw,if=mtd \
> 		-drive file=palmetto.pnor,format=raw,if=mtd \ 
> 		-net nic -net user,hostfwd=:127.0.0.1:2222-:22,hostname=qemu
> 		-serial stdio
> 
> and if you have a libvirt bridge :
> 
> 	 	-net nic,macaddr=C0:FF:EE:00:00:02,model=ftgmac100 \
> 		-net bridge,id=net0,helper=/usr/lib/qemu/qemu-bridge-helper,br=virbr0 
> 
> 
> I could boot the OpenBMC 1.0 image with it. Comments welcome !

I've updated openbmc/qemu master with the latest version of these
patches based on the upstream SCU code. So Joel, you can now use the
openbmc/qemu tree for CI testing on the flash blobs.

Cheers,

Andrew

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2016-06-27 16:10 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-29 21:19 [PATCH qemu 00/12] SMC and network support Cédric Le Goater
2016-05-29 21:19 ` [PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI) Cédric Le Goater
2016-06-07  2:08   ` Andrew Jeffery
2016-06-08 11:53     ` Cédric Le Goater
2016-05-29 21:19 ` [PATCH qemu 02/12] ast2400: add SPI flash slave object Cédric Le Goater
2016-06-07  5:34   ` Andrew Jeffery
2016-06-08 12:42     ` Cédric Le Goater
2016-05-29 21:19 ` [PATCH qemu 03/12] ast2400: create SPI flash slaves Cédric Le Goater
2016-06-07  5:56   ` Andrew Jeffery
2016-06-08 12:46     ` Cédric Le Goater
2016-05-29 21:19 ` [PATCH qemu 04/12] m25p80: add a get storage routine Cédric Le Goater
2016-05-29 21:19 ` [PATCH qemu 05/12] ast2400: handle SPI flash Command mode (read only) Cédric Le Goater
2016-06-07  6:04   ` Andrew Jeffery
2016-05-29 21:19 ` [PATCH qemu 06/12] ast2400: use contents of first SPI flash as a rom Cédric Le Goater
2016-06-07  6:24   ` Andrew Jeffery
2016-06-09 10:42     ` Cédric Le Goater
2016-05-29 21:20 ` [PATCH qemu 07/12] m25p80: add RDCR instruction for Macronix chip Cédric Le Goater
2016-05-29 21:20 ` [PATCH qemu 08/12] m25p80: add mx25l25635f chip Cédric Le Goater
2016-05-29 21:20 ` [PATCH qemu 09/12] ast2400: use a " Cédric Le Goater
2016-06-07  6:29   ` Andrew Jeffery
2016-05-29 21:20 ` [PATCH qemu 10/12] ast2400: add a BT device Cédric Le Goater
2016-06-07  7:15   ` Andrew Jeffery
2016-05-29 21:20 ` [PATCH qemu 11/12] net: add FTGMAC100 support Cédric Le Goater
2016-06-08  3:50   ` Andrew Jeffery
2016-05-29 21:20 ` [PATCH qemu 12/12] ast2400: add a FTGMAC100 nic Cédric Le Goater
2016-06-07  7:26   ` Andrew Jeffery
2016-06-23 17:00 ` [PATCH qemu 00/12] SMC and network support Cédric Le Goater
2016-06-24  7:06   ` Joel Stanley
2016-06-24  7:18     ` Cédric Le Goater
2016-06-27 16:09   ` Andrew Jeffery

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.