From 4b7f956862dc2db4c5c60762abcb3b6179751cc6 Mon Sep 17 00:00:00 2001 From: Rashmica Gupta Date: Wed, 4 Sep 2019 09:04:57 +0200 Subject: [PATCH 01/12] hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Tested-by: Andrew Jeffery Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater Message-id: 20190904070506.1052-2-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 Signed-off-by: Peter Maydell --- hw/gpio/Makefile.objs | 1 + hw/gpio/aspeed_gpio.c | 884 ++++++++++++++++++++++++++++++++++ include/hw/gpio/aspeed_gpio.h | 100 ++++ 3 files changed, 985 insertions(+) create mode 100644 hw/gpio/aspeed_gpio.c create mode 100644 include/hw/gpio/aspeed_gpio.h diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index e5da0cb54f..d305b3b24b 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 diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c new file mode 100644 index 0000000000..25fbfec3b8 --- /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 + +#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/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h new file mode 100644 index 0000000000..a2deac046a --- /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 { + /* */ + 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_ */ From fdcc7c0631144933e955b6ddbbd10325abc7f688 Mon Sep 17 00:00:00 2001 From: Rashmica Gupta Date: Wed, 4 Sep 2019 09:04:58 +0200 Subject: [PATCH 02/12] aspeed: add a GPIO controller to the SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rashmica Gupta Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater Message-id: 20190904070506.1052-3-clg@kaod.org Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 17 +++++++++++++++++ include/hw/arm/aspeed_soc.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 9ee8104832..04480875d0 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), diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 976fd6be93..a56effebc1 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; From 3a714681507c561f1b1f509061c1e8e8f501eaf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:04:59 +0200 Subject: [PATCH 03/12] aspeed: Remove unused SoC definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no QEMU Aspeed machines using the SoCs "ast2400-a0" or "ast2400". Signed-off-by: Cédric Le Goater Message-id: 20190904070506.1052-4-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- 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 04480875d0..3aa73d2438 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"), From 811a5b1d6c2192cb9092040231dab173758bcca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:00 +0200 Subject: [PATCH 04/12] aspeed: Use consistent typenames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Message-id: 20190904070506.1052-5-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 25 ++++++++++++------------- hw/ssi/aspeed_smc.c | 12 ++++++------ include/hw/arm/aspeed_soc.h | 3 --- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index 3aa73d2438..25dbc409d3 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 9f3cff5fb6..f4f7c18183 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, diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index a56effebc1..ab5052b12c 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; From c4e1f0b48322a9bc98c37f8413553cb6131daafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:01 +0200 Subject: [PATCH 05/12] aspeed/smc: Add support for DMAs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Acked-by: Joel Stanley Message-id: 20190904070506.1052-6-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/aspeed.c | 2 + hw/arm/aspeed_soc.c | 2 + hw/ssi/aspeed_smc.c | 222 +++++++++++++++++++++++++++++++++++- include/hw/ssi/aspeed_smc.h | 6 + 4 files changed, 226 insertions(+), 6 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 13e208c78c..aa72be309d 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 25dbc409d3..da508c99c3 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 f4f7c18183..c1a45c10dc 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(), }; diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index aa07dac4fe..32ce6916f6 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; From 0d72c717029f59fa0531fee419734ad7f14b1331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:02 +0200 Subject: [PATCH 06/12] aspeed/smc: Add DMA calibration settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Acked-by: Joel Stanley Reviewed-by: Peter Maydell Message-id: 20190904070506.1052-7-clg@kaod.org Signed-off-by: Peter Maydell --- 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 c1a45c10dc..7a0cd7607f 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); From 5258c2a69ce6cea0b9ab90f1c83223c0daa8d72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:03 +0200 Subject: [PATCH 07/12] aspeed/smc: Inject errors in DMA checksum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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é Signed-off-by: Cédric Le Goater Reviewed-by: Joel Stanley Message-id: 20190904070506.1052-8-clg@kaod.org Signed-off-by: Peter Maydell --- hw/ssi/aspeed_smc.c | 36 ++++++++++++++++++++++++++++++++++++ include/hw/ssi/aspeed_smc.h | 1 + 2 files changed, 37 insertions(+) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 7a0cd7607f..5c3436db5e 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 *), diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 32ce6916f6..5176ff6bf9 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; From ae275f71338ad4e567c7f2683bf28e66847eabe4 Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Wed, 4 Sep 2019 09:05:04 +0200 Subject: [PATCH 08/12] aspeed/smc: Calculate checksum on normal DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Joel Stanley Signed-off-by: Cédric Le Goater Message-id: 20190904070506.1052-9-clg@kaod.org Signed-off-by: Peter Maydell --- 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 5c3436db5e..9ffc7e0117 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; } } From 9a937f6cc4c18a335e813882d15c83252d611042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:05 +0200 Subject: [PATCH 09/12] aspeed/scu: Introduce per-SoC SCU types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and use a class AspeedSCUClass to define each SoC characteristics. Signed-off-by: Cédric Le Goater Message-id: 20190904070506.1052-10-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/aspeed_soc.c | 3 +- hw/misc/aspeed_scu.c | 83 ++++++++++++++++++++---------------- include/hw/misc/aspeed_scu.h | 15 +++++++ 3 files changed, 64 insertions(+), 37 deletions(-) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index da508c99c3..cf1d0cf921 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 268cb24e56..d284458b9b 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); + AspeedSCUClass *asc = ASPEED_SCU_GET_CLASS(dev); - 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(); - } - - 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); diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 38996adc59..90dd4daded 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 /* From a8f07376c9b3e5bce736bdcf6f49dc63fa4a516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 4 Sep 2019 09:05:06 +0200 Subject: [PATCH 10/12] aspeed/scu: Introduce a aspeed_scu_get_apb_freq() routine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Message-id: 20190904070506.1052-11-clg@kaod.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/aspeed_scu.c | 25 +++++++++---------------- hw/timer/aspeed_timer.c | 3 ++- include/hw/misc/aspeed_scu.h | 8 +++----- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index d284458b9b..620b25c204 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 59c2bbeee6..2bda826882 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) diff --git a/include/hw/misc/aspeed_scu.h b/include/hw/misc/aspeed_scu.h index 90dd4daded..239e94fe2c 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. From d2fac5f678559474134bf1f5915a0bb58d946433 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 6 Sep 2019 21:26:35 +0100 Subject: [PATCH 11/12] atomic_template: fix indentation in GEN_ATOMIC_HELPER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Signed-off-by: Emilio G. Cota Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Signed-off-by: Peter Maydell --- accel/tcg/atomic_template.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index df9c838817..287433d809 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -149,7 +149,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \ - ABI_TYPE val EXTRA_ARGS) \ + ABI_TYPE val EXTRA_ARGS) \ { \ ATOMIC_MMU_DECLS; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ From 27a296fce9821e3608d537756cffa6e43a46df3b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 5 Sep 2019 14:10:40 +0100 Subject: [PATCH 12/12] qemu-ga: Convert invocation documentation to rST The qemu-ga documentation is currently in qemu-ga.texi in Texinfo format, which we present to the user as: * a qemu-ga manpage * a section of the main qemu-doc HTML documentation Convert the documentation to rST format, and present it to the user as: * a qemu-ga manpage * part of the interop/ Sphinx manual Signed-off-by: Peter Maydell Reviewed-by: Michael Roth Tested-by: Michael Roth Message-id: 20190905131040.8350-1-peter.maydell@linaro.org --- MAINTAINERS | 2 +- Makefile | 24 ++++--- docs/conf.py | 18 ++--- docs/interop/conf.py | 7 ++ docs/interop/index.rst | 1 + docs/interop/qemu-ga.rst | 133 +++++++++++++++++++++++++++++++++++++ qemu-doc.texi | 5 -- qemu-ga.texi | 137 --------------------------------------- 8 files changed, 166 insertions(+), 161 deletions(-) create mode 100644 docs/interop/qemu-ga.rst delete mode 100644 qemu-ga.texi diff --git a/MAINTAINERS b/MAINTAINERS index 50eaf005f4..f0e30b5248 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2040,7 +2040,7 @@ QEMU Guest Agent M: Michael Roth S: Maintained F: qga/ -F: qemu-ga.texi +F: docs/interop/qemu-ga.rst F: scripts/qemu-guest-agent/ F: tests/test-qga.c F: docs/interop/qemu-ga-ref.texi diff --git a/Makefile b/Makefile index b3528617e4..111082ce54 100644 --- a/Makefile +++ b/Makefile @@ -325,7 +325,7 @@ endif endif ifdef BUILD_DOCS -DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 +DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 docs/interop/qemu-ga.8 DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 DOCS+=docs/qemu-block-drivers.7 @@ -783,10 +783,11 @@ DESCS= endif # Note that we manually filter-out the non-Sphinx documentation which -# is currently built into the docs/interop directory in the build tree. +# is currently built into the docs/interop directory in the build tree, +# and also any sphinx-built manpages. define install-manual = for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done -for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done +for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done endef # Note that we deliberately do not install the "devel" manual: it is @@ -818,7 +819,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP $(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" endif ifneq (,$(findstring qemu-ga,$(TOOLS))) - $(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" + $(INSTALL_DATA) docs/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8" $(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7" @@ -977,18 +978,22 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html # Canned command to build a single manual -build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -W -n -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") +# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man') +build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" sphinx-build $(if $(V),,-q) -W -n -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") # We assume all RST files in the manual's directory are used in it manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py $(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel) - $(call build-manual,devel) + $(call build-manual,devel,html) $(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) - $(call build-manual,interop) + $(call build-manual,interop,html) $(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs) - $(call build-manual,specs) + $(call build-manual,specs,html) + +$(MANUAL_BUILDDIR)/interop/qemu-ga.8: $(call manual-deps,interop) + $(call build-manual,interop,man) qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") @@ -1013,7 +1018,6 @@ qemu.1: qemu-option-trace.texi qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi -qemu-ga.8: qemu-ga.texi docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi @@ -1026,7 +1030,7 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ qemu-img.texi qemu-nbd.texi qemu-options.texi \ qemu-tech.texi qemu-option-trace.texi \ - qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ + qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi \ qemu-monitor-info.texi docs/qemu-block-drivers.texi \ docs/qemu-cpu-models.texi docs/security.texi diff --git a/docs/conf.py b/docs/conf.py index e46b299b71..b7edb0666b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -115,6 +115,14 @@ # with "option::" in the document being processed. Turn that off. suppress_warnings = ["ref.option"] +# The rst_epilog fragment is effectively included in every rST file. +# We use it to define substitutions based on build config that +# can then be used in the documentation. The fallback if the +# environment variable is not set is for the benefit of readthedocs +# style document building; our Makefile always sets the variable. +confdir = os.getenv('CONFDIR', "/etc/qemu") +rst_epilog = ".. |CONFDIR| replace:: ``" + confdir + "``\n" + # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for @@ -192,14 +200,8 @@ # -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'qemu', u'QEMU Documentation', - [author], 1) -] - +# Individual manual/conf.py can override this to create man pages +man_pages = [] # -- Options for Texinfo output ------------------------------------------- diff --git a/docs/interop/conf.py b/docs/interop/conf.py index cf3c69d4a7..e87b8c22be 100644 --- a/docs/interop/conf.py +++ b/docs/interop/conf.py @@ -13,3 +13,10 @@ # This slightly misuses the 'description', but is the best way to get # the manual title to appear in the sidebar. html_theme_options['description'] = u'System Emulation Management and Interoperability Guide' + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('qemu-ga', 'qemu-ga', u'QEMU Guest Agent', + ['Michael Roth '], 8) +] diff --git a/docs/interop/index.rst b/docs/interop/index.rst index b4bfcab417..3e33fb5933 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -15,5 +15,6 @@ Contents: bitmaps live-block-operations pr-helper + qemu-ga vhost-user vhost-user-gpu diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst new file mode 100644 index 0000000000..1313a4ae1c --- /dev/null +++ b/docs/interop/qemu-ga.rst @@ -0,0 +1,133 @@ +QEMU Guest Agent +================ + +Synopsis +-------- + +**qemu-ga** [*OPTIONS*] + +Description +----------- + +The QEMU Guest Agent is a daemon intended to be run within virtual +machines. It allows the hypervisor host to perform various operations +in the guest, such as: + +- get information from the guest +- set the guest's system time +- read/write a file +- sync and freeze the filesystems +- suspend the guest +- reconfigure guest local processors +- set user's password +- ... + +qemu-ga will read a system configuration file on startup (located at +|CONFDIR|\ ``/qemu-ga.conf`` by default), then parse remaining +configuration options on the command line. For the same key, the last +option wins, but the lists accumulate (see below for configuration +file format). + +Options +------- + +.. program:: qemu-ga + +.. option:: -m, --method=METHOD + + Transport method: one of ``unix-listen``, ``virtio-serial``, or + ``isa-serial`` (``virtio-serial`` is the default). + +.. option:: -p, --path=PATH + + Device/socket path (the default for virtio-serial is + ``/dev/virtio-ports/org.qemu.guest_agent.0``, + the default for isa-serial is ``/dev/ttyS0``) + +.. option:: -l, --logfile=PATH + + Set log file path (default is stderr). + +.. option:: -f, --pidfile=PATH + + Specify pid file (default is ``/var/run/qemu-ga.pid``). + +.. option:: -F, --fsfreeze-hook=PATH + + Enable fsfreeze hook. Accepts an optional argument that specifies + script to run on freeze/thaw. Script will be called with + 'freeze'/'thaw' arguments accordingly (default is + |CONFDIR|\ ``/fsfreeze-hook``). If using -F with an argument, do + not follow -F with a space (for example: + ``-F/var/run/fsfreezehook.sh``). + +.. option:: -t, --statedir=PATH + + Specify the directory to store state information (absolute paths only, + default is ``/var/run``). + +.. option:: -v, --verbose + + Log extra debugging information. + +.. option:: -V, --version + + Print version information and exit. + +.. option:: -d, --daemon + + Daemonize after startup (detach from terminal). + +.. option:: -b, --blacklist=LIST + + Comma-separated list of RPCs to disable (no spaces, ``?`` to list + available RPCs). + +.. option:: -D, --dump-conf + + Dump the configuration in a format compatible with ``qemu-ga.conf`` + and exit. + +.. option:: -h, --help + + Display this help and exit. + +Files +----- + + +The syntax of the ``qemu-ga.conf`` configuration file follows the +Desktop Entry Specification, here is a quick summary: it consists of +groups of key-value pairs, interspersed with comments. + +:: + + # qemu-ga configuration sample + [general] + daemonize = 0 + pidfile = /var/run/qemu-ga.pid + verbose = 0 + method = virtio-serial + path = /dev/virtio-ports/org.qemu.guest_agent.0 + statedir = /var/run + +The list of keys follows the command line options: + +============= =========== +Key Key type +============= =========== +daemon boolean +method string +path string +logfile string +pidfile string +fsfreeze-hook string +statedir string +verbose boolean +blacklist string list +============= =========== + +See also +-------- + +:manpage:`qemu(1)` diff --git a/qemu-doc.texi b/qemu-doc.texi index b47e89cfca..2ba6c90c08 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -2535,11 +2535,6 @@ so should only be used with trusted guest OS. @c man end -@node QEMU Guest Agent -@chapter QEMU Guest Agent invocation - -@include qemu-ga.texi - @node QEMU User space emulator @chapter QEMU User space emulator diff --git a/qemu-ga.texi b/qemu-ga.texi deleted file mode 100644 index f00ad830f2..0000000000 --- a/qemu-ga.texi +++ /dev/null @@ -1,137 +0,0 @@ -@example -@c man begin SYNOPSIS -@command{qemu-ga} [@var{OPTIONS}] -@c man end -@end example - -@c man begin DESCRIPTION - -The QEMU Guest Agent is a daemon intended to be run within virtual -machines. It allows the hypervisor host to perform various operations -in the guest, such as: - -@itemize -@item -get information from the guest -@item -set the guest's system time -@item -read/write a file -@item -sync and freeze the filesystems -@item -suspend the guest -@item -reconfigure guest local processors -@item -set user's password -@item -... -@end itemize - -qemu-ga will read a system configuration file on startup (located at -@file{@value{CONFDIR}/qemu-ga.conf} by default), then parse remaining -configuration options on the command line. For the same key, the last -option wins, but the lists accumulate (see below for configuration -file format). - -@c man end - -@c man begin OPTIONS -@table @option -@item -m, --method=@var{method} - Transport method: one of @samp{unix-listen}, @samp{virtio-serial}, or - @samp{isa-serial} (@samp{virtio-serial} is the default). - -@item -p, --path=@var{path} - Device/socket path (the default for virtio-serial is - @samp{/dev/virtio-ports/org.qemu.guest_agent.0}, - the default for isa-serial is @samp{/dev/ttyS0}) - -@item -l, --logfile=@var{path} - Set log file path (default is stderr). - -@item -f, --pidfile=@var{path} - Specify pid file (default is @samp{/var/run/qemu-ga.pid}). - -@item -F, --fsfreeze-hook=@var{path} - Enable fsfreeze hook. Accepts an optional argument that specifies - script to run on freeze/thaw. Script will be called with - 'freeze'/'thaw' arguments accordingly (default is - @samp{@value{CONFDIR}/fsfreeze-hook}). If using -F with an argument, do - not follow -F with a space (for example: - @samp{-F/var/run/fsfreezehook.sh}). - -@item -t, --statedir=@var{path} - Specify the directory to store state information (absolute paths only, - default is @samp{/var/run}). - -@item -v, --verbose - Log extra debugging information. - -@item -V, --version - Print version information and exit. - -@item -d, --daemon - Daemonize after startup (detach from terminal). - -@item -b, --blacklist=@var{list} - Comma-separated list of RPCs to disable (no spaces, @samp{?} to list - available RPCs). - -@item -D, --dump-conf - Dump the configuration in a format compatible with @file{qemu-ga.conf} - and exit. - -@item -h, --help - Display this help and exit. -@end table - -@c man end - -@c man begin FILES - -The syntax of the @file{qemu-ga.conf} configuration file follows the -Desktop Entry Specification, here is a quick summary: it consists of -groups of key-value pairs, interspersed with comments. - -@example -# qemu-ga configuration sample -[general] -daemonize = 0 -pidfile = /var/run/qemu-ga.pid -verbose = 0 -method = virtio-serial -path = /dev/virtio-ports/org.qemu.guest_agent.0 -statedir = /var/run -@end example - -The list of keys follows the command line options: -@table @option -@item daemon= boolean -@item method= string -@item path= string -@item logfile= string -@item pidfile= string -@item fsfreeze-hook= string -@item statedir= string -@item verbose= boolean -@item blacklist= string list -@end table - -@c man end - -@ignore - -@setfilename qemu-ga -@settitle QEMU Guest Agent - -@c man begin AUTHOR -Michael Roth -@c man end - -@c man begin SEEALSO -qemu(1) -@c man end - -@end ignore