In patchv2, reviewer suggests using qemu_log_mask to print when receiving an
incorrect ack signal.
Added unexpected value printing in the k230_decomp_gzip_handle_ack function in
this commit.
MAINTAINERS | 2 +
docs/system/riscv/k230.rst | 1 +
hw/misc/Kconfig | 3 +
hw/misc/k230_decomp_gzip.c | 511 +++++++++++++++++++++++++++++
hw/misc/meson.build | 1 +
hw/misc/trace-events | 4 +
include/hw/misc/k230_decomp_gzip.h | 92 ++++++
7 files changed, 614 insertions(+)
create mode 100644 hw/misc/k230_decomp_gzip.c
create mode 100644 include/hw/misc/k230_decomp_gzip.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 9c546596e7..c260199142 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1830,9 +1830,11 @@ F: docs/system/riscv/k230.rst
F: hw/riscv/k230.c
F: hw/watchdog/k230_wdt.c
F: hw/dma/k230_gsdma.c
+F: hw/misc/k230_decomp_gzip.c
F: include/hw/riscv/k230.h
F: include/hw/watchdog/k230_wdt.h
F: include/hw/dma/k230_gsdma.h
+F: include/hw/misc/k230_decomp_gzip.h
F: tests/functional/riscv64/test_k230.py
F: tests/qtest/k230-wdt-test.c
F: tests/qtest/k230-gsdma-test.c
diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst
index a426a1d776..ebd002b62f 100644
--- a/docs/system/riscv/k230.rst
+++ b/docs/system/riscv/k230.rst
@@ -21,6 +21,7 @@ The ``k230`` machine supports the following devices:
* 2 K230 Watchdog Timer
* 5 UART
* System Direct Memory Access (SDMA)
+* GZIP Decompress Engine (Decomp_gzip)
Boot options
------------
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 1543ee6653..28687482f8 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -257,4 +257,7 @@ config XLNX_VERSAL_TRNG
config XLNX_ZYNQ_DDRC
bool
+config K230_DECOMP_GZIP
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/k230_decomp_gzip.c b/hw/misc/k230_decomp_gzip.c
new file mode 100644
index 0000000000..0d8a3b2d2e
--- /dev/null
+++ b/hw/misc/k230_decomp_gzip.c
@@ -0,0 +1,511 @@
+/*
+ * Kendryte K230 GZIP decompression engine
+ *
+ * Copyright (c) 2026 Tao Ding <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Decompression accelerator is mainly used to implement hardware
+ * GZIP decompression function. (K230 TRM section 15)
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/core/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "system/address-spaces.h"
+#include "hw/core/irq.h"
+#include "hw/misc/k230_decomp_gzip.h"
+#include "trace.h"
+
+#define GZIP_METHOD_DEFLATE 8
+#define GZIP_METHOD_VENDOR_9 9
+
+#define DEFLATE_BTYPE_DYNAMIC 2
+
+#define K230_DECOMP_GZIP_OUTPUT_SLOTS 4
+#define K230_DECOMP_GZIP_INPUT_SLOTS 2
+
+static void k230_decomp_gzip_set_output(K230DecompGzipState *s, int line,
+ int level)
+{
+ qemu_set_irq(s->signal_out[line], level);
+}
+
+static bool k230_decomp_gzip_read_mem(K230DecompGzipState *s, hwaddr addr,
+ void *buf, size_t len)
+{
+ MemTxResult ret = address_space_read(&address_space_memory,
+ s->sram_base + addr,
+ MEMTXATTRS_UNSPECIFIED, buf, len);
+
+ return ret == MEMTX_OK;
+}
+
+static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr,
+ const void *buf, size_t len)
+{
+ return address_space_write(&address_space_memory, s->sram_base + addr,
+ MEMTXATTRS_UNSPECIFIED, buf, len) == MEMTX_OK;
+}
+
+static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s)
+{
+ return K230_DECOMP_GZIP_SRAM_OUT_BASE +
+ s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
+ s->output.current_offset;
+}
+
+static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s)
+{
+ return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
+ K230_DECOMP_GZIP_SRAM_IN_BASE;
+}
+
+static uint32_t k230_decomp_gzip_current_input_size(K230DecompGzipState *s)
+{
+ if (s->total_requested == 0) {
+ return 0;
+ }
+
+ return ((s->total_requested - 1) % K230_DECOMP_GZIP_BLOCK_SIZE) + 1;
+}
+
+static void k230_decomp_gzip_update_ctrl_en(K230DecompGzipState *s)
+{
+ int level = s->active && !!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN);
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN,
+ level);
+}
+
+static void k230_decomp_gzip_reset_stream(K230DecompGzipState *s)
+{
+ if (s->zstream_inited) {
+ inflateEnd(&s->zs);
+ s->zstream_inited = false;
+ }
+ memset(&s->zs, 0, sizeof(s->zs));
+}
+
+static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok)
+{
+ s->active = false;
+ k230_decomp_gzip_reset_stream(s);
+ k230_decomp_gzip_update_ctrl_en(s);
+ if (crc_ok) {
+ s->decomp_stat |= K230_DECOMP_GZIP_STAT_CRC_OK;
+ } else {
+ s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
+ }
+}
+
+static bool k230_decomp_gzip_load_input(K230DecompGzipState *s,
+ uint32_t addr, uint32_t size)
+{
+ s->input.current_offset = 0;
+
+ return k230_decomp_gzip_read_mem(s, addr, s->input_buf, size);
+}
+
+static bool k230_decomp_gzip_request_input(K230DecompGzipState *s)
+{
+ uint32_t total_requested = s->total_requested;
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, 1);
+ if (s->total_requested == total_requested) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: dma write request not acknowledged\n",
+ TYPE_K230_DECOMP_GZIP);
+ return false;
+ }
+ return true;
+}
+
+static bool k230_decomp_gzip_request_output(K230DecompGzipState *s)
+{
+ uint32_t slot = s->output.slot;
+ uint32_t current_offset = s->output.current_offset;
+
+ k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, 1);
+ if (s->output.slot == slot &&
+ s->output.current_offset == current_offset) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: dma read request not acknowledged\n",
+ TYPE_K230_DECOMP_GZIP);
+ return false;
+ }
+ return true;
+}
+
+static bool k230_decomp_gzip_init_stream(K230DecompGzipState *s)
+{
+ uint8_t btype = (s->input_buf[10] >> 1) & 0x3;
+ if (btype != DEFLATE_BTYPE_DYNAMIC) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: unsupported DEFLATE BTYPE %u\n",
+ TYPE_K230_DECOMP_GZIP, btype);
+ return false;
+ }
+
+ /* The compression script for K230 will modify the third byte to 0x9 */
+ if (s->input_buf[2] == GZIP_METHOD_VENDOR_9) {
+ s->input_buf[2] = GZIP_METHOD_DEFLATE;
+ }
+
+ if (inflateInit2(&s->zs, 15 + 16) != Z_OK) {
+ return false;
+ }
+
+ s->zstream_inited = true;
+ return true;
+}
+
+static bool k230_decomp_gzip_run_inflate(K230DecompGzipState *s)
+{
+ uint32_t output_size = s->gzip_out_size;
+ uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
+ uint32_t avail_in;
+ uint32_t avail_out;
+ uint32_t consumed;
+ uint32_t produced;
+ int ret;
+
+ avail_in = current_input_size - s->input.current_offset;
+ avail_out = K230_DECOMP_GZIP_BLOCK_SIZE - s->output.current_offset;
+
+ s->zs.next_in = s->input_buf + s->input.current_offset;
+ s->zs.avail_in = avail_in;
+ s->zs.next_out = s->output_buf;
+ s->zs.avail_out = avail_out;
+
+ ret = inflate(&s->zs, Z_NO_FLUSH);
+ if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
+ return false;
+ }
+
+ consumed = avail_in - s->zs.avail_in;
+ produced = avail_out - s->zs.avail_out;
+
+ if (produced) {
+ if (s->total_produced + produced > output_size ||
+ !k230_decomp_gzip_write_mem(s, k230_decomp_gzip_output_addr(s),
+ s->output_buf, produced)) {
+ return false;
+ }
+ s->output.current_offset += produced;
+ s->total_produced += produced;
+ }
+
+ if (consumed) {
+ s->input.current_offset += consumed;
+ }
+
+ if (ret == Z_STREAM_END) {
+ s->stream_end = true;
+ if (s->total_produced != output_size) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void k230_decomp_gzip_kick(K230DecompGzipState *s)
+{
+ if (!s->active || s->in_kick) {
+ return;
+ }
+
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+ s->in_kick = true;
+ while (s->active) {
+ uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
+ /* Output is full or last block */
+ if (s->output.current_offset == K230_DECOMP_GZIP_BLOCK_SIZE ||
+ (s->stream_end && s->output.current_offset != 0)) {
+ if (!k230_decomp_gzip_request_output(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+
+ if (!s->zstream_inited) {
+ if (!k230_decomp_gzip_request_input(s) ||
+ !k230_decomp_gzip_init_stream(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+
+ /* Transfer finish */
+ if (s->stream_end) {
+ k230_decomp_gzip_finish(s,
+ s->total_produced == s->gzip_out_size);
+ break;
+ }
+
+ /* Input buf has has been fully decompreed, need request more */
+ if (s->input.current_offset == current_input_size) {
+ if (s->total_requested < input_size) {
+ if (!k230_decomp_gzip_request_input(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ continue;
+ }
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+
+ /* Decompress the data, input buf or output buf are consumed in one
block */
+ if (!k230_decomp_gzip_run_inflate(s)) {
+ k230_decomp_gzip_finish(s, false);
+ break;
+ }
+ }
+ s->in_kick = false;
+}
+
+static void k230_decomp_gzip_handle_ack(void *opaque, int n, int level)
+{
+ K230DecompGzipState *s = opaque;
+
+ if (!level || !s->active) {
+ return;
+ }
+
+ switch (n) {
+ case K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK:
+ {
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+ uint32_t chunk = MIN(K230_DECOMP_GZIP_BLOCK_SIZE,
+ input_size - s->total_requested);
+
+ if (!k230_decomp_gzip_load_input(s, k230_decomp_gzip_input_addr(s),
+ chunk)) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+ s->total_requested += chunk;
+ s->input.slot = (s->input.slot + 1) % K230_DECOMP_GZIP_INPUT_SLOTS;
+ break;
+ }
+ case K230_DECOMP_GZIP_GPIO_DMA_READ_ACK:
+ if (s->output.current_offset == 0) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+ s->output.slot = (s->output.slot + 1) % K230_DECOMP_GZIP_OUTPUT_SLOTS;
+ s->output.current_offset = 0;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid DMA acknowledgment signal %d\n",
+ TYPE_K230_DECOMP_GZIP, n);
+ return;
+ }
+
+ k230_decomp_gzip_kick(s);
+}
+
+static void k230_decomp_gzip_start(K230DecompGzipState *s)
+{
+ uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
+
+ k230_decomp_gzip_reset_stream(s);
+ s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
+ s->total_requested = 0;
+ s->total_produced = 0;
+ s->stream_end = false;
+ memset(&s->input, 0, sizeof(s->input));
+ memset(&s->output, 0, sizeof(s->output));
+
+ if (!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN) ||
+ input_size == 0 || s->gzip_out_size == 0) {
+ k230_decomp_gzip_finish(s, false);
+ return;
+ }
+
+ s->active = true;
+ k230_decomp_gzip_update_ctrl_en(s);
+ k230_decomp_gzip_kick(s);
+}
+
+static uint64_t k230_decomp_gzip_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ K230DecompGzipState *s = opaque;
+ uint64_t value = 0;
+
+ switch (offset) {
+ case K230_DECOMP_GZIP_DECOMP_START:
+ value = s->decomp_start & ~K230_DECOMP_GZIP_START; /* start bit is
write only */
+ break;
+ case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
+ value = s->gzip_src_size;
+ break;
+ case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
+ value = s->gzip_out_size;
+ break;
+ case K230_DECOMP_GZIP_DECOMP_STAT:
+ value = s->decomp_stat;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad read offset 0x%" HWADDR_PRIx "\n",
+ TYPE_K230_DECOMP_GZIP, offset);
+ break;
+ }
+
+ trace_k230_decomp_gzip_read(offset, size, value);
+ return value;
+}
+
+static void k230_decomp_gzip_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ K230DecompGzipState *s = opaque;
+
+ trace_k230_decomp_gzip_write(offset, size, value);
+
+ switch (offset) {
+ case K230_DECOMP_GZIP_DECOMP_START:
+ s->decomp_start = value;
+ if (value & K230_DECOMP_GZIP_START) {
+ k230_decomp_gzip_start(s);
+ }
+ break;
+ case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
+ s->gzip_src_size = value;
+ k230_decomp_gzip_update_ctrl_en(s);
+ break;
+ case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
+ s->gzip_out_size = value;
+ break;
+ case K230_DECOMP_GZIP_DECOMP_STAT:
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: bad write offset 0x%" HWADDR_PRIx "\n",
+ TYPE_K230_DECOMP_GZIP, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps k230_decomp_gzip_ops = {
+ .read = k230_decomp_gzip_read,
+ .write = k230_decomp_gzip_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 4,
+ .impl.max_access_size = 4,
+};
+
+static const VMStateDescription vmstate_k230_decomp_gzip_slot = {
+ .name = "k230.decomp-gzip.slot",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(slot, K230DecompGzipSlotState),
+ VMSTATE_UINT32(current_offset, K230DecompGzipSlotState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_k230_decomp_gzip = {
+ .name = "k230.decomp-gzip",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(decomp_start, K230DecompGzipState),
+ VMSTATE_UINT32(gzip_src_size, K230DecompGzipState),
+ VMSTATE_UINT32(gzip_out_size, K230DecompGzipState),
+ VMSTATE_UINT32(decomp_stat, K230DecompGzipState),
+ VMSTATE_STRUCT(input, K230DecompGzipState, 1,
+ vmstate_k230_decomp_gzip_slot,
+ K230DecompGzipSlotState),
+ VMSTATE_STRUCT(output, K230DecompGzipState, 1,
+ vmstate_k230_decomp_gzip_slot,
+ K230DecompGzipSlotState),
+ VMSTATE_UINT32(total_requested, K230DecompGzipState),
+ VMSTATE_UINT32(total_produced, K230DecompGzipState),
+ VMSTATE_BOOL(active, K230DecompGzipState),
+ VMSTATE_BOOL(in_kick, K230DecompGzipState),
+ VMSTATE_BOOL(zstream_inited, K230DecompGzipState),
+ VMSTATE_BOOL(stream_end, K230DecompGzipState),
+ VMSTATE_UINT8_ARRAY(input_buf, K230DecompGzipState,
+ K230_DECOMP_GZIP_BLOCK_SIZE),
+ VMSTATE_UINT8_ARRAY(output_buf, K230DecompGzipState,
+ K230_DECOMP_GZIP_BLOCK_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void k230_decomp_gzip_reset_hold(Object *obj, ResetType type)
+{
+ K230DecompGzipState *s = K230_DECOMP_GZIP(obj);
+
+ k230_decomp_gzip_reset_stream(s);
+ memset(&s->zs, 0, sizeof(s->zs));
+ s->decomp_start = 0;
+ s->gzip_src_size = 0;
+ s->gzip_out_size = 0;
+ s->decomp_stat = 0;
+ memset(&s->input, 0, sizeof(s->input));
+ memset(&s->output, 0, sizeof(s->output));
+ s->total_requested = 0;
+ s->total_produced = 0;
+ s->active = false;
+ s->in_kick = false;
+ s->stream_end = false;
+}
+
+static void k230_decomp_gzip_realize(DeviceState *dev, Error **errp)
+{
+ K230DecompGzipState *s = K230_DECOMP_GZIP(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &k230_decomp_gzip_ops, s,
+ TYPE_K230_DECOMP_GZIP,
+ K230_DECOMP_GZIP_MMIO_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+ qdev_init_gpio_in(dev, k230_decomp_gzip_handle_ack,
+ K230_DECOMP_GZIP_NUM_GPIOS_IN);
+ qdev_init_gpio_out(dev, s->signal_out,
+ K230_DECOMP_GZIP_NUM_GPIOS_OUT);
+}
+
+static const Property k230_decomp_gzip_properties[] = {
+ DEFINE_PROP_UINT64("sram-base", K230DecompGzipState, sram_base, 0),
+};
+
+static void k230_decomp_gzip_class_init(ObjectClass *oc, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
+
+ dc->realize = k230_decomp_gzip_realize;
+ rc->phases.hold = k230_decomp_gzip_reset_hold;
+ device_class_set_props(dc, k230_decomp_gzip_properties);
+ dc->vmsd = &vmstate_k230_decomp_gzip;
+ dc->desc = "Kendryte K230 GZIP decompression engine";
+}
+
+static const TypeInfo k230_decomp_gzip_info = {
+ .name = TYPE_K230_DECOMP_GZIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(K230DecompGzipState),
+ .class_init = k230_decomp_gzip_class_init,
+};
+
+static void k230_decomp_gzip_register_types(void)
+{
+ type_register_static(&k230_decomp_gzip_info);
+}
+
+type_init(k230_decomp_gzip_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 23265f6035..6f6bf3c85c 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -36,6 +36,7 @@ system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true:
files('sifive_e_prci.c'))
system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
+system_ss.add(when: 'CONFIG_K230_DECOMP_GZIP', if_true:
files('k230_decomp_gzip.c'))
subdir('macio')
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index c9a868b3ef..16db4ba0cc 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -442,3 +442,7 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64
" len=%u"
iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x
actual=0x%x"
iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
iommu_testdev_dma_armed(bool armed) "armed=%d"
+
+# k230_decomp_gzip.c
+k230_decomp_gzip_read(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP
read: [0x%"PRIx64"] size %u -> 0x%"PRIx64
+k230_decomp_gzip_write(uint64_t offset, unsigned int size, uint64_t value) "K230 DECOMP GZIP
write: [0x%"PRIx64"] size %u <- 0x%"PRIx64
diff --git a/include/hw/misc/k230_decomp_gzip.h
b/include/hw/misc/k230_decomp_gzip.h
new file mode 100644
index 0000000000..9911af0095
--- /dev/null
+++ b/include/hw/misc/k230_decomp_gzip.h
@@ -0,0 +1,92 @@
+/*
+ * K230 Decompress Engine
+ *
+ * Copyright (c) 2026 Tao Ding <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MISC_K230_DECOMP_GZIP_H
+#define HW_MISC_K230_DECOMP_GZIP_H
+
+#include <zlib.h>
+#include "hw/core/sysbus.h"
+
+#define TYPE_K230_DECOMP_GZIP "riscv.k230.decomp-gzip"
+OBJECT_DECLARE_SIMPLE_TYPE(K230DecompGzipState, K230_DECOMP_GZIP)
+
+/* K230 DECOMP GZIP ACK input */
+enum {
+ K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK,
+ K230_DECOMP_GZIP_GPIO_DMA_READ_ACK,
+ K230_DECOMP_GZIP_NUM_GPIOS_IN,
+};
+
+/* K230 DECOMP GZIP REQ output */
+enum {
+ K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN,
+ K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ,
+ K230_DECOMP_GZIP_GPIO_DMA_READ_REQ,
+ K230_DECOMP_GZIP_NUM_GPIOS_OUT,
+};
+
+#define K230_DECOMP_GZIP_MMIO_SIZE 0x4000
+
+/* K230 DECOMP GZIP Registers map */
+/* Start decompression controller */
+#define K230_DECOMP_GZIP_DECOMP_START 0x00
+/* Source data length register */
+#define K230_DECOMP_GZIP_GZIP_SRC_SIZE 0x04
+/* Output data length register */
+#define K230_DECOMP_GZIP_GZIP_OUT_SIZE 0x08
+/* Decompress status register */
+#define K230_DECOMP_GZIP_DECOMP_STAT 0x0c
+
+/* Start decompression controller */
+#define K230_DECOMP_GZIP_START (1U << 0)
+
+#define K230_DECOMP_GZIP_CTRL_EN (1U << 31)
+#define K230_DECOMP_GZIP_DMA_IN_MASK 0x7fffffffU
+
+#define K230_DECOMP_GZIP_STAT_CRC_OK (1U << 10)
+#define K230_DECOMP_GZIP_STAT_STATE_MASK 0xfU
+
+#define K230_DECOMP_GZIP_BLOCK_SIZE 0x00020000
+#define K230_DECOMP_GZIP_SRAM_IN_BASE 0x80000
+#define K230_DECOMP_GZIP_SRAM_OUT_BASE 0
+
+typedef struct K230DecompGzipSlotState {
+ uint32_t slot; /* Slot index */
+ /*
+ * Data offset in current slot.
+ * First valid data in input buffer. Last valid data in output buffer.
+ */
+ uint32_t current_offset;
+} K230DecompGzipSlotState;
+
+struct K230DecompGzipState {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ hwaddr sram_base;
+ qemu_irq signal_out[K230_DECOMP_GZIP_NUM_GPIOS_OUT];
+ uint32_t decomp_start;
+ uint32_t gzip_src_size;
+ uint32_t gzip_out_size;
+ uint32_t decomp_stat;
+ K230DecompGzipSlotState input; /* decomp gzip ring input */
+ K230DecompGzipSlotState output; /* decomp gzip ring output */
+ uint32_t total_requested;
+ uint32_t total_produced;
+ bool active;
+ bool in_kick;
+ bool zstream_inited;
+ bool stream_end;
+ /* Read from ring input. */
+ uint8_t input_buf[K230_DECOMP_GZIP_BLOCK_SIZE];
+ /* Write to ring output. */
+ uint8_t output_buf[K230_DECOMP_GZIP_BLOCK_SIZE];
+ z_stream zs;
+};
+
+#endif