Re: [PATCH v3 1/2] hw/gpio: add K230 GPIO controller model
Bin Meng <[email protected]>
| Newsgroups | org.nongnu.qemu-riscv,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <CAEUhbmWjR3cc4jkdBAqpXcmt9z3ExHWsPzCthX7d+G1wgqTbTQ@mail.gmail.com> |
On Mon, Aug 10, 2026 at 4:02 AM guochun wang <[email protected]> wrote: > > > Implement a K230 SoC APB GPIO controller model for QEMU, derived from > the Synopsys DesignWare APB GPIO (the Linux gpio-k230 driver is based > on gpio-dwapb.c and shares the same register layout), with a > Canaan-specific compatible ("canaan,k230-apb-gpio") and hardlock > integration. The model is capable of running the Linux gpio-k230 driver > and driving external input/output lines. Behaviour follows the K230 > Technical Reference Manual v0.3.1. Please split the k230_gpio model into 2 separate models: - One is generic Synopsys DesignWare GPIO IP model - Optional Canann K230 specific wrapper glue model to the dw-gpio IP (if the generic Synopsys DesignWare GPIO IP model, or plus some unimp region, is good enough to make the software happy, no need to create this one) > > Implemented: > - Port A registers: SWPORTA_DR/DDR/CTL, EXT_PORTA, INTEN, INTMASK, > INTTYPE_LEVEL, INT_POLARITY, INTSTATUS, RAW_INTSTATUS, DEBOUNCE, > LS_SYNC, INT_BOTHEDGE, PORTA_EOI, ID_CODE, VER_ID_CODE, > CONFIG_REG1/2 > - Software control mode: DR drives output pad, DDR selects direction, > EXT_PORTA multiplexes external input vs. DR by DDR > - Per-pin IRQ output lines (single interrupt scheme), 32 per group > - Edge detection: rising / falling / both-edge via INT_BOTHEDGE > - Level-sensitive interrupts: active-high / active-low > - PORTA_EOI clears edge interrupts only; level interrupts cleared by > source removal or INTMASK > - INTMASK masking and INTEN gating > - DDR/CTL mode switching: no new interrupts while output/hardware mode, > pending interrupts preserved; on switch back to input/software mode, > level interrupts re-evaluated against current ext level, edge left > untouched, per TRM v0.3.1 > - Three-phase Resettable API (enter/hold); VMState migration > > Not implemented (no observable effect under QEMU): > - Hardware control mode (aux_porta_out/en/in signals, single-ctl params) > - Debounce logic (dbclk, glitch filtering, both-edge debounce timing) > - Combined interrupt output (gpio_intr_flag OR of all lines) > - Clock-domain synchronization (pclk_intr, ls_sync metastability > registers, gpio_intrclk_en output) > - Config-time parameters GPIO_PA_SYNC_EXT_DATA / GPIO_PA_SYNC_INTERRUPTS > / GPIO_INT_BOTH_EDGE / GPIO_PORTX_SINGLE_CTL > > Two controllers are instantiated in the K230 SoC (gpio0 at 0x9140B000, > gpio1 at 0x9140C000), replacing the previous unimplemented-device > placeholders. Each controller exposes 32 per-pin IRQ lines connected > to the PLIC: > - GPIO0: PLIC sources 32..63 > - GPIO1: PLIC sources 64..95 Please split the integration of the GPIO device to the K230 machine out of this patch. > > Signed-off-by: guochun wang <[email protected]> > --- > MAINTAINERS | 2 + > hw/gpio/Kconfig | 3 + > hw/gpio/k230_gpio.c | 362 ++++++++++++++++++++++++++++++++++++ > hw/gpio/meson.build | 1 + > hw/riscv/Kconfig | 1 + > hw/riscv/k230.c | 27 ++- > include/hw/gpio/k230_gpio.h | 71 +++++++ > include/hw/riscv/k230.h | 5 + > 8 files changed, 466 insertions(+), 6 deletions(-) > create mode 100644 hw/gpio/k230_gpio.c > create mode 100644 include/hw/gpio/k230_gpio.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 902db77218..4301d29f7e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1827,8 +1827,10 @@ M: Chao Liu <[email protected]> > L: [email protected] > S: Maintained > F: docs/system/riscv/k230.rst > +F: hw/gpio/k230_gpio.c > F: hw/riscv/k230.c > F: hw/watchdog/k230_wdt.c > +F: include/hw/gpio/k230_gpio.h > F: include/hw/riscv/k230.h > F: include/hw/watchdog/k230_wdt.h > F: tests/functional/riscv64/test_k230.py > diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig > index a209294c20..2d37a8bb6f 100644 > --- a/hw/gpio/Kconfig > +++ b/hw/gpio/Kconfig > @@ -16,6 +16,9 @@ config SIFIVE_GPIO > config STM32L4X5_GPIO > bool > > +config K230_GPIO > + bool > + > config PCA9552 > bool > depends on I2C > diff --git a/hw/gpio/k230_gpio.c b/hw/gpio/k230_gpio.c > new file mode 100644 > index 0000000000..9d27f0bbfd > --- /dev/null > +++ b/hw/gpio/k230_gpio.c > @@ -0,0 +1,362 @@ > +/* > + * QEMU K230 GPIO Controller > + * > + * Copyright (c) 2025 Wang Guochun <[email protected]> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * K230 Technical Reference Manual V0.3.1 (2024-11-18), section 12.5 GPIO > + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf > + */ > + > +#include "qemu/osdep.h" > +#include "hw/gpio/k230_gpio.h" > +#include "hw/core/irq.h" > +#include "migration/vmstate.h" > +#include "qemu/log.h" > +#include "qemu/module.h" > + > +static void k230_gpio_update_int(K230GPIOState *s) > +{ > + uint32_t masked_status = s->raw_intstatus & ~s->intmask & s->inten; > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + if (s->irq[i]) { > + qemu_set_irq(s->irq[i], extract32(masked_status, i, 1)); > + } > + } > +} > + > +static void k230_gpio_set_int_line(K230GPIOState *s, int line, int level) > +{ > + if (line >= K230_GPIO_PINS_PER_GROUP) { > + return; > + } > + > + uint32_t prev_level = extract32(s->ext_porta, line, 1); > + uint32_t curr_level = level; > + > + s->ext_porta = deposit32(s->ext_porta, line, 1, curr_level); > + > + if (extract32(s->swporta_ddr, line, 1)) { > + return; > + } > + > + if (extract32(s->swporta_ctl, line, 1)) { > + return; > + } > + > + uint32_t pol = extract32(s->int_polarity, line, 1); > + uint32_t both = extract32(s->int_bothedge, line, 1); > + uint32_t is_level = !extract32(s->inttype_level, line, 1); > + > + if (both) { > + if (prev_level != curr_level) { > + s->raw_intstatus |= (1U << line); > + } > + } else if (is_level) { > + if (curr_level == pol) { > + s->raw_intstatus |= (1U << line); > + } else { > + s->raw_intstatus &= ~(1U << line); > + } > + } else { > + if (prev_level != curr_level && curr_level == pol) { > + s->raw_intstatus |= (1U << line); > + } > + } > +} > + > +static void k230_gpio_set(void *opaque, int line, int level) > +{ > + K230GPIOState *s = K230_GPIO(opaque); > + > + k230_gpio_set_int_line(s, line, level); > + k230_gpio_update_int(s); > +} > + > +static void k230_gpio_set_all_output_lines(K230GPIOState *s) > +{ > + int i; > + > + for (i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + if (extract32(s->swporta_ddr, i, 1) && s->output[i]) { This should check the CTL bit too, like: if (extract32(s->swporta_ddr, i, 1) && !extract32(s->swporta_ctl, i, 1) && s->output[i]) > + qemu_set_irq(s->output[i], extract32(s->swporta_dr, i, 1)); > + } > + } > +} > + > +static uint64_t k230_gpio_read(void *opaque, hwaddr offset, unsigned size) > +{ > + K230GPIOState *s = K230_GPIO(opaque); > + > + switch (offset) { > + case K230_GPIO_SWPORTA_DR: > + return s->swporta_dr; > + case K230_GPIO_SWPORTA_DDR: > + return s->swporta_ddr; > + case K230_GPIO_SWPORTA_CTL: > + return s->swporta_ctl; > + case K230_GPIO_INTEN: > + return s->inten; > + case K230_GPIO_INTMASK: > + return s->intmask; > + case K230_GPIO_INTTYPE_LEVEL: > + return s->inttype_level; > + case K230_GPIO_INT_POLARITY: > + return s->int_polarity; > + case K230_GPIO_INTSTATUS: > + return s->raw_intstatus & ~s->intmask; > + case K230_GPIO_RAW_INTSTATUS: > + return s->raw_intstatus; > + case K230_GPIO_DEBOUNCE: > + return s->debounce; > + case K230_GPIO_EXT_PORTA: > + return (s->ext_porta & ~s->swporta_ddr) | > + (s->swporta_dr & s->swporta_ddr); > + case K230_GPIO_LS_SYNC: > + return s->ls_sync; Bits 1:31 are reserved and should read as zero. > + case K230_GPIO_ID_CODE: > + return s->id_code; > + case K230_GPIO_INT_BOTHEDGE: > + return s->int_bothedge; > + case K230_GPIO_VER_ID_CODE: > + return s->ver_id_code; > + case K230_GPIO_CONFIG_REG2: > + return s->config_reg2; > + case K230_GPIO_CONFIG_REG1: > + return s->config_reg1; > + case K230_GPIO_PORTA_EOI: > + return 0; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" > + HWADDR_PRIx "\n", TYPE_K230_GPIO, __func__, offset); > + return 0; > + } > +} > + > +static void k230_gpio_write(void *opaque, hwaddr offset, uint64_t value, > + unsigned size) > +{ > + K230GPIOState *s = K230_GPIO(opaque); > + uint32_t curr_level, pol; > + > + switch (offset) { > + case K230_GPIO_SWPORTA_DR: > + s->swporta_dr = value; > + k230_gpio_set_all_output_lines(s); > + break; > + case K230_GPIO_SWPORTA_DDR: { > + uint32_t prev_ddr = s->swporta_ddr; > + s->swporta_ddr = value; > + k230_gpio_set_all_output_lines(s); > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + /* Skip pins whose direction did not change */ > + if (extract32(prev_ddr, i, 1) == extract32(s->swporta_ddr, i, 1)) { > + continue; > + } > + /* > + * When switching back to input mode (and still in software > + * control), re-evaluate level-sensitive interrupts. Edge > + * interrupts are left untouched, matching the manual TRM v0.3.1. > + */ > + if (extract32(s->swporta_ddr, i, 1) || > + extract32(s->swporta_ctl, i, 1) || > + extract32(s->inttype_level, i, 1)) { Should we test s->int_bothedge here? e.g.: (extract32(s->inttype_level, i, 1) || extract32(s->int_bothedge, i, 1)) > + continue; > + } > + > + curr_level = extract32(s->ext_porta, i, 1); > + pol = extract32(s->int_polarity, i, 1); > + if (curr_level == pol) { > + s->raw_intstatus |= (1U << i); > + } else { > + s->raw_intstatus &= ~(1U << i); > + } > + } > + k230_gpio_update_int(s); > + break; > + } > + case K230_GPIO_SWPORTA_CTL: { > + uint32_t prev_ctl = s->swporta_ctl; > + s->swporta_ctl = value; > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + /* Skip pins whose control mode did not change */ > + if (extract32(prev_ctl, i, 1) == extract32(s->swporta_ctl, i, 1)) { > + continue; > + } > + /* > + * When switching back from hardware to software control (and DDR > + * is input), re-evaluate level-sensitive interrupts. Edge > + * interrupts are left untouched, matching the manual TRM v0.3.1. > + */ > + if (extract32(s->swporta_ddr, i, 1) || > + extract32(s->swporta_ctl, i, 1) || > + extract32(s->inttype_level, i, 1)) { Should we test s->int_bothedge here? e.g.: (extract32(s->inttype_level, i, 1) || extract32(s->int_bothedge, i, 1)) > + continue; > + } > + > + curr_level = extract32(s->ext_porta, i, 1); > + pol = extract32(s->int_polarity, i, 1); > + if (curr_level == pol) { > + s->raw_intstatus |= (1U << i); > + } else { > + s->raw_intstatus &= ~(1U << i); > + } > + } > + k230_gpio_update_int(s); > + break; > + } > + case K230_GPIO_INTEN: > + s->inten = value; > + k230_gpio_update_int(s); > + break; > + case K230_GPIO_INTMASK: > + s->intmask = value; > + k230_gpio_update_int(s); > + break; > + case K230_GPIO_INTTYPE_LEVEL: > + s->inttype_level = value; > + break; > + case K230_GPIO_INT_POLARITY: > + s->int_polarity = value; > + break; > + case K230_GPIO_DEBOUNCE: > + s->debounce = value; > + break; > + case K230_GPIO_PORTA_EOI: > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + if (extract32(value, i, 1) && extract32(s->inttype_level, i, 1)) { Should we test s->int_bothedge here? e.g.: (extract32(s->inttype_level, i, 1) || extract32(s->int_bothedge, i, 1)) > + s->raw_intstatus = deposit32(s->raw_intstatus, i, 1, 0); > + } > + } > + k230_gpio_update_int(s); > + break; > + case K230_GPIO_LS_SYNC: > + s->ls_sync = value; Only bit 0 is writable. > + break; > + case K230_GPIO_ID_CODE: > + break; > + case K230_GPIO_INT_BOTHEDGE: > + s->int_bothedge = value; > + break; > + case K230_GPIO_VER_ID_CODE: > + break; > + case K230_GPIO_CONFIG_REG2: > + break; > + case K230_GPIO_CONFIG_REG1: > + break; > + case K230_GPIO_INTSTATUS: > + break; > + case K230_GPIO_RAW_INTSTATUS: > + break; > + case K230_GPIO_EXT_PORTA: > + break; > + default: > + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%" > + HWADDR_PRIx "\n", TYPE_K230_GPIO, __func__, offset); > + break; > + } > +} > + > +static const MemoryRegionOps k230_gpio_ops = { > + .read = k230_gpio_read, > + .write = k230_gpio_write, > + .valid.min_access_size = 4, > + .valid.max_access_size = 4, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; > + > +static const VMStateDescription vmstate_k230_gpio = { > + .name = TYPE_K230_GPIO, > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (const VMStateField[]) { > + VMSTATE_UINT32(swporta_dr, K230GPIOState), > + VMSTATE_UINT32(swporta_ddr, K230GPIOState), > + VMSTATE_UINT32(swporta_ctl, K230GPIOState), > + VMSTATE_UINT32(inten, K230GPIOState), > + VMSTATE_UINT32(intmask, K230GPIOState), > + VMSTATE_UINT32(inttype_level, K230GPIOState), > + VMSTATE_UINT32(int_polarity, K230GPIOState), > + VMSTATE_UINT32(raw_intstatus, K230GPIOState), > + VMSTATE_UINT32(debounce, K230GPIOState), > + VMSTATE_UINT32(ext_porta, K230GPIOState), > + VMSTATE_UINT32(ls_sync, K230GPIOState), > + VMSTATE_UINT32(int_bothedge, K230GPIOState), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +static void k230_gpio_enter_reset(Object *obj, ResetType type) > +{ > + K230GPIOState *s = K230_GPIO(obj); > + > + s->swporta_dr = 0; > + s->swporta_ddr = 0; > + s->swporta_ctl = 0; > + s->inten = 0; > + s->intmask = 0; > + s->inttype_level = 0; > + s->int_polarity = 0; > + s->raw_intstatus = 0; > + s->debounce = 0; > + s->porta_eoi = 0; > + s->ext_porta = 0; > + s->ls_sync = 0; > + s->id_code = 0; > + s->int_bothedge = 0; > + s->ver_id_code = 0; > + s->config_reg2 = 0; > + s->config_reg1 = 0; > +} > + > +static void k230_gpio_hold_reset(Object *obj, ResetType type) > +{ > + K230GPIOState *s = K230_GPIO(obj); > + > + k230_gpio_update_int(s); > +} > + > +static void k230_gpio_realize(DeviceState *dev, Error **errp) > +{ > + K230GPIOState *s = K230_GPIO(dev); > + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); > + int i; > + > + memory_region_init_io(&s->iomem, OBJECT(s), &k230_gpio_ops, s, > + TYPE_K230_GPIO, K230_GPIO_MEM_SIZE); > + sysbus_init_mmio(sbd, &s->iomem); > + > + qdev_init_gpio_in(DEVICE(s), k230_gpio_set, K230_GPIO_PINS_PER_GROUP); > + qdev_init_gpio_out(DEVICE(s), s->output, K230_GPIO_PINS_PER_GROUP); > + > + for (i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + sysbus_init_irq(sbd, &s->irq[i]); > + } > +} > + > +static void k230_gpio_class_init(ObjectClass *klass, const void *data) > +{ > + DeviceClass *dc = DEVICE_CLASS(klass); > + ResettableClass *rc = RESETTABLE_CLASS(klass); > + > + dc->realize = k230_gpio_realize; > + rc->phases.enter = k230_gpio_enter_reset; > + rc->phases.hold = k230_gpio_hold_reset; > + dc->vmsd = &vmstate_k230_gpio; > + dc->desc = "K230 GPIO controller"; > +} > + > +static const TypeInfo k230_gpio_info = { > + .name = TYPE_K230_GPIO, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(K230GPIOState), > + .class_init = k230_gpio_class_init, > +}; > + > +static void k230_gpio_register_types(void) > +{ > + type_register_static(&k230_gpio_info); > +} > + > +type_init(k230_gpio_register_types) > diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build > index 6a67ee958f..94b5d9e2fa 100644 > --- a/hw/gpio/meson.build > +++ b/hw/gpio/meson.build > @@ -19,3 +19,4 @@ system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c')) > system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_sgpio.c')) > system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c')) > system_ss.add(when: 'CONFIG_PCF8574', if_true: files('pcf8574.c')) > +system_ss.add(when: 'CONFIG_K230_GPIO', if_true: files('k230_gpio.c')) > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig > index de37c08cae..410877425c 100644 > --- a/hw/riscv/Kconfig > +++ b/hw/riscv/Kconfig > @@ -162,3 +162,4 @@ config K230 > select SERIAL_MM > select UNIMP > select K230_WDT > + select K230_GPIO > diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c > index 656f28190c..62967a67d0 100644 > --- a/hw/riscv/k230.c > +++ b/hw/riscv/k230.c > @@ -110,6 +110,8 @@ static void k230_soc_init(Object *obj) > object_initialize_child(obj, "c908-cpu", cpu0, TYPE_RISCV_HART_ARRAY); > object_initialize_child(obj, "k230-wdt0", &s->wdt[0], TYPE_K230_WDT); > object_initialize_child(obj, "k230-wdt1", &s->wdt[1], TYPE_K230_WDT); > + object_initialize_child(obj, "k230-gpio0", &s->gpio[0], TYPE_K230_GPIO); > + object_initialize_child(obj, "k230-gpio1", &s->gpio[1], TYPE_K230_GPIO); > > qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); > qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C908); > @@ -206,6 +208,25 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) > sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[1]), 0, > qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT1_IRQ)); > > + /* GPIO */ > + sysbus_realize(SYS_BUS_DEVICE(&s->gpio[0]), &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[0]), 0, > + memmap[K230_DEV_GPIO0].base); > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[0]), i, > + qdev_get_gpio_in(DEVICE(s->c908_plic), > + K230_GPIO0_IRQ_BASE + i)); > + } > + > + sysbus_realize(SYS_BUS_DEVICE(&s->gpio[1]), &error_fatal); > + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[1]), 0, > + memmap[K230_DEV_GPIO1].base); > + for (int i = 0; i < K230_GPIO_PINS_PER_GROUP; i++) { > + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[1]), i, > + qdev_get_gpio_in(DEVICE(s->c908_plic), > + K230_GPIO1_IRQ_BASE + i)); > + } > + > /* unimplemented devices */ > create_unimplemented_device("kpu.l2-cache", > memmap[K230_DEV_KPU_L2_CACHE].base, > @@ -322,12 +343,6 @@ static void k230_soc_realize(DeviceState *dev, Error **errp) > create_unimplemented_device("pwm", memmap[K230_DEV_PWM].base, > memmap[K230_DEV_PWM].size); > > - create_unimplemented_device("gpio0", memmap[K230_DEV_GPIO0].base, > - memmap[K230_DEV_GPIO0].size); > - > - create_unimplemented_device("gpio1", memmap[K230_DEV_GPIO1].base, > - memmap[K230_DEV_GPIO1].size); > - > create_unimplemented_device("adc", memmap[K230_DEV_ADC].base, > memmap[K230_DEV_ADC].size); > > diff --git a/include/hw/gpio/k230_gpio.h b/include/hw/gpio/k230_gpio.h > new file mode 100644 > index 0000000000..32005815d2 > --- /dev/null > +++ b/include/hw/gpio/k230_gpio.h > @@ -0,0 +1,71 @@ > +/* > + * QEMU K230 GPIO Controller > + * > + * Copyright (c) 2025 Wang Guochun <[email protected]> > + * > + * SPDX-License-Identifier: GPL-2.0-or-later > + * > + * K230 Technical Reference Manual V0.3.1 (2024-11-18), section 12.5 GPIO > + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Technical_Reference_Manual_V0.3.1_20241118.pdf > + */ > + > +#ifndef HW_K230_GPIO_H > +#define HW_K230_GPIO_H > + > +#include "hw/core/sysbus.h" > +#include "qom/object.h" > + > +#define TYPE_K230_GPIO "k230.gpio" > +OBJECT_DECLARE_SIMPLE_TYPE(K230GPIOState, K230_GPIO) > + > +#define K230_GPIO_MEM_SIZE 0x1000 > + > +#define K230_GPIO_SWPORTA_DR 0x00 > +#define K230_GPIO_SWPORTA_DDR 0x04 > +#define K230_GPIO_SWPORTA_CTL 0x08 > +#define K230_GPIO_INTEN 0x30 > +#define K230_GPIO_INTMASK 0x34 > +#define K230_GPIO_INTTYPE_LEVEL 0x38 > +#define K230_GPIO_INT_POLARITY 0x3c > +#define K230_GPIO_INTSTATUS 0x40 > +#define K230_GPIO_RAW_INTSTATUS 0x44 > +#define K230_GPIO_DEBOUNCE 0x48 > +#define K230_GPIO_PORTA_EOI 0x4c > +#define K230_GPIO_EXT_PORTA 0x50 > +#define K230_GPIO_LS_SYNC 0x60 > +#define K230_GPIO_ID_CODE 0x64 > +#define K230_GPIO_INT_BOTHEDGE 0x68 > +#define K230_GPIO_VER_ID_CODE 0x6c > +#define K230_GPIO_CONFIG_REG2 0x70 > +#define K230_GPIO_CONFIG_REG1 0x74 > + > +#define K230_GPIO_PINS_PER_GROUP 32 > + > +struct K230GPIOState { > + SysBusDevice parent_obj; > + > + MemoryRegion iomem; > + > + uint32_t swporta_dr; > + uint32_t swporta_ddr; > + uint32_t swporta_ctl; > + uint32_t inten; > + uint32_t intmask; > + uint32_t inttype_level; > + uint32_t int_polarity; > + uint32_t raw_intstatus; > + uint32_t debounce; > + uint32_t porta_eoi; This member seems not to be used anywhere. Drop it. > + uint32_t ext_porta; > + uint32_t ls_sync; > + uint32_t id_code; > + uint32_t int_bothedge; > + uint32_t ver_id_code; > + uint32_t config_reg2; > + uint32_t config_reg1; > + > + qemu_irq irq[K230_GPIO_PINS_PER_GROUP]; > + qemu_irq output[K230_GPIO_PINS_PER_GROUP]; > +}; > + > +#endif > diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h > index 592e1c26bf..5fccea7fd5 100644 > --- a/include/hw/riscv/k230.h > +++ b/include/hw/riscv/k230.h > @@ -18,6 +18,7 @@ > #include "hw/core/boards.h" > #include "hw/riscv/riscv_hart.h" > #include "hw/watchdog/k230_wdt.h" > +#include "hw/gpio/k230_gpio.h" > > #define C908_CPU_HARTID (0) > > @@ -33,6 +34,7 @@ typedef struct K230SoCState { > RISCVHartArrayState c908_cpu; /* Small core */ > > K230WdtState wdt[2]; > + K230GPIOState gpio[2]; > MemoryRegion sram; > MemoryRegion bootrom; > > @@ -127,8 +129,11 @@ enum { > K230_UART2_IRQ = 18, > K230_UART3_IRQ = 19, > K230_UART4_IRQ = 20, > + K230_GPIO0_IRQ_BASE = 32, > + K230_GPIO1_IRQ_BASE = 64, > K230_WDT0_IRQ = 107, > K230_WDT1_IRQ = 108, > + Remove this blank line. > }; > > #define K230_UART_COUNT 5 Regards, Bin