All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes
@ 2019-09-04  7:04 Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 01/10] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500 Cédric Le Goater
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:04 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

Hello,

This series improves the current models of the Aspeed machines in QEMU
and adds new ones. It also prepares ground for the models of the
Aspeed AST2600 SoC by calculating the model typenames using the SoC
name.

You will find patches for :

 - DMA support for the SMC controller, now using address_space_stl/ldl_le
 - GPIO v5 model from Rashmica

Thanks,

C.

Christian Svensson (1):
  aspeed/smc: Calculate checksum on normal DMA

Cédric Le Goater (7):
  aspeed: Remove unused SoC definitions
  aspeed: Use consistent typenames
  aspeed/smc: Add support for DMAs
  aspeed/smc: Add DMA calibration settings
  aspeed/smc: Inject errors in DMA checksum
  aspeed/scu: Introduce per-SoC SCU types
  aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine

Rashmica Gupta (2):
  hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500
  aspeed: add a GPIO controller to the SoC

 include/hw/arm/aspeed_soc.h   |   4 +-
 include/hw/gpio/aspeed_gpio.h | 100 ++++
 include/hw/misc/aspeed_scu.h  |  21 +-
 include/hw/ssi/aspeed_smc.h   |   7 +
 hw/arm/aspeed.c               |   2 +
 hw/arm/aspeed_soc.c           |  63 ++-
 hw/gpio/aspeed_gpio.c         | 884 ++++++++++++++++++++++++++++++++++
 hw/misc/aspeed_scu.c          | 102 ++--
 hw/ssi/aspeed_smc.c           | 335 ++++++++++++-
 hw/timer/aspeed_timer.c       |   3 +-
 hw/gpio/Makefile.objs         |   1 +
 11 files changed, 1418 insertions(+), 104 deletions(-)
 create mode 100644 include/hw/gpio/aspeed_gpio.h
 create mode 100644 hw/gpio/aspeed_gpio.c

-- 
2.21.0



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

* [Qemu-devel] [PATCH 01/10] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
@ 2019-09-04  7:04 ` Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 02/10] aspeed: add a GPIO controller to the SoC Cédric Le Goater
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:04 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, qemu-devel, qemu-arm, Cédric Le Goater,
	Rashmica Gupta, Joel Stanley

From: Rashmica Gupta <rashmica.g@gmail.com>

GPIO pins are arranged in groups of 8 pins labeled A,B,..,Y,Z,AA,AB,AC.
(Note that the ast2400 controller only goes up to group AB).
A set has four groups (except set AC which only has one) and is
referred to by the groups it is composed of (eg ABCD,EFGH,...,YZAAAB).
Each set is accessed and controlled by a bank of 14 registers.

These registers operate on a per pin level where each bit in the register
corresponds to a pin, except for the command source registers. The command
source registers operate on a per group level where bits 24, 16, 8 and 0
correspond to each group in the set.

 eg. registers for set ABCD:
 |D7...D0|C7...C0|B7...B0|A7...A0| <- GPIOs
 |31...24|23...16|15....8|7.....0| <- bit position

Note that there are a couple of groups that only have 4 pins.

There are two ways that this model deviates from the behaviour of the
actual controller:
(1) The only control source driving the GPIO pins in the model is the ARM
model (as there currently aren't models for the LPC or Coprocessor).

(2) None of the registers in the model are reset tolerant (needs
integration with the watchdog).

Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
Tested-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
[clg: fixed missing header files
      made use of HWADDR_PRIx to fix compilation on windows ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 include/hw/gpio/aspeed_gpio.h | 100 ++++
 hw/gpio/aspeed_gpio.c         | 884 ++++++++++++++++++++++++++++++++++
 hw/gpio/Makefile.objs         |   1 +
 3 files changed, 985 insertions(+)
 create mode 100644 include/hw/gpio/aspeed_gpio.h
 create mode 100644 hw/gpio/aspeed_gpio.c

diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h
new file mode 100644
index 000000000000..a2deac046af6
--- /dev/null
+++ b/include/hw/gpio/aspeed_gpio.h
@@ -0,0 +1,100 @@
+/*
+ *  ASPEED GPIO Controller
+ *
+ *  Copyright (C) 2017-2018 IBM Corp.
+ *
+ * This code is licensed under the GPL version 2 or later.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef ASPEED_GPIO_H
+#define ASPEED_GPIO_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ASPEED_GPIO "aspeed.gpio"
+#define ASPEED_GPIO(obj) OBJECT_CHECK(AspeedGPIOState, (obj), TYPE_ASPEED_GPIO)
+#define ASPEED_GPIO_CLASS(klass) \
+     OBJECT_CLASS_CHECK(AspeedGPIOClass, (klass), TYPE_ASPEED_GPIO)
+#define ASPEED_GPIO_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(AspeedGPIOClass, (obj), TYPE_ASPEED_GPIO)
+
+#define ASPEED_GPIO_MAX_NR_SETS 8
+#define ASPEED_REGS_PER_BANK 14
+#define ASPEED_GPIO_MAX_NR_REGS (ASPEED_REGS_PER_BANK * ASPEED_GPIO_MAX_NR_SETS)
+#define ASPEED_GPIO_NR_PINS 228
+#define ASPEED_GROUPS_PER_SET 4
+#define ASPEED_GPIO_NR_DEBOUNCE_REGS 3
+#define ASPEED_CHARS_PER_GROUP_LABEL 4
+
+typedef struct GPIOSets GPIOSets;
+
+typedef struct GPIOSetProperties {
+    uint32_t input;
+    uint32_t output;
+    char group_label[ASPEED_GROUPS_PER_SET][ASPEED_CHARS_PER_GROUP_LABEL];
+} GPIOSetProperties;
+
+enum GPIORegType {
+    gpio_not_a_reg,
+    gpio_reg_data_value,
+    gpio_reg_direction,
+    gpio_reg_int_enable,
+    gpio_reg_int_sens_0,
+    gpio_reg_int_sens_1,
+    gpio_reg_int_sens_2,
+    gpio_reg_int_status,
+    gpio_reg_reset_tolerant,
+    gpio_reg_debounce_1,
+    gpio_reg_debounce_2,
+    gpio_reg_cmd_source_0,
+    gpio_reg_cmd_source_1,
+    gpio_reg_data_read,
+    gpio_reg_input_mask,
+};
+
+typedef struct AspeedGPIOReg {
+    uint16_t set_idx;
+    enum GPIORegType type;
+ } AspeedGPIOReg;
+
+typedef struct  AspeedGPIOClass {
+    SysBusDevice parent_obj;
+    const GPIOSetProperties *props;
+    uint32_t nr_gpio_pins;
+    uint32_t nr_gpio_sets;
+    uint32_t gap;
+    const AspeedGPIOReg *reg_table;
+}  AspeedGPIOClass;
+
+typedef struct AspeedGPIOState {
+    /* <private> */
+    SysBusDevice parent;
+
+    /*< public >*/
+    MemoryRegion iomem;
+    int pending;
+    qemu_irq irq;
+    qemu_irq gpios[ASPEED_GPIO_NR_PINS];
+
+/* Parallel GPIO Registers */
+    uint32_t debounce_regs[ASPEED_GPIO_NR_DEBOUNCE_REGS];
+    struct GPIOSets {
+        uint32_t data_value; /* Reflects pin values */
+        uint32_t data_read; /* Contains last value written to data value */
+        uint32_t direction;
+        uint32_t int_enable;
+        uint32_t int_sens_0;
+        uint32_t int_sens_1;
+        uint32_t int_sens_2;
+        uint32_t int_status;
+        uint32_t reset_tol;
+        uint32_t cmd_source_0;
+        uint32_t cmd_source_1;
+        uint32_t debounce_1;
+        uint32_t debounce_2;
+        uint32_t input_mask;
+    } sets[ASPEED_GPIO_MAX_NR_SETS];
+} AspeedGPIOState;
+
+#endif /* _ASPEED_GPIO_H_ */
diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c
new file mode 100644
index 000000000000..25fbfec3b84e
--- /dev/null
+++ b/hw/gpio/aspeed_gpio.c
@@ -0,0 +1,884 @@
+/*
+ *  ASPEED GPIO Controller
+ *
+ *  Copyright (C) 2017-2019 IBM Corp.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <assert.h>
+
+#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
+#include "qemu/log.h"
+#include "hw/gpio/aspeed_gpio.h"
+#include "include/hw/misc/aspeed_scu.h"
+#include "qapi/error.h"
+#include "qapi/visitor.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+
+#define GPIOS_PER_REG 32
+#define GPIOS_PER_SET GPIOS_PER_REG
+#define GPIO_PIN_GAP_SIZE 4
+#define GPIOS_PER_GROUP 8
+#define GPIO_GROUP_SHIFT 3
+
+/* GPIO Source Types */
+#define ASPEED_CMD_SRC_MASK         0x01010101
+#define ASPEED_SOURCE_ARM           0
+#define ASPEED_SOURCE_LPC           1
+#define ASPEED_SOURCE_COPROCESSOR   2
+#define ASPEED_SOURCE_RESERVED      3
+
+/* GPIO Interrupt Triggers */
+/*
+ *  For each set of gpios there are three sensitivity registers that control
+ *  the interrupt trigger mode.
+ *
+ *  | 2 | 1 | 0 | trigger mode
+ *  -----------------------------
+ *  | 0 | 0 | 0 | falling-edge
+ *  | 0 | 0 | 1 | rising-edge
+ *  | 0 | 1 | 0 | level-low
+ *  | 0 | 1 | 1 | level-high
+ *  | 1 | X | X | dual-edge
+ */
+#define ASPEED_FALLING_EDGE 0
+#define ASPEED_RISING_EDGE  1
+#define ASPEED_LEVEL_LOW    2
+#define ASPEED_LEVEL_HIGH   3
+#define ASPEED_DUAL_EDGE    4
+
+/* GPIO Register Address Offsets */
+#define GPIO_ABCD_DATA_VALUE       (0x000 >> 2)
+#define GPIO_ABCD_DIRECTION        (0x004 >> 2)
+#define GPIO_ABCD_INT_ENABLE       (0x008 >> 2)
+#define GPIO_ABCD_INT_SENS_0       (0x00C >> 2)
+#define GPIO_ABCD_INT_SENS_1       (0x010 >> 2)
+#define GPIO_ABCD_INT_SENS_2       (0x014 >> 2)
+#define GPIO_ABCD_INT_STATUS       (0x018 >> 2)
+#define GPIO_ABCD_RESET_TOLERANT   (0x01C >> 2)
+#define GPIO_EFGH_DATA_VALUE       (0x020 >> 2)
+#define GPIO_EFGH_DIRECTION        (0x024 >> 2)
+#define GPIO_EFGH_INT_ENABLE       (0x028 >> 2)
+#define GPIO_EFGH_INT_SENS_0       (0x02C >> 2)
+#define GPIO_EFGH_INT_SENS_1       (0x030 >> 2)
+#define GPIO_EFGH_INT_SENS_2       (0x034 >> 2)
+#define GPIO_EFGH_INT_STATUS       (0x038 >> 2)
+#define GPIO_EFGH_RESET_TOLERANT   (0x03C >> 2)
+#define GPIO_ABCD_DEBOUNCE_1       (0x040 >> 2)
+#define GPIO_ABCD_DEBOUNCE_2       (0x044 >> 2)
+#define GPIO_EFGH_DEBOUNCE_1       (0x048 >> 2)
+#define GPIO_EFGH_DEBOUNCE_2       (0x04C >> 2)
+#define GPIO_DEBOUNCE_TIME_1       (0x050 >> 2)
+#define GPIO_DEBOUNCE_TIME_2       (0x054 >> 2)
+#define GPIO_DEBOUNCE_TIME_3       (0x058 >> 2)
+#define GPIO_ABCD_COMMAND_SRC_0    (0x060 >> 2)
+#define GPIO_ABCD_COMMAND_SRC_1    (0x064 >> 2)
+#define GPIO_EFGH_COMMAND_SRC_0    (0x068 >> 2)
+#define GPIO_EFGH_COMMAND_SRC_1    (0x06C >> 2)
+#define GPIO_IJKL_DATA_VALUE       (0x070 >> 2)
+#define GPIO_IJKL_DIRECTION        (0x074 >> 2)
+#define GPIO_MNOP_DATA_VALUE       (0x078 >> 2)
+#define GPIO_MNOP_DIRECTION        (0x07C >> 2)
+#define GPIO_QRST_DATA_VALUE       (0x080 >> 2)
+#define GPIO_QRST_DIRECTION        (0x084 >> 2)
+#define GPIO_UVWX_DATA_VALUE       (0x088 >> 2)
+#define GPIO_UVWX_DIRECTION        (0x08C >> 2)
+#define GPIO_IJKL_COMMAND_SRC_0    (0x090 >> 2)
+#define GPIO_IJKL_COMMAND_SRC_1    (0x094 >> 2)
+#define GPIO_IJKL_INT_ENABLE       (0x098 >> 2)
+#define GPIO_IJKL_INT_SENS_0       (0x09C >> 2)
+#define GPIO_IJKL_INT_SENS_1       (0x0A0 >> 2)
+#define GPIO_IJKL_INT_SENS_2       (0x0A4 >> 2)
+#define GPIO_IJKL_INT_STATUS       (0x0A8 >> 2)
+#define GPIO_IJKL_RESET_TOLERANT   (0x0AC >> 2)
+#define GPIO_IJKL_DEBOUNCE_1       (0x0B0 >> 2)
+#define GPIO_IJKL_DEBOUNCE_2       (0x0B4 >> 2)
+#define GPIO_IJKL_INPUT_MASK       (0x0B8 >> 2)
+#define GPIO_ABCD_DATA_READ        (0x0C0 >> 2)
+#define GPIO_EFGH_DATA_READ        (0x0C4 >> 2)
+#define GPIO_IJKL_DATA_READ        (0x0C8 >> 2)
+#define GPIO_MNOP_DATA_READ        (0x0CC >> 2)
+#define GPIO_QRST_DATA_READ        (0x0D0 >> 2)
+#define GPIO_UVWX_DATA_READ        (0x0D4 >> 2)
+#define GPIO_YZAAAB_DATA_READ      (0x0D8 >> 2)
+#define GPIO_AC_DATA_READ          (0x0DC >> 2)
+#define GPIO_MNOP_COMMAND_SRC_0    (0x0E0 >> 2)
+#define GPIO_MNOP_COMMAND_SRC_1    (0x0E4 >> 2)
+#define GPIO_MNOP_INT_ENABLE       (0x0E8 >> 2)
+#define GPIO_MNOP_INT_SENS_0       (0x0EC >> 2)
+#define GPIO_MNOP_INT_SENS_1       (0x0F0 >> 2)
+#define GPIO_MNOP_INT_SENS_2       (0x0F4 >> 2)
+#define GPIO_MNOP_INT_STATUS       (0x0F8 >> 2)
+#define GPIO_MNOP_RESET_TOLERANT   (0x0FC >> 2)
+#define GPIO_MNOP_DEBOUNCE_1       (0x100 >> 2)
+#define GPIO_MNOP_DEBOUNCE_2       (0x104 >> 2)
+#define GPIO_MNOP_INPUT_MASK       (0x108 >> 2)
+#define GPIO_QRST_COMMAND_SRC_0    (0x110 >> 2)
+#define GPIO_QRST_COMMAND_SRC_1    (0x114 >> 2)
+#define GPIO_QRST_INT_ENABLE       (0x118 >> 2)
+#define GPIO_QRST_INT_SENS_0       (0x11C >> 2)
+#define GPIO_QRST_INT_SENS_1       (0x120 >> 2)
+#define GPIO_QRST_INT_SENS_2       (0x124 >> 2)
+#define GPIO_QRST_INT_STATUS       (0x128 >> 2)
+#define GPIO_QRST_RESET_TOLERANT   (0x12C >> 2)
+#define GPIO_QRST_DEBOUNCE_1       (0x130 >> 2)
+#define GPIO_QRST_DEBOUNCE_2       (0x134 >> 2)
+#define GPIO_QRST_INPUT_MASK       (0x138 >> 2)
+#define GPIO_UVWX_COMMAND_SRC_0    (0x140 >> 2)
+#define GPIO_UVWX_COMMAND_SRC_1    (0x144 >> 2)
+#define GPIO_UVWX_INT_ENABLE       (0x148 >> 2)
+#define GPIO_UVWX_INT_SENS_0       (0x14C >> 2)
+#define GPIO_UVWX_INT_SENS_1       (0x150 >> 2)
+#define GPIO_UVWX_INT_SENS_2       (0x154 >> 2)
+#define GPIO_UVWX_INT_STATUS       (0x158 >> 2)
+#define GPIO_UVWX_RESET_TOLERANT   (0x15C >> 2)
+#define GPIO_UVWX_DEBOUNCE_1       (0x160 >> 2)
+#define GPIO_UVWX_DEBOUNCE_2       (0x164 >> 2)
+#define GPIO_UVWX_INPUT_MASK       (0x168 >> 2)
+#define GPIO_YZAAAB_COMMAND_SRC_0  (0x170 >> 2)
+#define GPIO_YZAAAB_COMMAND_SRC_1  (0x174 >> 2)
+#define GPIO_YZAAAB_INT_ENABLE     (0x178 >> 2)
+#define GPIO_YZAAAB_INT_SENS_0     (0x17C >> 2)
+#define GPIO_YZAAAB_INT_SENS_1     (0x180 >> 2)
+#define GPIO_YZAAAB_INT_SENS_2     (0x184 >> 2)
+#define GPIO_YZAAAB_INT_STATUS     (0x188 >> 2)
+#define GPIO_YZAAAB_RESET_TOLERANT (0x18C >> 2)
+#define GPIO_YZAAAB_DEBOUNCE_1     (0x190 >> 2)
+#define GPIO_YZAAAB_DEBOUNCE_2     (0x194 >> 2)
+#define GPIO_YZAAAB_INPUT_MASK     (0x198 >> 2)
+#define GPIO_AC_COMMAND_SRC_0      (0x1A0 >> 2)
+#define GPIO_AC_COMMAND_SRC_1      (0x1A4 >> 2)
+#define GPIO_AC_INT_ENABLE         (0x1A8 >> 2)
+#define GPIO_AC_INT_SENS_0         (0x1AC >> 2)
+#define GPIO_AC_INT_SENS_1         (0x1B0 >> 2)
+#define GPIO_AC_INT_SENS_2         (0x1B4 >> 2)
+#define GPIO_AC_INT_STATUS         (0x1B8 >> 2)
+#define GPIO_AC_RESET_TOLERANT     (0x1BC >> 2)
+#define GPIO_AC_DEBOUNCE_1         (0x1C0 >> 2)
+#define GPIO_AC_DEBOUNCE_2         (0x1C4 >> 2)
+#define GPIO_AC_INPUT_MASK         (0x1C8 >> 2)
+#define GPIO_ABCD_INPUT_MASK       (0x1D0 >> 2)
+#define GPIO_EFGH_INPUT_MASK       (0x1D4 >> 2)
+#define GPIO_YZAAAB_DATA_VALUE     (0x1E0 >> 2)
+#define GPIO_YZAAAB_DIRECTION      (0x1E4 >> 2)
+#define GPIO_AC_DATA_VALUE         (0x1E8 >> 2)
+#define GPIO_AC_DIRECTION          (0x1EC >> 2)
+#define GPIO_3_6V_MEM_SIZE         0x1F0
+#define GPIO_3_6V_REG_ARRAY_SIZE   (GPIO_3_6V_MEM_SIZE >> 2)
+
+static int aspeed_evaluate_irq(GPIOSets *regs, int gpio_prev_high, int gpio)
+{
+    uint32_t falling_edge = 0, rising_edge = 0;
+    uint32_t int_trigger = extract32(regs->int_sens_0, gpio, 1)
+                           | extract32(regs->int_sens_1, gpio, 1) << 1
+                           | extract32(regs->int_sens_2, gpio, 1) << 2;
+    uint32_t gpio_curr_high = extract32(regs->data_value, gpio, 1);
+    uint32_t gpio_int_enabled = extract32(regs->int_enable, gpio, 1);
+
+    if (!gpio_int_enabled) {
+        return 0;
+    }
+
+    /* Detect edges */
+    if (gpio_curr_high && !gpio_prev_high) {
+        rising_edge = 1;
+    } else if (!gpio_curr_high && gpio_prev_high) {
+        falling_edge = 1;
+    }
+
+    if (((int_trigger == ASPEED_FALLING_EDGE)  && falling_edge)  ||
+        ((int_trigger == ASPEED_RISING_EDGE)  && rising_edge)    ||
+        ((int_trigger == ASPEED_LEVEL_LOW)  && !gpio_curr_high)  ||
+        ((int_trigger == ASPEED_LEVEL_HIGH)  && gpio_curr_high)  ||
+        ((int_trigger >= ASPEED_DUAL_EDGE)  && (rising_edge || falling_edge)))
+    {
+        regs->int_status = deposit32(regs->int_status, gpio, 1, 1);
+        return 1;
+    }
+    return 0;
+}
+
+#define nested_struct_index(ta, pa, m, tb, pb) \
+        (pb - ((tb *)(((char *)pa) + offsetof(ta, m))))
+
+static ptrdiff_t aspeed_gpio_set_idx(AspeedGPIOState *s, GPIOSets *regs)
+{
+    return nested_struct_index(AspeedGPIOState, s, sets, GPIOSets, regs);
+}
+
+static void aspeed_gpio_update(AspeedGPIOState *s, GPIOSets *regs,
+                               uint32_t value)
+{
+    uint32_t input_mask = regs->input_mask;
+    uint32_t direction = regs->direction;
+    uint32_t old = regs->data_value;
+    uint32_t new = value;
+    uint32_t diff;
+    int gpio;
+
+    diff = old ^ new;
+    if (diff) {
+        for (gpio = 0; gpio < GPIOS_PER_REG; gpio++) {
+            uint32_t mask = 1 << gpio;
+
+            /* If the gpio needs to be updated... */
+            if (!(diff & mask)) {
+                continue;
+            }
+
+            /* ...and we're output or not input-masked... */
+            if (!(direction & mask) && (input_mask & mask)) {
+                continue;
+            }
+
+            /* ...then update the state. */
+            if (mask & new) {
+                regs->data_value |= mask;
+            } else {
+                regs->data_value &= ~mask;
+            }
+
+            /* If the gpio is set to output... */
+            if (direction & mask) {
+                /* ...trigger the line-state IRQ */
+                ptrdiff_t set = aspeed_gpio_set_idx(s, regs);
+                size_t offset = set * GPIOS_PER_SET + gpio;
+                qemu_set_irq(s->gpios[offset], !!(new & mask));
+            } else {
+                /* ...otherwise if we meet the line's current IRQ policy... */
+                if (aspeed_evaluate_irq(regs, old & mask, gpio)) {
+                    /* ...trigger the VIC IRQ */
+                    s->pending++;
+                }
+            }
+        }
+    }
+    qemu_set_irq(s->irq, !!(s->pending));
+}
+
+static uint32_t aspeed_adjust_pin(AspeedGPIOState *s, uint32_t pin)
+{
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    /*
+     * The 2500 has a 4 pin gap in group AB and the 2400 has a 4 pin
+     * gap in group Y (and only four pins in AB but this is the last group so
+     * it doesn't matter).
+     */
+    if (agc->gap && pin >= agc->gap) {
+        pin += GPIO_PIN_GAP_SIZE;
+    }
+
+    return pin;
+}
+
+static bool aspeed_gpio_get_pin_level(AspeedGPIOState *s, uint32_t set_idx,
+                                      uint32_t pin)
+{
+    uint32_t reg_val;
+    uint32_t pin_mask = 1 << pin;
+
+    reg_val = s->sets[set_idx].data_value;
+
+    return !!(reg_val & pin_mask);
+}
+
+static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t set_idx,
+                                      uint32_t pin, bool level)
+{
+    uint32_t value = s->sets[set_idx].data_value;
+    uint32_t pin_mask = 1 << pin;
+
+    if (level) {
+        value |= pin_mask;
+    } else {
+        value &= !pin_mask;
+    }
+
+    aspeed_gpio_update(s, &s->sets[set_idx], value);
+}
+
+/*
+ *  | src_1 | src_2 |  source     |
+ *  |-----------------------------|
+ *  |   0   |   0   |  ARM        |
+ *  |   0   |   1   |  LPC        |
+ *  |   1   |   0   |  Coprocessor|
+ *  |   1   |   1   |  Reserved   |
+ *
+ *  Once the source of a set is programmed, corresponding bits in the
+ *  data_value, direction, interrupt [enable, sens[0-2]], reset_tol and
+ *  debounce registers can only be written by the source.
+ *
+ *  Source is ARM by default
+ *  only bits 24, 16, 8, and 0 can be set
+ *
+ *  we don't currently have a model for the LPC or Coprocessor
+ */
+static uint32_t update_value_control_source(GPIOSets *regs, uint32_t old_value,
+                                            uint32_t value)
+{
+    int i;
+    int cmd_source;
+
+    /* assume the source is always ARM for now */
+    int source = ASPEED_SOURCE_ARM;
+
+    uint32_t new_value = 0;
+
+    /* for each group in set */
+    for (i = 0; i < GPIOS_PER_REG; i += GPIOS_PER_GROUP) {
+        cmd_source = extract32(regs->cmd_source_0, i, 1)
+                | (extract32(regs->cmd_source_1, i, 1) << 1);
+
+        if (source == cmd_source) {
+            new_value |= (0xff << i) & value;
+        } else {
+            new_value |= (0xff << i) & old_value;
+        }
+    }
+    return new_value;
+}
+
+static const AspeedGPIOReg aspeed_3_6v_gpios[GPIO_3_6V_REG_ARRAY_SIZE] = {
+    /* Set ABCD */
+    [GPIO_ABCD_DATA_VALUE] =     { 0, gpio_reg_data_value },
+    [GPIO_ABCD_DIRECTION] =      { 0, gpio_reg_direction },
+    [GPIO_ABCD_INT_ENABLE] =     { 0, gpio_reg_int_enable },
+    [GPIO_ABCD_INT_SENS_0] =     { 0, gpio_reg_int_sens_0 },
+    [GPIO_ABCD_INT_SENS_1] =     { 0, gpio_reg_int_sens_1 },
+    [GPIO_ABCD_INT_SENS_2] =     { 0, gpio_reg_int_sens_2 },
+    [GPIO_ABCD_INT_STATUS] =     { 0, gpio_reg_int_status },
+    [GPIO_ABCD_RESET_TOLERANT] = { 0, gpio_reg_reset_tolerant },
+    [GPIO_ABCD_DEBOUNCE_1] =     { 0, gpio_reg_debounce_1 },
+    [GPIO_ABCD_DEBOUNCE_2] =     { 0, gpio_reg_debounce_2 },
+    [GPIO_ABCD_COMMAND_SRC_0] =  { 0, gpio_reg_cmd_source_0 },
+    [GPIO_ABCD_COMMAND_SRC_1] =  { 0, gpio_reg_cmd_source_1 },
+    [GPIO_ABCD_DATA_READ] =      { 0, gpio_reg_data_read },
+    [GPIO_ABCD_INPUT_MASK] =     { 0, gpio_reg_input_mask },
+    /* Set EFGH */
+    [GPIO_EFGH_DATA_VALUE] =     { 1, gpio_reg_data_value },
+    [GPIO_EFGH_DIRECTION] =      { 1, gpio_reg_direction },
+    [GPIO_EFGH_INT_ENABLE] =     { 1, gpio_reg_int_enable },
+    [GPIO_EFGH_INT_SENS_0] =     { 1, gpio_reg_int_sens_0 },
+    [GPIO_EFGH_INT_SENS_1] =     { 1, gpio_reg_int_sens_1 },
+    [GPIO_EFGH_INT_SENS_2] =     { 1, gpio_reg_int_sens_2 },
+    [GPIO_EFGH_INT_STATUS] =     { 1, gpio_reg_int_status },
+    [GPIO_EFGH_RESET_TOLERANT] = { 1, gpio_reg_reset_tolerant },
+    [GPIO_EFGH_DEBOUNCE_1] =     { 1, gpio_reg_debounce_1 },
+    [GPIO_EFGH_DEBOUNCE_2] =     { 1, gpio_reg_debounce_2 },
+    [GPIO_EFGH_COMMAND_SRC_0] =  { 1, gpio_reg_cmd_source_0 },
+    [GPIO_EFGH_COMMAND_SRC_1] =  { 1, gpio_reg_cmd_source_1 },
+    [GPIO_EFGH_DATA_READ] =      { 1, gpio_reg_data_read },
+    [GPIO_EFGH_INPUT_MASK] =     { 1, gpio_reg_input_mask },
+    /* Set IJKL */
+    [GPIO_IJKL_DATA_VALUE] =     { 2, gpio_reg_data_value },
+    [GPIO_IJKL_DIRECTION] =      { 2, gpio_reg_direction },
+    [GPIO_IJKL_INT_ENABLE] =     { 2, gpio_reg_int_enable },
+    [GPIO_IJKL_INT_SENS_0] =     { 2, gpio_reg_int_sens_0 },
+    [GPIO_IJKL_INT_SENS_1] =     { 2, gpio_reg_int_sens_1 },
+    [GPIO_IJKL_INT_SENS_2] =     { 2, gpio_reg_int_sens_2 },
+    [GPIO_IJKL_INT_STATUS] =     { 2, gpio_reg_int_status },
+    [GPIO_IJKL_RESET_TOLERANT] = { 2, gpio_reg_reset_tolerant },
+    [GPIO_IJKL_DEBOUNCE_1] =     { 2, gpio_reg_debounce_1 },
+    [GPIO_IJKL_DEBOUNCE_2] =     { 2, gpio_reg_debounce_2 },
+    [GPIO_IJKL_COMMAND_SRC_0] =  { 2, gpio_reg_cmd_source_0 },
+    [GPIO_IJKL_COMMAND_SRC_1] =  { 2, gpio_reg_cmd_source_1 },
+    [GPIO_IJKL_DATA_READ] =      { 2, gpio_reg_data_read },
+    [GPIO_IJKL_INPUT_MASK] =     { 2, gpio_reg_input_mask },
+    /* Set MNOP */
+    [GPIO_MNOP_DATA_VALUE] =     { 3, gpio_reg_data_value },
+    [GPIO_MNOP_DIRECTION] =      { 3, gpio_reg_direction },
+    [GPIO_MNOP_INT_ENABLE] =     { 3, gpio_reg_int_enable },
+    [GPIO_MNOP_INT_SENS_0] =     { 3, gpio_reg_int_sens_0 },
+    [GPIO_MNOP_INT_SENS_1] =     { 3, gpio_reg_int_sens_1 },
+    [GPIO_MNOP_INT_SENS_2] =     { 3, gpio_reg_int_sens_2 },
+    [GPIO_MNOP_INT_STATUS] =     { 3, gpio_reg_int_status },
+    [GPIO_MNOP_RESET_TOLERANT] = { 3, gpio_reg_reset_tolerant },
+    [GPIO_MNOP_DEBOUNCE_1] =     { 3, gpio_reg_debounce_1 },
+    [GPIO_MNOP_DEBOUNCE_2] =     { 3, gpio_reg_debounce_2 },
+    [GPIO_MNOP_COMMAND_SRC_0] =  { 3, gpio_reg_cmd_source_0 },
+    [GPIO_MNOP_COMMAND_SRC_1] =  { 3, gpio_reg_cmd_source_1 },
+    [GPIO_MNOP_DATA_READ] =      { 3, gpio_reg_data_read },
+    [GPIO_MNOP_INPUT_MASK] =     { 3, gpio_reg_input_mask },
+    /* Set QRST */
+    [GPIO_QRST_DATA_VALUE] =     { 4, gpio_reg_data_value },
+    [GPIO_QRST_DIRECTION] =      { 4, gpio_reg_direction },
+    [GPIO_QRST_INT_ENABLE] =     { 4, gpio_reg_int_enable },
+    [GPIO_QRST_INT_SENS_0] =     { 4, gpio_reg_int_sens_0 },
+    [GPIO_QRST_INT_SENS_1] =     { 4, gpio_reg_int_sens_1 },
+    [GPIO_QRST_INT_SENS_2] =     { 4, gpio_reg_int_sens_2 },
+    [GPIO_QRST_INT_STATUS] =     { 4, gpio_reg_int_status },
+    [GPIO_QRST_RESET_TOLERANT] = { 4, gpio_reg_reset_tolerant },
+    [GPIO_QRST_DEBOUNCE_1] =     { 4, gpio_reg_debounce_1 },
+    [GPIO_QRST_DEBOUNCE_2] =     { 4, gpio_reg_debounce_2 },
+    [GPIO_QRST_COMMAND_SRC_0] =  { 4, gpio_reg_cmd_source_0 },
+    [GPIO_QRST_COMMAND_SRC_1] =  { 4, gpio_reg_cmd_source_1 },
+    [GPIO_QRST_DATA_READ] =      { 4, gpio_reg_data_read },
+    [GPIO_QRST_INPUT_MASK] =     { 4, gpio_reg_input_mask },
+    /* Set UVWX */
+    [GPIO_UVWX_DATA_VALUE] =     { 5, gpio_reg_data_value },
+    [GPIO_UVWX_DIRECTION] =      { 5, gpio_reg_direction },
+    [GPIO_UVWX_INT_ENABLE] =     { 5, gpio_reg_int_enable },
+    [GPIO_UVWX_INT_SENS_0] =     { 5, gpio_reg_int_sens_0 },
+    [GPIO_UVWX_INT_SENS_1] =     { 5, gpio_reg_int_sens_1 },
+    [GPIO_UVWX_INT_SENS_2] =     { 5, gpio_reg_int_sens_2 },
+    [GPIO_UVWX_INT_STATUS] =     { 5, gpio_reg_int_status },
+    [GPIO_UVWX_RESET_TOLERANT] = { 5, gpio_reg_reset_tolerant },
+    [GPIO_UVWX_DEBOUNCE_1] =     { 5, gpio_reg_debounce_1 },
+    [GPIO_UVWX_DEBOUNCE_2] =     { 5, gpio_reg_debounce_2 },
+    [GPIO_UVWX_COMMAND_SRC_0] =  { 5, gpio_reg_cmd_source_0 },
+    [GPIO_UVWX_COMMAND_SRC_1] =  { 5, gpio_reg_cmd_source_1 },
+    [GPIO_UVWX_DATA_READ] =      { 5, gpio_reg_data_read },
+    [GPIO_UVWX_INPUT_MASK] =     { 5, gpio_reg_input_mask },
+    /* Set YZAAAB */
+    [GPIO_YZAAAB_DATA_VALUE] =     { 6, gpio_reg_data_value },
+    [GPIO_YZAAAB_DIRECTION] =      { 6, gpio_reg_direction },
+    [GPIO_YZAAAB_INT_ENABLE] =     { 6, gpio_reg_int_enable },
+    [GPIO_YZAAAB_INT_SENS_0] =     { 6, gpio_reg_int_sens_0 },
+    [GPIO_YZAAAB_INT_SENS_1] =     { 6, gpio_reg_int_sens_1 },
+    [GPIO_YZAAAB_INT_SENS_2] =     { 6, gpio_reg_int_sens_2 },
+    [GPIO_YZAAAB_INT_STATUS] =     { 6, gpio_reg_int_status },
+    [GPIO_YZAAAB_RESET_TOLERANT] = { 6, gpio_reg_reset_tolerant },
+    [GPIO_YZAAAB_DEBOUNCE_1] =     { 6, gpio_reg_debounce_1 },
+    [GPIO_YZAAAB_DEBOUNCE_2] =     { 6, gpio_reg_debounce_2 },
+    [GPIO_YZAAAB_COMMAND_SRC_0] =  { 6, gpio_reg_cmd_source_0 },
+    [GPIO_YZAAAB_COMMAND_SRC_1] =  { 6, gpio_reg_cmd_source_1 },
+    [GPIO_YZAAAB_DATA_READ] =      { 6, gpio_reg_data_read },
+    [GPIO_YZAAAB_INPUT_MASK] =     { 6, gpio_reg_input_mask },
+    /* Set AC  (ast2500 only) */
+    [GPIO_AC_DATA_VALUE] =         { 7, gpio_reg_data_value },
+    [GPIO_AC_DIRECTION] =          { 7, gpio_reg_direction },
+    [GPIO_AC_INT_ENABLE] =         { 7, gpio_reg_int_enable },
+    [GPIO_AC_INT_SENS_0] =         { 7, gpio_reg_int_sens_0 },
+    [GPIO_AC_INT_SENS_1] =         { 7, gpio_reg_int_sens_1 },
+    [GPIO_AC_INT_SENS_2] =         { 7, gpio_reg_int_sens_2 },
+    [GPIO_AC_INT_STATUS] =         { 7, gpio_reg_int_status },
+    [GPIO_AC_RESET_TOLERANT] =     { 7, gpio_reg_reset_tolerant },
+    [GPIO_AC_DEBOUNCE_1] =         { 7, gpio_reg_debounce_1 },
+    [GPIO_AC_DEBOUNCE_2] =         { 7, gpio_reg_debounce_2 },
+    [GPIO_AC_COMMAND_SRC_0] =      { 7, gpio_reg_cmd_source_0 },
+    [GPIO_AC_COMMAND_SRC_1] =      { 7, gpio_reg_cmd_source_1 },
+    [GPIO_AC_DATA_READ] =          { 7, gpio_reg_data_read },
+    [GPIO_AC_INPUT_MASK] =         { 7, gpio_reg_input_mask },
+};
+
+static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
+{
+    AspeedGPIOState *s = ASPEED_GPIO(opaque);
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    uint64_t idx = -1;
+    const AspeedGPIOReg *reg;
+    GPIOSets *set;
+
+    idx = offset >> 2;
+    if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
+        idx -= GPIO_DEBOUNCE_TIME_1;
+        return (uint64_t) s->debounce_regs[idx];
+    }
+
+    reg = &agc->reg_table[idx];
+    if (reg->set_idx >= agc->nr_gpio_sets) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
+                      HWADDR_PRIx"\n", __func__, offset);
+        return 0;
+    }
+
+    set = &s->sets[reg->set_idx];
+    switch (reg->type) {
+    case gpio_reg_data_value:
+        return set->data_value;
+    case gpio_reg_direction:
+        return set->direction;
+    case gpio_reg_int_enable:
+        return set->int_enable;
+    case gpio_reg_int_sens_0:
+        return set->int_sens_0;
+    case gpio_reg_int_sens_1:
+        return set->int_sens_1;
+    case gpio_reg_int_sens_2:
+        return set->int_sens_2;
+    case gpio_reg_int_status:
+        return set->int_status;
+    case gpio_reg_reset_tolerant:
+        return set->reset_tol;
+    case gpio_reg_debounce_1:
+        return set->debounce_1;
+    case gpio_reg_debounce_2:
+        return set->debounce_2;
+    case gpio_reg_cmd_source_0:
+        return set->cmd_source_0;
+    case gpio_reg_cmd_source_1:
+        return set->cmd_source_1;
+    case gpio_reg_data_read:
+        return set->data_read;
+    case gpio_reg_input_mask:
+        return set->input_mask;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%"
+                      HWADDR_PRIx"\n", __func__, offset);
+        return 0;
+    };
+}
+
+static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
+                              uint32_t size)
+{
+    AspeedGPIOState *s = ASPEED_GPIO(opaque);
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    const GPIOSetProperties *props;
+    uint64_t idx = -1;
+    const AspeedGPIOReg *reg;
+    GPIOSets *set;
+    uint32_t cleared;
+
+    idx = offset >> 2;
+    if (idx >= GPIO_DEBOUNCE_TIME_1 && idx <= GPIO_DEBOUNCE_TIME_3) {
+        idx -= GPIO_DEBOUNCE_TIME_1;
+        s->debounce_regs[idx] = (uint32_t) data;
+        return;
+    }
+
+    reg = &agc->reg_table[idx];
+    if (reg->set_idx >= agc->nr_gpio_sets) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
+                      HWADDR_PRIx"\n", __func__, offset);
+        return;
+    }
+
+    set = &s->sets[reg->set_idx];
+    props = &agc->props[reg->set_idx];
+
+    switch (reg->type) {
+    case gpio_reg_data_value:
+        data &= props->output;
+        data = update_value_control_source(set, set->data_value, data);
+        set->data_read = data;
+        aspeed_gpio_update(s, set, data);
+        return;
+    case gpio_reg_direction:
+        /*
+         *   where data is the value attempted to be written to the pin:
+         *    pin type      | input mask | output mask | expected value
+         *    ------------------------------------------------------------
+         *   bidirectional  |   1       |   1        |  data
+         *   input only     |   1       |   0        |   0
+         *   output only    |   0       |   1        |   1
+         *   no pin / gap   |   0       |   0        |   0
+         *
+         *  which is captured by:
+         *  data = ( data | ~input) & output;
+         */
+        data = (data | ~props->input) & props->output;
+        set->direction = update_value_control_source(set, set->direction, data);
+        break;
+    case gpio_reg_int_enable:
+        set->int_enable = update_value_control_source(set, set->int_enable,
+                                                      data);
+        break;
+    case gpio_reg_int_sens_0:
+        set->int_sens_0 = update_value_control_source(set, set->int_sens_0,
+                                                      data);
+        break;
+    case gpio_reg_int_sens_1:
+        set->int_sens_1 = update_value_control_source(set, set->int_sens_1,
+                                                      data);
+        break;
+    case gpio_reg_int_sens_2:
+        set->int_sens_2 = update_value_control_source(set, set->int_sens_2,
+                                                      data);
+        break;
+    case gpio_reg_int_status:
+        cleared = ctpop32(data & set->int_status);
+        if (s->pending && cleared) {
+            assert(s->pending >= cleared);
+            s->pending -= cleared;
+        }
+        set->int_status &= ~data;
+        break;
+    case gpio_reg_reset_tolerant:
+        set->reset_tol = update_value_control_source(set, set->reset_tol,
+                                                     data);
+        return;
+    case gpio_reg_debounce_1:
+        set->debounce_1 = update_value_control_source(set, set->debounce_1,
+                                                      data);
+        return;
+    case gpio_reg_debounce_2:
+        set->debounce_2 = update_value_control_source(set, set->debounce_2,
+                                                      data);
+        return;
+    case gpio_reg_cmd_source_0:
+        set->cmd_source_0 = data & ASPEED_CMD_SRC_MASK;
+        return;
+    case gpio_reg_cmd_source_1:
+        set->cmd_source_1 = data & ASPEED_CMD_SRC_MASK;
+        return;
+    case gpio_reg_data_read:
+        /* Read only register */
+        return;
+    case gpio_reg_input_mask:
+        /*
+         * feeds into interrupt generation
+         * 0: read from data value reg will be updated
+         * 1: read from data value reg will not be updated
+         */
+         set->input_mask = data & props->input;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%"
+                      HWADDR_PRIx"\n", __func__, offset);
+        return;
+    }
+    aspeed_gpio_update(s, set, set->data_value);
+    return;
+}
+
+static int get_set_idx(AspeedGPIOState *s, const char *group, int *group_idx)
+{
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    int set_idx, g_idx = *group_idx;
+
+    for (set_idx = 0; set_idx < agc->nr_gpio_sets; set_idx++) {
+        const GPIOSetProperties *set_props = &agc->props[set_idx];
+        for (g_idx = 0; g_idx < ASPEED_GROUPS_PER_SET; g_idx++) {
+            if (!strncmp(group, set_props->group_label[g_idx], strlen(group))) {
+                *group_idx = g_idx;
+                return set_idx;
+            }
+        }
+    }
+    return -1;
+}
+
+static void aspeed_gpio_get_pin(Object *obj, Visitor *v, const char *name,
+                                void *opaque, Error **errp)
+{
+    int pin = 0xfff;
+    bool level = true;
+    char group[3];
+    AspeedGPIOState *s = ASPEED_GPIO(obj);
+    int set_idx, group_idx = 0;
+
+    if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    set_idx = get_set_idx(s, group, &group_idx);
+    if (set_idx == -1) {
+        error_setg(errp, "%s: invalid group %s", __func__, group);
+        return;
+    }
+    pin =  pin + group_idx * GPIOS_PER_GROUP;
+    level = aspeed_gpio_get_pin_level(s, set_idx, pin);
+    visit_type_bool(v, name, &level, errp);
+}
+
+static void aspeed_gpio_set_pin(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    Error *local_err = NULL;
+    bool level;
+    int pin = 0xfff;
+    char group[3];
+    AspeedGPIOState *s = ASPEED_GPIO(obj);
+    int set_idx, group_idx = 0;
+
+    visit_type_bool(v, name, &level, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
+        error_setg(errp, "%s: error reading %s", __func__, name);
+        return;
+    }
+    set_idx = get_set_idx(s, group, &group_idx);
+    if (set_idx == -1) {
+        error_setg(errp, "%s: invalid group %s", __func__, group);
+        return;
+    }
+    pin =  pin + group_idx * GPIOS_PER_GROUP;
+    aspeed_gpio_set_pin_level(s, set_idx, pin, level);
+}
+
+/****************** Setup functions ******************/
+static const GPIOSetProperties ast2400_set_props[] = {
+    [0] = {0xffffffff,  0xffffffff,  {"A", "B", "C", "D"} },
+    [1] = {0xffffffff,  0xffffffff,  {"E", "F", "G", "H"} },
+    [2] = {0xffffffff,  0xffffffff,  {"I", "J", "K", "L"} },
+    [3] = {0xffffffff,  0xffffffff,  {"M", "N", "O", "P"} },
+    [4] = {0xffffffff,  0xffffffff,  {"Q", "R", "S", "T"} },
+    [5] = {0xffffffff,  0x0000ffff,  {"U", "V", "W", "X"} },
+    [6] = {0x0000000f,  0x0fffff0f,  {"Y", "Z", "AA", "AB"} },
+};
+
+static const GPIOSetProperties ast2500_set_props[] = {
+    [0] = {0xffffffff,  0xffffffff,  {"A", "B", "C", "D"} },
+    [1] = {0xffffffff,  0xffffffff,  {"E", "F", "G", "H"} },
+    [2] = {0xffffffff,  0xffffffff,  {"I", "J", "K", "L"} },
+    [3] = {0xffffffff,  0xffffffff,  {"M", "N", "O", "P"} },
+    [4] = {0xffffffff,  0xffffffff,  {"Q", "R", "S", "T"} },
+    [5] = {0xffffffff,  0x0000ffff,  {"U", "V", "W", "X"} },
+    [6] = {0xffffff0f,  0x0fffff0f,  {"Y", "Z", "AA", "AB"} },
+    [7] = {0x000000ff,  0x000000ff,  {"AC"} },
+};
+
+static const MemoryRegionOps aspeed_gpio_ops = {
+    .read       = aspeed_gpio_read,
+    .write      = aspeed_gpio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+};
+
+static void aspeed_gpio_reset(DeviceState *dev)
+{
+    AspeedGPIOState *s = ASPEED_GPIO(dev);
+
+    /* TODO: respect the reset tolerance registers */
+    memset(s->sets, 0, sizeof(s->sets));
+}
+
+static void aspeed_gpio_realize(DeviceState *dev, Error **errp)
+{
+    AspeedGPIOState *s = ASPEED_GPIO(dev);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    int pin;
+
+    /* Interrupt parent line */
+    sysbus_init_irq(sbd, &s->irq);
+
+    /* Individual GPIOs */
+    for (pin = 0; pin < agc->nr_gpio_pins; pin++) {
+        sysbus_init_irq(sbd, &s->gpios[pin]);
+    }
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s,
+            TYPE_ASPEED_GPIO, GPIO_3_6V_MEM_SIZE);
+
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void aspeed_gpio_init(Object *obj)
+{
+    AspeedGPIOState *s = ASPEED_GPIO(obj);
+    AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
+    int pin;
+
+    for (pin = 0; pin < agc->nr_gpio_pins; pin++) {
+        char *name;
+        int set_idx = pin / GPIOS_PER_SET;
+        int pin_idx = aspeed_adjust_pin(s, pin) - (set_idx * GPIOS_PER_SET);
+        int group_idx = pin_idx >> GPIO_GROUP_SHIFT;
+        const GPIOSetProperties *props = &agc->props[set_idx];
+
+        name = g_strdup_printf("gpio%s%d", props->group_label[group_idx],
+                               pin_idx % GPIOS_PER_GROUP);
+        object_property_add(obj, name, "bool", aspeed_gpio_get_pin,
+                            aspeed_gpio_set_pin, NULL, NULL, NULL);
+    }
+}
+
+static const VMStateDescription vmstate_gpio_regs = {
+    .name = TYPE_ASPEED_GPIO"/regs",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(data_value,   GPIOSets),
+        VMSTATE_UINT32(data_read,    GPIOSets),
+        VMSTATE_UINT32(direction,    GPIOSets),
+        VMSTATE_UINT32(int_enable,   GPIOSets),
+        VMSTATE_UINT32(int_sens_0,   GPIOSets),
+        VMSTATE_UINT32(int_sens_1,   GPIOSets),
+        VMSTATE_UINT32(int_sens_2,   GPIOSets),
+        VMSTATE_UINT32(int_status,   GPIOSets),
+        VMSTATE_UINT32(reset_tol,    GPIOSets),
+        VMSTATE_UINT32(cmd_source_0, GPIOSets),
+        VMSTATE_UINT32(cmd_source_1, GPIOSets),
+        VMSTATE_UINT32(debounce_1,   GPIOSets),
+        VMSTATE_UINT32(debounce_2,   GPIOSets),
+        VMSTATE_UINT32(input_mask,   GPIOSets),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static const VMStateDescription vmstate_aspeed_gpio = {
+    .name = TYPE_ASPEED_GPIO,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_STRUCT_ARRAY(sets, AspeedGPIOState, ASPEED_GPIO_MAX_NR_SETS,
+                             1, vmstate_gpio_regs, GPIOSets),
+        VMSTATE_UINT32_ARRAY(debounce_regs, AspeedGPIOState,
+                             ASPEED_GPIO_NR_DEBOUNCE_REGS),
+        VMSTATE_END_OF_LIST(),
+   }
+};
+
+static void aspeed_gpio_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = aspeed_gpio_realize;
+    dc->reset = aspeed_gpio_reset;
+    dc->desc = "Aspeed GPIO Controller";
+    dc->vmsd = &vmstate_aspeed_gpio;
+}
+
+static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data)
+{
+    AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
+
+    agc->props = ast2400_set_props;
+    agc->nr_gpio_pins = 216;
+    agc->nr_gpio_sets = 7;
+    agc->gap = 196;
+    agc->reg_table = aspeed_3_6v_gpios;
+}
+
+static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data)
+{
+    AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
+
+    agc->props = ast2500_set_props;
+    agc->nr_gpio_pins = 228;
+    agc->nr_gpio_sets = 8;
+    agc->gap = 220;
+    agc->reg_table = aspeed_3_6v_gpios;
+}
+
+static const TypeInfo aspeed_gpio_info = {
+    .name           = TYPE_ASPEED_GPIO,
+    .parent         = TYPE_SYS_BUS_DEVICE,
+    .instance_size  = sizeof(AspeedGPIOState),
+    .class_size     = sizeof(AspeedGPIOClass),
+    .class_init     = aspeed_gpio_class_init,
+    .abstract       = true,
+};
+
+static const TypeInfo aspeed_gpio_ast2400_info = {
+    .name           = TYPE_ASPEED_GPIO "-ast2400",
+    .parent         = TYPE_ASPEED_GPIO,
+    .class_init     = aspeed_gpio_ast2400_class_init,
+    .instance_init  = aspeed_gpio_init,
+};
+
+static const TypeInfo aspeed_gpio_ast2500_info = {
+    .name           = TYPE_ASPEED_GPIO "-ast2500",
+    .parent         = TYPE_ASPEED_GPIO,
+    .class_init     = aspeed_gpio_2500_class_init,
+    .instance_init  = aspeed_gpio_init,
+};
+
+static void aspeed_gpio_register_types(void)
+{
+    type_register_static(&aspeed_gpio_info);
+    type_register_static(&aspeed_gpio_ast2400_info);
+    type_register_static(&aspeed_gpio_ast2500_info);
+}
+
+type_init(aspeed_gpio_register_types);
diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs
index e5da0cb54feb..d305b3b24b10 100644
--- a/hw/gpio/Makefile.objs
+++ b/hw/gpio/Makefile.objs
@@ -9,3 +9,4 @@ obj-$(CONFIG_OMAP) += omap_gpio.o
 obj-$(CONFIG_IMX) += imx_gpio.o
 obj-$(CONFIG_RASPI) += bcm2835_gpio.o
 obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o
+obj-$(CONFIG_ASPEED_SOC) += aspeed_gpio.o
-- 
2.21.0



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

* [Qemu-devel] [PATCH 02/10] aspeed: add a GPIO controller to the SoC
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 01/10] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500 Cédric Le Goater
@ 2019-09-04  7:04 ` Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 03/10] aspeed: Remove unused SoC definitions Cédric Le Goater
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:04 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, qemu-devel, qemu-arm, Cédric Le Goater,
	Rashmica Gupta, Joel Stanley

From: Rashmica Gupta <rashmica.g@gmail.com>

Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 include/hw/arm/aspeed_soc.h |  3 +++
 hw/arm/aspeed_soc.c         | 17 +++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index 976fd6be93f6..a56effebc16e 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -23,6 +23,7 @@
 #include "hw/watchdog/wdt_aspeed.h"
 #include "hw/net/ftgmac100.h"
 #include "target/arm/cpu.h"
+#include "hw/gpio/aspeed_gpio.h"
 
 #define ASPEED_SPIS_NUM  2
 #define ASPEED_WDTS_NUM  3
@@ -48,6 +49,7 @@ typedef struct AspeedSoCState {
     AspeedSDMCState sdmc;
     AspeedWDTState wdt[ASPEED_WDTS_NUM];
     FTGMAC100State ftgmac100[ASPEED_MACS_NUM];
+    AspeedGPIOState gpio;
 } AspeedSoCState;
 
 #define TYPE_ASPEED_SOC "aspeed-soc"
@@ -61,6 +63,7 @@ typedef struct AspeedSoCInfo {
     int spis_num;
     const char *fmc_typename;
     const char **spi_typename;
+    const char *gpio_typename;
     int wdts_num;
     const int *irqmap;
     const hwaddr *memmap;
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 9ee81048326e..04480875d0d8 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -125,6 +125,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .spis_num     = 1,
         .fmc_typename = "aspeed.smc.fmc",
         .spi_typename = aspeed_soc_ast2400_typenames,
+        .gpio_typename = "aspeed.gpio-ast2400",
         .wdts_num     = 2,
         .irqmap       = aspeed_soc_ast2400_irqmap,
         .memmap       = aspeed_soc_ast2400_memmap,
@@ -137,6 +138,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .spis_num     = 1,
         .fmc_typename = "aspeed.smc.fmc",
         .spi_typename = aspeed_soc_ast2400_typenames,
+        .gpio_typename = "aspeed.gpio-ast2400",
         .wdts_num     = 2,
         .irqmap       = aspeed_soc_ast2400_irqmap,
         .memmap       = aspeed_soc_ast2400_memmap,
@@ -149,6 +151,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .spis_num     = 1,
         .fmc_typename = "aspeed.smc.fmc",
         .spi_typename = aspeed_soc_ast2400_typenames,
+        .gpio_typename = "aspeed.gpio-ast2400",
         .wdts_num     = 2,
         .irqmap       = aspeed_soc_ast2400_irqmap,
         .memmap       = aspeed_soc_ast2400_memmap,
@@ -161,6 +164,7 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .spis_num     = 2,
         .fmc_typename = "aspeed.smc.ast2500-fmc",
         .spi_typename = aspeed_soc_ast2500_typenames,
+        .gpio_typename = "aspeed.gpio-ast2500",
         .wdts_num     = 3,
         .irqmap       = aspeed_soc_ast2500_irqmap,
         .memmap       = aspeed_soc_ast2500_memmap,
@@ -247,6 +251,9 @@ static void aspeed_soc_init(Object *obj)
 
     sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma),
                           TYPE_ASPEED_XDMA);
+
+    sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio),
+                          sc->info->gpio_typename);
 }
 
 static void aspeed_soc_realize(DeviceState *dev, Error **errp)
@@ -426,6 +433,16 @@ static void aspeed_soc_realize(DeviceState *dev, Error **errp)
                     sc->info->memmap[ASPEED_XDMA]);
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->xdma), 0,
                        aspeed_soc_get_irq(s, ASPEED_XDMA));
+
+    /* GPIO */
+    object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio), 0, sc->info->memmap[ASPEED_GPIO]);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio), 0,
+                       aspeed_soc_get_irq(s, ASPEED_GPIO));
 }
 static Property aspeed_soc_properties[] = {
     DEFINE_PROP_UINT32("num-cpus", AspeedSoCState, num_cpus, 0),
-- 
2.21.0



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

* [Qemu-devel] [PATCH 03/10] aspeed: Remove unused SoC definitions
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 01/10] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500 Cédric Le Goater
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 02/10] aspeed: add a GPIO controller to the SoC Cédric Le Goater
@ 2019-09-04  7:04 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 04/10] aspeed: Use consistent typenames Cédric Le Goater
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:04 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

There are no QEMU Aspeed machines using the SoCs "ast2400-a0" or
"ast2400".

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/arm/aspeed_soc.c | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 04480875d0d8..3aa73d2438ef 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -118,19 +118,6 @@ static const char *aspeed_soc_ast2500_typenames[] = {
 
 static const AspeedSoCInfo aspeed_socs[] = {
     {
-        .name         = "ast2400-a0",
-        .cpu_type     = ARM_CPU_TYPE_NAME("arm926"),
-        .silicon_rev  = AST2400_A0_SILICON_REV,
-        .sram_size    = 0x8000,
-        .spis_num     = 1,
-        .fmc_typename = "aspeed.smc.fmc",
-        .spi_typename = aspeed_soc_ast2400_typenames,
-        .gpio_typename = "aspeed.gpio-ast2400",
-        .wdts_num     = 2,
-        .irqmap       = aspeed_soc_ast2400_irqmap,
-        .memmap       = aspeed_soc_ast2400_memmap,
-        .num_cpus     = 1,
-    }, {
         .name         = "ast2400-a1",
         .cpu_type     = ARM_CPU_TYPE_NAME("arm926"),
         .silicon_rev  = AST2400_A1_SILICON_REV,
@@ -143,19 +130,6 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .irqmap       = aspeed_soc_ast2400_irqmap,
         .memmap       = aspeed_soc_ast2400_memmap,
         .num_cpus     = 1,
-    }, {
-        .name         = "ast2400",
-        .cpu_type     = ARM_CPU_TYPE_NAME("arm926"),
-        .silicon_rev  = AST2400_A0_SILICON_REV,
-        .sram_size    = 0x8000,
-        .spis_num     = 1,
-        .fmc_typename = "aspeed.smc.fmc",
-        .spi_typename = aspeed_soc_ast2400_typenames,
-        .gpio_typename = "aspeed.gpio-ast2400",
-        .wdts_num     = 2,
-        .irqmap       = aspeed_soc_ast2400_irqmap,
-        .memmap       = aspeed_soc_ast2400_memmap,
-        .num_cpus     = 1,
     }, {
         .name         = "ast2500-a1",
         .cpu_type     = ARM_CPU_TYPE_NAME("arm1176"),
-- 
2.21.0



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

* [Qemu-devel] [PATCH 04/10] aspeed: Use consistent typenames
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (2 preceding siblings ...)
  2019-09-04  7:04 ` [Qemu-devel] [PATCH 03/10] aspeed: Remove unused SoC definitions Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 05/10] aspeed/smc: Add support for DMAs Cédric Le Goater
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

Improve the naming of the different controller models to ease their
generation when initializing the SoC. The rename of the SMC types is
breaking migration compatibility.

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

diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h
index a56effebc16e..ab5052b12cb5 100644
--- a/include/hw/arm/aspeed_soc.h
+++ b/include/hw/arm/aspeed_soc.h
@@ -61,9 +61,6 @@ typedef struct AspeedSoCInfo {
     uint32_t silicon_rev;
     uint64_t sram_size;
     int spis_num;
-    const char *fmc_typename;
-    const char **spi_typename;
-    const char *gpio_typename;
     int wdts_num;
     const int *irqmap;
     const hwaddr *memmap;
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 3aa73d2438ef..25dbc409d358 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -112,10 +112,6 @@ static const int aspeed_soc_ast2400_irqmap[] = {
 
 #define aspeed_soc_ast2500_irqmap aspeed_soc_ast2400_irqmap
 
-static const char *aspeed_soc_ast2400_typenames[] = { "aspeed.smc.spi" };
-static const char *aspeed_soc_ast2500_typenames[] = {
-    "aspeed.smc.ast2500-spi1", "aspeed.smc.ast2500-spi2" };
-
 static const AspeedSoCInfo aspeed_socs[] = {
     {
         .name         = "ast2400-a1",
@@ -123,9 +119,6 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .silicon_rev  = AST2400_A1_SILICON_REV,
         .sram_size    = 0x8000,
         .spis_num     = 1,
-        .fmc_typename = "aspeed.smc.fmc",
-        .spi_typename = aspeed_soc_ast2400_typenames,
-        .gpio_typename = "aspeed.gpio-ast2400",
         .wdts_num     = 2,
         .irqmap       = aspeed_soc_ast2400_irqmap,
         .memmap       = aspeed_soc_ast2400_memmap,
@@ -136,9 +129,6 @@ static const AspeedSoCInfo aspeed_socs[] = {
         .silicon_rev  = AST2500_A1_SILICON_REV,
         .sram_size    = 0x9000,
         .spis_num     = 2,
-        .fmc_typename = "aspeed.smc.ast2500-fmc",
-        .spi_typename = aspeed_soc_ast2500_typenames,
-        .gpio_typename = "aspeed.gpio-ast2500",
         .wdts_num     = 3,
         .irqmap       = aspeed_soc_ast2500_irqmap,
         .memmap       = aspeed_soc_ast2500_memmap,
@@ -158,6 +148,12 @@ static void aspeed_soc_init(Object *obj)
     AspeedSoCState *s = ASPEED_SOC(obj);
     AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s);
     int i;
+    char socname[8];
+    char typename[64];
+
+    if (sscanf(sc->info->name, "%7s", socname) != 1) {
+        g_assert_not_reached();
+    }
 
     for (i = 0; i < sc->info->num_cpus; i++) {
         object_initialize_child(obj, "cpu[*]", OBJECT(&s->cpu[i]),
@@ -190,14 +186,16 @@ static void aspeed_soc_init(Object *obj)
     sysbus_init_child_obj(obj, "i2c", OBJECT(&s->i2c), sizeof(s->i2c),
                           TYPE_ASPEED_I2C);
 
+    snprintf(typename, sizeof(typename), "aspeed.fmc-%s", socname);
     sysbus_init_child_obj(obj, "fmc", OBJECT(&s->fmc), sizeof(s->fmc),
-                          sc->info->fmc_typename);
+                          typename);
     object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs",
                               &error_abort);
 
     for (i = 0; i < sc->info->spis_num; i++) {
+        snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
         sysbus_init_child_obj(obj, "spi[*]", OBJECT(&s->spi[i]),
-                              sizeof(s->spi[i]), sc->info->spi_typename[i]);
+                              sizeof(s->spi[i]), typename);
     }
 
     sysbus_init_child_obj(obj, "sdmc", OBJECT(&s->sdmc), sizeof(s->sdmc),
@@ -226,8 +224,9 @@ static void aspeed_soc_init(Object *obj)
     sysbus_init_child_obj(obj, "xdma", OBJECT(&s->xdma), sizeof(s->xdma),
                           TYPE_ASPEED_XDMA);
 
+    snprintf(typename, sizeof(typename), "aspeed.gpio-%s", socname);
     sysbus_init_child_obj(obj, "gpio", OBJECT(&s->gpio), sizeof(s->gpio),
-                          sc->info->gpio_typename);
+                          typename);
 }
 
 static void aspeed_soc_realize(DeviceState *dev, Error **errp)
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 9f3cff5fb6f5..f4f7c1818307 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -190,7 +190,7 @@ static const AspeedSegments aspeed_segments_ast2500_spi2[] = {
 
 static const AspeedSMCController controllers[] = {
     {
-        .name              = "aspeed.smc.smc",
+        .name              = "aspeed.smc-ast2400",
         .r_conf            = R_CONF,
         .r_ce_ctrl         = R_CE_CTRL,
         .r_ctrl0           = R_CTRL0,
@@ -203,7 +203,7 @@ static const AspeedSMCController controllers[] = {
         .has_dma           = false,
         .nregs             = ASPEED_SMC_R_SMC_MAX,
     }, {
-        .name              = "aspeed.smc.fmc",
+        .name              = "aspeed.fmc-ast2400",
         .r_conf            = R_CONF,
         .r_ce_ctrl         = R_CE_CTRL,
         .r_ctrl0           = R_CTRL0,
@@ -216,7 +216,7 @@ static const AspeedSMCController controllers[] = {
         .has_dma           = true,
         .nregs             = ASPEED_SMC_R_MAX,
     }, {
-        .name              = "aspeed.smc.spi",
+        .name              = "aspeed.spi1-ast2400",
         .r_conf            = R_SPI_CONF,
         .r_ce_ctrl         = 0xff,
         .r_ctrl0           = R_SPI_CTRL0,
@@ -229,7 +229,7 @@ static const AspeedSMCController controllers[] = {
         .has_dma           = false,
         .nregs             = ASPEED_SMC_R_SPI_MAX,
     }, {
-        .name              = "aspeed.smc.ast2500-fmc",
+        .name              = "aspeed.fmc-ast2500",
         .r_conf            = R_CONF,
         .r_ce_ctrl         = R_CE_CTRL,
         .r_ctrl0           = R_CTRL0,
@@ -242,7 +242,7 @@ static const AspeedSMCController controllers[] = {
         .has_dma           = true,
         .nregs             = ASPEED_SMC_R_MAX,
     }, {
-        .name              = "aspeed.smc.ast2500-spi1",
+        .name              = "aspeed.spi1-ast2500",
         .r_conf            = R_CONF,
         .r_ce_ctrl         = R_CE_CTRL,
         .r_ctrl0           = R_CTRL0,
@@ -255,7 +255,7 @@ static const AspeedSMCController controllers[] = {
         .has_dma           = false,
         .nregs             = ASPEED_SMC_R_MAX,
     }, {
-        .name              = "aspeed.smc.ast2500-spi2",
+        .name              = "aspeed.spi2-ast2500",
         .r_conf            = R_CONF,
         .r_ce_ctrl         = R_CE_CTRL,
         .r_ctrl0           = R_CTRL0,
-- 
2.21.0



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

* [Qemu-devel] [PATCH 05/10] aspeed/smc: Add support for DMAs
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (3 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 04/10] aspeed: Use consistent typenames Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 06/10] aspeed/smc: Add DMA calibration settings Cédric Le Goater
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

The FMC controller on the Aspeed SoCs support DMA to access the flash
modules. It can operate in a normal mode, to copy to or from the flash
module mapping window, or in a checksum calculation mode, to evaluate
the best clock settings for reads.

The model introduces two custom address spaces for DMAs: one for the
AHB window of the FMC flash devices and one for the DRAM. The latter
is populated using a "dram" link set from the machine with the RAM
container region.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Acked-by: Joel Stanley <joel@jms.id.au>
---

 Changes since v2 (18/06/2019):

 - usage of address_space_stl/ldl_le

 include/hw/ssi/aspeed_smc.h |   6 +
 hw/arm/aspeed.c             |   2 +
 hw/arm/aspeed_soc.c         |   2 +
 hw/ssi/aspeed_smc.c         | 222 +++++++++++++++++++++++++++++++++++-
 4 files changed, 226 insertions(+), 6 deletions(-)

diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index aa07dac4fe37..32ce6916f6cc 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -46,6 +46,8 @@ typedef struct AspeedSMCController {
     hwaddr flash_window_base;
     uint32_t flash_window_size;
     bool has_dma;
+    hwaddr dma_flash_mask;
+    hwaddr dma_dram_mask;
     uint32_t nregs;
 } AspeedSMCController;
 
@@ -101,6 +103,10 @@ typedef struct AspeedSMCState {
     /* for DMA support */
     uint64_t sdram_base;
 
+    AddressSpace flash_as;
+    MemoryRegion *dram_mr;
+    AddressSpace dram_as;
+
     AspeedSMCFlash *flashes;
 
     uint8_t snoop_index;
diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c
index 7a2e885e0b9c..5c679c80feca 100644
--- a/hw/arm/aspeed.c
+++ b/hw/arm/aspeed.c
@@ -190,6 +190,8 @@ static void aspeed_board_init(MachineState *machine,
                             &error_abort);
     object_property_set_int(OBJECT(&bmc->soc), machine->smp.cpus, "num-cpus",
                             &error_abort);
+    object_property_set_link(OBJECT(&bmc->soc), OBJECT(&bmc->ram_container),
+                             "dram", &error_abort);
     if (machine->kernel_filename) {
         /*
          * When booting with a -kernel command line there is no u-boot
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index 25dbc409d358..da508c99c335 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -191,6 +191,8 @@ static void aspeed_soc_init(Object *obj)
                           typename);
     object_property_add_alias(obj, "num-cs", OBJECT(&s->fmc), "num-cs",
                               &error_abort);
+    object_property_add_alias(obj, "dram", OBJECT(&s->fmc), "dram",
+                              &error_abort);
 
     for (i = 0; i < sc->info->spis_num; i++) {
         snprintf(typename, sizeof(typename), "aspeed.spi%d-%s", i + 1, socname);
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index f4f7c1818307..c1a45c10dc14 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -28,6 +28,8 @@
 #include "qemu/log.h"
 #include "qemu/module.h"
 #include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
 
 #include "hw/irq.h"
 #include "hw/qdev-properties.h"
@@ -112,8 +114,8 @@
 #define   DMA_CTRL_FREQ_SHIFT   4
 #define   DMA_CTRL_MODE         (1 << 3)
 #define   DMA_CTRL_CKSUM        (1 << 2)
-#define   DMA_CTRL_DIR          (1 << 1)
-#define   DMA_CTRL_EN           (1 << 0)
+#define   DMA_CTRL_WRITE        (1 << 1)
+#define   DMA_CTRL_ENABLE       (1 << 0)
 
 /* DMA Flash Side Address */
 #define R_DMA_FLASH_ADDR  (0x84 / 4)
@@ -145,6 +147,24 @@
 #define ASPEED_SOC_SPI_FLASH_BASE   0x30000000
 #define ASPEED_SOC_SPI2_FLASH_BASE  0x38000000
 
+/*
+ * DMA DRAM addresses should be 4 bytes aligned and the valid address
+ * range is 0x40000000 - 0x5FFFFFFF (AST2400)
+ *          0x80000000 - 0xBFFFFFFF (AST2500)
+ *
+ * DMA flash addresses should be 4 bytes aligned and the valid address
+ * range is 0x20000000 - 0x2FFFFFFF.
+ *
+ * DMA length is from 4 bytes to 32MB
+ *   0: 4 bytes
+ *   0x7FFFFF: 32M bytes
+ */
+#define DMA_DRAM_ADDR(s, val)   ((s)->sdram_base | \
+                                 ((val) & (s)->ctrl->dma_dram_mask))
+#define DMA_FLASH_ADDR(s, val)  ((s)->ctrl->flash_window_base | \
+                                ((val) & (s)->ctrl->dma_flash_mask))
+#define DMA_LENGTH(val)         ((val) & 0x01FFFFFC)
+
 /* Flash opcodes. */
 #define SPI_OP_READ       0x03    /* Read data bytes (low frequency) */
 
@@ -214,6 +234,8 @@ static const AspeedSMCController controllers[] = {
         .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE,
         .flash_window_size = 0x10000000,
         .has_dma           = true,
+        .dma_flash_mask    = 0x0FFFFFFC,
+        .dma_dram_mask     = 0x1FFFFFFC,
         .nregs             = ASPEED_SMC_R_MAX,
     }, {
         .name              = "aspeed.spi1-ast2400",
@@ -240,6 +262,8 @@ static const AspeedSMCController controllers[] = {
         .flash_window_base = ASPEED_SOC_FMC_FLASH_BASE,
         .flash_window_size = 0x10000000,
         .has_dma           = true,
+        .dma_flash_mask    = 0x0FFFFFFC,
+        .dma_dram_mask     = 0x3FFFFFFC,
         .nregs             = ASPEED_SMC_R_MAX,
     }, {
         .name              = "aspeed.spi1-ast2500",
@@ -732,9 +756,6 @@ static void aspeed_smc_reset(DeviceState *d)
 
     memset(s->regs, 0, sizeof s->regs);
 
-    /* Pretend DMA is done (u-boot initialization) */
-    s->regs[R_INTR_CTRL] = INTR_CTRL_DMA_STATUS;
-
     /* Unselect all slaves */
     for (i = 0; i < s->num_cs; ++i) {
         s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
@@ -775,6 +796,11 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
         addr == s->r_ce_ctrl ||
         addr == R_INTR_CTRL ||
         addr == R_DUMMY_DATA ||
+        (s->ctrl->has_dma && addr == R_DMA_CTRL) ||
+        (s->ctrl->has_dma && addr == R_DMA_FLASH_ADDR) ||
+        (s->ctrl->has_dma && addr == R_DMA_DRAM_ADDR) ||
+        (s->ctrl->has_dma && addr == R_DMA_LEN) ||
+        (s->ctrl->has_dma && addr == R_DMA_CHECKSUM) ||
         (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) ||
         (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) {
         return s->regs[addr];
@@ -785,6 +811,149 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
     }
 }
 
+/*
+ * Accumulate the result of the reads to provide a checksum that will
+ * be used to validate the read timing settings.
+ */
+static void aspeed_smc_dma_checksum(AspeedSMCState *s)
+{
+    MemTxResult result;
+    uint32_t data;
+
+    if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: invalid direction for DMA checksum\n",  __func__);
+        return;
+    }
+
+    while (s->regs[R_DMA_LEN]) {
+        data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR],
+                                    MEMTXATTRS_UNSPECIFIED, &result);
+        if (result != MEMTX_OK) {
+            qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash read failed @%08x\n",
+                          __func__, s->regs[R_DMA_FLASH_ADDR]);
+            return;
+        }
+
+        /*
+         * When the DMA is on-going, the DMA registers are updated
+         * with the current working addresses and length.
+         */
+        s->regs[R_DMA_CHECKSUM] += data;
+        s->regs[R_DMA_FLASH_ADDR] += 4;
+        s->regs[R_DMA_LEN] -= 4;
+    }
+}
+
+static void aspeed_smc_dma_rw(AspeedSMCState *s)
+{
+    MemTxResult result;
+    uint32_t data;
+
+    while (s->regs[R_DMA_LEN]) {
+        if (s->regs[R_DMA_CTRL] & DMA_CTRL_WRITE) {
+            data = address_space_ldl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR],
+                                        MEMTXATTRS_UNSPECIFIED, &result);
+            if (result != MEMTX_OK) {
+                qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM read failed @%08x\n",
+                              __func__, s->regs[R_DMA_DRAM_ADDR]);
+                return;
+            }
+
+            address_space_stl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR],
+                                 data, MEMTXATTRS_UNSPECIFIED, &result);
+            if (result != MEMTX_OK) {
+                qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash write failed @%08x\n",
+                              __func__, s->regs[R_DMA_FLASH_ADDR]);
+                return;
+            }
+        } else {
+            data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR],
+                                        MEMTXATTRS_UNSPECIFIED, &result);
+            if (result != MEMTX_OK) {
+                qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash read failed @%08x\n",
+                              __func__, s->regs[R_DMA_FLASH_ADDR]);
+                return;
+            }
+
+            address_space_stl_le(&s->dram_as, s->regs[R_DMA_DRAM_ADDR],
+                                 data, MEMTXATTRS_UNSPECIFIED, &result);
+            if (result != MEMTX_OK) {
+                qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed @%08x\n",
+                              __func__, s->regs[R_DMA_DRAM_ADDR]);
+                return;
+            }
+        }
+
+        /*
+         * When the DMA is on-going, the DMA registers are updated
+         * with the current working addresses and length.
+         */
+        s->regs[R_DMA_FLASH_ADDR] += 4;
+        s->regs[R_DMA_DRAM_ADDR] += 4;
+        s->regs[R_DMA_LEN] -= 4;
+    }
+}
+
+static void aspeed_smc_dma_stop(AspeedSMCState *s)
+{
+    /*
+     * When the DMA is disabled, INTR_CTRL_DMA_STATUS=0 means the
+     * engine is idle
+     */
+    s->regs[R_INTR_CTRL] &= ~INTR_CTRL_DMA_STATUS;
+    s->regs[R_DMA_CHECKSUM] = 0;
+
+    /*
+     * Lower the DMA irq in any case. The IRQ control register could
+     * have been cleared before disabling the DMA.
+     */
+    qemu_irq_lower(s->irq);
+}
+
+/*
+ * When INTR_CTRL_DMA_STATUS=1, the DMA has completed and a new DMA
+ * can start even if the result of the previous was not collected.
+ */
+static bool aspeed_smc_dma_in_progress(AspeedSMCState *s)
+{
+    return s->regs[R_DMA_CTRL] & DMA_CTRL_ENABLE &&
+        !(s->regs[R_INTR_CTRL] & INTR_CTRL_DMA_STATUS);
+}
+
+static void aspeed_smc_dma_done(AspeedSMCState *s)
+{
+    s->regs[R_INTR_CTRL] |= INTR_CTRL_DMA_STATUS;
+    if (s->regs[R_INTR_CTRL] & INTR_CTRL_DMA_EN) {
+        qemu_irq_raise(s->irq);
+    }
+}
+
+static void aspeed_smc_dma_ctrl(AspeedSMCState *s, uint64_t dma_ctrl)
+{
+    if (!(dma_ctrl & DMA_CTRL_ENABLE)) {
+        s->regs[R_DMA_CTRL] = dma_ctrl;
+
+        aspeed_smc_dma_stop(s);
+        return;
+    }
+
+    if (aspeed_smc_dma_in_progress(s)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: DMA in progress\n",  __func__);
+        return;
+    }
+
+    s->regs[R_DMA_CTRL] = dma_ctrl;
+
+    if (s->regs[R_DMA_CTRL] & DMA_CTRL_CKSUM) {
+        aspeed_smc_dma_checksum(s);
+    } else {
+        aspeed_smc_dma_rw(s);
+    }
+
+    aspeed_smc_dma_done(s);
+}
+
 static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
                              unsigned int size)
 {
@@ -810,6 +979,16 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
         }
     } else if (addr == R_DUMMY_DATA) {
         s->regs[addr] = value & 0xff;
+    } else if (addr == R_INTR_CTRL) {
+        s->regs[addr] = value;
+    } else if (s->ctrl->has_dma && addr == R_DMA_CTRL) {
+        aspeed_smc_dma_ctrl(s, value);
+    } else if (s->ctrl->has_dma && addr == R_DMA_DRAM_ADDR) {
+        s->regs[addr] = DMA_DRAM_ADDR(s, value);
+    } else if (s->ctrl->has_dma && addr == R_DMA_FLASH_ADDR) {
+        s->regs[addr] = DMA_FLASH_ADDR(s, value);
+    } else if (s->ctrl->has_dma && addr == R_DMA_LEN) {
+        s->regs[addr] = DMA_LENGTH(value);
     } else {
         qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
                       __func__, addr);
@@ -824,6 +1003,28 @@ static const MemoryRegionOps aspeed_smc_ops = {
     .valid.unaligned = true,
 };
 
+
+/*
+ * Initialize the custom address spaces for DMAs
+ */
+static void aspeed_smc_dma_setup(AspeedSMCState *s, Error **errp)
+{
+    char *name;
+
+    if (!s->dram_mr) {
+        error_setg(errp, TYPE_ASPEED_SMC ": 'dram' link not set");
+        return;
+    }
+
+    name = g_strdup_printf("%s-dma-flash", s->ctrl->name);
+    address_space_init(&s->flash_as, &s->mmio_flash, name);
+    g_free(name);
+
+    name = g_strdup_printf("%s-dma-dram", s->ctrl->name);
+    address_space_init(&s->dram_as, s->dram_mr, name);
+    g_free(name);
+}
+
 static void aspeed_smc_realize(DeviceState *dev, Error **errp)
 {
     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
@@ -849,10 +1050,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
         s->num_cs = s->ctrl->max_slaves;
     }
 
+    /* DMA irq. Keep it first for the initialization in the SoC */
+    sysbus_init_irq(sbd, &s->irq);
+
     s->spi = ssi_create_bus(dev, "spi");
 
     /* 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);
 
@@ -899,6 +1102,11 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
         memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio);
         offset += fl->size;
     }
+
+    /* DMA support */
+    if (s->ctrl->has_dma) {
+        aspeed_smc_dma_setup(s, errp);
+    }
 }
 
 static const VMStateDescription vmstate_aspeed_smc = {
@@ -916,6 +1124,8 @@ static const VMStateDescription vmstate_aspeed_smc = {
 static Property aspeed_smc_properties[] = {
     DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
     DEFINE_PROP_UINT64("sdram-base", AspeedSMCState, sdram_base, 0),
+    DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr,
+                     TYPE_MEMORY_REGION, MemoryRegion *),
     DEFINE_PROP_END_OF_LIST(),
 };
 
-- 
2.21.0



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

* [Qemu-devel] [PATCH 06/10] aspeed/smc: Add DMA calibration settings
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (4 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 05/10] aspeed/smc: Add support for DMAs Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 07/10] aspeed/smc: Inject errors in DMA checksum Cédric Le Goater
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

When doing calibration, the SPI clock rate in the CE0 Control Register
and the read delay cycles in the Read Timing Compensation Register are
set using bit[11:4] of the DMA Control Register.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
Acked-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/ssi/aspeed_smc.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index c1a45c10dc14..7a0cd7607fd0 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -77,6 +77,10 @@
 #define   CTRL_CMD_MASK            0xff
 #define   CTRL_DUMMY_HIGH_SHIFT    14
 #define   CTRL_AST2400_SPI_4BYTE   (1 << 13)
+#define CE_CTRL_CLOCK_FREQ_SHIFT   8
+#define CE_CTRL_CLOCK_FREQ_MASK    0xf
+#define CE_CTRL_CLOCK_FREQ(div)                                         \
+    (((div) & CE_CTRL_CLOCK_FREQ_MASK) << CE_CTRL_CLOCK_FREQ_SHIFT)
 #define   CTRL_DUMMY_LOW_SHIFT     6 /* 2 bits [7:6] */
 #define   CTRL_CE_STOP_ACTIVE      (1 << 2)
 #define   CTRL_CMD_MODE_MASK       0x3
@@ -112,7 +116,7 @@
 #define   DMA_CTRL_DELAY_SHIFT  8
 #define   DMA_CTRL_FREQ_MASK    0xf
 #define   DMA_CTRL_FREQ_SHIFT   4
-#define   DMA_CTRL_MODE         (1 << 3)
+#define   DMA_CTRL_CALIB        (1 << 3)
 #define   DMA_CTRL_CKSUM        (1 << 2)
 #define   DMA_CTRL_WRITE        (1 << 1)
 #define   DMA_CTRL_ENABLE       (1 << 0)
@@ -811,6 +815,60 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
     }
 }
 
+static uint8_t aspeed_smc_hclk_divisor(uint8_t hclk_mask)
+{
+    /* HCLK/1 .. HCLK/16 */
+    const uint8_t hclk_divisors[] = {
+        15, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 0
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(hclk_divisors); i++) {
+        if (hclk_mask == hclk_divisors[i]) {
+            return i + 1;
+        }
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR, "invalid HCLK mask %x", hclk_mask);
+    return 0;
+}
+
+/*
+ * When doing calibration, the SPI clock rate in the CE0 Control
+ * Register and the read delay cycles in the Read Timing Compensation
+ * Register are set using bit[11:4] of the DMA Control Register.
+ */
+static void aspeed_smc_dma_calibration(AspeedSMCState *s)
+{
+    uint8_t delay =
+        (s->regs[R_DMA_CTRL] >> DMA_CTRL_DELAY_SHIFT) & DMA_CTRL_DELAY_MASK;
+    uint8_t hclk_mask =
+        (s->regs[R_DMA_CTRL] >> DMA_CTRL_FREQ_SHIFT) & DMA_CTRL_FREQ_MASK;
+    uint8_t hclk_div = aspeed_smc_hclk_divisor(hclk_mask);
+    uint32_t hclk_shift = (hclk_div - 1) << 2;
+    uint8_t cs;
+
+    /*
+     * The Read Timing Compensation Register values apply to all CS on
+     * the SPI bus and only HCLK/1 - HCLK/5 can have tunable delays
+     */
+    if (hclk_div && hclk_div < 6) {
+        s->regs[s->r_timings] &= ~(0xf << hclk_shift);
+        s->regs[s->r_timings] |= delay << hclk_shift;
+    }
+
+    /*
+     * TODO: compute the CS from the DMA address and the segment
+     * registers. This is not really a problem for now because the
+     * Timing Register values apply to all CS and software uses CS0 to
+     * do calibration.
+     */
+    cs = 0;
+    s->regs[s->r_ctrl0 + cs] &=
+        ~(CE_CTRL_CLOCK_FREQ_MASK << CE_CTRL_CLOCK_FREQ_SHIFT);
+    s->regs[s->r_ctrl0 + cs] |= CE_CTRL_CLOCK_FREQ(hclk_div);
+}
+
 /*
  * Accumulate the result of the reads to provide a checksum that will
  * be used to validate the read timing settings.
@@ -826,6 +884,10 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s)
         return;
     }
 
+    if (s->regs[R_DMA_CTRL] & DMA_CTRL_CALIB) {
+        aspeed_smc_dma_calibration(s);
+    }
+
     while (s->regs[R_DMA_LEN]) {
         data = address_space_ldl_le(&s->flash_as, s->regs[R_DMA_FLASH_ADDR],
                                     MEMTXATTRS_UNSPECIFIED, &result);
-- 
2.21.0



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

* [Qemu-devel] [PATCH 07/10] aspeed/smc: Inject errors in DMA checksum
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (5 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 06/10] aspeed/smc: Add DMA calibration settings Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 08/10] aspeed/smc: Calculate checksum on normal DMA Cédric Le Goater
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, qemu-devel, qemu-arm, Cédric Le Goater,
	Philippe Mathieu-Daudé,
	Joel Stanley

Emulate read errors in the DMA Checksum Register for high frequencies
and optimistic settings of the Read Timing Compensation Register. This
will help in tuning the SPI timing calibration algorithm. Errors are
only injected when the property "inject_failure" is set to true as
suggested by Philippe.

The values below are those to expect from the first flash device of
the FMC controller of a palmetto-bmc machine.

Cc: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
Reviewed-by: Joel Stanley <joel@jms.id.au>
---
 include/hw/ssi/aspeed_smc.h |  1 +
 hw/ssi/aspeed_smc.c         | 36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
index 32ce6916f6cc..5176ff6bf95f 100644
--- a/include/hw/ssi/aspeed_smc.h
+++ b/include/hw/ssi/aspeed_smc.h
@@ -88,6 +88,7 @@ typedef struct AspeedSMCState {
 
     uint32_t num_cs;
     qemu_irq *cs_lines;
+    bool inject_failure;
 
     SSIBus *spi;
 
diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 7a0cd7607fd0..5c3436db5e8f 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -869,6 +869,36 @@ static void aspeed_smc_dma_calibration(AspeedSMCState *s)
     s->regs[s->r_ctrl0 + cs] |= CE_CTRL_CLOCK_FREQ(hclk_div);
 }
 
+/*
+ * Emulate read errors in the DMA Checksum Register for high
+ * frequencies and optimistic settings of the Read Timing Compensation
+ * Register. This will help in tuning the SPI timing calibration
+ * algorithm.
+ */
+static bool aspeed_smc_inject_read_failure(AspeedSMCState *s)
+{
+    uint8_t delay =
+        (s->regs[R_DMA_CTRL] >> DMA_CTRL_DELAY_SHIFT) & DMA_CTRL_DELAY_MASK;
+    uint8_t hclk_mask =
+        (s->regs[R_DMA_CTRL] >> DMA_CTRL_FREQ_SHIFT) & DMA_CTRL_FREQ_MASK;
+
+    /*
+     * Typical values of a palmetto-bmc machine.
+     */
+    switch (aspeed_smc_hclk_divisor(hclk_mask)) {
+    case 4 ... 16:
+        return false;
+    case 3: /* at least one HCLK cycle delay */
+        return (delay & 0x7) < 1;
+    case 2: /* at least two HCLK cycle delay */
+        return (delay & 0x7) < 2;
+    case 1: /* (> 100MHz) is above the max freq of the controller */
+        return true;
+    default:
+        g_assert_not_reached();
+    }
+}
+
 /*
  * Accumulate the result of the reads to provide a checksum that will
  * be used to validate the read timing settings.
@@ -905,6 +935,11 @@ static void aspeed_smc_dma_checksum(AspeedSMCState *s)
         s->regs[R_DMA_FLASH_ADDR] += 4;
         s->regs[R_DMA_LEN] -= 4;
     }
+
+    if (s->inject_failure && aspeed_smc_inject_read_failure(s)) {
+        s->regs[R_DMA_CHECKSUM] = 0xbadc0de;
+    }
+
 }
 
 static void aspeed_smc_dma_rw(AspeedSMCState *s)
@@ -1185,6 +1220,7 @@ static const VMStateDescription vmstate_aspeed_smc = {
 
 static Property aspeed_smc_properties[] = {
     DEFINE_PROP_UINT32("num-cs", AspeedSMCState, num_cs, 1),
+    DEFINE_PROP_BOOL("inject-failure", AspeedSMCState, inject_failure, false),
     DEFINE_PROP_UINT64("sdram-base", AspeedSMCState, sdram_base, 0),
     DEFINE_PROP_LINK("dram", AspeedSMCState, dram_mr,
                      TYPE_MEMORY_REGION, MemoryRegion *),
-- 
2.21.0



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

* [Qemu-devel] [PATCH 08/10] aspeed/smc: Calculate checksum on normal DMA
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (6 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 07/10] aspeed/smc: Inject errors in DMA checksum Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 09/10] aspeed/scu: Introduce per-SoC SCU types Cédric Le Goater
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Christian Svensson, Andrew Jeffery, qemu-devel, qemu-arm,
	Cédric Le Goater, Joel Stanley

From: Christian Svensson <bluecmd@google.com>

This patch adds the missing checksum calculation on normal DMA transfer.
According to the datasheet this is how the SMC should behave.

Verified on AST1250 that the hardware matches the behaviour.

Signed-off-by: Christian Svensson <bluecmd@google.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 hw/ssi/aspeed_smc.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
index 5c3436db5e8f..9ffc7e01179a 100644
--- a/hw/ssi/aspeed_smc.c
+++ b/hw/ssi/aspeed_smc.c
@@ -989,6 +989,7 @@ static void aspeed_smc_dma_rw(AspeedSMCState *s)
         s->regs[R_DMA_FLASH_ADDR] += 4;
         s->regs[R_DMA_DRAM_ADDR] += 4;
         s->regs[R_DMA_LEN] -= 4;
+        s->regs[R_DMA_CHECKSUM] += data;
     }
 }
 
-- 
2.21.0



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

* [Qemu-devel] [PATCH 09/10] aspeed/scu: Introduce per-SoC SCU types
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (7 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 08/10] aspeed/smc: Calculate checksum on normal DMA Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 10/10] aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine Cédric Le Goater
  2019-09-11 10:30 ` [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Peter Maydell
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

and use a class AspeedSCUClass to define each SoC characteristics.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 include/hw/misc/aspeed_scu.h | 15 +++++++
 hw/arm/aspeed_soc.c          |  3 +-
 hw/misc/aspeed_scu.c         | 83 ++++++++++++++++++++----------------
 3 files changed, 64 insertions(+), 37 deletions(-)

diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h
index 38996adc59fe..90dd4dadedeb 100644
--- a/include/hw/misc/aspeed_scu.h
+++ b/include/hw/misc/aspeed_scu.h
@@ -15,6 +15,8 @@
 
 #define TYPE_ASPEED_SCU "aspeed.scu"
 #define ASPEED_SCU(obj) OBJECT_CHECK(AspeedSCUState, (obj), TYPE_ASPEED_SCU)
+#define TYPE_ASPEED_2400_SCU TYPE_ASPEED_SCU "-ast2400"
+#define TYPE_ASPEED_2500_SCU TYPE_ASPEED_SCU "-ast2500"
 
 #define ASPEED_SCU_NR_REGS (0x1A8 >> 2)
 
@@ -45,6 +47,19 @@ typedef struct AspeedSCUState {
 
 extern bool is_supported_silicon_rev(uint32_t silicon_rev);
 
+#define ASPEED_SCU_CLASS(klass) \
+     OBJECT_CLASS_CHECK(AspeedSCUClass, (klass), TYPE_ASPEED_SCU)
+#define ASPEED_SCU_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(AspeedSCUClass, (obj), TYPE_ASPEED_SCU)
+
+typedef struct  AspeedSCUClass {
+    SysBusDeviceClass parent_class;
+
+    const uint32_t *resets;
+    uint32_t (*calc_hpll)(AspeedSCUState *s);
+    uint32_t apb_divider;
+}  AspeedSCUClass;
+
 #define ASPEED_SCU_PROT_KEY      0x1688A8A8
 
 /*
diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c
index da508c99c335..cf1d0cf921ba 100644
--- a/hw/arm/aspeed_soc.c
+++ b/hw/arm/aspeed_soc.c
@@ -161,8 +161,9 @@ static void aspeed_soc_init(Object *obj)
                                 &error_abort, NULL);
     }
 
+    snprintf(typename, sizeof(typename), "aspeed.scu-%s", socname);
     sysbus_init_child_obj(obj, "scu", OBJECT(&s->scu), sizeof(s->scu),
-                          TYPE_ASPEED_SCU);
+                          typename);
     qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev",
                          sc->info->silicon_rev);
     object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu),
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
index 268cb24e565c..d284458b9b3d 100644
--- a/hw/misc/aspeed_scu.c
+++ b/hw/misc/aspeed_scu.c
@@ -166,23 +166,10 @@ static uint32_t aspeed_scu_get_random(void)
 
 static void aspeed_scu_set_apb_freq(AspeedSCUState *s)
 {
-    uint32_t apb_divider;
-
-    switch (s->silicon_rev) {
-    case AST2400_A0_SILICON_REV:
-    case AST2400_A1_SILICON_REV:
-        apb_divider = 2;
-        break;
-    case AST2500_A0_SILICON_REV:
-    case AST2500_A1_SILICON_REV:
-        apb_divider = 4;
-        break;
-    default:
-        g_assert_not_reached();
-    }
+    AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s);
 
     s->apb_freq = s->hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[CLK_SEL]) + 1)
-        / apb_divider;
+        / asc->apb_divider;
 }
 
 static uint64_t aspeed_scu_read(void *opaque, hwaddr offset, unsigned size)
@@ -303,7 +290,7 @@ static const uint32_t hpll_ast2400_freqs[][4] = {
     { 400, 375, 350, 425 }, /* 25MHz */
 };
 
-static uint32_t aspeed_scu_calc_hpll_ast2400(AspeedSCUState *s)
+static uint32_t aspeed_2400_scu_calc_hpll(AspeedSCUState *s)
 {
     uint32_t hpll_reg = s->regs[HPLL_PARAM];
     uint8_t freq_select;
@@ -334,7 +321,7 @@ static uint32_t aspeed_scu_calc_hpll_ast2400(AspeedSCUState *s)
     return hpll_ast2400_freqs[clk_25m_in][freq_select] * 1000000;
 }
 
-static uint32_t aspeed_scu_calc_hpll_ast2500(AspeedSCUState *s)
+static uint32_t aspeed_2500_scu_calc_hpll(AspeedSCUState *s)
 {
     uint32_t hpll_reg   = s->regs[HPLL_PARAM];
     uint32_t multiplier = 1;
@@ -357,25 +344,9 @@ static uint32_t aspeed_scu_calc_hpll_ast2500(AspeedSCUState *s)
 static void aspeed_scu_reset(DeviceState *dev)
 {
     AspeedSCUState *s = ASPEED_SCU(dev);
-    const uint32_t *reset;
-    uint32_t (*calc_hpll)(AspeedSCUState *s);
-
-    switch (s->silicon_rev) {
-    case AST2400_A0_SILICON_REV:
-    case AST2400_A1_SILICON_REV:
-        reset = ast2400_a0_resets;
-        calc_hpll = aspeed_scu_calc_hpll_ast2400;
-        break;
-    case AST2500_A0_SILICON_REV:
-    case AST2500_A1_SILICON_REV:
-        reset = ast2500_a1_resets;
-        calc_hpll = aspeed_scu_calc_hpll_ast2500;
-        break;
-    default:
-        g_assert_not_reached();
-    }
+    AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev);
 
-    memcpy(s->regs, reset, sizeof(s->regs));
+    memcpy(s->regs, asc->resets, sizeof(s->regs));
     s->regs[SILICON_REV] = s->silicon_rev;
     s->regs[HW_STRAP1] = s->hw_strap1;
     s->regs[HW_STRAP2] = s->hw_strap2;
@@ -385,7 +356,7 @@ static void aspeed_scu_reset(DeviceState *dev)
      * All registers are set. Now compute the frequencies of the main clocks
      */
     s->clkin = aspeed_scu_get_clkin(s);
-    s->hpll = calc_hpll(s);
+    s->hpll = asc->calc_hpll(s);
     aspeed_scu_set_apb_freq(s);
 }
 
@@ -459,11 +430,51 @@ static const TypeInfo aspeed_scu_info = {
     .parent = TYPE_SYS_BUS_DEVICE,
     .instance_size = sizeof(AspeedSCUState),
     .class_init = aspeed_scu_class_init,
+    .class_size    = sizeof(AspeedSCUClass),
+    .abstract      = true,
+};
+
+static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass);
+
+    dc->desc = "ASPEED 2400 System Control Unit";
+    asc->resets = ast2400_a0_resets;
+    asc->calc_hpll = aspeed_2400_scu_calc_hpll;
+    asc->apb_divider = 2;
+}
+
+static const TypeInfo aspeed_2400_scu_info = {
+    .name = TYPE_ASPEED_2400_SCU,
+    .parent = TYPE_ASPEED_SCU,
+    .instance_size = sizeof(AspeedSCUState),
+    .class_init = aspeed_2400_scu_class_init,
+};
+
+static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass);
+
+    dc->desc = "ASPEED 2500 System Control Unit";
+    asc->resets = ast2500_a1_resets;
+    asc->calc_hpll = aspeed_2500_scu_calc_hpll;
+    asc->apb_divider = 4;
+}
+
+static const TypeInfo aspeed_2500_scu_info = {
+    .name = TYPE_ASPEED_2500_SCU,
+    .parent = TYPE_ASPEED_SCU,
+    .instance_size = sizeof(AspeedSCUState),
+    .class_init = aspeed_2500_scu_class_init,
 };
 
 static void aspeed_scu_register_types(void)
 {
     type_register_static(&aspeed_scu_info);
+    type_register_static(&aspeed_2400_scu_info);
+    type_register_static(&aspeed_2500_scu_info);
 }
 
 type_init(aspeed_scu_register_types);
-- 
2.21.0



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

* [Qemu-devel] [PATCH 10/10] aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (8 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 09/10] aspeed/scu: Introduce per-SoC SCU types Cédric Le Goater
@ 2019-09-04  7:05 ` Cédric Le Goater
  2019-09-11 10:30 ` [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Peter Maydell
  10 siblings, 0 replies; 12+ messages in thread
From: Cédric Le Goater @ 2019-09-04  7:05 UTC (permalink / raw)
  To: Peter Maydell
  Cc: Andrew Jeffery, Cédric Le Goater, qemu-arm, qemu-devel,
	Joel Stanley

The APB frequency can be calculated directly when needed from the
HPLL_PARAM and CLK_SEL register values. This removes useless state in
the model.

Signed-off-by: Cédric Le Goater <clg@kaod.org>
---
 include/hw/misc/aspeed_scu.h |  8 +++-----
 hw/misc/aspeed_scu.c         | 25 +++++++++----------------
 hw/timer/aspeed_timer.c      |  3 ++-
 3 files changed, 14 insertions(+), 22 deletions(-)

diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h
index 90dd4dadedeb..239e94fe2c47 100644
--- a/include/hw/misc/aspeed_scu.h
+++ b/include/hw/misc/aspeed_scu.h
@@ -32,10 +32,6 @@ typedef struct AspeedSCUState {
     uint32_t hw_strap1;
     uint32_t hw_strap2;
     uint32_t hw_prot_key;
-
-    uint32_t clkin;
-    uint32_t hpll;
-    uint32_t apb_freq;
 } AspeedSCUState;
 
 #define AST2400_A0_SILICON_REV   0x02000303U
@@ -56,12 +52,14 @@ typedef struct  AspeedSCUClass {
     SysBusDeviceClass parent_class;
 
     const uint32_t *resets;
-    uint32_t (*calc_hpll)(AspeedSCUState *s);
+    uint32_t (*calc_hpll)(AspeedSCUState *s, uint32_t hpll_reg);
     uint32_t apb_divider;
 }  AspeedSCUClass;
 
 #define ASPEED_SCU_PROT_KEY      0x1688A8A8
 
+uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s);
+
 /*
  * Extracted from Aspeed SDK v00.03.21. Fixes and extra definitions
  * were added.
diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c
index d284458b9b3d..620b25c20476 100644
--- a/hw/misc/aspeed_scu.c
+++ b/hw/misc/aspeed_scu.c
@@ -164,11 +164,12 @@ static uint32_t aspeed_scu_get_random(void)
     return num;
 }
 
-static void aspeed_scu_set_apb_freq(AspeedSCUState *s)
+uint32_t aspeed_scu_get_apb_freq(AspeedSCUState *s)
 {
     AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(s);
+    uint32_t hpll = asc->calc_hpll(s, s->regs[HPLL_PARAM]);
 
-    s->apb_freq = s->hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[CLK_SEL]) + 1)
+    return hpll / (SCU_CLK_GET_PCLK_DIV(s->regs[CLK_SEL]) + 1)
         / asc->apb_divider;
 }
 
@@ -228,7 +229,6 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data,
         return;
     case CLK_SEL:
         s->regs[reg] = data;
-        aspeed_scu_set_apb_freq(s);
         break;
     case HW_STRAP1:
         if (ASPEED_IS_AST2500(s->regs[SILICON_REV])) {
@@ -290,11 +290,11 @@ static const uint32_t hpll_ast2400_freqs[][4] = {
     { 400, 375, 350, 425 }, /* 25MHz */
 };
 
-static uint32_t aspeed_2400_scu_calc_hpll(AspeedSCUState *s)
+static uint32_t aspeed_2400_scu_calc_hpll(AspeedSCUState *s, uint32_t hpll_reg)
 {
-    uint32_t hpll_reg = s->regs[HPLL_PARAM];
     uint8_t freq_select;
     bool clk_25m_in;
+    uint32_t clkin = aspeed_scu_get_clkin(s);
 
     if (hpll_reg & SCU_AST2400_H_PLL_OFF) {
         return 0;
@@ -311,7 +311,7 @@ static uint32_t aspeed_2400_scu_calc_hpll(AspeedSCUState *s)
             multiplier = (2 - od) * ((n + 2) / (d + 1));
         }
 
-        return s->clkin * multiplier;
+        return clkin * multiplier;
     }
 
     /* HW strapping */
@@ -321,10 +321,10 @@ static uint32_t aspeed_2400_scu_calc_hpll(AspeedSCUState *s)
     return hpll_ast2400_freqs[clk_25m_in][freq_select] * 1000000;
 }
 
-static uint32_t aspeed_2500_scu_calc_hpll(AspeedSCUState *s)
+static uint32_t aspeed_2500_scu_calc_hpll(AspeedSCUState *s, uint32_t hpll_reg)
 {
-    uint32_t hpll_reg   = s->regs[HPLL_PARAM];
     uint32_t multiplier = 1;
+    uint32_t clkin = aspeed_scu_get_clkin(s);
 
     if (hpll_reg & SCU_H_PLL_OFF) {
         return 0;
@@ -338,7 +338,7 @@ static uint32_t aspeed_2500_scu_calc_hpll(AspeedSCUState *s)
         multiplier = ((m + 1) / (n + 1)) / (p + 1);
     }
 
-    return s->clkin * multiplier;
+    return clkin * multiplier;
 }
 
 static void aspeed_scu_reset(DeviceState *dev)
@@ -351,13 +351,6 @@ static void aspeed_scu_reset(DeviceState *dev)
     s->regs[HW_STRAP1] = s->hw_strap1;
     s->regs[HW_STRAP2] = s->hw_strap2;
     s->regs[PROT_KEY] = s->hw_prot_key;
-
-    /*
-     * All registers are set. Now compute the frequencies of the main clocks
-     */
-    s->clkin = aspeed_scu_get_clkin(s);
-    s->hpll = asc->calc_hpll(s);
-    aspeed_scu_set_apb_freq(s);
 }
 
 static uint32_t aspeed_silicon_revs[] = {
diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c
index 59c2bbeee602..2bda826882d9 100644
--- a/hw/timer/aspeed_timer.c
+++ b/hw/timer/aspeed_timer.c
@@ -93,7 +93,8 @@ static inline uint32_t calculate_rate(struct AspeedTimer *t)
 {
     AspeedTimerCtrlState *s = timer_to_ctrl(t);
 
-    return timer_external_clock(t) ? TIMER_CLOCK_EXT_HZ : s->scu->apb_freq;
+    return timer_external_clock(t) ? TIMER_CLOCK_EXT_HZ :
+        aspeed_scu_get_apb_freq(s->scu);
 }
 
 static inline uint32_t calculate_ticks(struct AspeedTimer *t, uint64_t now_ns)
-- 
2.21.0



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

* Re: [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes
  2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
                   ` (9 preceding siblings ...)
  2019-09-04  7:05 ` [Qemu-devel] [PATCH 10/10] aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine Cédric Le Goater
@ 2019-09-11 10:30 ` Peter Maydell
  10 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2019-09-11 10:30 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Andrew Jeffery, qemu-arm, QEMU Developers, Joel Stanley

On Wed, 4 Sep 2019 at 08:05, Cédric Le Goater <clg@kaod.org> wrote:
>
> Hello,
>
> This series improves the current models of the Aspeed machines in QEMU
> and adds new ones. It also prepares ground for the models of the
> Aspeed AST2600 SoC by calculating the model typenames using the SoC
> name.
>
> You will find patches for :
>
>  - DMA support for the SMC controller, now using address_space_stl/ldl_le
>  - GPIO v5 model from Rashmica
>
> Thanks,
>
> C.
>
> Christian Svensson (1):
>   aspeed/smc: Calculate checksum on normal DMA
>
> Cédric Le Goater (7):
>   aspeed: Remove unused SoC definitions
>   aspeed: Use consistent typenames
>   aspeed/smc: Add support for DMAs
>   aspeed/smc: Add DMA calibration settings
>   aspeed/smc: Inject errors in DMA checksum
>   aspeed/scu: Introduce per-SoC SCU types
>   aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine
>
> Rashmica Gupta (2):
>   hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500
>   aspeed: add a GPIO controller to the SoC



Applied to target-arm.next, thanks.

-- PMM


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

end of thread, other threads:[~2019-09-11 10:32 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-04  7:04 [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Cédric Le Goater
2019-09-04  7:04 ` [Qemu-devel] [PATCH 01/10] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500 Cédric Le Goater
2019-09-04  7:04 ` [Qemu-devel] [PATCH 02/10] aspeed: add a GPIO controller to the SoC Cédric Le Goater
2019-09-04  7:04 ` [Qemu-devel] [PATCH 03/10] aspeed: Remove unused SoC definitions Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 04/10] aspeed: Use consistent typenames Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 05/10] aspeed/smc: Add support for DMAs Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 06/10] aspeed/smc: Add DMA calibration settings Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 07/10] aspeed/smc: Inject errors in DMA checksum Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 08/10] aspeed/smc: Calculate checksum on normal DMA Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 09/10] aspeed/scu: Introduce per-SoC SCU types Cédric Le Goater
2019-09-04  7:05 ` [Qemu-devel] [PATCH 10/10] aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine Cédric Le Goater
2019-09-11 10:30 ` [Qemu-devel] [PATCH 00/10] Aspeed: machine extensions and fixes Peter Maydell

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.