Browse Source
* Removed the ast2700-a0 SoC and ast2700a0-evb machine. * Added SGPIO support to the ast2700 SoC, including unit tests. * Added several FRU EEPROMs to the Catalina board. * Added support for the new AST1060 SoC and ast1060-evb machine, including functional tests. * Fixed the silicon revision ID register for AST2600 and AST1030 SoCs. * Added an SFDP table for a Winbond flash chip. * Updated documentation for Aspeed boards. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmlbtGMACgkQUaNDx8/7 7KGtVBAAgiCDME1oXrc+ZVmYn8RtRsx/lakp4prbqRbs6A8qoITk3ao6WjNd2k3P kOkvB/xQCqRDSSJIpp8BQhUzDet80b8KUz16iTw1m7KubMIqapmOxVkGJqSc9kGT PfLVjB60rHqiCkInNaaMXxMQuY0iAFKS+SDM8mCGaay0MXdvsEIi+XIcxv2TX0Jo tnHMaJxtSKfzY3QiM7yeB+WryzOlzQ7IMqRkai9tCN1y3XyDy8PodelZFCIGXw/N tw04Z3q/LhmxT6kjvYLmeTETne+1k69gfME+7JhB1hPSrIgdIqM9qyXCROb0U8/W 5mJDkm84blHc4QWITdewiyhIGps9ITxqlIelCExPL1GpEubSg5BVkqFbIjMv+6HJ 2XcBbP+rG6IdEpxeT+PcHw5HvOfLjMY6kGrawpK4s2id5jI9GSSyNY5k576tqRzg P10GfeKUDNDj1nYhUfOmzL6qXb9TqIyQtOVJVGcHkaASnXIqFvL6rYQHcttFej08 YepKqvYBBSwsb2TdSb1t6VKTgCublFUD4jdhph3iqVmfQRl3ei8WJHFKCt8aQ235 HqXzcmBDoTFYqJ97SAg1bvydhVL+rDpd//gcPqVw2cwMnTa2S3zvgI6xj1oBTEf8 YiRPtFYJ03Y9baNZRdGdYAErxIdiliZh+OYfaEFNNn5Zuqp6tYQ= =64bG -----END PGP SIGNATURE----- Merge tag 'pull-aspeed-20260105' of https://github.com/legoater/qemu into staging aspeed queue: * Removed the ast2700-a0 SoC and ast2700a0-evb machine. * Added SGPIO support to the ast2700 SoC, including unit tests. * Added several FRU EEPROMs to the Catalina board. * Added support for the new AST1060 SoC and ast1060-evb machine, including functional tests. * Fixed the silicon revision ID register for AST2600 and AST1030 SoCs. * Added an SFDP table for a Winbond flash chip. * Updated documentation for Aspeed boards. # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmlbtGMACgkQUaNDx8/7 # 7KGtVBAAgiCDME1oXrc+ZVmYn8RtRsx/lakp4prbqRbs6A8qoITk3ao6WjNd2k3P # kOkvB/xQCqRDSSJIpp8BQhUzDet80b8KUz16iTw1m7KubMIqapmOxVkGJqSc9kGT # PfLVjB60rHqiCkInNaaMXxMQuY0iAFKS+SDM8mCGaay0MXdvsEIi+XIcxv2TX0Jo # tnHMaJxtSKfzY3QiM7yeB+WryzOlzQ7IMqRkai9tCN1y3XyDy8PodelZFCIGXw/N # tw04Z3q/LhmxT6kjvYLmeTETne+1k69gfME+7JhB1hPSrIgdIqM9qyXCROb0U8/W # 5mJDkm84blHc4QWITdewiyhIGps9ITxqlIelCExPL1GpEubSg5BVkqFbIjMv+6HJ # 2XcBbP+rG6IdEpxeT+PcHw5HvOfLjMY6kGrawpK4s2id5jI9GSSyNY5k576tqRzg # P10GfeKUDNDj1nYhUfOmzL6qXb9TqIyQtOVJVGcHkaASnXIqFvL6rYQHcttFej08 # YepKqvYBBSwsb2TdSb1t6VKTgCublFUD4jdhph3iqVmfQRl3ei8WJHFKCt8aQ235 # HqXzcmBDoTFYqJ97SAg1bvydhVL+rDpd//gcPqVw2cwMnTa2S3zvgI6xj1oBTEf8 # YiRPtFYJ03Y9baNZRdGdYAErxIdiliZh+OYfaEFNNn5Zuqp6tYQ= # =64bG # -----END PGP SIGNATURE----- # gpg: Signature made Mon 05 Jan 2026 11:53:55 PM AEDT # gpg: using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1 # gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full] # gpg: aka "Cédric Le Goater <clg@kaod.org>" [full] * tag 'pull-aspeed-20260105' of https://github.com/legoater/qemu: (36 commits) hw/i2c/aspeed: Fix wrong I2CC_DMA_LEN when I2CM_DMA_TX/RX_ADDR set first hw/intc/aspeed: Remove TSP 128 - 138 hw/intc/aspeed: Remove SSP 128 - 138 docs/specs/aspeed-intc: Remove GIC 128 - 136 hw/intc/aspeed: Remove GIC 128 - 136 hw/arm/aspeed_ast27x0: Remove ast2700-a0 SOC hw/arm: Remove ast2700a0-evb machine tests/functional: Fix URL of gb200nvl-bmc image test/qtest: Add Unit test for Aspeed SGPIO hw/arm/aspeed_ast27x0: Wire SGPIO controller to AST2700 SoC hw/arm/aspeed_soc: Update Aspeed SoC to support two SGPIO controllers hw/gpio/aspeed_sgpio: Implement SGPIO interrupt handling hw/gpio/aspeed_sgpio: Add QOM property accessors for SGPIO pins hw/gpio/aspeed_sgpio: Add basic device model for Aspeed SGPIO hw/arm/aspeed: catalina: add Cable Cartridge FRU EEPROM hw/arm/aspeed: catalina: add NIC FRU EEPROM hw/arm/aspeed: catalina: add HMC FRU EEPROM hw/arm/aspeed: catalina: add GB200-IO FRU EEPROM hw/arm/aspeed: catalina: add GB200 FRU EEPROM hw/arm/aspeed: catalina: add HDD FRU EEPROM ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>pull/316/head
27 changed files with 1403 additions and 504 deletions
@ -0,0 +1,346 @@ |
|||
/*
|
|||
* ASPEED Serial GPIO Controller |
|||
* |
|||
* Copyright 2025 Google LLC. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "qemu/host-utils.h" |
|||
#include "qemu/log.h" |
|||
#include "qemu/error-report.h" |
|||
#include "qapi/error.h" |
|||
#include "qapi/visitor.h" |
|||
#include "hw/core/irq.h" |
|||
#include "hw/core/qdev-properties.h" |
|||
#include "hw/gpio/aspeed_sgpio.h" |
|||
|
|||
/*
|
|||
* 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 |
|||
*/ |
|||
|
|||
/* 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 |
|||
|
|||
static void aspeed_clear_irq(AspeedSGPIOState *s, int idx) |
|||
{ |
|||
uint32_t reg_index = idx / 32; |
|||
uint32_t bit_index = idx % 32; |
|||
uint32_t pending = extract32(s->int_regs[reg_index], bit_index, 1); |
|||
|
|||
assert(s->pending >= pending); |
|||
|
|||
/* No change to s->pending if pending is 0 */ |
|||
s->pending -= pending; |
|||
|
|||
/*
|
|||
* The write acknowledged the interrupt regardless of whether it |
|||
* was pending or not. The post-condition is that it mustn't be |
|||
* pending. Unconditionally clear the status bit. |
|||
*/ |
|||
s->int_regs[reg_index] = deposit32(s->int_regs[reg_index], bit_index, 1, 0); |
|||
} |
|||
|
|||
static void aspeed_evaluate_irq(AspeedSGPIOState *s, int sgpio_prev_high, |
|||
int sgpio_curr_high, int idx) |
|||
{ |
|||
uint32_t ctrl = s->ctrl_regs[idx]; |
|||
uint32_t falling_edge = 0, rising_edge = 0; |
|||
uint32_t int_trigger = SHARED_FIELD_EX32(ctrl, SGPIO_INT_TYPE); |
|||
uint32_t int_enabled = SHARED_FIELD_EX32(ctrl, SGPIO_INT_EN); |
|||
uint32_t reg_index = idx / 32; |
|||
uint32_t bit_index = idx % 32; |
|||
|
|||
if (!int_enabled) { |
|||
return; |
|||
} |
|||
|
|||
/* Detect edges */ |
|||
if (sgpio_curr_high && !sgpio_prev_high) { |
|||
rising_edge = 1; |
|||
} else if (!sgpio_curr_high && sgpio_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) && !sgpio_curr_high) || |
|||
((int_trigger == ASPEED_LEVEL_HIGH) && sgpio_curr_high) || |
|||
((int_trigger >= ASPEED_DUAL_EDGE) && (rising_edge || falling_edge))) |
|||
{ |
|||
s->int_regs[reg_index] = deposit32(s->int_regs[reg_index], |
|||
bit_index, 1, 1); |
|||
/* Trigger the VIC IRQ */ |
|||
s->pending++; |
|||
} |
|||
} |
|||
|
|||
static void aspeed_sgpio_update(AspeedSGPIOState *s, uint32_t idx, |
|||
uint32_t value) |
|||
{ |
|||
uint32_t old = s->ctrl_regs[idx]; |
|||
uint32_t new = value; |
|||
uint32_t diff = (old ^ new); |
|||
if (diff) { |
|||
/* If the interrupt clear bit is set */ |
|||
if (SHARED_FIELD_EX32(new, SGPIO_INT_STATUS)) { |
|||
aspeed_clear_irq(s, idx); |
|||
/* Clear the interrupt clear bit */ |
|||
new &= ~SGPIO_INT_STATUS_MASK; |
|||
} |
|||
|
|||
/* Update the control register. */ |
|||
s->ctrl_regs[idx] = new; |
|||
|
|||
/* If the output value is changed */ |
|||
if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_OUT_VAL)) { |
|||
/* ...trigger the line-state IRQ */ |
|||
qemu_set_irq(s->sgpios[idx], 1); |
|||
} |
|||
|
|||
/* If the input value is changed */ |
|||
if (SHARED_FIELD_EX32(diff, SGPIO_SERIAL_IN_VAL)) { |
|||
aspeed_evaluate_irq(s, |
|||
SHARED_FIELD_EX32(old, SGPIO_SERIAL_IN_VAL), |
|||
SHARED_FIELD_EX32(new, SGPIO_SERIAL_IN_VAL), |
|||
idx); |
|||
} |
|||
} |
|||
qemu_set_irq(s->irq, !!(s->pending)); |
|||
} |
|||
|
|||
static uint64_t aspeed_sgpio_2700_read_int_status_reg(AspeedSGPIOState *s, |
|||
uint32_t reg) |
|||
{ |
|||
uint32_t idx = reg - R_SGPIO_INT_STATUS_0; |
|||
if (idx >= ASPEED_SGPIO_MAX_INT) { |
|||
qemu_log_mask(LOG_GUEST_ERROR, |
|||
"%s: interrupt status index: %d, out of bounds\n", |
|||
__func__, idx); |
|||
return 0; |
|||
} |
|||
return s->int_regs[idx]; |
|||
} |
|||
|
|||
static uint64_t aspeed_sgpio_2700_read_control_reg(AspeedSGPIOState *s, |
|||
uint32_t reg) |
|||
{ |
|||
AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s); |
|||
uint32_t idx = reg - R_SGPIO_0_CONTROL; |
|||
if (idx >= agc->nr_sgpio_pin_pairs) { |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: pin index: %d, out of bounds\n", |
|||
__func__, idx); |
|||
return 0; |
|||
} |
|||
return s->ctrl_regs[idx]; |
|||
} |
|||
|
|||
static void aspeed_sgpio_2700_write_control_reg(AspeedSGPIOState *s, |
|||
uint32_t reg, uint64_t data) |
|||
{ |
|||
AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s); |
|||
uint32_t idx = reg - R_SGPIO_0_CONTROL; |
|||
if (idx >= agc->nr_sgpio_pin_pairs) { |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: pin index: %d, out of bounds\n", |
|||
__func__, idx); |
|||
return; |
|||
} |
|||
aspeed_sgpio_update(s, idx, data); |
|||
} |
|||
|
|||
static uint64_t aspeed_sgpio_2700_read(void *opaque, hwaddr offset, |
|||
uint32_t size) |
|||
{ |
|||
AspeedSGPIOState *s = ASPEED_SGPIO(opaque); |
|||
uint64_t value = 0; |
|||
uint64_t reg; |
|||
|
|||
reg = offset >> 2; |
|||
|
|||
switch (reg) { |
|||
case R_SGPIO_INT_STATUS_0 ... R_SGPIO_INT_STATUS_7: |
|||
value = aspeed_sgpio_2700_read_int_status_reg(s, reg); |
|||
break; |
|||
case R_SGPIO_0_CONTROL ... R_SGPIO_255_CONTROL: |
|||
value = aspeed_sgpio_2700_read_control_reg(s, reg); |
|||
break; |
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: no getter for offset 0x%" |
|||
HWADDR_PRIx"\n", __func__, offset); |
|||
return 0; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
static void aspeed_sgpio_2700_write(void *opaque, hwaddr offset, uint64_t data, |
|||
uint32_t size) |
|||
{ |
|||
AspeedSGPIOState *s = ASPEED_SGPIO(opaque); |
|||
uint64_t reg; |
|||
|
|||
reg = offset >> 2; |
|||
|
|||
switch (reg) { |
|||
case R_SGPIO_0_CONTROL ... R_SGPIO_255_CONTROL: |
|||
aspeed_sgpio_2700_write_control_reg(s, reg, data); |
|||
break; |
|||
default: |
|||
qemu_log_mask(LOG_GUEST_ERROR, "%s: no setter for offset 0x%" |
|||
HWADDR_PRIx"\n", __func__, offset); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
static bool aspeed_sgpio_get_pin_level(AspeedSGPIOState *s, int pin) |
|||
{ |
|||
uint32_t value = s->ctrl_regs[pin >> 1]; |
|||
bool is_input = !(pin % 2); |
|||
uint32_t bit_mask = 0; |
|||
|
|||
if (is_input) { |
|||
bit_mask = SGPIO_SERIAL_IN_VAL_MASK; |
|||
} else { |
|||
bit_mask = SGPIO_SERIAL_OUT_VAL_MASK; |
|||
} |
|||
|
|||
return value & bit_mask; |
|||
} |
|||
|
|||
static void aspeed_sgpio_set_pin_level(AspeedSGPIOState *s, int pin, bool level) |
|||
{ |
|||
uint32_t value = s->ctrl_regs[pin >> 1]; |
|||
bool is_input = !(pin % 2); |
|||
uint32_t bit_mask = 0; |
|||
|
|||
if (is_input) { |
|||
bit_mask = SGPIO_SERIAL_IN_VAL_MASK; |
|||
} else { |
|||
bit_mask = SGPIO_SERIAL_OUT_VAL_MASK; |
|||
} |
|||
|
|||
if (level) { |
|||
value |= bit_mask; |
|||
} else { |
|||
value &= ~bit_mask; |
|||
} |
|||
aspeed_sgpio_update(s, pin >> 1, value); |
|||
} |
|||
|
|||
static void aspeed_sgpio_get_pin(Object *obj, Visitor *v, const char *name, |
|||
void *opaque, Error **errp) |
|||
{ |
|||
bool level = true; |
|||
int pin = 0xfff; |
|||
AspeedSGPIOState *s = ASPEED_SGPIO(obj); |
|||
|
|||
if (sscanf(name, "sgpio%03d", &pin) != 1) { |
|||
error_setg(errp, "%s: error reading %s", __func__, name); |
|||
return; |
|||
} |
|||
level = aspeed_sgpio_get_pin_level(s, pin); |
|||
visit_type_bool(v, name, &level, errp); |
|||
} |
|||
|
|||
static void aspeed_sgpio_set_pin(Object *obj, Visitor *v, const char *name, |
|||
void *opaque, Error **errp) |
|||
{ |
|||
bool level; |
|||
int pin = 0xfff; |
|||
AspeedSGPIOState *s = ASPEED_SGPIO(obj); |
|||
|
|||
if (!visit_type_bool(v, name, &level, errp)) { |
|||
return; |
|||
} |
|||
if (sscanf(name, "sgpio%03d", &pin) != 1) { |
|||
error_setg(errp, "%s: error reading %s", __func__, name); |
|||
return; |
|||
} |
|||
aspeed_sgpio_set_pin_level(s, pin, level); |
|||
} |
|||
|
|||
static const MemoryRegionOps aspeed_sgpio_2700_ops = { |
|||
.read = aspeed_sgpio_2700_read, |
|||
.write = aspeed_sgpio_2700_write, |
|||
.endianness = DEVICE_LITTLE_ENDIAN, |
|||
.valid.min_access_size = 4, |
|||
.valid.max_access_size = 4, |
|||
}; |
|||
|
|||
static void aspeed_sgpio_realize(DeviceState *dev, Error **errp) |
|||
{ |
|||
AspeedSGPIOState *s = ASPEED_SGPIO(dev); |
|||
SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
|||
AspeedSGPIOClass *agc = ASPEED_SGPIO_GET_CLASS(s); |
|||
|
|||
/* Interrupt parent line */ |
|||
sysbus_init_irq(sbd, &s->irq); |
|||
|
|||
memory_region_init_io(&s->iomem, OBJECT(s), agc->reg_ops, s, |
|||
TYPE_ASPEED_SGPIO, agc->mem_size); |
|||
|
|||
sysbus_init_mmio(sbd, &s->iomem); |
|||
} |
|||
|
|||
static void aspeed_sgpio_init(Object *obj) |
|||
{ |
|||
for (int i = 0; i < ASPEED_SGPIO_MAX_PIN_PAIR * 2; i++) { |
|||
g_autofree char *name = g_strdup_printf("sgpio%03d", i); |
|||
object_property_add(obj, name, "bool", aspeed_sgpio_get_pin, |
|||
aspeed_sgpio_set_pin, NULL, NULL); |
|||
} |
|||
} |
|||
|
|||
static void aspeed_sgpio_class_init(ObjectClass *klass, const void *data) |
|||
{ |
|||
DeviceClass *dc = DEVICE_CLASS(klass); |
|||
|
|||
dc->realize = aspeed_sgpio_realize; |
|||
dc->desc = "Aspeed SGPIO Controller"; |
|||
} |
|||
|
|||
static void aspeed_sgpio_2700_class_init(ObjectClass *klass, const void *data) |
|||
{ |
|||
AspeedSGPIOClass *agc = ASPEED_SGPIO_CLASS(klass); |
|||
agc->nr_sgpio_pin_pairs = ASPEED_SGPIO_MAX_PIN_PAIR; |
|||
agc->mem_size = 0x1000; |
|||
agc->reg_ops = &aspeed_sgpio_2700_ops; |
|||
} |
|||
|
|||
static const TypeInfo aspeed_sgpio_info = { |
|||
.name = TYPE_ASPEED_SGPIO, |
|||
.parent = TYPE_SYS_BUS_DEVICE, |
|||
.instance_size = sizeof(AspeedSGPIOState), |
|||
.class_size = sizeof(AspeedSGPIOClass), |
|||
.class_init = aspeed_sgpio_class_init, |
|||
.abstract = true, |
|||
}; |
|||
|
|||
static const TypeInfo aspeed_sgpio_ast2700_info = { |
|||
.name = TYPE_ASPEED_SGPIO "-ast2700", |
|||
.parent = TYPE_ASPEED_SGPIO, |
|||
.class_init = aspeed_sgpio_2700_class_init, |
|||
.instance_init = aspeed_sgpio_init, |
|||
}; |
|||
|
|||
static void aspeed_sgpio_register_types(void) |
|||
{ |
|||
type_register_static(&aspeed_sgpio_info); |
|||
type_register_static(&aspeed_sgpio_ast2700_info); |
|||
} |
|||
|
|||
type_init(aspeed_sgpio_register_types); |
|||
@ -0,0 +1,68 @@ |
|||
/*
|
|||
* ASPEED Serial GPIO Controller |
|||
* |
|||
* Copyright 2025 Google LLC. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
*/ |
|||
#ifndef ASPEED_SGPIO_H |
|||
#define ASPEED_SGPIO_H |
|||
|
|||
#include "hw/core/sysbus.h" |
|||
#include "qom/object.h" |
|||
#include "hw/core/registerfields.h" |
|||
|
|||
#define TYPE_ASPEED_SGPIO "aspeed.sgpio" |
|||
OBJECT_DECLARE_TYPE(AspeedSGPIOState, AspeedSGPIOClass, ASPEED_SGPIO) |
|||
|
|||
#define ASPEED_SGPIO_MAX_PIN_PAIR 256 |
|||
#define ASPEED_SGPIO_MAX_INT 8 |
|||
|
|||
/* AST2700 SGPIO Register Address Offsets */ |
|||
REG32(SGPIO_INT_STATUS_0, 0x40) |
|||
REG32(SGPIO_INT_STATUS_1, 0x44) |
|||
REG32(SGPIO_INT_STATUS_2, 0x48) |
|||
REG32(SGPIO_INT_STATUS_3, 0x4C) |
|||
REG32(SGPIO_INT_STATUS_4, 0x50) |
|||
REG32(SGPIO_INT_STATUS_5, 0x54) |
|||
REG32(SGPIO_INT_STATUS_6, 0x58) |
|||
REG32(SGPIO_INT_STATUS_7, 0x5C) |
|||
/* AST2700 SGPIO_0 - SGPIO_255 Control Register */ |
|||
REG32(SGPIO_0_CONTROL, 0x80) |
|||
SHARED_FIELD(SGPIO_SERIAL_OUT_VAL, 0, 1) |
|||
SHARED_FIELD(SGPIO_PARALLEL_OUT_VAL, 1, 1) |
|||
SHARED_FIELD(SGPIO_INT_EN, 2, 1) |
|||
SHARED_FIELD(SGPIO_INT_TYPE, 3, 3) |
|||
SHARED_FIELD(SGPIO_RESET_POLARITY, 6, 1) |
|||
SHARED_FIELD(SGPIO_RESERVED_1, 7, 2) |
|||
SHARED_FIELD(SGPIO_INPUT_MASK, 9, 1) |
|||
SHARED_FIELD(SGPIO_PARALLEL_EN, 10, 1) |
|||
SHARED_FIELD(SGPIO_PARALLEL_IN_MODE, 11, 1) |
|||
SHARED_FIELD(SGPIO_INT_STATUS, 12, 1) |
|||
SHARED_FIELD(SGPIO_SERIAL_IN_VAL, 13, 1) |
|||
SHARED_FIELD(SGPIO_PARALLEL_IN_VAL, 14, 1) |
|||
SHARED_FIELD(SGPIO_RESERVED_2, 15, 12) |
|||
SHARED_FIELD(SGPIO_WRITE_PROTECT, 31, 1) |
|||
REG32(SGPIO_255_CONTROL, 0x47C) |
|||
|
|||
struct AspeedSGPIOClass { |
|||
SysBusDeviceClass parent_class; |
|||
uint32_t nr_sgpio_pin_pairs; |
|||
uint64_t mem_size; |
|||
const MemoryRegionOps *reg_ops; |
|||
}; |
|||
|
|||
struct AspeedSGPIOState { |
|||
/* <private> */ |
|||
SysBusDevice parent; |
|||
|
|||
/*< public >*/ |
|||
MemoryRegion iomem; |
|||
int pending; |
|||
qemu_irq irq; |
|||
qemu_irq sgpios[ASPEED_SGPIO_MAX_PIN_PAIR]; |
|||
uint32_t ctrl_regs[ASPEED_SGPIO_MAX_PIN_PAIR]; |
|||
uint32_t int_regs[ASPEED_SGPIO_MAX_INT]; |
|||
}; |
|||
|
|||
#endif /* ASPEED_SGPIO_H */ |
|||
@ -0,0 +1,52 @@ |
|||
#!/usr/bin/env python3 |
|||
# |
|||
# Functional test that boots the ASPEED SoCs with firmware |
|||
# |
|||
# Copyright (C) 2025 ASPEED Technology Inc |
|||
# |
|||
# SPDX-License-Identifier: GPL-2.0-or-later |
|||
|
|||
from aspeed import AspeedTest |
|||
from qemu_test import Asset, exec_command_and_wait_for_pattern |
|||
|
|||
|
|||
class AST1060Machine(AspeedTest): |
|||
ASSET_ASPEED_AST1060_PROT_3_02 = Asset( |
|||
('https://github.com/AspeedTech-BMC' |
|||
'/aspeed-zephyr-project/releases/download/v03.02' |
|||
'/ast1060_prot_v03.02.tgz'), |
|||
'dd5f1adc935316ddd1906506a02e15567bd7290657b52320f1a225564cc175bd') |
|||
|
|||
def test_arm_ast1060_prot_3_02(self): |
|||
self.set_machine('ast1060-evb') |
|||
|
|||
kernel_name = "ast1060_prot/zephyr.bin" |
|||
kernel_file = self.archive_extract( |
|||
self.ASSET_ASPEED_AST1060_PROT_3_02, member=kernel_name) |
|||
|
|||
self.vm.set_console() |
|||
self.vm.add_args('-kernel', kernel_file, '-nographic') |
|||
self.vm.launch() |
|||
self.wait_for_console_pattern("Booting Zephyr OS") |
|||
exec_command_and_wait_for_pattern(self, "help", |
|||
"Available commands") |
|||
|
|||
def test_arm_ast1060_otp_blockdev_device(self): |
|||
self.vm.set_machine("ast1060-evb") |
|||
|
|||
kernel_name = "ast1060_prot/zephyr.bin" |
|||
kernel_file = self.archive_extract(self.ASSET_ASPEED_AST1060_PROT_3_02, |
|||
member=kernel_name) |
|||
otp_img = self.generate_otpmem_image() |
|||
|
|||
self.vm.set_console() |
|||
self.vm.add_args( |
|||
"-kernel", kernel_file, |
|||
"-blockdev", f"driver=file,filename={otp_img},node-name=otp", |
|||
"-global", "aspeed-otp.drive=otp", |
|||
) |
|||
self.vm.launch() |
|||
self.wait_for_console_pattern("Booting Zephyr OS") |
|||
|
|||
if __name__ == '__main__': |
|||
AspeedTest.main() |
|||
@ -0,0 +1,165 @@ |
|||
/*
|
|||
* QTest testcase for the ASPEED AST2700 SGPIO Controller. |
|||
* |
|||
* SPDX-License-Identifier: GPL-2.0-or-later |
|||
* Copyright (C) 2025 Google LLC. |
|||
*/ |
|||
|
|||
#include "qemu/osdep.h" |
|||
#include "qemu/bitops.h" |
|||
#include "qobject/qdict.h" |
|||
#include "libqtest-single.h" |
|||
#include "hw/core/registerfields.h" |
|||
#include "hw/gpio/aspeed_sgpio.h" |
|||
|
|||
#define AST2700_SGPIO0_BASE 0x14C0C000 |
|||
#define AST2700_SGPIO1_BASE 0x14C0D000 |
|||
|
|||
static void test_output_pins(const char *machine, const uint32_t base, int idx) |
|||
{ |
|||
QTestState *s = qtest_init(machine); |
|||
char name[16]; |
|||
char qom_path[64]; |
|||
uint32_t offset = 0; |
|||
uint32_t value = 0; |
|||
for (int i = 0; i < ASPEED_SGPIO_MAX_PIN_PAIR; i++) { |
|||
/* Odd index is output port */ |
|||
sprintf(name, "sgpio%03d", i * 2 + 1); |
|||
sprintf(qom_path, "/machine/soc/sgpio[%d]", idx); |
|||
offset = base + (R_SGPIO_0_CONTROL + i) * 4; |
|||
/* set serial output */ |
|||
qtest_writel(s, offset, 0x00000001); |
|||
value = qtest_readl(s, offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_OUT_VAL), ==, 1); |
|||
g_assert_cmphex(qtest_qom_get_bool(s, qom_path, name), ==, true); |
|||
|
|||
/* clear serial output */ |
|||
qtest_writel(s, offset, 0x00000000); |
|||
value = qtest_readl(s, offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_OUT_VAL), ==, 0); |
|||
g_assert_cmphex(qtest_qom_get_bool(s, qom_path, name), ==, false); |
|||
} |
|||
qtest_quit(s); |
|||
} |
|||
|
|||
static void test_input_pins(const char *machine, const uint32_t base, int idx) |
|||
{ |
|||
QTestState *s = qtest_init(machine); |
|||
char name[16]; |
|||
char qom_path[64]; |
|||
uint32_t offset = 0; |
|||
uint32_t value = 0; |
|||
for (int i = 0; i < ASPEED_SGPIO_MAX_PIN_PAIR; i++) { |
|||
/* Even index is input port */ |
|||
sprintf(name, "sgpio%03d", i * 2); |
|||
sprintf(qom_path, "/machine/soc/sgpio[%d]", idx); |
|||
offset = base + (R_SGPIO_0_CONTROL + i) * 4; |
|||
/* set serial input */ |
|||
qtest_qom_set_bool(s, qom_path, name, true); |
|||
value = qtest_readl(s, offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_IN_VAL), ==, 1); |
|||
g_assert_cmphex(qtest_qom_get_bool(s, qom_path, name), ==, true); |
|||
|
|||
/* clear serial input */ |
|||
qtest_qom_set_bool(s, qom_path, name, false); |
|||
value = qtest_readl(s, offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_IN_VAL), ==, 0); |
|||
g_assert_cmphex(qtest_qom_get_bool(s, qom_path, name), ==, false); |
|||
} |
|||
qtest_quit(s); |
|||
} |
|||
|
|||
static void test_irq_level_high(const char *machine, |
|||
const uint32_t base, int idx) |
|||
{ |
|||
QTestState *s = qtest_init(machine); |
|||
char name[16]; |
|||
char qom_path[64]; |
|||
uint32_t ctrl_offset = 0; |
|||
uint32_t int_offset = 0; |
|||
uint32_t int_reg_idx = 0; |
|||
uint32_t int_bit_idx = 0; |
|||
uint32_t value = 0; |
|||
for (int i = 0; i < ASPEED_SGPIO_MAX_PIN_PAIR; i++) { |
|||
/* Even index is input port */ |
|||
sprintf(name, "sgpio%03d", i * 2); |
|||
sprintf(qom_path, "/machine/soc/sgpio[%d]", idx); |
|||
int_reg_idx = i / 32; |
|||
int_bit_idx = i % 32; |
|||
int_offset = base + (R_SGPIO_INT_STATUS_0 + int_reg_idx) * 4; |
|||
ctrl_offset = base + (R_SGPIO_0_CONTROL + i) * 4; |
|||
|
|||
/* Enable the interrupt */ |
|||
value = SHARED_FIELD_DP32(value, SGPIO_INT_EN, 1); |
|||
qtest_writel(s, ctrl_offset, value); |
|||
|
|||
/* Set the interrupt type to level-high trigger */ |
|||
value = SHARED_FIELD_DP32(qtest_readl(s, ctrl_offset), |
|||
SGPIO_INT_TYPE, 3); |
|||
qtest_writel(s, ctrl_offset, value); |
|||
|
|||
/* Set serial input high */ |
|||
qtest_qom_set_bool(s, qom_path, name, true); |
|||
value = qtest_readl(s, ctrl_offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_IN_VAL), ==, 1); |
|||
|
|||
/* Interrupt status is set */ |
|||
value = qtest_readl(s, int_offset); |
|||
g_assert_cmphex(extract32(value, int_bit_idx, 1), ==, 1); |
|||
|
|||
/* Clear Interrupt */ |
|||
value = SHARED_FIELD_DP32(qtest_readl(s, ctrl_offset), |
|||
SGPIO_INT_STATUS, 1); |
|||
qtest_writel(s, ctrl_offset, value); |
|||
value = qtest_readl(s, int_offset); |
|||
g_assert_cmphex(extract32(value, int_bit_idx, 1), ==, 0); |
|||
|
|||
/* Clear serial input */ |
|||
qtest_qom_set_bool(s, qom_path, name, false); |
|||
value = qtest_readl(s, ctrl_offset); |
|||
g_assert_cmphex(SHARED_FIELD_EX32(value, SGPIO_SERIAL_IN_VAL), ==, 0); |
|||
} |
|||
qtest_quit(s); |
|||
} |
|||
|
|||
static void test_ast_2700_sgpio_input(void) |
|||
{ |
|||
test_input_pins("-machine ast2700-evb", |
|||
AST2700_SGPIO0_BASE, 0); |
|||
test_input_pins("-machine ast2700-evb", |
|||
AST2700_SGPIO1_BASE, 1); |
|||
} |
|||
|
|||
static void test_ast_2700_sgpio_output(void) |
|||
{ |
|||
test_output_pins("-machine ast2700-evb", |
|||
AST2700_SGPIO0_BASE, 0); |
|||
test_output_pins("-machine ast2700-evb", |
|||
AST2700_SGPIO1_BASE, 1); |
|||
test_irq_level_high("-machine ast2700-evb", |
|||
AST2700_SGPIO0_BASE, 0); |
|||
test_irq_level_high("-machine ast2700-evb", |
|||
AST2700_SGPIO1_BASE, 1); |
|||
} |
|||
|
|||
static void test_ast_2700_sgpio_irq(void) |
|||
{ |
|||
test_irq_level_high("-machine ast2700-evb", |
|||
AST2700_SGPIO0_BASE, 0); |
|||
test_irq_level_high("-machine ast2700-evb", |
|||
AST2700_SGPIO1_BASE, 1); |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
g_test_init(&argc, &argv, NULL); |
|||
|
|||
qtest_add_func("/ast2700/sgpio/ast_2700_sgpio_input", |
|||
test_ast_2700_sgpio_input); |
|||
qtest_add_func("/ast2700/sgpio/ast_2700_sgpio_output", |
|||
test_ast_2700_sgpio_output); |
|||
qtest_add_func("/ast2700/sgpio/ast_2700_sgpio_irq", |
|||
test_ast_2700_sgpio_irq); |
|||
|
|||
return g_test_run(); |
|||
} |
|||
Loading…
Reference in new issue