[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-arm] [PATCH 1/2] hw/gpio: Add basic Aspeed GPIO model
From: |
Cédric Le Goater |
Subject: |
Re: [Qemu-arm] [PATCH 1/2] hw/gpio: Add basic Aspeed GPIO model |
Date: |
Tue, 18 Jun 2019 11:04:35 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.7.0 |
On 18/06/2019 10:51, Rashmica Gupta wrote:
> Add in details for GPIO controller for AST 2400 and 2500
I agree.
C.
>
> Signed-off-by: Rashmica Gupta <address@hidden>
> ---
> hw/gpio/Makefile.objs | 1 +
> hw/gpio/aspeed_gpio.c | 869 ++++++++++++++++++++++++++++++++++
> include/hw/gpio/aspeed_gpio.h | 76 +++
> 3 files changed, 946 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..d84dd69255
> --- /dev/null
> +++ b/hw/gpio/aspeed_gpio.c
> @@ -0,0 +1,869 @@
> +/*
> + * ASPEED GPIO Controller
> + *
> + * Copyright (C) 2017-2019 IBM Corp.
> + *
> + * This code is licensed under the GPL version 2 or later. See
> + * the COPYING file in the top-level directory.
> + *
> + *
> + * This models the ast2400 and ast2500 GPIO controllers.
> + *
> + * 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 three ways that this model deviates from the behaviour of the
> + * actual controller:
> + * (1) There are three debounce registers which aren't modeled and so the per
> + * set debounce setting registers don't affect anything.
> + *
> + * (2) 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
> [TODO]).
> + *
> + * (3) None of the registers in the model are reset tolerant. [TODO]
> + *
> + */
> +
> +#include "qemu/osdep.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"
> +
> +#define ASPEED_GPIOS_PER_REG 32
> +
> +/* 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 */
> +#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
> +#define GPIO_ABCD_DIRECTION 0x004
> +#define GPIO_ABCD_INT_ENABLE 0x008
> +#define GPIO_ABCD_INT_SENS_0 0x00C
> +#define GPIO_ABCD_INT_SENS_1 0x010
> +#define GPIO_ABCD_INT_SENS_2 0x014
> +#define GPIO_ABCD_INT_STATUS 0x018
> +#define GPIO_ABCD_RESET_TOLERANT 0x01C
> +#define GPIO_EFGH_DATA_VALUE 0x020
> +#define GPIO_EFGH_DIRECTION 0x024
> +#define GPIO_EFGH_INT_ENABLE 0x028
> +#define GPIO_EFGH_INT_SENS_0 0x02C
> +#define GPIO_EFGH_INT_SENS_1 0x030
> +#define GPIO_EFGH_INT_SENS_2 0x034
> +#define GPIO_EFGH_INT_STATUS 0x038
> +#define GPIO_EFGH_RESET_TOL 0x03C
> +#define GPIO_ABCD_DEBOUNCE_1 0x040
> +#define GPIO_ABCD_DEBOUNCE_2 0x044
> +#define GPIO_EFGH_DEBOUNCE_1 0x048
> +#define GPIO_EFGH_DEBOUNCE_2 0x04C
> +#define GPIO_DEBOUNCE_TIME_1 0x050
> +#define GPIO_DEBOUNCE_TIME_2 0x054
> +#define GPIO_DEBOUNCE_TIME_3 0x058
> +#define GPIO_ABCD_COMMAND_SRC_0 0x060
> +#define GPIO_ABCD_COMMAND_SRC_1 0x064
> +#define GPIO_EFGH_COMMAND_SRC_0 0x068
> +#define GPIO_EFGH_COMMAND_SRC_1 0x06C
> +#define GPIO_IJKL_DATA_VALUE 0x070
> +#define GPIO_IJKL_DIRECTION 0x074
> +#define GPIO_MNOP_DATA_VALUE 0x078
> +#define GPIO_MNOP_DIRECTION 0x07C
> +#define GPIO_QRST_DATA_VALUE 0x080
> +#define GPIO_QRST_DIRECTION 0x084
> +#define GPIO_UVWX_DATA_VALUE 0x088
> +#define GPIO_UWVX_DIRECTION 0x08C
> +#define GPIO_IJKL_COMMAND_SRC_0 0x090
> +#define GPIO_IJKL_COMMAND_SRC_1 0x094
> +#define GPIO_IJKL_INT_ENABLE 0x098
> +#define GPIO_IJKL_INT_SENS_0 0x09C
> +#define GPIO_IJKL_INT_SENS_1 0x0A0
> +#define GPIO_IJKL_INT_SENS_2 0x0A4
> +#define GPIO_IJKL_INT_STATUS 0x0A8
> +#define GPIO_IJKL_RESET_TOLERANT 0x0AC
> +#define GPIO_IJKL_DEBOUNCE_1 0x0B0
> +#define GPIO_IJKL_DEBOUNCE_2 0x0B4
> +#define GPIO_IJKL_INPUT_MASK 0x0B8
> +#define GPIO_ABCD_DATA_READ 0x0C0
> +#define GPIO_EFGH_DATA_READ 0x0C4
> +#define GPIO_IJKL_DATA_READ 0x0C8
> +#define GPIO_MNOP_DATA_READ 0x0CC
> +#define GPIO_QRST_DATA_READ 0x0D0
> +#define GPIO_UVWX_DATA_READ 0x0D4
> +#define GPIO_YZAAAB_DATA_READ 0x0D8
> +#define GPIO_AC_DATA_READ 0x0DC
> +#define GPIO_MNOP_COMMAND_SRC_0 0x0E0
> +#define GPIO_MNOP_COMMAND_SRC_1 0x0E4
> +#define GPIO_MNOP_INT_ENABLE 0x0E8
> +#define GPIO_MNOP_INT_SENS_0 0x0EC
> +#define GPIO_MNOP_INT_SENS_1 0x0F0
> +#define GPIO_MNOP_INT_SENS_2 0x0F4
> +#define GPIO_MNOP_INT_STATUS 0x0F8
> +#define GPIO_MNOP_RESET_TOLERANT 0x0FC
> +#define GPIO_MNOP_DEBOUNCE_1 0x100
> +#define GPIO_MNOP_DEBOUNCE_2 0x104
> +#define GPIO_MNOP_INPUT_MASK 0x108
> +#define GPIO_QRST_COMMAND_SRC_0 0x110
> +#define GPIO_QRST_COMMAND_SRC_1 0x114
> +#define GPIO_QRST_INT_ENABLE 0x118
> +#define GPIO_QRST_INT_SENS_0 0x11C
> +#define GPIO_QRST_INT_SENS_1 0x120
> +#define GPIO_QRST_INT_SENS_2 0x124
> +#define GPIO_QRST_INT_STATUS 0x128
> +#define GPIO_QRST_RESET_TOLERANT 0x12C
> +#define GPIO_QRST_DEBOUNCE_1 0x130
> +#define GPIO_QRST_DEBOUNCE_2 0x134
> +#define GPIO_QRST_INPUT_MASK 0x138
> +#define GPIO_UVWX_COMMAND_SRC_0 0x140
> +#define GPIO_UVWX_COMMAND_SRC_1 0x144
> +#define GPIO_UVWX_INT_ENABLE 0x148
> +#define GPIO_UVWX_INT_SENS_0 0x14C
> +#define GPIO_UVWX_INT_SENS_1 0x150
> +#define GPIO_UVWX_INT_SENS_2 0x154
> +#define GPIO_UVWX_INT_STATUS 0x158
> +#define GPIO_UVWX_RESET_TOLERANT 0x15C
> +#define GPIO_UVWX_DEBOUNCE_1 0x160
> +#define GPIO_UVWX_DEBOUNCE_2 0x164
> +#define GPIO_UVWX_INPUT_MASK 0x168
> +#define GPIO_YZAAAB_COMMAND_SRC_0 0x170
> +#define GPIO_YZAAAB_COMMAND_SRC_1 0x174
> +#define GPIO_YZAAAB_INT_ENABLE 0x178
> +#define GPIO_YZAAAB_INT_SENS_0 0x17C
> +#define GPIO_YZAAAB_INT_SENS_1 0x180
> +#define GPIO_YZAAAB_INT_SENS_2 0x184
> +#define GPIO_YZAAAB_INT_STATUS 0x188
> +#define GPIO_YZAAAB_RESET_TOLERANT 0x18C
> +#define GPIO_YZAAAB_DEBOUNCE_1 0x190
> +#define GPIO_YZAAAB_DEBOUNCE_2 0x194
> +#define GPIO_YZAAAB_INPUT_MASK 0x198
> +#define GPIO_AC_COMMAND_SRC_0 0x1A0
> +#define GPIO_AC_COMMAND_SRC_1 0x1A4
> +#define GPIO_AC_INT_ENABLE 0x1A8
> +#define GPIO_AC_INT_SENS_0 0x1AC
> +#define GPIO_AC_INT_SENS_1 0x1B0
> +#define GPIO_AC_INT_SENS_2 0x1B4
> +#define GPIO_AC_INT_STATUS 0x1B8
> +#define GPIO_AC_RESET_TOLERANT 0x1BC
> +#define GPIO_AC_DEBOUNCE_1 0x1C0
> +#define GPIO_AC_DEBOUNCE_2 0x1C4
> +#define GPIO_AC_INPUT_MASK 0x1C8
> +#define GPIO_ABCD_INPUT_MASK 0x1D0
> +#define GPIO_EFGH_INPUT_MASK 0x1D4
> +#define GPIO_YZAAAB_DATA_VALUE 0x1E0
> +#define GPIO_YZAAAB_DIRECTION 0x1E4
> +#define GPIO_AC_DATA_VALUE 0x1E8
> +#define GPIO_AC_DIRECTION 0x1EC
> +
> +struct AspeedGPIO {
> + uint16_t set_idx;
> + uint32_t (*get)(GPIORegs *regs);
> + void (*set)(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val);
> +};
> +
> +static int aspeed_evaluate_irq(GPIORegs *regs, int prev_value_high, int bit)
> +{
> + uint32_t falling_edge = 0, rising_edge = 0;
> + uint32_t int_trigger = extract32(regs->int_sens_0, bit, 1)
> + | extract32(regs->int_sens_1, bit, 1) << 1
> + | extract32(regs->int_sens_2, bit, 1) << 2 ;
> + uint32_t curr_pin_high = extract32(regs->data_value, bit, 1);
> +
> + /* Detect edges */
> + if (curr_pin_high && !prev_value_high) {
> + rising_edge = 1;
> + } else if (!curr_pin_high && prev_value_high) {
> + falling_edge = 1;
> + }
> +
> + if (((int_trigger == ASPEED_FALLING_EDGE) && falling_edge) ||
> + ((int_trigger == ASPEED_RISING_EDGE) && rising_edge) ||
> + ((int_trigger == ASPEED_LEVEL_LOW) && !curr_pin_high) ||
> + ((int_trigger == ASPEED_LEVEL_HIGH) && curr_pin_high) ||
> + ((int_trigger >= ASPEED_DUAL_EDGE) && (rising_edge ||
> falling_edge)))
> + {
> + regs->int_status = deposit32(regs->int_status, bit, 1, 1);
> + return 1;
> + }
> + return 0;
> +}
> +
> +static void aspeed_gpio_update(AspeedGPIOState *s, GPIORegs *regs)
> +{
> + uint32_t input_mask = regs->input_mask;
> + uint32_t direction = regs->direction;
> + uint32_t old = regs->data_value;
> + uint32_t new = regs->data_read;
> + uint32_t diff;
> + int bit;
> +
> + diff = old ^ new;
> + if (!diff) {
> + return;
> + }
> +
> + if (!direction) {
> + return;
> + }
> +
> + for (bit = 0; bit < ASPEED_GPIOS_PER_REG; bit++) {
> + uint32_t mask = 1 << bit;
> + /* If the bit needs to be updated... */
> + if (!(diff & mask)) {
> + continue;
> + }
> + /* ...and corresponds to an output pin...*/
> + if (!(direction & mask)) {
> + continue;
> + }
> + /* ...and isn't masked... */
> + if (input_mask & mask) {
> + continue;
> + }
> + /* ... then update it*/
> + if (mask & new) {
> + regs->data_value |= mask;
> + } else {
> + regs->data_value &= ~mask;
> + }
> +
> + if (aspeed_evaluate_irq(regs, old & mask, bit)) {
> + qemu_set_irq(s->output[bit], 1);
> + }
> + }
> +}
> +
> +static uint32_t aspeed_adjust_pin(AspeedGPIOState *s, uint32_t pin)
> +{
> + if (pin >= s->ctrl->gap) {
> + pin += 4;
> + }
> +
> + return pin;
> +}
> +
> +static uint32_t aspeed_get_set_idx_from_pin(AspeedGPIOState *s, uint32_t pin)
> +{
> + /*
> + * For most pins, dividing by 32 gets the set index.
> + *
> + * However 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).
> + */
> + return aspeed_adjust_pin(s, pin) >> 5;
> +}
> +
> +static bool aspeed_gpio_get_pin_level(AspeedGPIOState *s, uint32_t pin)
> +{
> + uint32_t reg_val;
> + uint32_t mask = 1 << (pin);
> + uint32_t set_idx = aspeed_get_set_idx_from_pin(s, pin);
> +
> + reg_val = s->sets[set_idx].data_value;
> +
> + return !!(reg_val & mask);
> +}
> +
> +static void aspeed_gpio_set_pin_level(AspeedGPIOState *s, uint32_t pin,
> + bool level)
> +{
> + uint32_t mask = 1 << (pin);
> + uint32_t set_idx = aspeed_get_set_idx_from_pin(s, pin);
> +
> + if (level) {
> + s->sets[set_idx].data_read |= mask;
> + } else {
> + s->sets[set_idx].data_read &= !mask;
> + }
> +
> + aspeed_gpio_update(s, &s->sets[set_idx]);
> +}
> +
> +/*
> + * | 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(GPIORegs *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 < ASPEED_GPIOS_PER_REG; i += 8) {
> + 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;
> +}
> +
> +/* Reader helper functions */
> +static uint32_t read_direction(GPIORegs *regs)
> +{
> + return regs->direction;
> +}
> +
> +static uint32_t read_data_value(GPIORegs *regs)
> +{
> + return regs->data_value;
> +}
> +
> +static uint32_t read_int_enable(GPIORegs *regs)
> +{
> + return regs->int_enable;
> +}
> +
> +static uint32_t read_int_sens_0(GPIORegs *regs)
> +{
> + return regs->int_sens_0;
> +}
> +
> +static uint32_t read_int_sens_1(GPIORegs *regs)
> +{
> + return regs->int_sens_1;
> +}
> +
> +static uint32_t read_int_sens_2(GPIORegs *regs)
> +{
> + return regs->int_sens_2;
> +}
> +
> +static uint32_t read_int_status(GPIORegs *regs)
> +{
> + return regs->int_status;
> +}
> +
> +static uint32_t read_reset_tol(GPIORegs *regs)
> +{
> + return regs->reset_tol;
> +}
> +
> +static uint32_t read_debounce_1(GPIORegs *regs)
> +{
> + return regs->debounce_1;
> +}
> +
> +static uint32_t read_debounce_2(GPIORegs *regs)
> +{
> + return regs->debounce_2;
> +}
> +
> +static uint32_t read_cmd_source_0(GPIORegs *regs)
> +{
> + return regs->cmd_source_0;
> +}
> +
> +static uint32_t read_cmd_source_1(GPIORegs *regs)
> +{
> + return regs->cmd_source_1;
> +}
> +
> +static uint32_t read_data(GPIORegs *regs)
> +{
> + return regs->data_read;
> +}
> +
> +static uint32_t read_input_mask(GPIORegs *regs)
> +{
> + return regs->input_mask;
> +}
> +
> +/* Write helper functions */
> +
> +static void _write_data_value(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + val &= props->output | ~props->input;
> + regs->data_read = update_value_control_source(regs, regs->data_read,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_direction(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + val &= props->output | ~props->input;
> + regs->direction = update_value_control_source(regs, regs->direction,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_int_enable(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->int_enable = update_value_control_source(regs, regs->int_enable,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_int_sens_0(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->int_sens_0 = update_value_control_source(regs, regs->int_sens_0,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_int_sens_1(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->int_sens_1 = update_value_control_source(regs, regs->int_sens_1,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_int_sens_2(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->int_sens_2 = update_value_control_source(regs, regs->int_sens_2,
> val);
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_int_status(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->int_status = val;
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static void _write_reset_tol(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->reset_tol = update_value_control_source(regs, regs->reset_tol,
> val);
> +}
> +
> +static void _write_debounce_1(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->debounce_1 = update_value_control_source(regs, regs->debounce_1,
> val);
> +}
> +
> +static void _write_debounce_2(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->debounce_2 = update_value_control_source(regs, regs->debounce_2,
> val);
> +}
> +
> +static void _write_cmd_source_0(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->cmd_source_0 = val & ASPEED_CMD_SRC_MASK;
> +}
> +
> +static void _write_cmd_source_1(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->cmd_source_1 = val & ASPEED_CMD_SRC_MASK;
> +}
> +
> +/*
> + * feeds into interrupt generation
> + * 0: read from data value reg will be updated
> + * 1: read from data value reg will not be updated
> + */
> +static void _write_input_mask(AspeedGPIOState *s, GPIORegs *regs,
> + GPIOSetProperties *props, uint32_t val)
> +{
> + regs->input_mask = val & props->input;
> + aspeed_gpio_update(s, regs);
> +}
> +
> +static const struct AspeedGPIO gpios[0x1f0] = {
> + /* Set ABCD */
> + [GPIO_ABCD_DATA_VALUE] = {0, read_data_value, _write_data_value},
> + [GPIO_ABCD_DIRECTION] = {0, read_direction, _write_direction},
> + [GPIO_ABCD_INT_ENABLE] = {0, read_int_enable, _write_int_enable},
> + [GPIO_ABCD_INT_SENS_0] = {0, read_int_sens_0, _write_int_sens_0},
> + [GPIO_ABCD_INT_SENS_1] = {0, read_int_sens_1, _write_int_sens_1},
> + [GPIO_ABCD_INT_SENS_2] = {0, read_int_sens_2, _write_int_sens_2},
> + [GPIO_ABCD_INT_STATUS] = {0, read_int_status, _write_int_status},
> + [GPIO_ABCD_RESET_TOLERANT] = {0, read_reset_tol, _write_reset_tol},
> + [GPIO_ABCD_DEBOUNCE_1] = {0, read_debounce_1, _write_debounce_1},
> + [GPIO_ABCD_DEBOUNCE_2] = {0, read_debounce_2, _write_debounce_2},
> + [GPIO_ABCD_COMMAND_SRC_0] = {0, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_ABCD_COMMAND_SRC_1] = {0, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_ABCD_DATA_READ] = {0, read_data, NULL},
> + [GPIO_ABCD_INPUT_MASK] = {0, read_input_mask, _write_input_mask},
> + /* Set EFGH */
> + [GPIO_EFGH_DATA_VALUE] = {1, read_data_value, _write_data_value},
> + [GPIO_EFGH_DIRECTION] = {1, read_direction, _write_direction },
> + [GPIO_EFGH_INT_ENABLE] = {1, read_int_enable, _write_int_enable},
> + [GPIO_EFGH_INT_SENS_0] = {1, read_int_sens_0, _write_int_sens_0},
> + [GPIO_EFGH_INT_SENS_1] = {1, read_int_sens_1, _write_int_sens_1},
> + [GPIO_EFGH_INT_SENS_2] = {1, read_int_sens_2, _write_int_sens_2},
> + [GPIO_EFGH_INT_STATUS] = {1, read_int_status, _write_int_status},
> + [GPIO_EFGH_RESET_TOL] = {1, read_reset_tol, _write_reset_tol},
> + [GPIO_EFGH_DEBOUNCE_1] = {1, read_debounce_1, _write_debounce_1},
> + [GPIO_EFGH_DEBOUNCE_2] = {1, read_debounce_2, _write_debounce_2},
> + [GPIO_EFGH_COMMAND_SRC_0] = {1, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_EFGH_COMMAND_SRC_1] = {1, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_EFGH_DATA_READ] = {1, read_data, NULL},
> + [GPIO_EFGH_INPUT_MASK] = {1, read_input_mask, _write_input_mask},
> + /* Set IJKL */
> + [GPIO_IJKL_DATA_VALUE] = {2, read_data_value, _write_data_value},
> + [GPIO_IJKL_DIRECTION] = {2, read_direction, _write_direction},
> + [GPIO_IJKL_INT_ENABLE] = {2, read_int_enable, _write_int_enable},
> + [GPIO_IJKL_INT_SENS_0] = {2, read_int_sens_0, _write_int_sens_0},
> + [GPIO_IJKL_INT_SENS_1] = {2, read_int_sens_1, _write_int_sens_1},
> + [GPIO_IJKL_INT_SENS_2] = {2, read_int_sens_2, _write_int_sens_2},
> + [GPIO_IJKL_INT_STATUS] = {2, read_int_status, _write_int_status},
> + [GPIO_IJKL_RESET_TOLERANT] = {2, read_reset_tol, _write_reset_tol},
> + [GPIO_IJKL_DEBOUNCE_1] = {2, read_debounce_1, _write_debounce_1},
> + [GPIO_IJKL_DEBOUNCE_2] = {2, read_debounce_2, _write_debounce_2},
> + [GPIO_IJKL_COMMAND_SRC_0] = {2, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_IJKL_COMMAND_SRC_1] = {2, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_IJKL_DATA_READ] = {2, read_data, NULL},
> + [GPIO_IJKL_INPUT_MASK] = {2, read_input_mask, _write_input_mask},
> + /* Set MNOP */
> + [GPIO_MNOP_DATA_VALUE] = {3, read_data_value, _write_data_value},
> + [GPIO_MNOP_DIRECTION] = {3, read_direction, _write_direction},
> + [GPIO_MNOP_INT_ENABLE] = {3, read_int_enable, _write_int_enable},
> + [GPIO_MNOP_INT_SENS_0] = {3, read_int_sens_0, _write_int_sens_0},
> + [GPIO_MNOP_INT_SENS_1] = {3, read_int_sens_1, _write_int_sens_1},
> + [GPIO_MNOP_INT_SENS_2] = {3, read_int_sens_2, _write_int_sens_2},
> + [GPIO_MNOP_INT_STATUS] = {3, read_int_status, _write_int_status},
> + [GPIO_MNOP_RESET_TOLERANT] = {3, read_reset_tol, _write_reset_tol},
> + [GPIO_MNOP_DEBOUNCE_1] = {3, read_debounce_1, _write_debounce_1},
> + [GPIO_MNOP_DEBOUNCE_2] = {3, read_debounce_2, _write_debounce_2},
> + [GPIO_MNOP_COMMAND_SRC_0] = {3, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_MNOP_COMMAND_SRC_1] = {3, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_MNOP_DATA_READ] = {3, read_data, NULL},
> + [GPIO_MNOP_INPUT_MASK] = {3, read_input_mask, _write_input_mask},
> + /* Set QRST */
> + [GPIO_QRST_DATA_VALUE] = {4, read_data_value, _write_data_value},
> + [GPIO_QRST_DIRECTION] = {4, read_direction, _write_direction},
> + [GPIO_QRST_INT_ENABLE] = {4, read_int_enable, _write_int_enable},
> + [GPIO_QRST_INT_SENS_0] = {4, read_int_sens_0, _write_int_sens_0},
> + [GPIO_QRST_INT_SENS_1] = {4, read_int_sens_1, _write_int_sens_1},
> + [GPIO_QRST_INT_SENS_2] = {4, read_int_sens_2, _write_int_sens_2},
> + [GPIO_QRST_INT_STATUS] = {4, read_int_status, _write_int_status},
> + [GPIO_QRST_RESET_TOLERANT] = {4, read_reset_tol, _write_reset_tol},
> + [GPIO_QRST_DEBOUNCE_1] = {4, read_debounce_1, _write_debounce_1},
> + [GPIO_QRST_DEBOUNCE_2] = {4, read_debounce_2, _write_debounce_2},
> + [GPIO_QRST_COMMAND_SRC_0] = {4, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_QRST_COMMAND_SRC_1] = {4, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_QRST_DATA_READ] = {4, read_data, NULL},
> + [GPIO_QRST_INPUT_MASK] = {4, read_input_mask, _write_input_mask},
> + /* Set UVWX */
> + [GPIO_UVWX_DATA_VALUE] = {5, read_data_value, _write_data_value},
> + [GPIO_UWVX_DIRECTION] = {5, read_direction, _write_direction},
> + [GPIO_UVWX_INT_ENABLE] = {5, read_int_enable, _write_int_enable},
> + [GPIO_UVWX_INT_SENS_0] = {5, read_int_sens_0, _write_int_sens_0},
> + [GPIO_UVWX_INT_SENS_1] = {5, read_int_sens_1, _write_int_sens_1},
> + [GPIO_UVWX_INT_SENS_2] = {5, read_int_sens_2, _write_int_sens_2},
> + [GPIO_UVWX_INT_STATUS] = {5, read_int_status, _write_int_status},
> + [GPIO_UVWX_RESET_TOLERANT] = {5, read_reset_tol, _write_reset_tol},
> + [GPIO_UVWX_DEBOUNCE_1] = {5, read_debounce_1, _write_debounce_1},
> + [GPIO_UVWX_DEBOUNCE_2] = {5, read_debounce_2, _write_debounce_2},
> + [GPIO_UVWX_COMMAND_SRC_0] = {5, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_UVWX_COMMAND_SRC_1] = {5, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_UVWX_DATA_READ] = {5, read_data, NULL},
> + [GPIO_UVWX_INPUT_MASK] = {5, read_input_mask, _write_input_mask},
> + /* Set YZAAAB */
> + [GPIO_YZAAAB_DATA_VALUE] = {6, read_data_value, _write_data_value},
> + [GPIO_YZAAAB_DIRECTION] = {6, read_direction, _write_direction},
> + [GPIO_YZAAAB_INT_ENABLE] = {6, read_int_enable, _write_int_enable},
> + [GPIO_YZAAAB_INT_SENS_0] = {6, read_int_sens_0, _write_int_sens_0},
> + [GPIO_YZAAAB_INT_SENS_1] = {6, read_int_sens_1, _write_int_sens_1},
> + [GPIO_YZAAAB_INT_SENS_2] = {6, read_int_sens_2, _write_int_sens_2},
> + [GPIO_YZAAAB_INT_STATUS] = {6, read_int_status, _write_int_status},
> + [GPIO_YZAAAB_RESET_TOLERANT] = {6, read_reset_tol, _write_reset_tol},
> + [GPIO_YZAAAB_DEBOUNCE_1] = {6, read_debounce_1, _write_debounce_1},
> + [GPIO_YZAAAB_DEBOUNCE_2] = {6, read_debounce_2, _write_debounce_2},
> + [GPIO_YZAAAB_COMMAND_SRC_0] = {6, read_cmd_source_0,
> _write_cmd_source_0},
> + [GPIO_YZAAAB_COMMAND_SRC_1] = {6, read_cmd_source_1,
> _write_cmd_source_1},
> + [GPIO_YZAAAB_DATA_READ] = {6, read_data, NULL},
> + [GPIO_YZAAAB_INPUT_MASK] = {6, read_input_mask, _write_input_mask},
> + /* Set AC */
> + [GPIO_AC_DATA_VALUE] = {7, read_data_value, _write_data_value},
> + [GPIO_AC_DIRECTION] = {7, read_direction, _write_direction},
> + [GPIO_AC_INT_ENABLE] = {7, read_int_enable, _write_int_enable},
> + [GPIO_AC_INT_SENS_0] = {7, read_int_sens_0, _write_int_sens_0},
> + [GPIO_AC_INT_SENS_1] = {7, read_int_sens_1, _write_int_sens_1},
> + [GPIO_AC_INT_SENS_2] = {7, read_int_sens_2, _write_int_sens_2},
> + [GPIO_AC_INT_STATUS] = {7, read_int_status, _write_int_status},
> + [GPIO_AC_RESET_TOLERANT] = {7, read_reset_tol, _write_reset_tol},
> + [GPIO_AC_DEBOUNCE_1] = {7, read_debounce_1, _write_debounce_1},
> + [GPIO_AC_DEBOUNCE_2] = {7, read_debounce_2, _write_debounce_2},
> + [GPIO_AC_COMMAND_SRC_0] = {7, read_cmd_source_0, _write_cmd_source_0},
> + [GPIO_AC_COMMAND_SRC_1] = {7, read_cmd_source_1, _write_cmd_source_1},
> + [GPIO_AC_DATA_READ] = {7, read_data, NULL},
> + [GPIO_AC_INPUT_MASK] = {7, read_input_mask, _write_input_mask},
> + /* Debounce registers */
> + [GPIO_DEBOUNCE_TIME_1] = {-1, NULL, NULL},
> +};
> +
> +static uint64_t aspeed_gpio_read(void *opaque, hwaddr offset, uint32_t size)
> +{
> + AspeedGPIOState *s = ASPEED_GPIO(opaque);
> + uint32_t val = 0;
> +
> + if (size != 4) {
> + return 0;
> + }
> +
> + if (gpios[offset].get == NULL) {
> + qemu_log_mask(LOG_GUEST_ERROR, "no getter for offset %lx", offset);
> + return 0;
> + }
> +
> + val = gpios[offset].get(&s->sets[gpios[offset].set_idx]);
> + return (uint64_t) val;
> +}
> +
> +static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data,
> + uint32_t size)
> +{
> + AspeedGPIOState *s = ASPEED_GPIO(opaque);
> + GPIOSetProperties *props = &s->ctrl->props[gpios[offset].set_idx];
> +
> + if (gpios[offset].set == NULL) {
> + qemu_log_mask(LOG_GUEST_ERROR, "no setter for offset %lx", offset);
> + return;
> + }
> +
> + uint32_t mask = props->input | props->output;
> +
> + gpios[offset].set(s, &s->sets[gpios[offset].set_idx],
> + props, data & mask);
> +}
> +
> +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);
> + if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
> + qemu_log_mask(LOG_GUEST_ERROR, "error reading %s", name);
> + return;
> + }
> + level = aspeed_gpio_get_pin_level(s, 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);
> + visit_type_bool(v, name, &level, &local_err);
> + if (sscanf(name, "gpio%2[A-Z]%1d", group, &pin) != 2) {
> + qemu_log_mask(LOG_GUEST_ERROR, "error reading %s", name);
> + return;
> + }
> + aspeed_gpio_set_pin_level(s, pin, level);
> +}
> +
> +
> +/* Setup functions */
> +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)
> +{
> + struct 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);
> +
> + sysbus_init_irq(sbd, &s->irq);
> +
> + memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_gpio_ops, s,
> + TYPE_ASPEED_GPIO, 0x1000);
> +
> + sysbus_init_mmio(sbd, &s->iomem);
> +}
> +
> +static const VMStateDescription vmstate_gpio_regs = {
> + .name = TYPE_ASPEED_GPIO"/regs",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(data_value, GPIORegs),
> + VMSTATE_UINT32(data_read, GPIORegs),
> + VMSTATE_UINT32(direction, GPIORegs),
> + VMSTATE_UINT32(int_enable, GPIORegs),
> + VMSTATE_UINT32(int_sens_0, GPIORegs),
> + VMSTATE_UINT32(int_sens_1, GPIORegs),
> + VMSTATE_UINT32(int_sens_2, GPIORegs),
> + VMSTATE_UINT32(int_status, GPIORegs),
> + VMSTATE_UINT32(reset_tol, GPIORegs),
> + VMSTATE_UINT32(cmd_source_0, GPIORegs),
> + VMSTATE_UINT32(cmd_source_1, GPIORegs),
> + VMSTATE_UINT32(debounce_1, GPIORegs),
> + VMSTATE_UINT32(debounce_2, GPIORegs),
> + VMSTATE_UINT32(input_mask, GPIORegs),
> + 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, GPIORegs),
> + VMSTATE_END_OF_LIST(),
> + }
> +};
> +
> +static void aspeed_gpio_init(Object *obj)
> +{
> + AspeedGPIOState *s = ASPEED_GPIO(obj);
> + AspeedGPIOClass *agc = ASPEED_GPIO_GET_CLASS(s);
> + int pin;
> +
> + s->ctrl = agc->ctrl;
> +
> + for (pin = 0; pin < agc->ctrl->nr_gpio_pins; pin++) {
> + char *name;
> + int set_idx = aspeed_get_set_idx_from_pin(s, pin);
> + int _pin = aspeed_adjust_pin(s, pin);
> + int pin_idx = _pin - (set_idx * 32);
> + int group_idx = pin_idx >> 3;
> +
> + name = g_strdup_printf("gpio%s%d",
> + agc->ctrl->props[set_idx].set[group_idx],
> + pin_idx % 8);
> + object_property_add(obj, name, "bool",
> + aspeed_gpio_get_pin,
> + aspeed_gpio_set_pin,
> + NULL, NULL, NULL);
> + }
> +}
> +
> +static void aspeed_gpio_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass);
> +
> + dc->realize = aspeed_gpio_realize;
> + dc->reset = aspeed_gpio_reset;
> + dc->desc = "Aspeed GPIO Controller";
> + dc->vmsd = &vmstate_aspeed_gpio;
> + agc->ctrl = data;
> +}
> +
> +static 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 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 AspeedGPIOController controllers[] = {
> + {
> + .name = TYPE_ASPEED_GPIO "-ast2500",
> + .props = ast2500_set_props,
> + .nr_gpio_pins = 228,
> + .nr_gpio_sets = 8,
> + .gap = 220,
> + }, {
> + .name = TYPE_ASPEED_GPIO "-ast2400",
> + .props = ast2400_set_props,
> + .nr_gpio_pins = 216,
> + .nr_gpio_sets = 7,
> + .gap = 196,
> + }
> +};
> +
> +static const TypeInfo aspeed_gpio_info = {
> + .name = TYPE_ASPEED_GPIO,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(AspeedGPIOState),
> + .class_size = sizeof(AspeedGPIOClass),
> + .instance_init = aspeed_gpio_init,
> + .abstract = true,
> +};
> +
> +static void aspeed_gpio_register_types(void)
> +{
> + int i;
> +
> + type_register_static(&aspeed_gpio_info);
> + for (i = 0; i < ARRAY_SIZE(controllers); i++) {
> + TypeInfo t = {
> + .name = controllers[i].name,
> + .parent = TYPE_ASPEED_GPIO,
> + .class_init = aspeed_gpio_class_init,
> + .class_data = (void *)&controllers[i],
> + };
> + type_register(&t);
> + };
> +}
> +
> +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..97a84a5da1
> --- /dev/null
> +++ b/include/hw/gpio/aspeed_gpio.h
> @@ -0,0 +1,76 @@
> +/*
> + * 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
> +
> +typedef struct GPIORegs GPIORegs;
> +typedef const struct GPIOSetProperties {
> + uint32_t input;
> + uint32_t output;
> + char set[4][3];
> +} GPIOSetProperties;
> +
> +typedef const struct AspeedGPIOController {
> + const char *name;
> + GPIOSetProperties *props;
> + uint32_t nr_gpio_pins;
> + uint32_t nr_gpio_sets;
> + uint32_t gap;
> +} AspeedGPIOController;
> +
> +typedef struct AspeedGPIOClass {
> + SysBusDevice parent_obj;
> + AspeedGPIOController *ctrl;
> +} AspeedGPIOClass;
> +
> +typedef struct AspeedGPIOState {
> + /* <private> */
> + SysBusDevice parent;
> +
> + /*< public >*/
> + MemoryRegion iomem;
> + qemu_irq output[ASPEED_GPIO_NR_PINS];
> + qemu_irq irq;
> + AspeedGPIOController *ctrl;
> +
> +/* Parallel GPIO Registers */
> + struct GPIORegs {
> + 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_ */
>