From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069062; cv=none; d=zohomail.com; s=zohoarc; b=fGmTJGHJWVUdMgRcb9kTmqBQmsJXYcmyt4H4IX8xHSO/W7zRctdtmvPPf7Pxd8qf6XTAZb2oz2LmU2wN87+rfTKjlP8PXeC6CWq2Td+RZt2WDbyXzybJkUFsK/A7j57NVHSrs6eV0ZwndIxQFZyCrzJtLxdFOsYk5GV+GQ24FdM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069062; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=fvncanYCyzys4CM+KI8EyRNfFpb7xenD64P3Z3txnoM=; b=BFIUijsLyKIfx6adsHyM15N6t9yVBb1GtDQGzqpb1DlvVP67riYcsHxflCEbIEckDBEtiCroNrhAxKrp8xquNil0zCb3BSLSkueqqAGIXcJAoecOYbp6RYPsm+EMrC175XkkO2vnenBbKWvq/afai+QD3wwBE7mcX12TRn55gKk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069062591912.7045946519202; Sun, 26 Jul 2026 05:31:02 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxyd-000181-Us; Sun, 26 Jul 2026 08:29:07 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxyb-00015Y-Ic for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:05 -0400 Received: from mail-pf1-x432.google.com ([2607:f8b0:4864:20::432]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxyY-00039b-3p for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:05 -0400 Received: by mail-pf1-x432.google.com with SMTP id d2e1a72fcca58-848595b338cso2056928b3a.0 for ; Sun, 26 Jul 2026 05:29:01 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.28.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:28:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068941; x=1785673741; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=fvncanYCyzys4CM+KI8EyRNfFpb7xenD64P3Z3txnoM=; b=cJLDHeSVqNUW4WHDG8gKyVZz1adTDe9spYAHdyiKXvoXZOjbRnSlezF5D/5eIflELq 2u9Vt6iresXEMPiq3jtiYaA+Qphw9d5Jb1X3qo+Jrak0l6tirG7xyj/EHI2lTDp8ZV11 XtOxhjv/VGEUga7NS8LQAOxjNVnPr+HauyQNhNvHjenhU1fBrPBVbkApeoXXj2z511qs yZY+tODgcbQZHRLq9T8UqVv4hEc6KLpTTgT3Azo3wRFMoxGHvScd/BGqOuEv5nQHDNmx OPEjcRTVt3rv1jqKA4v3xFO9JoJZB1d7O697Hc/C2EzOlggo8QooCM9ary0dVEKtIGS/ QfGA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068941; x=1785673741; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=fvncanYCyzys4CM+KI8EyRNfFpb7xenD64P3Z3txnoM=; b=Gpr/+jOZLT118TOJq9s7D7bBD9SR3j1rGGWz5SzGmyfn5ZNnqHSiKSQeL0IbFKpICt d7DsS+ZKahpyqpQ9pEbuwbLJRCbazN9gFuSEtiK2aB4KmMVfggo9BYUORC4p4SCTXff3 +vaRmdXfUSyXCNSqMFU77kK/1uJjEQ7YlXWi+rRr7I3hiJlPat+0UgtOVe6lGhukQVmx CLlQ9SuPYG8Jojjnn3RbMuEDiKK0D6QWv7QdH4Kjr4NCLLi6pdBExrpdEvNSaMBSGSMK FGSY6SO7hVYhcWRS3OwkGgZY9YAi18nVn+ZgtIovf03pZP0yF1DzybNjHaD6UlqzUn8m C2ig== X-Gm-Message-State: AOJu0Yw57hLPgwaeoQVQ2DH8cQd+uuU8U9+zxV3kxBsdiAs9nHRxClbh cqd+LqFrrdoIkceZ9YKBmfFub5d6tlPca85DH+AvVycVJ8NcCKEtIDS0r58/INEeMYs= X-Gm-Gg: AR+sD128Zvscf+E0VLy3st3wn2aYWQqdJh7TgURTI/oSTuGFNvWWIgQi/xzVhnq4Ts+ FAnq9p5uPkaUvmxld2GndXUA+vajz9BbDkcG00wwmBB2fK6lWG6KykVBlOJGQD+qCvAOHUj5pJl T5U1TzXNficXi4Nyk2rue1NOKiAwsFJYrCisG3Vf0gdLXT7W3icrk5mB09hXtNuo8moqBjEgkrQ C5drifdoXTvM0in+Zt+1eKebRQRAuIWFsTR4MBSSsv1B203rQ0v6qM9cx5pdc0ZjyVSwWNxSSuw +bZQW4DGvGKnKyacWwgZtUsVpB8YveInQghbSo3k9CPv7Ga6ZuCq6JmVeKWnJDkOM4JbBP0ZZXK GjoKV3wNuQpqgYDm7uccZ35lHMC0ZMmrstRw62oznHlWWS5YJB+CC6pDrOBpAs9EmAs+8cA4wN/ 44gQFHqVG/zsASO3DwgglO X-Received: by 2002:a05:6a00:194b:b0:848:42d0:bc91 with SMTP id d2e1a72fcca58-84e5943007dmr3975804b3a.12.1785068940242; Sun, 26 Jul 2026 05:29:00 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 01/11] hw/ssi: Add K230 DesignWare SSI register model Date: Sun, 26 Jul 2026 20:28:19 +0800 Message-ID: <12e325190a15045cff5c429b154217814cd9e618.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::432; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x432.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069063332158500 Content-Type: text/plain; charset="utf-8" Add a SysBus model for the K230 DesignWare SSI controllers. Implement the register layout, reset values, writable masks, FIFO state, chip-select GPIOs, and the MMIO regions needed by the K230 machine, based on the K230 Technical Reference Manual. The controller is not instantiated by this patch. Signed-off-by: Kangjie Huang --- hw/ssi/Kconfig | 4 + hw/ssi/k230_dw_ssi.c | 795 +++++++++++++++++++++++++++++++++++ hw/ssi/meson.build | 1 + include/hw/ssi/k230_dw_ssi.h | 50 +++ 4 files changed, 850 insertions(+) create mode 100644 hw/ssi/k230_dw_ssi.c create mode 100644 include/hw/ssi/k230_dw_ssi.h diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig index 1bd56463c1..05ff0e89ee 100644 --- a/hw/ssi/Kconfig +++ b/hw/ssi/Kconfig @@ -6,6 +6,10 @@ config SIFIVE_SPI bool select SSI =20 +config K230_DW_SSI + bool + select SSI + config SSI bool =20 diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c new file mode 100644 index 0000000000..d0dcad4312 --- /dev/null +++ b/hw/ssi/k230_dw_ssi.c @@ -0,0 +1,795 @@ +/* + * Kendryte K230 DesignWare SSI + * + * Copyright (c) 2026 Kangjie Huang + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Emulates the DesignWare SSI controllers documented by the K230 + * Technical Reference Manual, including standard SPI, Dual/Quad SDR, + * internal DMA, and the XIP read window. + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Tec= hnical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see + */ + +#include "qemu/osdep.h" +#include "hw/core/registerfields.h" +#include "hw/core/qdev-properties.h" +#include "hw/core/irq.h" +#include "hw/ssi/k230_dw_ssi.h" +#include "migration/vmstate.h" +#include "qapi/error.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/module.h" + +#define K230_DW_SSI_FIFO_CAPACITY 256 + +#define K230_DW_SSI_CTRLR0_RESET 0x00004007 +#define K230_DW_SSI_SR_RESET 0x00000006 +#define K230_DW_SSI_IMR_RESET 0x0000003f +#define K230_DW_SSI_IDR_RESET 0xa1b2c3d5 +#define K230_DW_SSI_SPI_CTRLR0_SPI_RESET 0x04000200 +#define K230_DW_SSI_SPI_CTRLR0_FMC_RESET 0x28000200 +#define K230_DW_SSI_AXIAWLEN_RESET 0x00000700 +#define K230_DW_SSI_AXIARLEN_RESET 0x00000700 +#define K230_DW_SSI_VERSION 0x3130332a + +REG32(CTRLR0, 0x000) + FIELD(CTRLR0, DFS, 0, 5) + FIELD(CTRLR0, FRF, 6, 2) + FIELD(CTRLR0, SCPH, 8, 1) + FIELD(CTRLR0, SCPOL, 9, 1) + FIELD(CTRLR0, TMOD, 10, 2) + FIELD(CTRLR0, SLV_OE, 12, 1) + FIELD(CTRLR0, SRL, 13, 1) + FIELD(CTRLR0, SSTE, 14, 1) + FIELD(CTRLR0, CFS, 16, 4) + FIELD(CTRLR0, SPI_FRF, 22, 2) + FIELD(CTRLR0, SPI_HYPERBUS_EN, 24, 1) + FIELD(CTRLR0, SPI_DWS_EN, 25, 1) +REG32(CTRLR1, 0x004) + FIELD(CTRLR1, NDF, 0, 16) +REG32(SSIENR, 0x008) + FIELD(SSIENR, SSIC_EN, 0, 1) +REG32(MWCR, 0x00c) + FIELD(MWCR, MWMOD, 0, 1) + FIELD(MWCR, MDD, 1, 1) + FIELD(MWCR, MHS, 2, 1) +REG32(SER, 0x010) +REG32(BAUDR, 0x014) + FIELD(BAUDR, SCKDV, 1, 15) +REG32(TXFTLR, 0x018) + FIELD(TXFTLR, TFT, 0, 8) + FIELD(TXFTLR, TXFTHR, 16, 11) +REG32(RXFTLR, 0x01c) + FIELD(RXFTLR, RFT, 0, 8) +REG32(TXFLR, 0x020) + FIELD(TXFLR, TXTFL, 0, 9) +REG32(RXFLR, 0x024) + FIELD(RXFLR, RXTFL, 0, 9) +REG32(SR, 0x028) + FIELD(SR, CMPLTD_DF, 15, 17) + FIELD(SR, DCOL, 6, 1) + FIELD(SR, TXE, 5, 1) + FIELD(SR, BUSY, 0, 1) + FIELD(SR, TFNF, 1, 1) + FIELD(SR, TFE, 2, 1) + FIELD(SR, RFNE, 3, 1) + FIELD(SR, RFF, 4, 1) +REG32(IMR, 0x02c) + FIELD(IMR, DONEM, 11, 1) + FIELD(IMR, SPITEM, 10, 1) + FIELD(IMR, AXIEM, 8, 1) + FIELD(IMR, TXUIM, 7, 1) + FIELD(IMR, XRXOIM, 6, 1) + FIELD(IMR, MSTIM, 5, 1) + FIELD(IMR, RXOIM, 3, 1) + FIELD(IMR, RXUIM, 2, 1) + FIELD(IMR, TXOIM, 1, 1) + FIELD(IMR, TXEIM, 0, 1) + FIELD(IMR, RXFIM, 4, 1) +REG32(ISR, 0x030) + FIELD(ISR, DONES, 11, 1) + FIELD(ISR, SPITES, 10, 1) + FIELD(ISR, AXIES, 8, 1) + FIELD(ISR, TXUIS, 7, 1) + FIELD(ISR, XRXOIS, 6, 1) + FIELD(ISR, MSTIS, 5, 1) + FIELD(ISR, RXOIS, 3, 1) + FIELD(ISR, RXUIS, 2, 1) + FIELD(ISR, TXOIS, 1, 1) + FIELD(ISR, TXEIS, 0, 1) + FIELD(ISR, RXFIS, 4, 1) +REG32(RISR, 0x034) + FIELD(RISR, DONER, 11, 1) + FIELD(RISR, SPITER, 10, 1) + FIELD(RISR, AXIER, 8, 1) + FIELD(RISR, TXUIR, 7, 1) + FIELD(RISR, XRXOIR, 6, 1) + FIELD(RISR, MSTIR, 5, 1) + FIELD(RISR, RXOIR, 3, 1) + FIELD(RISR, RXUIR, 2, 1) + FIELD(RISR, TXOIR, 1, 1) + FIELD(RISR, TXEIR, 0, 1) + FIELD(RISR, RXFIR, 4, 1) +REG32(TXEICR, 0x038) + FIELD(TXEICR, TXEICR, 0, 1) +REG32(RXOICR, 0x03c) + FIELD(RXOICR, RXOICR, 0, 1) +REG32(RXUICR, 0x040) + FIELD(RXUICR, RXUICR, 0, 1) +REG32(MSTICR, 0x044) + FIELD(MSTICR, MSTICR, 0, 1) +REG32(ICR, 0x048) + FIELD(ICR, ICR, 0, 1) +REG32(DMACR, 0x04c) + FIELD(DMACR, RDMAE, 0, 1) + FIELD(DMACR, TDMAE, 1, 1) + FIELD(DMACR, IDMAE, 2, 1) + FIELD(DMACR, ATW, 3, 2) + FIELD(DMACR, AINC, 6, 1) + FIELD(DMACR, ACACHE, 8, 4) + FIELD(DMACR, APROT, 12, 3) + FIELD(DMACR, AID, 15, 4) +REG32(AXIAWLEN, 0x050) + FIELD(AXIAWLEN, AWLEN, 8, 8) +REG32(AXIARLEN, 0x054) + FIELD(AXIARLEN, ARLEN, 8, 8) +REG32(IDR, 0x058) + FIELD(IDR, IDCODE, 0, 32) +REG32(SSIC_VERSION_ID, 0x05c) + FIELD(SSIC_VERSION_ID, VERSION_ID, 0, 32) +REG32(DR0, 0x060) +REG32(DR_END, 0x0ec) +REG32(RX_SAMPLE_DELAY, 0x0f0) + FIELD(RX_SAMPLE_DELAY, RSD, 0, 8) + FIELD(RX_SAMPLE_DELAY, SE, 16, 1) +REG32(SPI_CTRLR0, 0x0f4) + FIELD(SPI_CTRLR0, CLK_STRETCH_EN, 30, 1) + FIELD(SPI_CTRLR0, XIP_PREFETCH_EN, 29, 1) + FIELD(SPI_CTRLR0, XIP_MBL, 26, 2) + FIELD(SPI_CTRLR0, SPI_RXDS_SIG_EN, 25, 1) + FIELD(SPI_CTRLR0, SPI_DM_EN, 24, 1) + FIELD(SPI_CTRLR0, SSIC_XIP_CONT_XFER_EN, 21, 1) + FIELD(SPI_CTRLR0, XIP_INST_EN, 20, 1) + FIELD(SPI_CTRLR0, XIP_DFS_HC, 19, 1) + FIELD(SPI_CTRLR0, SPI_RXDS_EN, 18, 1) + FIELD(SPI_CTRLR0, INST_DDR_EN, 17, 1) + FIELD(SPI_CTRLR0, SPI_DDR_EN, 16, 1) + FIELD(SPI_CTRLR0, WAIT_CYCLES, 11, 5) + FIELD(SPI_CTRLR0, INST_L, 8, 2) + FIELD(SPI_CTRLR0, XIP_MD_BIT_EN, 7, 1) + FIELD(SPI_CTRLR0, ADDR_L, 2, 4) + FIELD(SPI_CTRLR0, TRANS_TYPE, 0, 2) +REG32(DDR_DRIVE_EDGE, 0x0f8) + FIELD(DDR_DRIVE_EDGE, TDE, 0, 8) +REG32(XIP_MODE_BITS, 0x0fc) + FIELD(XIP_MODE_BITS, XIP_MD_BITS, 0, 16) +REG32(XIP_INCR_INST, 0x100) + FIELD(XIP_INCR_INST, INCR_INST, 0, 16) +REG32(XIP_WRAP_INST, 0x104) + FIELD(XIP_WRAP_INST, WRAP_INST, 0, 16) +REG32(XIP_CTRL, 0x108) +REG32(XIP_SER, 0x10c) +REG32(XRXOICR, 0x110) +REG32(XIP_CNT_TIME_OUT, 0x114) +REG32(SPI_CTRLR1, 0x118) +REG32(SPITECR, 0x11c) +REG32(SPIDR, 0x120) + FIELD(SPIDR, SPI_INST, 0, 16) +REG32(SPIAR, 0x124) + FIELD(SPIAR, SDAR, 0, 32) +REG32(AXIAR0, 0x128) + FIELD(AXIAR0, AXIAR_0_31, 0, 32) +REG32(AXIAR1, 0x12c) + FIELD(AXIAR1, AXIAR_32_63, 0, 32) +REG32(AXIECR, 0x130) + FIELD(AXIECR, AXIECR, 0, 1) +REG32(DONECR, 0x134) + FIELD(DONECR, DONECR, 0, 1) +REG32(RSVD_138, 0x138) +REG32(RSVD_13C, 0x13c) +REG32(XIP_WRITE_INCR_INST, 0x140) +REG32(XIP_WRITE_WRAP_INST, 0x144) +REG32(XIP_WRITE_CTRL, 0x148) + +#define K230_DW_SSI_CTRLR0_WRITABLE_MASK \ + (R_CTRLR0_DFS_MASK | \ + R_CTRLR0_SCPH_MASK | \ + R_CTRLR0_SCPOL_MASK | \ + R_CTRLR0_TMOD_MASK | \ + R_CTRLR0_SLV_OE_MASK | \ + R_CTRLR0_SRL_MASK | \ + R_CTRLR0_SSTE_MASK | \ + R_CTRLR0_CFS_MASK | \ + R_CTRLR0_SPI_FRF_MASK | \ + R_CTRLR0_SPI_HYPERBUS_EN_MASK) + +#define K230_DW_SSI_CTRLR1_WRITABLE_MASK R_CTRLR1_NDF_MASK +#define K230_DW_SSI_MWCR_WRITABLE_MASK \ + (R_MWCR_MWMOD_MASK | R_MWCR_MDD_MASK) +#define K230_DW_SSI_BAUDR_WRITABLE_MASK R_BAUDR_SCKDV_MASK +#define K230_DW_SSI_TXFTLR_WRITABLE_MASK \ + (R_TXFTLR_TFT_MASK | R_TXFTLR_TXFTHR_MASK) +#define K230_DW_SSI_RXFTLR_WRITABLE_MASK R_RXFTLR_RFT_MASK +#define K230_DW_SSI_DMACR_WRITABLE_MASK \ + (R_DMACR_IDMAE_MASK | R_DMACR_ATW_MASK | R_DMACR_AINC_MASK | \ + R_DMACR_ACACHE_MASK | R_DMACR_APROT_MASK | R_DMACR_AID_MASK) +#define K230_DW_SSI_AXIAWLEN_WRITABLE_MASK R_AXIAWLEN_AWLEN_MASK +#define K230_DW_SSI_AXIARLEN_WRITABLE_MASK R_AXIARLEN_ARLEN_MASK +#define K230_DW_SSI_IMR_WRITABLE_MASK \ + (R_IMR_TXEIM_MASK | R_IMR_TXOIM_MASK | R_IMR_RXUIM_MASK | \ + R_IMR_RXOIM_MASK | R_IMR_RXFIM_MASK | R_IMR_MSTIM_MASK | \ + R_IMR_TXUIM_MASK | R_IMR_AXIEM_MASK | R_IMR_DONEM_MASK) +#define K230_DW_SSI_RX_SAMPLE_DELAY_WRITABLE_MASK \ + (R_RX_SAMPLE_DELAY_RSD_MASK | R_RX_SAMPLE_DELAY_SE_MASK) +#define K230_DW_SSI_SPI_CTRLR0_WRITABLE_MASK \ + (R_SPI_CTRLR0_CLK_STRETCH_EN_MASK | \ + R_SPI_CTRLR0_XIP_PREFETCH_EN_MASK | \ + R_SPI_CTRLR0_XIP_MBL_MASK | \ + R_SPI_CTRLR0_SPI_RXDS_SIG_EN_MASK | \ + R_SPI_CTRLR0_SPI_DM_EN_MASK | \ + R_SPI_CTRLR0_SSIC_XIP_CONT_XFER_EN_MASK | \ + R_SPI_CTRLR0_XIP_INST_EN_MASK | \ + R_SPI_CTRLR0_XIP_DFS_HC_MASK | \ + R_SPI_CTRLR0_INST_DDR_EN_MASK | \ + R_SPI_CTRLR0_SPI_DDR_EN_MASK | \ + R_SPI_CTRLR0_SPI_RXDS_EN_MASK | \ + R_SPI_CTRLR0_WAIT_CYCLES_MASK | \ + R_SPI_CTRLR0_INST_L_MASK | \ + R_SPI_CTRLR0_XIP_MD_BIT_EN_MASK | \ + R_SPI_CTRLR0_ADDR_L_MASK | \ + R_SPI_CTRLR0_TRANS_TYPE_MASK) +#define K230_DW_SSI_DDR_DRIVE_EDGE_WRITABLE_MASK \ + R_DDR_DRIVE_EDGE_TDE_MASK +#define K230_DW_SSI_XIP_MODE_BITS_WRITABLE_MASK \ + R_XIP_MODE_BITS_XIP_MD_BITS_MASK +#define K230_DW_SSI_XIP_INCR_INST_WRITABLE_MASK \ + R_XIP_INCR_INST_INCR_INST_MASK +#define K230_DW_SSI_XIP_WRAP_INST_WRITABLE_MASK \ + R_XIP_WRAP_INST_WRAP_INST_MASK +#define K230_DW_SSI_SPIDR_WRITABLE_MASK R_SPIDR_SPI_INST_MASK +#define K230_DW_SSI_SPIAR_WRITABLE_MASK R_SPIAR_SDAR_MASK +#define K230_DW_SSI_AXIAR0_WRITABLE_MASK R_AXIAR0_AXIAR_0_31_MASK +#define K230_DW_SSI_AXIAR1_WRITABLE_MASK R_AXIAR1_AXIAR_32_63_MASK + +static void k230_dw_ssi_write_masked(K230DwSsiState *s, unsigned int reg, + uint32_t value, uint32_t mask) +{ + s->regs[reg] =3D (s->regs[reg] & ~mask) | (value & mask); +} + +static uint32_t k230_dw_ssi_frame_masked(K230DwSsiState *s) +{ + unsigned int bits =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, DFS) + 1; + + return bits =3D=3D 32 ? UINT32_MAX : MAKE_64BIT_MASK(0, bits); +} + + +static bool k230_dw_ssi_enabled(K230DwSsiState *s) +{ + return FIELD_EX32(s->regs[R_SSIENR], SSIENR, SSIC_EN); +} + +static void k230_dw_ssi_deselect(K230DwSsiState *s) +{ + if (s->active_cs < 0) { + return; + } + + qemu_irq_raise(s->cs_lines[s->active_cs]); + s->active_cs =3D -1; +} + +static void k230_dw_ssi_select(K230DwSsiState *s, unsigned cs) +{ + if (cs >=3D s->num_cs) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid chip select %u\n", + DEVICE(s)->canonical_path, cs); + k230_dw_ssi_deselect(s); + return; + } + + if (s->active_cs =3D=3D cs) { + return; + } + + k230_dw_ssi_deselect(s); + qemu_irq_lower(s->cs_lines[cs]); + s->active_cs =3D cs; +} + +static void k230_dw_ssi_update_cs(K230DwSsiState *s) +{ + uint32_t ser =3D s->regs[R_SER]; + + if (!k230_dw_ssi_enabled(s) || !ser) { + k230_dw_ssi_deselect(s); + return; + } + + if (ser & (ser - 1)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: multiple chip selects enabled: 0x%x\n", + DEVICE(s)->canonical_path, ser); + k230_dw_ssi_deselect(s); + return; + } + + k230_dw_ssi_select(s, ctz32(ser)); +} + +static void k230_dw_ssi_abort_transfer(K230DwSsiState *s) +{ + k230_dw_ssi_deselect(s); + fifo32_reset(&s->tx_fifo); + fifo32_reset(&s->rx_fifo); +} + +static uint32_t k230_dw_ssi_status(K230DwSsiState *s) +{ + uint32_t tx_used =3D fifo32_num_used(&s->tx_fifo); + uint32_t rx_used =3D fifo32_num_used(&s->rx_fifo); + uint32_t sr =3D 0; + + sr =3D FIELD_DP32(sr, SR, TFNF, tx_used < K230_DW_SSI_FIFO_CAPACITY); + sr =3D FIELD_DP32(sr, SR, TFE, tx_used =3D=3D 0); + sr =3D FIELD_DP32(sr, SR, RFNE, rx_used !=3D 0); + sr =3D FIELD_DP32(sr, SR, RFF, + rx_used =3D=3D K230_DW_SSI_FIFO_CAPACITY); + + return sr; +} + +static void k230_dw_ssi_run_transfer(K230DwSsiState *s); + +static void k230_dw_ssi_push_tx(K230DwSsiState *s, uint32_t tx) +{ + if (!k230_dw_ssi_enabled(s) || s->active_cs < 0) { + return; + } + + if (fifo32_is_full(&s->tx_fifo)) { + return; + } + + fifo32_push(&s->tx_fifo, tx & k230_dw_ssi_frame_masked(s)); + k230_dw_ssi_run_transfer(s); +} + + +static void k230_dw_ssi_run_transfer(K230DwSsiState *s) +{ +} + + + +static bool k230_dw_ssi_is_dr(hwaddr addr) +{ + return addr >=3D A_DR0 && addr <=3D A_DR_END && + (addr & 0x3) =3D=3D 0; +} + +static bool k230_dw_ssi_is_razwi(hwaddr addr) +{ + switch (addr) { + case A_XIP_CTRL: + case A_XIP_SER: + case A_XRXOICR: + case A_XIP_CNT_TIME_OUT: + case A_SPI_CTRLR1: + case A_SPITECR: + case A_RSVD_138: + case A_RSVD_13C: + case A_XIP_WRITE_INCR_INST: + case A_XIP_WRITE_WRAP_INST: + case A_XIP_WRITE_CTRL: + return true; + default: + return false; + } +} + +static bool k230_dw_ssi_write_requires_disabled(hwaddr addr) +{ + switch (addr) { + case A_CTRLR0: + case A_CTRLR1: + case A_MWCR: + case A_BAUDR: + case A_SPI_CTRLR0: + return true; + default: + return false; + } +} + +static uint64_t k230_dw_ssi_read(void *opaque, hwaddr addr, unsigned int s= ize) +{ + K230DwSsiState *s =3D K230_DW_SSI(opaque); + uint32_t value =3D 0; + + if (k230_dw_ssi_is_dr(addr)) { + if (!fifo32_is_empty(&s->rx_fifo)) { + value =3D fifo32_pop(&s->rx_fifo) & k230_dw_ssi_frame_masked(s= ); + } + return value; + } + + if (k230_dw_ssi_is_razwi(addr)) { + return 0; + } + + switch (addr) { + case A_CTRLR0: + case A_CTRLR1: + case A_SSIENR: + case A_MWCR: + case A_BAUDR: + case A_TXFTLR: + case A_RXFTLR: + case A_IMR: + case A_DMACR: + case A_AXIAWLEN: + case A_AXIARLEN: + case A_IDR: + case A_SSIC_VERSION_ID: + case A_RX_SAMPLE_DELAY: + case A_SPI_CTRLR0: + case A_DDR_DRIVE_EDGE: + case A_XIP_MODE_BITS: + case A_XIP_INCR_INST: + case A_XIP_WRAP_INST: + case A_SPIDR: + case A_SPIAR: + case A_AXIAR0: + case A_AXIAR1: + value =3D s->regs[addr / sizeof(uint32_t)]; + break; + case A_SER: + value =3D s->regs[R_SER] & MAKE_64BIT_MASK(0, s->num_cs); + break; + case A_TXFLR: + value =3D fifo32_num_used(&s->tx_fifo); + break; + case A_RXFLR: + value =3D fifo32_num_used(&s->rx_fifo); + break; + case A_SR: + value =3D k230_dw_ssi_status(s); + break; + case A_ISR: + case A_RISR: + value =3D 0; + break; + case A_TXEICR: + case A_RXOICR: + case A_RXUICR: + case A_MSTICR: + case A_ICR: + value =3D 0; + break; + case A_AXIECR: + case A_DONECR: + value =3D 0; + break; + default: + if (addr >=3D K230_DW_SSI_REGS_SIZE || (addr & 0x3) !=3D 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + } + break; + } + + return value; +} + +static void k230_dw_ssi_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + K230DwSsiState *s =3D K230_DW_SSI(opaque); + + if (k230_dw_ssi_is_dr(addr)) { + k230_dw_ssi_push_tx(s, value); + return; + } + + if (k230_dw_ssi_is_razwi(addr)) { + return; + } + + if (k230_dw_ssi_write_requires_disabled(addr) && + k230_dw_ssi_enabled(s)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to offset 0x%" HWADDR_PRIx + " while SSI is enabled\n", + DEVICE(s)->canonical_path, addr); + return; + } + + switch (addr) { + case A_CTRLR0: + k230_dw_ssi_write_masked(s, R_CTRLR0, value, + K230_DW_SSI_CTRLR0_WRITABLE_MASK); + break; + case A_CTRLR1: + k230_dw_ssi_write_masked(s, R_CTRLR1, value, + K230_DW_SSI_CTRLR1_WRITABLE_MASK); + break; + case A_SSIENR: { + bool old_enabled =3D k230_dw_ssi_enabled(s); + bool new_enabled =3D value & R_SSIENR_SSIC_EN_MASK; + + if (old_enabled =3D=3D new_enabled) { + return; + } + + s->regs[R_SSIENR] =3D FIELD_DP32(0, SSIENR, SSIC_EN, new_enabled); + if (!new_enabled) { + k230_dw_ssi_abort_transfer(s); + return; + } + + k230_dw_ssi_update_cs(s); + break; + } + case A_MWCR: + k230_dw_ssi_write_masked(s, R_MWCR, value, + K230_DW_SSI_MWCR_WRITABLE_MASK); + break; + case A_SER: + s->regs[R_SER] =3D value & MAKE_64BIT_MASK(0, s->num_cs); + break; + case A_BAUDR: + k230_dw_ssi_write_masked(s, R_BAUDR, value, + K230_DW_SSI_BAUDR_WRITABLE_MASK); + break; + case A_TXFTLR: + k230_dw_ssi_write_masked(s, R_TXFTLR, value, + K230_DW_SSI_TXFTLR_WRITABLE_MASK); + break; + case A_RXFTLR: + k230_dw_ssi_write_masked(s, R_RXFTLR, value, + K230_DW_SSI_RXFTLR_WRITABLE_MASK); + break; + case A_TXFLR: + case A_RXFLR: + case A_SR: + break; + case A_IMR: + k230_dw_ssi_write_masked(s, R_IMR, value, + K230_DW_SSI_IMR_WRITABLE_MASK); + break; + case A_ISR: + case A_RISR: + break; + case A_TXEICR: + case A_RXOICR: + case A_RXUICR: + case A_MSTICR: + case A_ICR: + break; + case A_DMACR: + k230_dw_ssi_write_masked(s, R_DMACR, value, + K230_DW_SSI_DMACR_WRITABLE_MASK); + if (FIELD_EX32(s->regs[R_DMACR], DMACR, IDMAE)) { + qemu_log_mask(LOG_UNIMP, + "%s: DMACR.IDMAE enabled, internal DMA is not " + "implemented\n", + DEVICE(s)->canonical_path); + } + break; + case A_AXIAWLEN: + k230_dw_ssi_write_masked(s, R_AXIAWLEN, value, + K230_DW_SSI_AXIAWLEN_WRITABLE_MASK); + break; + case A_AXIARLEN: + k230_dw_ssi_write_masked(s, R_AXIARLEN, value, + K230_DW_SSI_AXIARLEN_WRITABLE_MASK); + break; + case A_IDR: + case A_SSIC_VERSION_ID: + break; + case A_RX_SAMPLE_DELAY: + k230_dw_ssi_write_masked( + s, R_RX_SAMPLE_DELAY, value, + K230_DW_SSI_RX_SAMPLE_DELAY_WRITABLE_MASK); + break; + case A_SPI_CTRLR0: + k230_dw_ssi_write_masked( + s, R_SPI_CTRLR0, value, + K230_DW_SSI_SPI_CTRLR0_WRITABLE_MASK); + break; + case A_DDR_DRIVE_EDGE: + k230_dw_ssi_write_masked( + s, R_DDR_DRIVE_EDGE, value, + K230_DW_SSI_DDR_DRIVE_EDGE_WRITABLE_MASK); + break; + case A_XIP_MODE_BITS: + k230_dw_ssi_write_masked( + s, R_XIP_MODE_BITS, value, + K230_DW_SSI_XIP_MODE_BITS_WRITABLE_MASK); + break; + case A_XIP_INCR_INST: + k230_dw_ssi_write_masked( + s, R_XIP_INCR_INST, value, + K230_DW_SSI_XIP_INCR_INST_WRITABLE_MASK); + break; + case A_XIP_WRAP_INST: + k230_dw_ssi_write_masked( + s, R_XIP_WRAP_INST, value, + K230_DW_SSI_XIP_WRAP_INST_WRITABLE_MASK); + break; + case A_SPIDR: + k230_dw_ssi_write_masked(s, R_SPIDR, value, + K230_DW_SSI_SPIDR_WRITABLE_MASK); + break; + case A_SPIAR: + k230_dw_ssi_write_masked(s, R_SPIAR, value, + K230_DW_SSI_SPIAR_WRITABLE_MASK); + break; + case A_AXIAR0: + k230_dw_ssi_write_masked(s, R_AXIAR0, value, + K230_DW_SSI_AXIAR0_WRITABLE_MASK); + break; + case A_AXIAR1: + k230_dw_ssi_write_masked(s, R_AXIAR1, value, + K230_DW_SSI_AXIAR1_WRITABLE_MASK); + break; + case A_AXIECR: + case A_DONECR: + break; + default: + if (addr >=3D K230_DW_SSI_REGS_SIZE || (addr & 0x3) !=3D 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, addr); + } + break; + } +} + +static const MemoryRegionOps k230_dw_ssi_ops =3D { + .read =3D k230_dw_ssi_read, + .write =3D k230_dw_ssi_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, +}; + +static void k230_dw_ssi_enter_reset(Object *obj, ResetType type) +{ + K230DwSsiState *s =3D K230_DW_SSI(obj); + + memset(s->regs, 0, sizeof(s->regs)); + fifo32_reset(&s->tx_fifo); + fifo32_reset(&s->rx_fifo); + + s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; + s->regs[R_SR] =3D K230_DW_SSI_SR_RESET; + s->regs[R_IMR] =3D K230_DW_SSI_IMR_RESET; + s->regs[R_AXIAWLEN] =3D K230_DW_SSI_AXIAWLEN_RESET; + s->regs[R_AXIARLEN] =3D K230_DW_SSI_AXIARLEN_RESET; + s->regs[R_IDR] =3D K230_DW_SSI_IDR_RESET; + s->regs[R_SSIC_VERSION_ID] =3D K230_DW_SSI_VERSION; + s->regs[R_SPI_CTRLR0] =3D s->max_lines =3D=3D 8 ? + K230_DW_SSI_SPI_CTRLR0_FMC_RESET : + K230_DW_SSI_SPI_CTRLR0_SPI_RESET; +} + +static void k230_dw_ssi_hold_reset(Object *obj, ResetType type) +{ + K230DwSsiState *s =3D K230_DW_SSI(obj); + s->active_cs =3D -1; + + if (s->cs_lines) { + for (int i =3D 0; i < s->num_cs; i++) { + qemu_irq_raise(s->cs_lines[i]); + } + } +} + +static const VMStateDescription vmstate_k230_dw_ssi =3D { + .name =3D TYPE_K230_DW_SSI, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, K230DwSsiState, K230_DW_SSI_NUM_REGS), + VMSTATE_FIFO32(tx_fifo, K230DwSsiState), + VMSTATE_FIFO32(rx_fifo, K230DwSsiState), + VMSTATE_INT32(active_cs, K230DwSsiState), + VMSTATE_END_OF_LIST() + }, +}; + +static void k230_dw_ssi_init(Object *obj) +{ + K230DwSsiState *s =3D K230_DW_SSI(obj); + DeviceState *dev =3D DEVICE(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + + s->spi =3D ssi_create_bus(dev, "spi"); + + memory_region_init_io(&s->mmio, obj, &k230_dw_ssi_ops, s, + TYPE_K230_DW_SSI, K230_DW_SSI_MMIO_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + fifo32_create(&s->tx_fifo, K230_DW_SSI_FIFO_CAPACITY); + fifo32_create(&s->rx_fifo, K230_DW_SSI_FIFO_CAPACITY); + s->active_cs =3D -1; +} + +static void k230_dw_ssi_realize(DeviceState *dev, Error **errp) +{ + K230DwSsiState *s =3D K230_DW_SSI(dev); + + if (s->num_cs =3D=3D 0 || s->num_cs > 8) { + error_setg(errp, "%s: num-cs must be in range 1..8", + dev->canonical_path); + return; + } + + if (s->max_lines !=3D 1 && s->max_lines !=3D 4 && s->max_lines !=3D 8)= { + error_setg(errp, "%s: max-lines must be 1, 4, or 8", + dev->canonical_path); + return; + } + + s->cs_lines =3D g_new0(qemu_irq, s->num_cs); + qdev_init_gpio_out_named(dev, s->cs_lines, "cs", s->num_cs); +} + +static void k230_dw_ssi_finalize(Object *obj) +{ + K230DwSsiState *s =3D K230_DW_SSI(obj); + + fifo32_destroy(&s->tx_fifo); + fifo32_destroy(&s->rx_fifo); + g_free(s->cs_lines); +} + +static const Property k230_dw_ssi_properties[] =3D { + DEFINE_PROP_UINT32("num-cs", K230DwSsiState, num_cs, 1), + DEFINE_PROP_UINT32("max-lines", K230DwSsiState, max_lines, 1), +}; + +static void k230_dw_ssi_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->realize =3D k230_dw_ssi_realize; + dc->vmsd =3D &vmstate_k230_dw_ssi; + device_class_set_props(dc, k230_dw_ssi_properties); + rc->phases.enter =3D k230_dw_ssi_enter_reset; + rc->phases.hold =3D k230_dw_ssi_hold_reset; +} + +static const TypeInfo k230_dw_ssi_info =3D { + .name =3D TYPE_K230_DW_SSI, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(K230DwSsiState), + .instance_init =3D k230_dw_ssi_init, + .instance_finalize =3D k230_dw_ssi_finalize, + .class_init =3D k230_dw_ssi_class_init, +}; + +static void k230_dw_ssi_register_types(void) +{ + type_register_static(&k230_dw_ssi_info); +} + +type_init(k230_dw_ssi_register_types) diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build index 6afb1ea200..1e9c1d72c4 100644 --- a/hw/ssi/meson.build +++ b/hw/ssi/meson.build @@ -1,6 +1,7 @@ system_ss.add(when: 'CONFIG_ALLWINNER_A10_SPI', if_true: files('allwinner-= a10-spi.c')) system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c')) system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c')) +system_ss.add(when: 'CONFIG_K230_DW_SSI', if_true: files('k230_dw_ssi.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npc= m_pspi.c')) system_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c')) system_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c')) diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h new file mode 100644 index 0000000000..953f6cc0e5 --- /dev/null +++ b/include/hw/ssi/k230_dw_ssi.h @@ -0,0 +1,50 @@ +/* + * Kendryte K230 DesignWare SSI + * + * Copyright (c) 2026 Kangjie Huang + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Emulates the DesignWare SSI controllers documented by the K230 + * Technical Reference Manual, including standard SPI, Dual/Quad SDR, + * internal DMA, and the XIP read window. + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Tec= hnical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see + */ + +#ifndef HW_SSI_K230_DW_SSI_H +#define HW_SSI_K230_DW_SSI_H + +#include "hw/core/sysbus.h" +#include "hw/ssi/ssi.h" +#include "qemu/fifo32.h" +#include "qom/object.h" + +#define TYPE_K230_DW_SSI "riscv.k230.dw-ssi" +OBJECT_DECLARE_SIMPLE_TYPE(K230DwSsiState, K230_DW_SSI) + +#define K230_DW_SSI_MMIO_SIZE 0x1000 +#define K230_DW_SSI_REGS_SIZE 0x14c +#define K230_DW_SSI_NUM_REGS \ + (K230_DW_SSI_REGS_SIZE / sizeof(uint32_t)) + +struct K230DwSsiState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + SSIBus *spi; + qemu_irq *cs_lines; + + Fifo32 tx_fifo; + Fifo32 rx_fifo; + uint32_t regs[K230_DW_SSI_NUM_REGS]; + + uint32_t num_cs; + uint32_t max_lines; + int active_cs; +}; + +#endif /* HW_SSI_K230_DW_SSI_H */ --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069004; cv=none; d=zohomail.com; s=zohoarc; b=XwM3zciZNN0BN0TdT0QabNJaEh2hS5neJMye2/Za4hBH3UpL0NMeUGGMkVM2mHg8zosqSLyhEDxpqpWgDFuObrcolP4tFVeJnQKgJrQmwJIRq0SKtkO39TddT7nxqZmVOG99HuGvEbIWvqQNK3p4rL22EPyN58iJoRCBBfH2Msk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069004; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=HjFRlm+7Pma7rC3OyN70J41dQ5x7B6INTRW4EeE9d/E=; b=lPmJ/4x4D5ZEa+0f54REW4IDCnI/O3HDYZVV/QCpGcq8uvth5LZ8rHoJaLj4llqLRFMqIe9NzY+v4O8GMmTtPVuvGD1H9g+uotKuvKScfFz2j3omsDVGxRVsJHPhnom1HL7wRNlDYcHbLtmwGEJCE+lF+h6xUnGD7NpNzTmmAOw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069004384226.0257775471971; Sun, 26 Jul 2026 05:30:04 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxym-0001Bh-DN; Sun, 26 Jul 2026 08:29:16 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxyg-00019n-8B for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:10 -0400 Received: from mail-pf1-x42d.google.com ([2607:f8b0:4864:20::42d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxyd-0003HR-W0 for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:09 -0400 Received: by mail-pf1-x42d.google.com with SMTP id d2e1a72fcca58-848643382fcso1938240b3a.1 for ; Sun, 26 Jul 2026 05:29:07 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068946; x=1785673746; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=HjFRlm+7Pma7rC3OyN70J41dQ5x7B6INTRW4EeE9d/E=; b=b2p37hLsafLVdOwx5oDuIFmP/m5eAZFym1p7IQaV7H/xbVexJt2Od9vYXCVxBllNj7 2qGjNwYwzUQ/rewyFBZ9ocYHBF9kz8XgT8GksXBJ5sek58qp2tE0X8KJlqpCH9DbYK79 Y+566no9GHAn3gL6Js7xaMA4vO3Aw89Sa8vHZ71DKSjr6sygYel+X0VXlM3wy+P+P4NR QoN3WOY+egso58+cLPzzd768dwaVCbifvSP5/GwVU/htJHzSHSzZRf9T5nw3nZTowKEl rJW7wTkVgjP32KLiM/Sl4l0KbaejKjg0nNj3btUWSBaQMn1jrCtWU0tRA6lyCsjT1YFk ERcg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068946; x=1785673746; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=HjFRlm+7Pma7rC3OyN70J41dQ5x7B6INTRW4EeE9d/E=; b=JDN57YxXKWNOsOVo9O1yZZDW5u2OXUuXVcFf7y3xdzn/tvQ06DLD09Tp15pBXCbOD9 1pHPV5g0f0faSARRitXEkoz++n8lIvFlz+gfl31BYRjSTXV/UD2SjlXezVP/FLWnKuRb sZDr1hEsRLrY34aibDb/6eS0assRCGE9zZkiTBib7zZI4MS5oLp6TnQ4sFmElMrwn4r6 +JpFWudLmJmZMOKmhE6hnArXPRDUDgt+Uw58in4mDH3G2gZdAE6DuDtSVnTT2Ku0PWV7 uc1pe/P8Cbsx43kUDJMrbHzrqyTTEhvAc1Rl+z3FHRgjDtipgGP8DrRqbeHblVm63Old FAfA== X-Gm-Message-State: AOJu0Ywf+g9nIasA/zmASf0e+ZHWMNt3EtK/+PHVYCvGfzVVpL4wXR1p 7bZ7IJT4Ff/UnCS3ZAV8fP92DAGw/WXIoxWFhFQKjJdORnn8xa0CTIzadOnMBqJyFcY= X-Gm-Gg: AR+sD1210enQnZ4G1oE3fgEK27d3Ma2M1P9MfGmKQeoKi60MW5K+KkdLeqWxtQP64lz iVFM+0HAzxZ/xPpTrmOTZ8h9nGJl/MTRIITV6LDymSWtQ6cN6JOgNWGF+MJzNuWsAhBTBCrXVNx jBJu7IFChPBLuGlV51OBlOSd7RpDsRscizK8EQXP1pyYwspmPGIaW2iQX0RtVLzfTHC5BZEvaN3 o+QONsmkTJe/6gotDq/oQrZGrW54Sx9KxB+MWRf6HGjHzNxW1cHwv3K/ADZygzxas7NEKZubKp3 4XFoDg8pfV8gn5rRgawRyZ1iOcMonFt5+pMCfk4d0WCVngbvk2fgsHYLe3ugARKWSh9esfimWAP DE5B2jNWPeCsfxKIYc5zBkcXMbZTuSOlm/vu4rAGcjju3yBxz6rnWtezyDoF8WtslOOmwyLwhiO gywve1bCWf/+8JlZCHWYwSqzN5goGWgjI= X-Received: by 2002:a05:6a00:6c95:b0:848:63c9:ee39 with SMTP id d2e1a72fcca58-84e5942e856mr3781574b3a.12.1785068946465; Sun, 26 Jul 2026 05:29:06 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 02/11] hw/riscv/k230: Instantiate K230 SSI controllers Date: Sun, 26 Jul 2026 20:28:20 +0800 Message-ID: <41750ee3b9b96b8f6caf234b0855f4cae79b761c.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42d; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42d.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069005207158500 Content-Type: text/plain; charset="utf-8" Instantiate the three SSI controller profiles used by K230 and map their MMIO regions at the documented addresses. Add the K230 SSI qtest target. Cover the three instance profiles, reset state, chip-select masks, representative register masks, and system reset behaviour. Signed-off-by: Kangjie Huang --- docs/system/riscv/k230.rst | 1 + hw/riscv/Kconfig | 1 + hw/riscv/k230.c | 35 +++++-- include/hw/riscv/k230.h | 2 + tests/qtest/k230-dw-ssi-test.c | 178 +++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 4 +- 6 files changed, 211 insertions(+), 10 deletions(-) create mode 100644 tests/qtest/k230-dw-ssi-test.c diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index cea8202e55..0358709d2c 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -20,6 +20,7 @@ The ``k230`` machine supports the following devices: * Platform-Level Interrupt Controller (PLIC) * 2 K230 Watchdog Timer * 5 UART +* 3 K230 SSI controllers for SPI and QSPI =20 Boot options ------------ diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index de37c08cae..69059ae044 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_DW_SSI diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 656f28190c..acd5445b31 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -110,6 +110,12 @@ 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-qspi0", &s->dw_ssi[0], + TYPE_K230_DW_SSI); + object_initialize_child(obj, "k230-qspi1", &s->dw_ssi[1], + TYPE_K230_DW_SSI); + object_initialize_child(obj, "k230-spi-opi", &s->dw_ssi[2], + TYPE_K230_DW_SSI); =20 qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C9= 08); @@ -198,6 +204,19 @@ static void k230_soc_realize(DeviceState *dev, Error *= *errp) } } =20 + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[0]), "num-cs", 5); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[1]), "num-cs", 5); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[2]), "num-cs", 1); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[0]), "max-lines", 4); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[1]), "max-lines", 4); + qdev_prop_set_uint32(DEVICE(&s->dw_ssi[2]), "max-lines", 8); + + for (int i =3D 0; i < ARRAY_SIZE(s->dw_ssi); i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->dw_ssi[i]), errp)) { + return; + } + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].b= ase); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[0]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT0_IR= Q)); @@ -206,6 +225,13 @@ 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_IR= Q)); =20 + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[0]), 0, + memmap[K230_DEV_QSPI0].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[1]), 0, + memmap[K230_DEV_QSPI1].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[2]), 0, + memmap[K230_DEV_SPI].base); + /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", memmap[K230_DEV_KPU_L2_CACHE].base, @@ -349,15 +375,6 @@ static void k230_soc_realize(DeviceState *dev, Error *= *errp) create_unimplemented_device("sd1", memmap[K230_DEV_SD1].base, memmap[K230_DEV_SD1].size); =20 - create_unimplemented_device("qspi0", memmap[K230_DEV_QSPI0].base, - memmap[K230_DEV_QSPI0].size); - - create_unimplemented_device("qspi1", memmap[K230_DEV_QSPI1].base, - memmap[K230_DEV_QSPI1].size); - - create_unimplemented_device("spi", memmap[K230_DEV_SPI].base, - memmap[K230_DEV_SPI].size); - create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].= base, memmap[K230_DEV_HI_SYS_CFG].size); =20 diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 592e1c26bf..4a79b7ed45 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -17,6 +17,7 @@ =20 #include "hw/core/boards.h" #include "hw/riscv/riscv_hart.h" +#include "hw/ssi/k230_dw_ssi.h" #include "hw/watchdog/k230_wdt.h" =20 #define C908_CPU_HARTID (0) @@ -33,6 +34,7 @@ typedef struct K230SoCState { RISCVHartArrayState c908_cpu; /* Small core */ =20 K230WdtState wdt[2]; + K230DwSsiState dw_ssi[3]; MemoryRegion sram; MemoryRegion bootrom; =20 diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c new file mode 100644 index 0000000000..dcd6cb1d6e --- /dev/null +++ b/tests/qtest/k230-dw-ssi-test.c @@ -0,0 +1,178 @@ +/* + * QTest for the Kendryte K230 DesignWare SSI + * + * Copyright (c) 2026 Kangjie Huang + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define K230_SPI0_BASE 0x91584000ULL +#define K230_SPI1_BASE 0x91582000ULL +#define K230_SPI2_BASE 0x91583000ULL +#define K230_SSI_CTRLR0 0x000 +#define K230_SSI_CTRLR1 0x004 +#define K230_SSI_SSIENR 0x008 +#define K230_SSI_SER 0x010 +#define K230_SSI_BAUDR 0x014 +#define K230_SSI_TXFTLR 0x018 +#define K230_SSI_TXFLR 0x020 +#define K230_SSI_RXFLR 0x024 +#define K230_SSI_SR 0x028 +#define K230_SSI_IMR 0x02c +#define K230_SSI_ISR 0x030 +#define K230_SSI_RISR 0x034 +#define K230_SSI_DMACR 0x04c +#define K230_SSI_IDR 0x058 +#define K230_SSI_VERSION_ID 0x05c +#define K230_SSI_SPI_CTRLR0 0x0f4 +#define K230_SSI_XIP_MODE_BITS 0x0fc +#define K230_SSI_XIP_INCR_INST 0x100 +#define K230_SSI_SPIDR 0x120 +#define K230_SSI_SPIAR 0x124 +#define K230_SSI_AXIAR0 0x128 +#define K230_SSI_AXIAR1 0x12c +#define K230_SSI_AXIECR 0x130 +#define K230_SSI_DONECR 0x134 + +#define K230_SSI_CTRLR0_RESET 0x00004007U +#define K230_SSI_SPI_CTRLR0_SPI_RESET 0x04000200U +#define K230_SSI_SPI_CTRLR0_FMC_RESET 0x28000200U +#define K230_SSI_IMR_RESET 0x0000003fU +#define K230_SSI_IDR_RESET 0xa1b2c3d5U +#define K230_SSI_VERSION_RESET 0x3130332aU + +#define K230_SSI_CTRLR0_WRITABLE_MASK 0x01cf7f1fU +#define K230_SSI_BAUDR_WRITABLE_MASK 0x0000fffeU + +#define K230_SSI_CTRLR0_DFS_MASK 0x1fU +#define K230_SSI_CTRLR0_TMOD_SHIFT 10 + +#define K230_SSI_TMOD_TR 0 +#define K230_SSI_TMOD_EEPROM_READ 3 + +#define K230_SSI_SR_BUSY BIT(0) +#define K230_SSI_SR_TFNF BIT(1) +#define K230_SSI_SR_TFE BIT(2) +#define K230_SSI_SR_RFNE BIT(3) + +#define K230_SSI_INT_AXIE BIT(8) +#define K230_SSI_INT_DONE BIT(11) + +typedef struct K230SsiInstance { + uint64_t base; + uint32_t num_cs; + uint32_t spi_ctrlr0_reset; +} K230SsiInstance; + +static const K230SsiInstance k230_ssi_instances[3] =3D { + { + .base =3D K230_SPI0_BASE, + .num_cs =3D 1, + .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_FMC_RESET, + }, { + .base =3D K230_SPI1_BASE, + .num_cs =3D 5, + .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_SPI_RESET, + }, { + .base =3D K230_SPI2_BASE, + .num_cs =3D 5, + .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_SPI_RESET, + }, +}; + +static QTestState *k230_ssi_start(void) +{ + return qtest_init("-machine k230"); +} + +static uint32_t k230_ssi_readl(QTestState *qts, uint64_t base, + uint32_t offset) +{ + return qtest_readl(qts, base + offset); +} + +static void k230_ssi_writel(QTestState *qts, uint64_t base, + uint32_t offset, uint32_t value) +{ + qtest_writel(qts, base + offset, value); +} + +static void k230_ssi_disable(QTestState *qts, uint64_t base) +{ + k230_ssi_writel(qts, base, K230_SSI_SSIENR, 0); +} + +static void k230_ssi_configure(QTestState *qts, uint64_t base, + uint32_t tmod, uint32_t dfs_bits, + uint32_t ndf) +{ + uint32_t ctrlr0; + + g_assert_cmpuint(dfs_bits, >=3D, 4); + g_assert_cmpuint(dfs_bits, <=3D, 32); + g_assert_cmpuint(tmod, <=3D, K230_SSI_TMOD_EEPROM_READ); + + k230_ssi_disable(qts, base); + ctrlr0 =3D (dfs_bits - 1) & K230_SSI_CTRLR0_DFS_MASK; + ctrlr0 |=3D tmod << K230_SSI_CTRLR0_TMOD_SHIFT; + k230_ssi_writel(qts, base, K230_SSI_CTRLR0, ctrlr0); + k230_ssi_writel(qts, base, K230_SSI_CTRLR1, ndf); + k230_ssi_writel(qts, base, K230_SSI_BAUDR, 2); +} + +static void k230_ssi_enable_cs(QTestState *qts, uint64_t base, uint32_t se= r) +{ + k230_ssi_writel(qts, base, K230_SSI_SER, ser); + k230_ssi_writel(qts, base, K230_SSI_SSIENR, 1); +} + +static void test_register_contract(void) +{ + QTestState *qts =3D k230_ssi_start(); + + for (int i =3D 0; i < ARRAY_SIZE(k230_ssi_instances); i++) { + const K230SsiInstance *inst =3D &k230_ssi_instances[i]; + + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_CTRLR0), + =3D=3D, K230_SSI_CTRLR0_RESET); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_SSIENR), + =3D=3D, 0); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, + K230_SSI_SPI_CTRLR0), + =3D=3D, inst->spi_ctrlr0_reset); + k230_ssi_writel(qts, inst->base, K230_SSI_SER, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, inst->base, K230_SSI_SER), + =3D=3D, MAKE_64BIT_MASK(0, inst->num_cs)); + } + + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_IDR), + =3D=3D, K230_SSI_IDR_RESET); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, + K230_SSI_VERSION_ID), + =3D=3D, K230_SSI_VERSION_RESET); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_CTRLR0, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_CTRLR0), + =3D=3D, K230_SSI_CTRLR0_WRITABLE_MASK); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_BAUDR, UINT32_MAX); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_BAUDR), + =3D=3D, K230_SSI_BAUDR_WRITABLE_MASK); + + qtest_system_reset(qts); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0), + =3D=3D, K230_SSI_CTRLR0_RESET); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_IMR), + =3D=3D, K230_SSI_IMR_RESET); + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/k230-dw-ssi/register-contract", test_register_contrac= t); + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 56ff860e21..f5bcc7bece 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -297,7 +297,8 @@ qtests_riscv64 =3D ['riscv-csr-test'] + \ (config_all_devices.has_key('CONFIG_IOMMU_TESTDEV') and config_all_devices.has_key('CONFIG_RISCV_IOMMU') ? ['iommu-riscv-test'] : []) + \ - (config_all_devices.has_key('CONFIG_K230') ? ['k230-wdt-test'] : []) + (config_all_devices.has_key('CONFIG_K230') ? + ['k230-wdt-test', 'k230-dw-ssi-test'] : []) =20 qtests_hexagon =3D ['boot-serial-test'] =20 @@ -400,6 +401,7 @@ qtests =3D { [gio], 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], + 'k230-dw-ssi-test': files('k230-dw-ssi-test.c'), 'migration-test': test_migration_files + migration_tls_files + migration= _colo_files, 'pxe-test': files('boot-sector.c'), 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c', --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069018; cv=none; d=zohomail.com; s=zohoarc; b=BT7LJ9s6xhwEbiQj5W/nAz+u97yspje2FtzmDH+hnLgvKcXl/r9Clklg6uCmU4wtujVfEpqm+i1EGDFyVUX6puLPrEHdGrk8RIgXawCcz8iGt1K8YD84p8fhK1A3XsCoVzlIU+sPo2DxZPCsCO44+k8+Xznet72I3VHmPIXUTy4= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069018; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=RfeUrKHUGv+U9oRVEXuUSDC6Vwliyabt5KfqsBfccrY=; b=LiwykFFdwocAuOlMrqn0mpeacZtzM0NqGedcQI9n8PC9llCpdnploztEI/SsNgCCp+IuDiOzatqrjk7fRl34D7tx68gOHCTr/OPPCejBmupcJ84whnBG2mDTSuRvI9HPRDft//Dv2lA8955WvtORF7Ko/l4yHhB2Iv8E6OUsxFw= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069018852506.89191400772347; Sun, 26 Jul 2026 05:30:18 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxyz-0001Jc-3P; Sun, 26 Jul 2026 08:29:29 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxyo-0001Fu-Qm for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:20 -0400 Received: from mail-pf1-x431.google.com ([2607:f8b0:4864:20::431]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxym-0003I2-LK for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:18 -0400 Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-84a4d8fd6ecso1856018b3a.1 for ; Sun, 26 Jul 2026 05:29:16 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068955; x=1785673755; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=RfeUrKHUGv+U9oRVEXuUSDC6Vwliyabt5KfqsBfccrY=; b=QaLS2140Sal3XJhimitHJlK2Ku9oME2A91EkFwvc5B1Jc/vnhNKheZ4JMavAGxSRJb BhmSi8Xo52HEpiN2h6kwzSYgld2O+7E63NcTjrMIX0028zo19lJ/Kk6aa6VuvAhxDTID Odqj/NIIijsO73d4XRD15tzODV1Tb0fpg3qAtG5OJB+U/iD8TF2tJ9OMWpEDP+FlKbpz D5rx6s7XglK/6eLlguRkxjPkssqtD0gU683CGTklBJqOQYyho14gomrX8EQhVHt2SaZk khBH437OlU0f8OmP/3qlxvVGp3STZIkSU+bZyzXLT0z0C36w9wgibhMakOsL2vIAIldy amGw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068955; x=1785673755; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=RfeUrKHUGv+U9oRVEXuUSDC6Vwliyabt5KfqsBfccrY=; b=YUHYdQuhb8UUCgN4LWglaq89Pa/Ep6aFKwjgOZ32NYldPPzpC4NMEB1BziQ5vSQF0q ofoU4kcouFAPvXMawvaRsgkrLeKEn0kgAnPhmk2KBSpWoxB3B+Hobiu1MpN7ZAXUXKEM ir17DmWKyvXt8fc9GXkvX29e+L2xm8OEKDZAGRiXgjK9xWSCy/tA911xgRbClxadKy1y /VUH41uaPNyi1DEiKlaWOHlOSeNknknYxGD8WM8rJbqvnKiAAoHqGDhZWMrFYKSh6Bf1 kVdDQod9+CYIA+sutxDdslygUFHkXu48PVLfATAn3HX/6DuALjGUAPDLG4WP27sS7a4Z uH8A== X-Gm-Message-State: AOJu0YzdG4xzBfp6Z5j3xDf1NypC3vwXp78N2CZ5BbdujgtGw0jdnUUm zQVahhEv9GhYKPJc6bpcANq+fo/9UtxhZTy8sHLSedTfxrchgWigeItltmQPN4nKrIE= X-Gm-Gg: AR+sD13MWv5naUu1PQXXX3gQZprdYeut5GYG3DmRI6D7kgZVxSl+J4vj2CE7AFBSqx/ nL0riyq6sfDYecp27H6+NqxHezOGnfq50mLs146ZGzxMoT0KDd/pq3YotF4nbPdjPKdNCmAb9+I 3txOuJ0wPae/HbzZNtWsbBT7qI7ID2O3R+Kfh9gatCiQUgAop9Cu+0bR0xhwP4k6QBfpU8jzueS v1FaPXZh9YUx/bJwkaE5/6K4wL72cG0prL9FMYabIdCtDNM5Y/HQrarmSinkerUaUCTXHBDmOaq UKVwZGmmI/NmVN/WjMN5TxYWx0Aqa2NnaB6Q3MvdVyxBs45tdwrZoOUQ3KD98w0QAw6UrvGEa+G T89ud5ySpPP3JlPdFS1P4BZ62sPbDG/u92eOAbNmNNRcTQdM7zUCifAgg/Z8FCKcTDZVWZW2xUN pEmYCpP6l2xn3724ozIdMxBH8jdnwEU3w= X-Received: by 2002:a05:6a00:1f05:b0:846:c7f4:74d with SMTP id d2e1a72fcca58-84e5955b5b4mr3526491b3a.41.1785068955003; Sun, 26 Jul 2026 05:29:15 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 03/11] hw/ssi: Implement K230 SSI FIFO and standard PIO transfers Date: Sun, 26 Jul 2026 20:28:21 +0800 Message-ID: <6fc734d599f433d0f4e3193be70fcf60615e031e.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::431; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x431.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069021296158500 Content-Type: text/plain; charset="utf-8" Implement FIFO-backed standard SPI transfers for all four TMOD modes. Writes to DR while the TX FIFO is full are dropped, and frames wider than the DFS field are truncated on read, per the K230 TRM. Disabling the controller stops the serial engine but leaves queued frames in the FIFOs. Extend the qtest with an 8-bit loopback transfer, receive-only NDF handling, FIFO status checks, and FIFO cleanup on controller disable. Signed-off-by: Kangjie Huang --- hw/ssi/k230_dw_ssi.c | 160 ++++++++++++++++++++++++++++++++- include/hw/ssi/k230_dw_ssi.h | 11 +++ tests/qtest/k230-dw-ssi-test.c | 77 ++++++++++++++++ 3 files changed, 245 insertions(+), 3 deletions(-) diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index d0dcad4312..d7315f28ef 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -37,6 +37,7 @@ #define K230_DW_SSI_AXIAWLEN_RESET 0x00000700 #define K230_DW_SSI_AXIARLEN_RESET 0x00000700 #define K230_DW_SSI_VERSION 0x3130332a +#define K230_DW_SSI_PIO_TX_BATCH 64 =20 REG32(CTRLR0, 0x000) FIELD(CTRLR0, DFS, 0, 5) @@ -330,6 +331,8 @@ static void k230_dw_ssi_abort_transfer(K230DwSsiState *= s) k230_dw_ssi_deselect(s); fifo32_reset(&s->tx_fifo); fifo32_reset(&s->rx_fifo); + s->phase =3D K230_DW_SSI_PHASE_IDLE; + s->remaining_frames =3D 0; } =20 static uint32_t k230_dw_ssi_status(K230DwSsiState *s) @@ -338,6 +341,7 @@ static uint32_t k230_dw_ssi_status(K230DwSsiState *s) uint32_t rx_used =3D fifo32_num_used(&s->rx_fifo); uint32_t sr =3D 0; =20 + sr =3D FIELD_DP32(sr, SR, BUSY, s->phase !=3D K230_DW_SSI_PHASE_IDLE); sr =3D FIELD_DP32(sr, SR, TFNF, tx_used < K230_DW_SSI_FIFO_CAPACITY); sr =3D FIELD_DP32(sr, SR, TFE, tx_used =3D=3D 0); sr =3D FIELD_DP32(sr, SR, RFNE, rx_used !=3D 0); @@ -351,7 +355,7 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s); =20 static void k230_dw_ssi_push_tx(K230DwSsiState *s, uint32_t tx) { - if (!k230_dw_ssi_enabled(s) || s->active_cs < 0) { + if (!k230_dw_ssi_enabled(s)) { return; } =20 @@ -360,12 +364,139 @@ static void k230_dw_ssi_push_tx(K230DwSsiState *s, u= int32_t tx) } =20 fifo32_push(&s->tx_fifo, tx & k230_dw_ssi_frame_masked(s)); - k230_dw_ssi_run_transfer(s); + + if (s->phase !=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { + k230_dw_ssi_run_transfer(s); + } } =20 +static uint32_t k230_dw_ssi_send_frame(K230DwSsiState *s, + uint32_t tx) +{ + uint32_t mask =3D k230_dw_ssi_frame_masked(s); + uint32_t rx; + + tx &=3D mask; + + if (FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SRL)) { + rx =3D tx; + } else { + rx =3D ssi_transfer(s->spi, tx); + } + + return rx & mask; +} =20 static void k230_dw_ssi_run_transfer(K230DwSsiState *s) { + uint32_t spi_frf; + uint32_t tmod; + + if (!k230_dw_ssi_enabled(s) || s->active_cs < 0) { + return; + } + + spi_frf =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); + if (spi_frf !=3D 0) { + return; + } + + tmod =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, TMOD); + + switch (tmod) { + case 0: /* TX_AND_RX */ + while (!fifo32_is_empty(&s->tx_fifo)) { + uint32_t tx =3D fifo32_pop(&s->tx_fifo); + uint32_t rx =3D k230_dw_ssi_send_frame(s, tx); + if (!fifo32_is_full(&s->rx_fifo)) { + fifo32_push(&s->rx_fifo, rx); + } + } + break; + case 1: { /* TX_ONLY */ + unsigned int frames =3D 0; + + if (fifo32_is_empty(&s->tx_fifo)) { + s->phase =3D K230_DW_SSI_PHASE_IDLE; + break; + } + + s->phase =3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY; + while (!fifo32_is_empty(&s->tx_fifo) && + frames < K230_DW_SSI_PIO_TX_BATCH) { + uint32_t tx =3D fifo32_pop(&s->tx_fifo); + + k230_dw_ssi_send_frame(s, tx); + frames++; + } + if (fifo32_is_empty(&s->tx_fifo)) { + s->phase =3D K230_DW_SSI_PHASE_IDLE; + } + break; + } + case 2: /* RX_ONLY */ + switch (s->phase) { + case K230_DW_SSI_PHASE_IDLE: + if (fifo32_is_empty(&s->tx_fifo)) { + break; + } + fifo32_pop(&s->tx_fifo); + s->phase =3D K230_DW_SSI_PHASE_RX_ONLY; + s->remaining_frames =3D FIELD_EX32(s->regs[R_CTRLR1], CTRLR1, + NDF) + 1; + /* Fall through. */ + case K230_DW_SSI_PHASE_RX_ONLY: + while (!fifo32_is_full(&s->rx_fifo) && + s->remaining_frames > 0) { + uint32_t rx =3D k230_dw_ssi_send_frame(s, 0x00); + + fifo32_push(&s->rx_fifo, rx); + s->remaining_frames--; + } + if (s->remaining_frames =3D=3D 0) { + s->phase =3D K230_DW_SSI_PHASE_IDLE; + } + break; + } + break; + case 3: /* EEPROM_READ */ + switch (s->phase) { + case K230_DW_SSI_PHASE_IDLE: + if (fifo32_is_empty(&s->tx_fifo)) { + break; + } + s->phase =3D K230_DW_SSI_PHASE_EEPROM_COMMAND; + /* Fall through. */ + case K230_DW_SSI_PHASE_EEPROM_COMMAND: + if (fifo32_is_empty(&s->tx_fifo)) { + break; + } + while (!fifo32_is_empty(&s->tx_fifo)) { + uint32_t tx =3D fifo32_pop(&s->tx_fifo); + + k230_dw_ssi_send_frame(s, tx); + } + s->phase =3D K230_DW_SSI_PHASE_EEPROM_DATA; + s->remaining_frames =3D FIELD_EX32(s->regs[R_CTRLR1], CTRLR1, + NDF) + 1; + /* Fall through. */ + case K230_DW_SSI_PHASE_EEPROM_DATA: + while (!fifo32_is_full(&s->rx_fifo) && + s->remaining_frames > 0) { + uint32_t rx =3D k230_dw_ssi_send_frame(s, 0x00); + + fifo32_push(&s->rx_fifo, rx); + s->remaining_frames--; + } + if (s->remaining_frames =3D=3D 0) { + s->phase =3D K230_DW_SSI_PHASE_IDLE; + } + break; + } + break; + default: + g_assert_not_reached(); + } } =20 =20 @@ -419,6 +550,8 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr a= ddr, unsigned int size) if (!fifo32_is_empty(&s->rx_fifo)) { value =3D fifo32_pop(&s->rx_fifo) & k230_dw_ssi_frame_masked(s= ); } + + k230_dw_ssi_run_transfer(s); return value; } =20 @@ -457,12 +590,18 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) break; case A_TXFLR: value =3D fifo32_num_used(&s->tx_fifo); + if (s->phase =3D=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { + k230_dw_ssi_run_transfer(s); + } break; case A_RXFLR: value =3D fifo32_num_used(&s->rx_fifo); break; case A_SR: value =3D k230_dw_ssi_status(s); + if (s->phase =3D=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { + k230_dw_ssi_run_transfer(s); + } break; case A_ISR: case A_RISR: @@ -538,15 +677,26 @@ static void k230_dw_ssi_write(void *opaque, hwaddr ad= dr, } =20 k230_dw_ssi_update_cs(s); + k230_dw_ssi_run_transfer(s); break; } case A_MWCR: k230_dw_ssi_write_masked(s, R_MWCR, value, K230_DW_SSI_MWCR_WRITABLE_MASK); break; - case A_SER: + case A_SER: { + uint32_t old_ser =3D s->regs[R_SER]; + s->regs[R_SER] =3D value & MAKE_64BIT_MASK(0, s->num_cs); + if (old_ser && !s->regs[R_SER]) { + k230_dw_ssi_abort_transfer(s); + break; + } + + k230_dw_ssi_update_cs(s); + k230_dw_ssi_run_transfer(s); break; + } case A_BAUDR: k230_dw_ssi_write_masked(s, R_BAUDR, value, K230_DW_SSI_BAUDR_WRITABLE_MASK); @@ -679,6 +829,8 @@ static void k230_dw_ssi_enter_reset(Object *obj, ResetT= ype type) memset(s->regs, 0, sizeof(s->regs)); fifo32_reset(&s->tx_fifo); fifo32_reset(&s->rx_fifo); + s->phase =3D K230_DW_SSI_PHASE_IDLE; + s->remaining_frames =3D 0; =20 s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; s->regs[R_SR] =3D K230_DW_SSI_SR_RESET; @@ -710,6 +862,8 @@ static const VMStateDescription vmstate_k230_dw_ssi =3D= { VMSTATE_UINT32_ARRAY(regs, K230DwSsiState, K230_DW_SSI_NUM_REGS), VMSTATE_FIFO32(tx_fifo, K230DwSsiState), VMSTATE_FIFO32(rx_fifo, K230DwSsiState), + VMSTATE_UINT32(phase, K230DwSsiState), + VMSTATE_UINT32(remaining_frames, K230DwSsiState), VMSTATE_INT32(active_cs, K230DwSsiState), VMSTATE_END_OF_LIST() }, diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index 953f6cc0e5..993cb19745 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -31,6 +31,14 @@ OBJECT_DECLARE_SIMPLE_TYPE(K230DwSsiState, K230_DW_SSI) #define K230_DW_SSI_NUM_REGS \ (K230_DW_SSI_REGS_SIZE / sizeof(uint32_t)) =20 +typedef enum K230DwSsiPhase { + K230_DW_SSI_PHASE_IDLE, + K230_DW_SSI_PHASE_STANDARD_TX_ONLY, + K230_DW_SSI_PHASE_RX_ONLY, + K230_DW_SSI_PHASE_EEPROM_COMMAND, + K230_DW_SSI_PHASE_EEPROM_DATA, +} K230DwSsiPhase; + struct K230DwSsiState { SysBusDevice parent_obj; =20 @@ -42,6 +50,9 @@ struct K230DwSsiState { Fifo32 rx_fifo; uint32_t regs[K230_DW_SSI_NUM_REGS]; =20 + uint32_t phase; + uint32_t remaining_frames; + uint32_t num_cs; uint32_t max_lines; int active_cs; diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index dcd6cb1d6e..a62b1a7ff6 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -28,6 +28,7 @@ #define K230_SSI_DMACR 0x04c #define K230_SSI_IDR 0x058 #define K230_SSI_VERSION_ID 0x05c +#define K230_SSI_DR0 0x060 #define K230_SSI_SPI_CTRLR0 0x0f4 #define K230_SSI_XIP_MODE_BITS 0x0fc #define K230_SSI_XIP_INCR_INST 0x100 @@ -50,8 +51,11 @@ =20 #define K230_SSI_CTRLR0_DFS_MASK 0x1fU #define K230_SSI_CTRLR0_TMOD_SHIFT 10 +#define K230_SSI_CTRLR0_SRL BIT(13) =20 #define K230_SSI_TMOD_TR 0 +#define K230_SSI_TMOD_TO 1 +#define K230_SSI_TMOD_RO 2 #define K230_SSI_TMOD_EEPROM_READ 3 =20 #define K230_SSI_SR_BUSY BIT(0) @@ -62,6 +66,8 @@ #define K230_SSI_INT_AXIE BIT(8) #define K230_SSI_INT_DONE BIT(11) =20 +#define K230_SSI_FIFO_DEPTH 256 + typedef struct K230SsiInstance { uint64_t base; uint32_t num_cs; @@ -130,6 +136,45 @@ static void k230_ssi_enable_cs(QTestState *qts, uint64= _t base, uint32_t ser) k230_ssi_writel(qts, base, K230_SSI_SSIENR, 1); } =20 +static void k230_ssi_write_frame(QTestState *qts, uint64_t base, + uint32_t value) +{ + k230_ssi_writel(qts, base, K230_SSI_DR0, value); +} + +static uint32_t k230_ssi_read_frame(QTestState *qts, uint64_t base) +{ + return k230_ssi_readl(qts, base, K230_SSI_DR0); +} + +static void k230_ssi_wait_mask(QTestState *qts, uint64_t base, + uint32_t offset, uint32_t mask, + uint32_t expected) +{ + for (int i =3D 0; i < 1000; i++) { + uint32_t value =3D k230_ssi_readl(qts, base, offset); + + if ((value & mask) =3D=3D expected) { + return; + } + qtest_clock_step(qts, 1000); + } + + g_assert_cmphex(k230_ssi_readl(qts, base, offset) & mask, + =3D=3D, expected); +} + +static void configure_loopback(QTestState *qts, uint32_t tmod, + uint32_t ndf) +{ + uint32_t ctrlr0; + + k230_ssi_configure(qts, K230_SPI1_BASE, tmod, 8, ndf); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_CTRLR0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_CTRLR0, + ctrlr0 | K230_SSI_CTRLR0_SRL); +} + static void test_register_contract(void) { QTestState *qts =3D k230_ssi_start(); @@ -169,10 +214,42 @@ static void test_register_contract(void) qtest_quit(qts); } =20 +static void test_pio_data_path(void) +{ + QTestState *qts =3D k230_ssi_start(); + uint32_t status; + + configure_loopback(qts, K230_SSI_TMOD_TR, 0); + k230_ssi_enable_cs(qts, K230_SPI1_BASE, BIT(0)); + k230_ssi_write_frame(qts, K230_SPI1_BASE, 0xa5); + k230_ssi_wait_mask(qts, K230_SPI1_BASE, K230_SSI_SR, + K230_SSI_SR_RFNE, K230_SSI_SR_RFNE); + g_assert_cmphex(k230_ssi_read_frame(qts, K230_SPI1_BASE), =3D=3D, 0xa5= ); + + configure_loopback(qts, K230_SSI_TMOD_RO, 3); + k230_ssi_enable_cs(qts, K230_SPI1_BASE, BIT(0)); + k230_ssi_write_frame(qts, K230_SPI1_BASE, 0); + k230_ssi_wait_mask(qts, K230_SPI1_BASE, K230_SSI_RXFLR, + UINT32_MAX, 4); + for (int i =3D 0; i < 4; i++) { + g_assert_cmphex(k230_ssi_read_frame(qts, K230_SPI1_BASE), =3D=3D, = 0); + } + + k230_ssi_disable(qts, K230_SPI1_BASE); + status =3D k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_SR); + g_assert_cmpuint(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_RXFLR), + =3D=3D, 0); + g_assert_cmphex(status & (K230_SSI_SR_BUSY | K230_SSI_SR_TFNF | + K230_SSI_SR_TFE | K230_SSI_SR_RFNE), + =3D=3D, K230_SSI_SR_TFNF | K230_SSI_SR_TFE); + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); =20 qtest_add_func("/k230-dw-ssi/register-contract", test_register_contrac= t); + qtest_add_func("/k230-dw-ssi/pio-data-path", test_pio_data_path); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069078; cv=none; d=zohomail.com; s=zohoarc; b=DQSohHCfAWANVXEKTKIsYIVlgIMJ/2JwyzzBW3lztOYt4qHkz5yT7JhKlwoPreDLO3kGxcA2UdLNk3zCTB9loClAuSMNLuwnma/ajrFbzE40p5e+AnNYpe77+AeRLXU2b44WV35jAqJyKosDPd+sme+fP/dS3pSqbiY8DuVmfi0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069078; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=1EUjSvAnMqQXZ+QbNsMcHRIM+RX+l2GUa0m0wryPHcA=; b=EitY1IE6Dy3oyiD3VIjm0hQMChTpPufDPluerclPoZC6q7OWds5FfIvF5L3P5FRCp8qy2i8K+UrBZujoCC/U4ejYWgFlZXhXAF86VbDMA2DVxUKwBcb7PUsL3Xdt/kPGe64CLNlu3f3svtjw759C55RTiLG8arKDyCJv1DZB3qs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069078876923.842416102171; Sun, 26 Jul 2026 05:31:18 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxz5-0001Nq-Eq; Sun, 26 Jul 2026 08:29:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxyv-0001JW-90 for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:27 -0400 Received: from mail-pf1-x42c.google.com ([2607:f8b0:4864:20::42c]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxys-0003IN-9O for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:24 -0400 Received: by mail-pf1-x42c.google.com with SMTP id d2e1a72fcca58-84847482584so1290940b3a.0 for ; Sun, 26 Jul 2026 05:29:21 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068960; x=1785673760; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=1EUjSvAnMqQXZ+QbNsMcHRIM+RX+l2GUa0m0wryPHcA=; b=sjPVjxu3ARcUsz8Af+TreCzTGKGfm1/ZP6YdxuM7E2yDb8WTbf9ItqfVAui/jEuH+A jIIPLTKa1GQZsdNv/OGIXyZK3W9h8iFdwmDzXvUqXDd/y6LIUBoXzjO0sAbnpob6BinP FElnKIasSjMGImv0yFVsgmFf55GL/RuNGLbNb/0b6sVERd6TiwqJ9XsiycQlO8OP101O qzCG4/lgECDbHim90LJ040egGILlpaan1pl0znuAFBtqb1EKe5DI0ccmAuAlV5ciccaA 1TMmI0C8lRwqy+zDrQ0HzKSRxTrGEkfQUxywmI2KBBA6o4+XbHejKjgGE4qE327Wd8IL NXfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068960; x=1785673760; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=1EUjSvAnMqQXZ+QbNsMcHRIM+RX+l2GUa0m0wryPHcA=; b=UfPh/9SImkElapdatgQVeQ5/0TTjUlXsdKXGIDjrRUzKR8nqhxaXPuiwRaHnwGubd5 u/YKymArzZp9lFENpmLB78GHMo+J4ce8/2JdFdUP4hzRh9HxkUx/+lgzyLIdN1p41Y6E e82+02fiOpjUon3CmYCG7xIjfcUR40bqR7OYSh56Uee/LeaxYeiqQhNCatrtrgIq0EdN mz4rY5W7BWDUE7Cu1OuNuXgMhKJMbS6nlEQR7QGXEtmi/6QGlKluXVb9jU0eqax0zpBU Yx03b+wy5tl49EkO0dO8/xUol21Zldl3Qhd8yG7CSfGHR8SAzUYIem5AluB6Mkbqpyph NcXg== X-Gm-Message-State: AOJu0YzAGiooJgKT1Z4+jPkly9nZjmCS+B66xcl6UpMx1/WUHrlBWkgw CMoQO1MeElFAqVWpTM2plhieayNr76i8mY4km0GB1WUjicV8Uft3WoIcAAUPOiadL44= X-Gm-Gg: AR+sD11HyDMPMKVO0CnDGfJQBW3OMVU1Jjj2fmo24M/bc3nfLgiPd18vpx1ZIKxbuvy 90RpIepMpW8oJzVoLpldrmxCJTsdDCcn5EVmpqeLQNmJgOWOKZ1uuCflEdmTA2YB5L5DuGSYPY5 iqPNN9rPjGjNmWXUtltURIXUHN+4tsVPLsJ1uHg1rzvYsVQEedw3FoYLS6pTjsZHM+6td9Y9bg0 9Fi0dWuHLTly2+/WalvWx1b9osABgCtYRwOvao2EDiji3pIqLK38fjTKsW9zQZ9zqt8tcLXRp4E NoR60BnA+5LikQGgWxMY5xDRNMr8rJ/gmpXMUwprKZn7hh+9WB56bfbejOhpU3xG8DRkCzc8doH nIbuRrfVq7m2I9utlCCLK3D7Ctb53sl+U2oADw8XcjEO2Y6XkHQmo+5x+JmJV9fdQcSbru+fIKY 6Uc/jtwrTTtKkvQ4a/WRiy X-Received: by 2002:a05:6a00:174c:b0:848:2ef5:50de with SMTP id d2e1a72fcca58-84e595b089emr3731467b3a.38.1785068960465; Sun, 26 Jul 2026 05:29:20 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 04/11] hw/ssi: Add K230 SSI interrupt controller Date: Sun, 26 Jul 2026 20:28:22 +0800 Message-ID: <087a0aba5b55336b11fe599e01da59cc6c5aba96.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42c; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42c.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069079513158500 Content-Type: text/plain; charset="utf-8" Implement the SSI interrupt state and GPIO outputs. Derive TXE and RXF from the FIFO thresholds. Keep RXU, TXO, and RXO as latched causes with their documented read-clear behaviour. Cover TXE level signalling, RXU latching, interrupt masking, and cause clearing in qtest. PLIC routing is added by the next patch. Signed-off-by: Kangjie Huang --- hw/ssi/k230_dw_ssi.c | 128 ++++++++++++++++++++++++++++++++- include/hw/ssi/k230_dw_ssi.h | 17 +++++ tests/qtest/k230-dw-ssi-test.c | 36 ++++++++++ 3 files changed, 179 insertions(+), 2 deletions(-) diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index d7315f28ef..26ac052a63 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -38,6 +38,7 @@ #define K230_DW_SSI_AXIARLEN_RESET 0x00000700 #define K230_DW_SSI_VERSION 0x3130332a #define K230_DW_SSI_PIO_TX_BATCH 64 +#define K230_DW_SSI_IRQ_VALID_MASK 0x000009bf =20 REG32(CTRLR0, 0x000) FIELD(CTRLR0, DFS, 0, 5) @@ -258,12 +259,64 @@ REG32(XIP_WRITE_CTRL, 0x148) #define K230_DW_SSI_AXIAR0_WRITABLE_MASK R_AXIAR0_AXIAR_0_31_MASK #define K230_DW_SSI_AXIAR1_WRITABLE_MASK R_AXIAR1_AXIAR_32_63_MASK =20 +static const uint32_t k230_dw_ssi_irq_status_mask[ + K230_DW_SSI_IRQ_COUNT] =3D { + [K230_DW_SSI_IRQ_TXE] =3D R_RISR_TXEIR_MASK, + [K230_DW_SSI_IRQ_TXO] =3D R_RISR_TXOIR_MASK, + [K230_DW_SSI_IRQ_RXF] =3D R_RISR_RXFIR_MASK, + [K230_DW_SSI_IRQ_RXO] =3D R_RISR_RXOIR_MASK, + [K230_DW_SSI_IRQ_TXU] =3D R_RISR_TXUIR_MASK, + [K230_DW_SSI_IRQ_RXU] =3D R_RISR_RXUIR_MASK, + [K230_DW_SSI_IRQ_MST] =3D R_RISR_MSTIR_MASK, + [K230_DW_SSI_IRQ_DONE] =3D R_RISR_DONER_MASK, + [K230_DW_SSI_IRQ_AXIE] =3D R_RISR_AXIER_MASK, +}; + static void k230_dw_ssi_write_masked(K230DwSsiState *s, unsigned int reg, uint32_t value, uint32_t mask) { s->regs[reg] =3D (s->regs[reg] & ~mask) | (value & mask); } =20 +static uint32_t k230_dw_ssi_irq_raw_status(K230DwSsiState *s) +{ + uint32_t status =3D s->irq_latched; + uint32_t tx_used =3D fifo32_num_used(&s->tx_fifo); + uint32_t rx_used =3D fifo32_num_used(&s->rx_fifo); + uint32_t tx_threshold =3D + FIELD_EX32(s->regs[R_TXFTLR], TXFTLR, TFT); + uint32_t rx_threshold =3D + FIELD_EX32(s->regs[R_RXFTLR], RXFTLR, RFT); + + if (tx_used <=3D tx_threshold) { + status |=3D R_RISR_TXEIR_MASK; + } + if (rx_used > rx_threshold) { + status |=3D R_RISR_RXFIR_MASK; + } + return status & K230_DW_SSI_IRQ_VALID_MASK; +} + +static void k230_dw_ssi_update_irq(K230DwSsiState *s) +{ + uint32_t status =3D k230_dw_ssi_irq_raw_status(s) & + s->regs[R_IMR] & K230_DW_SSI_IRQ_VALID_MASK; + + for (int i =3D 0; i < K230_DW_SSI_IRQ_COUNT; i++) { + qemu_set_irq(s->irqs[i], !!(status & k230_dw_ssi_irq_status_mask[i= ])); + } +} + +static uint32_t k230_dw_ssi_irq_read_clear(K230DwSsiState *s, + uint32_t clear_mask) +{ + uint32_t active =3D s->irq_latched & clear_mask; + + s->irq_latched &=3D ~clear_mask; + k230_dw_ssi_update_irq(s); + return !!active; +} + static uint32_t k230_dw_ssi_frame_masked(K230DwSsiState *s) { unsigned int bits =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, DFS) + 1; @@ -333,6 +386,7 @@ static void k230_dw_ssi_abort_transfer(K230DwSsiState *= s) fifo32_reset(&s->rx_fifo); s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; + k230_dw_ssi_update_irq(s); } =20 static uint32_t k230_dw_ssi_status(K230DwSsiState *s) @@ -360,6 +414,8 @@ static void k230_dw_ssi_push_tx(K230DwSsiState *s, uint= 32_t tx) } =20 if (fifo32_is_full(&s->tx_fifo)) { + s->irq_latched |=3D R_RISR_TXOIR_MASK; + k230_dw_ssi_update_irq(s); return; } =20 @@ -368,6 +424,7 @@ static void k230_dw_ssi_push_tx(K230DwSsiState *s, uint= 32_t tx) if (s->phase !=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { k230_dw_ssi_run_transfer(s); } + k230_dw_ssi_update_irq(s); } =20 static uint32_t k230_dw_ssi_send_frame(K230DwSsiState *s, @@ -410,6 +467,12 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s) uint32_t rx =3D k230_dw_ssi_send_frame(s, tx); if (!fifo32_is_full(&s->rx_fifo)) { fifo32_push(&s->rx_fifo, rx); + } else { + s->irq_latched |=3D R_RISR_RXOIR_MASK; + qemu_log_mask(LOG_GUEST_ERROR, + "%s: RX FIFO full, dropping frame\n", + DEVICE(s)->canonical_path); + break; } } break; @@ -549,9 +612,13 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr = addr, unsigned int size) if (k230_dw_ssi_is_dr(addr)) { if (!fifo32_is_empty(&s->rx_fifo)) { value =3D fifo32_pop(&s->rx_fifo) & k230_dw_ssi_frame_masked(s= ); + } else { + value =3D 0; + s->irq_latched |=3D R_RISR_RXUIR_MASK; } =20 k230_dw_ssi_run_transfer(s); + k230_dw_ssi_update_irq(s); return value; } =20 @@ -592,6 +659,7 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr a= ddr, unsigned int size) value =3D fifo32_num_used(&s->tx_fifo); if (s->phase =3D=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { k230_dw_ssi_run_transfer(s); + k230_dw_ssi_update_irq(s); } break; case A_RXFLR: @@ -601,18 +669,33 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) value =3D k230_dw_ssi_status(s); if (s->phase =3D=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { k230_dw_ssi_run_transfer(s); + k230_dw_ssi_update_irq(s); } break; case A_ISR: + value =3D k230_dw_ssi_irq_raw_status(s) & s->regs[R_IMR] & + K230_DW_SSI_IRQ_VALID_MASK; + break; case A_RISR: - value =3D 0; + value =3D k230_dw_ssi_irq_raw_status(s); break; case A_TXEICR: + value =3D k230_dw_ssi_irq_read_clear( + s, R_RISR_TXOIR_MASK | R_RISR_TXUIR_MASK); + break; case A_RXOICR: + value =3D k230_dw_ssi_irq_read_clear(s, R_RISR_RXOIR_MASK); + break; case A_RXUICR: + value =3D k230_dw_ssi_irq_read_clear(s, R_RISR_RXUIR_MASK); + break; case A_MSTICR: + value =3D k230_dw_ssi_irq_read_clear(s, R_RISR_MSTIR_MASK); + break; case A_ICR: - value =3D 0; + value =3D k230_dw_ssi_irq_read_clear( + s, R_RISR_TXOIR_MASK | R_RISR_RXUIR_MASK | + R_RISR_RXOIR_MASK | R_RISR_MSTIR_MASK); break; case A_AXIECR: case A_DONECR: @@ -678,6 +761,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr addr, =20 k230_dw_ssi_update_cs(s); k230_dw_ssi_run_transfer(s); + k230_dw_ssi_update_irq(s); break; } case A_MWCR: @@ -695,6 +779,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr addr, =20 k230_dw_ssi_update_cs(s); k230_dw_ssi_run_transfer(s); + k230_dw_ssi_update_irq(s); break; } case A_BAUDR: @@ -704,10 +789,12 @@ static void k230_dw_ssi_write(void *opaque, hwaddr ad= dr, case A_TXFTLR: k230_dw_ssi_write_masked(s, R_TXFTLR, value, K230_DW_SSI_TXFTLR_WRITABLE_MASK); + k230_dw_ssi_update_irq(s); break; case A_RXFTLR: k230_dw_ssi_write_masked(s, R_RXFTLR, value, K230_DW_SSI_RXFTLR_WRITABLE_MASK); + k230_dw_ssi_update_irq(s); break; case A_TXFLR: case A_RXFLR: @@ -716,6 +803,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr addr, case A_IMR: k230_dw_ssi_write_masked(s, R_IMR, value, K230_DW_SSI_IMR_WRITABLE_MASK); + k230_dw_ssi_update_irq(s); break; case A_ISR: case A_RISR: @@ -831,6 +919,7 @@ static void k230_dw_ssi_enter_reset(Object *obj, ResetT= ype type) fifo32_reset(&s->rx_fifo); s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; + s->irq_latched =3D 0; =20 s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; s->regs[R_SR] =3D K230_DW_SSI_SR_RESET; @@ -842,6 +931,8 @@ static void k230_dw_ssi_enter_reset(Object *obj, ResetT= ype type) s->regs[R_SPI_CTRLR0] =3D s->max_lines =3D=3D 8 ? K230_DW_SSI_SPI_CTRLR0_FMC_RESET : K230_DW_SSI_SPI_CTRLR0_SPI_RESET; + + k230_dw_ssi_update_irq(s); } =20 static void k230_dw_ssi_hold_reset(Object *obj, ResetType type) @@ -856,12 +947,40 @@ static void k230_dw_ssi_hold_reset(Object *obj, Reset= Type type) } } =20 +static void k230_dw_ssi_exit_reset(Object *obj, ResetType type) +{ + K230DwSsiState *s =3D K230_DW_SSI(obj); + + k230_dw_ssi_update_irq(s); +} + +static int k230_dw_ssi_post_load(void *opaque, int version_id) +{ + K230DwSsiState *s =3D opaque; + + if (s->active_cs < -1 || s->active_cs >=3D (int)s->num_cs) { + return -EINVAL; + } + + for (int i =3D 0; i < s->num_cs; i++) { + qemu_irq_raise(s->cs_lines[i]); + } + if (s->active_cs >=3D 0) { + qemu_irq_lower(s->cs_lines[s->active_cs]); + } + + k230_dw_ssi_update_irq(s); + return 0; +} + static const VMStateDescription vmstate_k230_dw_ssi =3D { .name =3D TYPE_K230_DW_SSI, + .post_load =3D k230_dw_ssi_post_load, .fields =3D (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, K230DwSsiState, K230_DW_SSI_NUM_REGS), VMSTATE_FIFO32(tx_fifo, K230DwSsiState), VMSTATE_FIFO32(rx_fifo, K230DwSsiState), + VMSTATE_UINT32(irq_latched, K230DwSsiState), VMSTATE_UINT32(phase, K230DwSsiState), VMSTATE_UINT32(remaining_frames, K230DwSsiState), VMSTATE_INT32(active_cs, K230DwSsiState), @@ -881,6 +1000,10 @@ static void k230_dw_ssi_init(Object *obj) TYPE_K230_DW_SSI, K230_DW_SSI_MMIO_SIZE); sysbus_init_mmio(sbd, &s->mmio); =20 + for (int i =3D 0; i < K230_DW_SSI_IRQ_COUNT; i++) { + sysbus_init_irq(sbd, &s->irqs[i]); + } + fifo32_create(&s->tx_fifo, K230_DW_SSI_FIFO_CAPACITY); fifo32_create(&s->rx_fifo, K230_DW_SSI_FIFO_CAPACITY); s->active_cs =3D -1; @@ -930,6 +1053,7 @@ static void k230_dw_ssi_class_init(ObjectClass *klass,= const void *data) device_class_set_props(dc, k230_dw_ssi_properties); rc->phases.enter =3D k230_dw_ssi_enter_reset; rc->phases.hold =3D k230_dw_ssi_hold_reset; + rc->phases.exit =3D k230_dw_ssi_exit_reset; } =20 static const TypeInfo k230_dw_ssi_info =3D { diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index 993cb19745..404215245b 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -31,6 +31,20 @@ OBJECT_DECLARE_SIMPLE_TYPE(K230DwSsiState, K230_DW_SSI) #define K230_DW_SSI_NUM_REGS \ (K230_DW_SSI_REGS_SIZE / sizeof(uint32_t)) =20 +/* SSI GPIO output ordering differs from RISR/ISR bit ordering. */ +typedef enum K230DwSsiIrq { + K230_DW_SSI_IRQ_TXE, + K230_DW_SSI_IRQ_TXO, + K230_DW_SSI_IRQ_RXF, + K230_DW_SSI_IRQ_RXO, + K230_DW_SSI_IRQ_TXU, + K230_DW_SSI_IRQ_RXU, + K230_DW_SSI_IRQ_MST, + K230_DW_SSI_IRQ_DONE, + K230_DW_SSI_IRQ_AXIE, + K230_DW_SSI_IRQ_COUNT, +} K230DwSsiIrq; + typedef enum K230DwSsiPhase { K230_DW_SSI_PHASE_IDLE, K230_DW_SSI_PHASE_STANDARD_TX_ONLY, @@ -45,11 +59,14 @@ struct K230DwSsiState { MemoryRegion mmio; SSIBus *spi; qemu_irq *cs_lines; + qemu_irq irqs[K230_DW_SSI_IRQ_COUNT]; =20 Fifo32 tx_fifo; Fifo32 rx_fifo; uint32_t regs[K230_DW_SSI_NUM_REGS]; =20 + uint32_t irq_latched; + uint32_t phase; uint32_t remaining_frames; =20 diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index a62b1a7ff6..0a3d03d6f3 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -25,6 +25,7 @@ #define K230_SSI_IMR 0x02c #define K230_SSI_ISR 0x030 #define K230_SSI_RISR 0x034 +#define K230_SSI_RXUICR 0x040 #define K230_SSI_DMACR 0x04c #define K230_SSI_IDR 0x058 #define K230_SSI_VERSION_ID 0x05c @@ -63,6 +64,8 @@ #define K230_SSI_SR_TFE BIT(2) #define K230_SSI_SR_RFNE BIT(3) =20 +#define K230_SSI_INT_TXE BIT(0) +#define K230_SSI_INT_RXU BIT(2) #define K230_SSI_INT_AXIE BIT(8) #define K230_SSI_INT_DONE BIT(11) =20 @@ -245,11 +248,44 @@ static void test_pio_data_path(void) qtest_quit(qts); } =20 +static void test_interrupt_controller(void) +{ + QTestState *qts =3D k230_ssi_start(); + + k230_ssi_configure(qts, K230_SPI1_BASE, K230_SSI_TMOD_TR, 8, 0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_TXFTLR, 0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_IMR, 0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_SSIENR, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_RISR) & + K230_SSI_INT_TXE, =3D=3D, K230_SSI_INT_TXE); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_ISR) & + K230_SSI_INT_TXE, =3D=3D, 0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_IMR, + K230_SSI_INT_TXE); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_ISR) & + K230_SSI_INT_TXE, =3D=3D, K230_SSI_INT_TXE); + + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_IMR, + K230_SSI_INT_RXU); + (void)k230_ssi_read_frame(qts, K230_SPI1_BASE); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_RISR) & + K230_SSI_INT_RXU, =3D=3D, K230_SSI_INT_RXU); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_ISR) & + K230_SSI_INT_RXU, =3D=3D, K230_SSI_INT_RXU); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_RXUICR), + =3D=3D, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_RISR) & + K230_SSI_INT_RXU, =3D=3D, 0); + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); =20 qtest_add_func("/k230-dw-ssi/register-contract", test_register_contrac= t); qtest_add_func("/k230-dw-ssi/pio-data-path", test_pio_data_path); + qtest_add_func("/k230-dw-ssi/interrupt-controller", + test_interrupt_controller); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069058; cv=none; d=zohomail.com; s=zohoarc; b=IJcrPeaS1DxBnfiORSXIKPaJqF5y0j6aZMw4dgVWnwqfbNiCxP8HgnEAg6PXNApG6S4lw6L3hDdvSorBylR204UlJoBKGAr+57owsOv6UPIYN1uvth3HLsiUJWhUyzUFwLufC5PgSyM8xUJsZFKW7HXF1msAubtoSP4PtGsxgKQ= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069058; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=TjDsmoQwXb9x1HeskAUizFx6TpagdSECUOBKC6EHW7o=; b=CxCcYVlwJETtVBay2J0yrxGyzeqwk8khLVdQDALM3MAGPxJWC/4hwLEmOsHsyqoy24szWK2wSb/AJr1edut10Z3+7FZSMsP56DlDRvBs0Kbk79tqf8khEUZ7x+MC+8mdkEKTxyx2gJsYOvglQMDZMtI+TMh69BCwH2BQrZX6yys= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069058844103.67960131098584; Sun, 26 Jul 2026 05:30:58 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxz7-0001SD-Jh; Sun, 26 Jul 2026 08:29:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxz0-0001M9-0i for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:33 -0400 Received: from mail-pf1-x436.google.com ([2607:f8b0:4864:20::436]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxyx-0003Ih-CK for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:29 -0400 Received: by mail-pf1-x436.google.com with SMTP id d2e1a72fcca58-84874b52eabso2506054b3a.0 for ; Sun, 26 Jul 2026 05:29:26 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068966; x=1785673766; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=TjDsmoQwXb9x1HeskAUizFx6TpagdSECUOBKC6EHW7o=; b=lFfaX00CUk7VeyqgihCu9GVkOClVvBJ3zkvf/umK3Zlk7F99Dqce21/snVlBGuaFDN wN085ywYBi35bVR5ESg2rEW9oOWowMRYNOIEewS3kNn1sFsK9YtTsoykR37Qqj+ORF6f Sh0FopBWqFX+o3W09jMyTFKDWSaTKgrJpLs9lAS0zTAarzRg1Yrzj8PN3QaFWtRhia2p fRDIS2OfEEB+m7NJh3vUN5M0DsE64aI0iM9LOlKI9lUszFr5jbXpo0TXG/l8gDVJ1TLI SOyKE2qZtrijNy5+mKRVR863qlqNvgdGSTDk2iUOMp/D14MtYoTNDNtdrQ0k4nge9QM7 2buQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068966; x=1785673766; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=TjDsmoQwXb9x1HeskAUizFx6TpagdSECUOBKC6EHW7o=; b=jJYte1S1fwUgn7FSJPKTiq9Y+vkW4/csIUm72MKgkUgYZKmiXrmdi4x0uR0Ac9sU/f tXn4Oyv/Qim4PcHGwf+qKlCDGQfjIC2wsyadm5wSk2PDSpUIcxjeIbSZwh5I+vaF1a0+ 5Cl2aXpFl9WHseDdsjiqSE7vFnx3zdfl9kqlJLK+4klG/dotNoKYZvxwfR65/TFb+yAw Vg2rj0Fb/2fja7ENuaF390DP/ydHSQOWP8RvDENmxilC6/2FsrwDb5h5GpgGRGlbAd9A dl3C0exyO8iS+6Yc2tjx8nSgvIYUFHKZ+Axr9/JV4p617th9Pmgyr2Nn9g0QvvBQ70HD dvxg== X-Gm-Message-State: AOJu0YwxU0hs76+/GPpRQ1IsjQbZgEFLIhR60EgbCLC1PTEvdHhuWatg 2DdS4u5ygTHfXGGuu9WOfKmXIqrTgQ1BmfsTpOzAMZ+9+W+Lbh+/8/unqeE+ER+jbeA= X-Gm-Gg: AR+sD12mlqcGQ2Ri9FKANBD1oeHVQVYbS59LiXMGPU4qrm4yq7yVKZAfzGSaaN9s24F CQb60JATWWCZ1Vp1AqA4TKq4ZxtTzoTTLZ+AfChTAxv5QCBt5fDidrlIy8C3sUsqSXcT5G7Vfx2 tPlYsWzUP1FVV392eOR3Lalag2wwEwJ5Kw2zJCN6rkldz3NAOwXFySDUcblvIz/isOORMUpeOqG AgPXMPIp+LwFymr35NBdqsCTb8Mp2m8uvcYfm0nHRBB9X7Be3sdw52uUgNsIUXHQaZIGNg7uzBS drsMXCUZnz8tdiL6Uf8dbZf21iG1+sIIPrO9sVggrZSL1GkC35E3tZ+w4CJosNhYS23AZs5R7ra QvRPisj8WaBuGOVyz9dvpYohHrbUyTbUI5vkxNp2il+sdWOymKR1DDxrjExiYqg+74TIMOZRjrH XLMUvFSPWKVWYzaAVGVWbBpbSeuQCATdU= X-Received: by 2002:a05:6a00:a21f:b0:845:c5d5:3745 with SMTP id d2e1a72fcca58-84e594ad97cmr3979000b3a.34.1785068966000; Sun, 26 Jul 2026 05:29:26 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 05/11] hw/riscv: Route K230 SSI IRQs to the PLIC Date: Sun, 26 Jul 2026 20:28:23 +0800 Message-ID: <662158aeebfeaaa278fc89554142cb9dd89f02d2.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::436; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x436.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069059340158500 Content-Type: text/plain; charset="utf-8" Connect the nine GPIO outputs from each K230 SSI controller to their PLIC sources. Check the TXE PLIC route for all three instances and verify RXU routing and instance isolation for spi1 in qtest. Signed-off-by: Kangjie Huang --- hw/riscv/k230.c | 36 +++++++++++++++++++++++++ include/hw/riscv/k230.h | 3 +++ tests/qtest/k230-dw-ssi-test.c | 48 ++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index acd5445b31..1863d5ecbb 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -102,6 +102,21 @@ static const MemMapEntry memmap[] =3D { [K230_DEV_CLINT] =3D { 0xF04000000, 0x00400000 }, }; =20 +typedef struct K230SsiRoute { + unsigned int ssi_index; + unsigned int irq_base; +} K230SsiRoute; + +/* + * SDK numbering follows the address map: spi0 is the SPI-OPI instance, + * while spi1 and spi2 are QSPI0 and QSPI1. + */ +static const K230SsiRoute k230_ssi_routes[] =3D { + { .ssi_index =3D 2, .irq_base =3D K230_SPI0_IRQ_BASE }, + { .ssi_index =3D 0, .irq_base =3D K230_SPI1_IRQ_BASE }, + { .ssi_index =3D 1, .irq_base =3D K230_SPI2_IRQ_BASE }, +}; + static void k230_soc_init(Object *obj) { K230SoCState *s =3D RISCV_K230_SOC(obj); @@ -157,6 +172,25 @@ static void k230_create_uart(MemoryRegion *sys_mem, De= viceState *plic, 399193, serial_hd(index), DEVICE_LITTLE_ENDIAN); } =20 +static void k230_connect_ssi_irqs(K230SoCState *s) +{ + for (size_t route_idx =3D 0; + route_idx < ARRAY_SIZE(k230_ssi_routes); route_idx++) { + const K230SsiRoute *route =3D &k230_ssi_routes[route_idx]; + SysBusDevice *ssi; + + g_assert(route->ssi_index < ARRAY_SIZE(s->dw_ssi)); + g_assert(route->irq_base + K230_DW_SSI_IRQ_COUNT <=3D + K230_PLIC_NUM_SOURCES); + + ssi =3D SYS_BUS_DEVICE(&s->dw_ssi[route->ssi_index]); + for (unsigned int i =3D 0; i < K230_DW_SSI_IRQ_COUNT; i++) { + sysbus_connect_irq(ssi, i, + qdev_get_gpio_in(DEVICE(s->c908_plic), route->irq_base + i= )); + } + } +} + static void k230_soc_realize(DeviceState *dev, Error **errp) { K230SoCState *s =3D RISCV_K230_SOC(dev); @@ -217,6 +251,8 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) } } =20 + k230_connect_ssi_irqs(s); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].b= ase); sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[0]), 0, qdev_get_gpio_in(DEVICE(s->c908_plic), K230_WDT0_IR= Q)); diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 4a79b7ed45..cfe30590cc 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -131,6 +131,9 @@ enum { K230_UART4_IRQ =3D 20, K230_WDT0_IRQ =3D 107, K230_WDT1_IRQ =3D 108, + K230_SPI0_IRQ_BASE =3D 146, + K230_SPI1_IRQ_BASE =3D 155, + K230_SPI2_IRQ_BASE =3D 164, }; =20 #define K230_UART_COUNT 5 diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index 0a3d03d6f3..e6c0e2ee8c 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -13,6 +13,8 @@ #define K230_SPI0_BASE 0x91584000ULL #define K230_SPI1_BASE 0x91582000ULL #define K230_SPI2_BASE 0x91583000ULL +#define K230_PLIC_BASE 0xf00000000ULL +#define K230_PLIC_PENDING_BASE 0x1000 #define K230_SSI_CTRLR0 0x000 #define K230_SSI_CTRLR1 0x004 #define K230_SSI_SSIENR 0x008 @@ -69,12 +71,18 @@ #define K230_SSI_INT_AXIE BIT(8) #define K230_SSI_INT_DONE BIT(11) =20 +#define K230_SSI_IRQ_TXE 0 +#define K230_SSI_IRQ_RXU 5 +#define K230_SSI_IRQ_DONE 7 +#define K230_SSI_IRQ_AXIE 8 + #define K230_SSI_FIFO_DEPTH 256 =20 typedef struct K230SsiInstance { uint64_t base; uint32_t num_cs; uint32_t spi_ctrlr0_reset; + uint32_t first_irq; } K230SsiInstance; =20 static const K230SsiInstance k230_ssi_instances[3] =3D { @@ -82,14 +90,17 @@ static const K230SsiInstance k230_ssi_instances[3] =3D { .base =3D K230_SPI0_BASE, .num_cs =3D 1, .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_FMC_RESET, + .first_irq =3D 146, }, { .base =3D K230_SPI1_BASE, .num_cs =3D 5, .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_SPI_RESET, + .first_irq =3D 155, }, { .base =3D K230_SPI2_BASE, .num_cs =3D 5, .spi_ctrlr0_reset =3D K230_SSI_SPI_CTRLR0_SPI_RESET, + .first_irq =3D 164, }, }; =20 @@ -178,6 +189,14 @@ static void configure_loopback(QTestState *qts, uint32= _t tmod, ctrlr0 | K230_SSI_CTRLR0_SRL); } =20 +static bool k230_ssi_plic_pending(QTestState *qts, uint32_t irq) +{ + uint64_t addr =3D K230_PLIC_BASE + K230_PLIC_PENDING_BASE + + (irq / 32) * sizeof(uint32_t); + + return qtest_readl(qts, addr) & BIT(irq % 32); +} + static void test_register_contract(void) { QTestState *qts =3D k230_ssi_start(); @@ -279,6 +298,34 @@ static void test_interrupt_controller(void) qtest_quit(qts); } =20 +static void test_plic_routing(void) +{ + QTestState *qts =3D k230_ssi_start(); + const K230SsiInstance *target =3D &k230_ssi_instances[1]; + + for (int i =3D 0; i < ARRAY_SIZE(k230_ssi_instances); i++) { + const K230SsiInstance *inst =3D &k230_ssi_instances[i]; + + g_assert_true(k230_ssi_plic_pending(qts, + inst->first_irq + + K230_SSI_IRQ_TXE)); + k230_ssi_writel(qts, inst->base, K230_SSI_IMR, 0); + } + + k230_ssi_writel(qts, target->base, K230_SSI_IMR, K230_SSI_INT_RXU); + (void)k230_ssi_read_frame(qts, target->base); + g_assert_true(k230_ssi_plic_pending(qts, + target->first_irq + + K230_SSI_IRQ_RXU)); + for (int i =3D 0; i < ARRAY_SIZE(k230_ssi_instances); i++) { + if (&k230_ssi_instances[i] !=3D target) { + g_assert_false(k230_ssi_plic_pending( + qts, k230_ssi_instances[i].first_irq + K230_SSI_IRQ_RXU)); + } + } + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -287,5 +334,6 @@ int main(int argc, char **argv) qtest_add_func("/k230-dw-ssi/pio-data-path", test_pio_data_path); qtest_add_func("/k230-dw-ssi/interrupt-controller", test_interrupt_controller); + qtest_add_func("/k230-dw-ssi/plic-routing", test_plic_routing); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069108; cv=none; d=zohomail.com; s=zohoarc; b=m9UF92OrkcKeyPLyumHB+X1+FH3dmFRZRY9/BD+TTrWSC+O47TPTdzBGkqpytRWbUGN06AwbqhDfrDua3mVqb0eukDrbytAu+XndT0VOtqY7g6HYqcVJk9MweGni9AoGNAHIaDsfDnT0M5v/uzeVfK+5yutRW0mRya3einifhec= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069108; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=u5wBFH4kuOWR6ro+3Rls6E6onh4AnTwIygY1OrxRai0=; b=apY9m8GCpFEP+Llk7rhn6KdOMHkCBg5oJH61KI8xfIGc/kJv2Q0rhG+EiQLjyoRH2/qL+inOyz72jfZMrxAQdz0QSmTBuze8JxQiwWkwDNuCjWLPYutzzne+a4xhYh1E2uhc0vFY0Lc2FMVm+/BP0Wd/Ejw/ctHw6mAuUURMwkk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069108356719.9187368788218; Sun, 26 Jul 2026 05:31:48 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxz7-0001SB-IP; Sun, 26 Jul 2026 08:29:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxz6-0001Po-2s for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:36 -0400 Received: from mail-pf1-x435.google.com ([2607:f8b0:4864:20::435]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxz3-0003JA-Ab for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:35 -0400 Received: by mail-pf1-x435.google.com with SMTP id d2e1a72fcca58-8485b358552so1933172b3a.2 for ; Sun, 26 Jul 2026 05:29:32 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068972; x=1785673772; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=u5wBFH4kuOWR6ro+3Rls6E6onh4AnTwIygY1OrxRai0=; b=NVF4i4lnU3wyVoiw4/ZNknmFC17J2nUeN33yavf3WaD8Yq03YBXLjjNukuxw1OzGrn KwDmEZjrCz5ZBVXLFRn/U9RLlzXC4r21cRiLnWxmkKT4wq6u5pZvpTUdBCgx7kebbsLI zlcvB3eivWgyJ098eDobzOqwTzdJ2VDaiIpG9P2VOgsCMaZXgpRgHNkeASvHQiEs/VNA reQB6J8nfoOc23LhTPhR3WAm4GPVitQbmf1mKbYwhXzlku8d7Kdq2Tf3eICaYRQTQu3T kP3sC8HA+M18IybaNZ+jP92UqgsdAfrxv3CmBWvEfXXJykCb69QXNjeJbg3qdVr4qvH5 ropg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068972; x=1785673772; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=u5wBFH4kuOWR6ro+3Rls6E6onh4AnTwIygY1OrxRai0=; b=hPKuFSzz6tFVqO/bn6aavnJ/y4P6wiJSjvxTBHYAjSObD+0tcOb7sq47Zn89yq6FK3 salaf5MnViVDAv7KU0tS+A336s+dxBu0ZlCuFLb1M//CX6lU/p/aZUK+p3v67+QVOcVN gjL2ePFc/5IUynJ1sXGfeWwnEUlr8yZNSL0+k5nXQEyRBqJtUWp3mgxy87aEocSe7Htk RSNzYdDPOuca58meM3cMIPueQYIaYgoKUR/oGYZWEQxT/bDtjQPR4eYy133005ATuBGX MrVDjGRpoD+XHnemB9Zek93AvePvQd/LwamXEeCtNyb+5JFTuFq0s2yChU3pTa+I1Yam /EoA== X-Gm-Message-State: AOJu0Yx2GHXPYLe6pT/cG843pUfq+5W/vnUzoRYJpjZ4xVXTFeC2e1gb 2uo1dioy7t47ZS/297OqvlWJ2YCkZWwOVTUmPmO8Cvb7S3Y9YxdO95i4n+vQMIbk4lU= X-Gm-Gg: AR+sD10Q/t5eoR/4FWtw+OdWZJF0veV9bWqubn+zNchnlNj7XnF29lr76Y7CHACIE5I 7GvA0W1Twx2BHPh9sMwL7onw99lhT0IwmUVEBsTcTE+jdSVocH0MXDrZTxbYwIx4Xsh8Zk2hdl+ 1qu+DhjqQffzxVyekcawrUv2dN+A36tsT18/BcPER4mCMJsd2LUZLsMJVsAbpfoxaevizCVCG8X rhZ1QUsbLg6l438d84J/FjTrRiZCvCDBjuD3SXIl7qmaOfn0a3wbY4Cit1GYEYDvNVCkVcgu9SY xuzecOJPGdcjyVLCUeDoZkcR/AfCp6D4/eLDL6dYca6zq4unSaeN9dEcjkx0GU50qVrLc94cMvr eorrIb8Fpx0dhqnqhzwnI/vW70r6kdgTdTpFptz5feuUhaQJSms0lKc293ASyV/v+bQSckO0w04 5bXo9+mUp/3HN9ddqe0GfH X-Received: by 2002:a05:6a00:2e1e:b0:848:2f84:732 with SMTP id d2e1a72fcca58-84e5959438amr3832009b3a.69.1785068971551; Sun, 26 Jul 2026 05:29:31 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 06/11] hw/ssi: Implement K230 enhanced QSPI transfers Date: Sun, 26 Jul 2026 20:28:24 +0800 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::435; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x435.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069109588158500 Content-Type: text/plain; charset="utf-8" Add controller-side Dual and Quad SDR phases for instruction, address, mode, dummy, and data fields. Reject unsupported Octal, DDR, RXDS, and invalid transfer configurations without consuming FIFO data. Use qtest to check an accepted Quad SDR configuration and representative Octal and DDR rejection paths. According to the K230 SDK driver, multi-line QSPI data transfers use the controller's internal IDMA path. That SDK-facing path is implemented by a later patch. Signed-off-by: Kangjie Huang --- hw/ssi/k230_dw_ssi.c | 297 ++++++++++++++++++++++++++++++++- include/hw/ssi/k230_dw_ssi.h | 22 +++ tests/qtest/k230-dw-ssi-test.c | 65 ++++++++ 3 files changed, 376 insertions(+), 8 deletions(-) diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index 26ac052a63..31da3a06a3 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -40,6 +40,13 @@ #define K230_DW_SSI_PIO_TX_BATCH 64 #define K230_DW_SSI_IRQ_VALID_MASK 0x000009bf =20 +enum { + K230_DW_SSI_TMOD_TR, + K230_DW_SSI_TMOD_TO, + K230_DW_SSI_TMOD_RO, + K230_DW_SSI_TMOD_EEPROM_READ, +}; + REG32(CTRLR0, 0x000) FIELD(CTRLR0, DFS, 0, 5) FIELD(CTRLR0, FRF, 6, 2) @@ -324,7 +331,6 @@ static uint32_t k230_dw_ssi_frame_masked(K230DwSsiState= *s) return bits =3D=3D 32 ? UINT32_MAX : MAKE_64BIT_MASK(0, bits); } =20 - static bool k230_dw_ssi_enabled(K230DwSsiState *s) { return FIELD_EX32(s->regs[R_SSIENR], SSIENR, SSIC_EN); @@ -386,6 +392,7 @@ static void k230_dw_ssi_abort_transfer(K230DwSsiState *= s) fifo32_reset(&s->rx_fifo); s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; + memset(&s->enhanced, 0, sizeof(s->enhanced)); k230_dw_ssi_update_irq(s); } =20 @@ -419,7 +426,7 @@ static void k230_dw_ssi_push_tx(K230DwSsiState *s, uint= 32_t tx) return; } =20 - fifo32_push(&s->tx_fifo, tx & k230_dw_ssi_frame_masked(s)); + fifo32_push(&s->tx_fifo, tx); =20 if (s->phase !=3D K230_DW_SSI_PHASE_STANDARD_TX_ONLY) { k230_dw_ssi_run_transfer(s); @@ -444,6 +451,267 @@ static uint32_t k230_dw_ssi_send_frame(K230DwSsiState= *s, return rx & mask; } =20 +static bool k230_dw_ssi_enhanced_config_supported(K230DwSsiState *s) +{ + uint32_t ctrlr0 =3D s->regs[R_CTRLR0]; + uint32_t spi_ctrlr0 =3D s->regs[R_SPI_CTRLR0]; + uint32_t spi_frf; + uint32_t trans_type; + uint32_t tmod; + uint32_t required_lines; + + spi_frf =3D FIELD_EX32(ctrlr0, CTRLR0, SPI_FRF); + trans_type =3D FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, TRANS_TYPE); + tmod =3D FIELD_EX32(ctrlr0, CTRLR0, TMOD); + + switch (spi_frf) { + case 1: /* Dual */ + required_lines =3D 2; + break; + case 2: /* Quad */ + required_lines =3D 4; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported SPI_FRF=3D%u\n", + DEVICE(s)->canonical_path, spi_frf); + return false; + } + + if (required_lines > s->max_lines) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SPI_FRF=3D%u requires %u lines, only %u availab= le\n", + DEVICE(s)->canonical_path, + spi_frf, required_lines, s->max_lines); + return false; + } + + if (trans_type > 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported TRANS_TYPE=3D%u\n", + DEVICE(s)->canonical_path, trans_type); + return false; + } + + if (tmod !=3D K230_DW_SSI_TMOD_RO && tmod !=3D K230_DW_SSI_TMOD_TO) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported enhanced TMOD=3D%u\n", + DEVICE(s)->canonical_path, tmod); + return false; + } + + if (FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_DDR_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, INST_DDR_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_RXDS_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_RXDS_SIG_EN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: DDR/RXDS enhanced mode is unsupported\n", + DEVICE(s)->canonical_path); + return false; + } + + return true; +} + +static bool k230_dw_ssi_decode_enhanced_command( + K230DwSsiState *s, K230DwSsiEnhancedCommand *command) +{ + uint32_t spi_frf =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); + uint32_t inst_l =3D FIELD_EX32(s->regs[R_SPI_CTRLR0], SPI_CTRLR0, INST= _L); + uint32_t addr_l =3D FIELD_EX32(s->regs[R_SPI_CTRLR0], SPI_CTRLR0, ADDR= _L); + uint32_t trans_type =3D FIELD_EX32(s->regs[R_SPI_CTRLR0], + SPI_CTRLR0, TRANS_TYPE); + uint32_t inst_bits; + uint32_t addr_bits =3D addr_l << 2; + uint32_t mode_bits =3D 0; + bool mode_bits_enabled =3D + FIELD_EX32(s->regs[R_SPI_CTRLR0], SPI_CTRLR0, XIP_MD_BIT_EN); + + if (!k230_dw_ssi_enhanced_config_supported(s)) { + return false; + } + + inst_bits =3D inst_l ? (1U << (inst_l + 1)) : 0; + + if (addr_bits > 32) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported enhanced address length %u bits\n", + DEVICE(s)->canonical_path, addr_bits); + return false; + } + + if (mode_bits_enabled) { + uint32_t mode_length_encoding =3D + FIELD_EX32(s->regs[R_SPI_CTRLR0], SPI_CTRLR0, XIP_MBL); + + mode_bits =3D 1U << (mode_length_encoding + 1); + } + + command->instruction_bits =3D inst_bits; + command->address_bits =3D addr_bits; + command->mode_bits =3D mode_bits; + command->mode_bits_enabled =3D mode_bits_enabled; + command->wait_cycles =3D + FIELD_EX32(s->regs[R_SPI_CTRLR0], SPI_CTRLR0, WAIT_CYCLES); + command->data_frames =3D + FIELD_EX32(s->regs[R_CTRLR1], CTRLR1, NDF) + 1; + command->spi_frf =3D spi_frf; + command->trans_type =3D trans_type; + command->tmod =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, TMOD); + + if (mode_bits_enabled) { + command->mode =3D s->regs[R_XIP_MODE_BITS] & + (uint32_t)MAKE_64BIT_MASK(0, mode_bits); + } + + return true; +} + +static bool k230_dw_ssi_prepare_enhanced_command(K230DwSsiState *s) +{ + K230DwSsiEnhancedCommand command =3D { 0 }; + uint32_t required_items; + + if (!k230_dw_ssi_decode_enhanced_command(s, &command)) { + return false; + } + + required_items =3D (command.instruction_bits !=3D 0) + + (command.address_bits !=3D 0); + if (fifo32_num_used(&s->tx_fifo) < required_items) { + return false; + } + + if (command.instruction_bits !=3D 0) { + command.instruction =3D fifo32_pop(&s->tx_fifo) & + (uint32_t)MAKE_64BIT_MASK(0, command.instruction_bits); + } + if (command.address_bits !=3D 0) { + command.address =3D fifo32_pop(&s->tx_fifo) & + (uint32_t)MAKE_64BIT_MASK(0, command.address_bits); + } + + s->enhanced =3D command; + s->remaining_frames =3D command.data_frames; + s->phase =3D K230_DW_SSI_PHASE_ENHANCED_INSTRUCTION; + return true; +} + +static void k230_dw_ssi_send_enhanced_field(K230DwSsiState *s, + uint32_t value, + uint32_t bits) +{ + uint32_t bytes =3D DIV_ROUND_UP(bits, 8); + + for (uint32_t i =3D 0; i < bytes; i++) { + uint32_t shift =3D (bytes - i - 1) * 8; + + ssi_transfer(s->spi, (value >> shift) & 0xff); + } +} + +static uint32_t k230_dw_ssi_dummy_bytes(uint32_t spi_frf, + uint32_t trans_type, + uint32_t wait_cycles) +{ + uint32_t lines =3D 1; + + if (trans_type !=3D 0) { + lines =3D spi_frf =3D=3D 1 ? 2 : 4; + } + + return DIV_ROUND_UP(wait_cycles * lines, 8); +} + +static void k230_dw_ssi_run_enhanced_rx_data(K230DwSsiState *s) +{ + while (!fifo32_is_full(&s->rx_fifo) && + s->remaining_frames > 0) { + uint32_t rx =3D ssi_transfer(s->spi, 0); + + fifo32_push(&s->rx_fifo, + rx & k230_dw_ssi_frame_masked(s)); + s->remaining_frames--; + } +} + +static void k230_dw_ssi_run_enhanced_tx_data(K230DwSsiState *s) +{ + uint32_t mask =3D k230_dw_ssi_frame_masked(s); + + while (!fifo32_is_empty(&s->tx_fifo) && + s->remaining_frames > 0) { + uint32_t tx =3D fifo32_pop(&s->tx_fifo); + + ssi_transfer(s->spi, tx & mask); + s->remaining_frames--; + } +} + +static void k230_dw_ssi_run_enhanced_transfer(K230DwSsiState *s) +{ + if (s->phase =3D=3D K230_DW_SSI_PHASE_IDLE) { + if (!k230_dw_ssi_prepare_enhanced_command(s)) { + return; + } + } + + if (s->phase =3D=3D K230_DW_SSI_PHASE_ENHANCED_INSTRUCTION) { + if (s->enhanced.instruction_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field( + s, s->enhanced.instruction, + s->enhanced.instruction_bits); + } + s->phase =3D K230_DW_SSI_PHASE_ENHANCED_ADDRESS; + } + + if (s->phase =3D=3D K230_DW_SSI_PHASE_ENHANCED_ADDRESS) { + if (s->enhanced.address_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field( + s, s->enhanced.address, s->enhanced.address_bits); + } + s->phase =3D K230_DW_SSI_PHASE_ENHANCED_MODE; + } + + if (s->phase =3D=3D K230_DW_SSI_PHASE_ENHANCED_MODE) { + if (s->enhanced.mode_bits_enabled) { + k230_dw_ssi_send_enhanced_field( + s, s->enhanced.mode, s->enhanced.mode_bits); + } + s->phase =3D K230_DW_SSI_PHASE_ENHANCED_DUMMY; + } + + if (s->phase =3D=3D K230_DW_SSI_PHASE_ENHANCED_DUMMY) { + uint32_t dummy_bytes =3D k230_dw_ssi_dummy_bytes( + s->enhanced.spi_frf, s->enhanced.trans_type, + s->enhanced.wait_cycles); + + for (uint32_t i =3D 0; i < dummy_bytes; i++) { + ssi_transfer(s->spi, 0); + } + s->phase =3D K230_DW_SSI_PHASE_ENHANCED_DATA; + } + + if (s->phase !=3D K230_DW_SSI_PHASE_ENHANCED_DATA) { + g_assert_not_reached(); + } + + switch (s->enhanced.tmod) { + case K230_DW_SSI_TMOD_RO: + k230_dw_ssi_run_enhanced_rx_data(s); + break; + case K230_DW_SSI_TMOD_TO: + k230_dw_ssi_run_enhanced_tx_data(s); + break; + default: + g_assert_not_reached(); + } + + if (s->remaining_frames =3D=3D 0) { + s->phase =3D K230_DW_SSI_PHASE_IDLE; + } +} + static void k230_dw_ssi_run_transfer(K230DwSsiState *s) { uint32_t spi_frf; @@ -455,16 +723,18 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *= s) =20 spi_frf =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); if (spi_frf !=3D 0) { + k230_dw_ssi_run_enhanced_transfer(s); return; } =20 tmod =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, TMOD); =20 switch (tmod) { - case 0: /* TX_AND_RX */ + case K230_DW_SSI_TMOD_TR: while (!fifo32_is_empty(&s->tx_fifo)) { uint32_t tx =3D fifo32_pop(&s->tx_fifo); uint32_t rx =3D k230_dw_ssi_send_frame(s, tx); + if (!fifo32_is_full(&s->rx_fifo)) { fifo32_push(&s->rx_fifo, rx); } else { @@ -476,7 +746,7 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s) } } break; - case 1: { /* TX_ONLY */ + case K230_DW_SSI_TMOD_TO: { unsigned int frames =3D 0; =20 if (fifo32_is_empty(&s->tx_fifo)) { @@ -497,7 +767,7 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s) } break; } - case 2: /* RX_ONLY */ + case K230_DW_SSI_TMOD_RO: switch (s->phase) { case K230_DW_SSI_PHASE_IDLE: if (fifo32_is_empty(&s->tx_fifo)) { @@ -522,7 +792,7 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s) break; } break; - case 3: /* EEPROM_READ */ + case K230_DW_SSI_TMOD_EEPROM_READ: switch (s->phase) { case K230_DW_SSI_PHASE_IDLE: if (fifo32_is_empty(&s->tx_fifo)) { @@ -562,8 +832,6 @@ static void k230_dw_ssi_run_transfer(K230DwSsiState *s) } } =20 - - static bool k230_dw_ssi_is_dr(hwaddr addr) { return addr >=3D A_DR0 && addr <=3D A_DR_END && @@ -920,6 +1188,7 @@ static void k230_dw_ssi_enter_reset(Object *obj, Reset= Type type) s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; s->irq_latched =3D 0; + memset(&s->enhanced, 0, sizeof(s->enhanced)); =20 s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; s->regs[R_SR] =3D K230_DW_SSI_SR_RESET; @@ -983,6 +1252,18 @@ static const VMStateDescription vmstate_k230_dw_ssi = =3D { VMSTATE_UINT32(irq_latched, K230DwSsiState), VMSTATE_UINT32(phase, K230DwSsiState), VMSTATE_UINT32(remaining_frames, K230DwSsiState), + VMSTATE_UINT32(enhanced.instruction, K230DwSsiState), + VMSTATE_UINT32(enhanced.address, K230DwSsiState), + VMSTATE_UINT32(enhanced.mode, K230DwSsiState), + VMSTATE_UINT32(enhanced.mode_bits, K230DwSsiState), + VMSTATE_UINT32(enhanced.instruction_bits, K230DwSsiState), + VMSTATE_UINT32(enhanced.address_bits, K230DwSsiState), + VMSTATE_UINT32(enhanced.wait_cycles, K230DwSsiState), + VMSTATE_UINT32(enhanced.data_frames, K230DwSsiState), + VMSTATE_UINT32(enhanced.spi_frf, K230DwSsiState), + VMSTATE_UINT32(enhanced.trans_type, K230DwSsiState), + VMSTATE_UINT32(enhanced.tmod, K230DwSsiState), + VMSTATE_BOOL(enhanced.mode_bits_enabled, K230DwSsiState), VMSTATE_INT32(active_cs, K230DwSsiState), VMSTATE_END_OF_LIST() }, diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index 404215245b..b692a616d6 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -51,8 +51,29 @@ typedef enum K230DwSsiPhase { K230_DW_SSI_PHASE_RX_ONLY, K230_DW_SSI_PHASE_EEPROM_COMMAND, K230_DW_SSI_PHASE_EEPROM_DATA, + + K230_DW_SSI_PHASE_ENHANCED_INSTRUCTION, + K230_DW_SSI_PHASE_ENHANCED_ADDRESS, + K230_DW_SSI_PHASE_ENHANCED_MODE, + K230_DW_SSI_PHASE_ENHANCED_DUMMY, + K230_DW_SSI_PHASE_ENHANCED_DATA, } K230DwSsiPhase; =20 +typedef struct K230DwSsiEnhancedCommand { + uint32_t instruction; + uint32_t address; + uint32_t mode; + uint32_t instruction_bits; + uint32_t address_bits; + uint32_t mode_bits; + uint32_t wait_cycles; + uint32_t data_frames; + uint32_t spi_frf; + uint32_t trans_type; + uint32_t tmod; + bool mode_bits_enabled; +} K230DwSsiEnhancedCommand; + struct K230DwSsiState { SysBusDevice parent_obj; =20 @@ -69,6 +90,7 @@ struct K230DwSsiState { =20 uint32_t phase; uint32_t remaining_frames; + K230DwSsiEnhancedCommand enhanced; =20 uint32_t num_cs; uint32_t max_lines; diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index e6c0e2ee8c..a507814b66 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -55,12 +55,23 @@ #define K230_SSI_CTRLR0_DFS_MASK 0x1fU #define K230_SSI_CTRLR0_TMOD_SHIFT 10 #define K230_SSI_CTRLR0_SRL BIT(13) +#define K230_SSI_CTRLR0_SPI_FRF_SHIFT 22 +#define K230_SSI_CTRLR0_SPI_FRF_MASK (3U << K230_SSI_CTRLR0_SPI_FRF_SHI= FT) =20 #define K230_SSI_TMOD_TR 0 #define K230_SSI_TMOD_TO 1 #define K230_SSI_TMOD_RO 2 #define K230_SSI_TMOD_EEPROM_READ 3 =20 +#define K230_SSI_FRF_QUAD 2 +#define K230_SSI_FRF_OCTAL 3 + +#define K230_SSI_SPI_CTRLR0_TRANS_TYPE(v) ((v) & 0x3U) +#define K230_SSI_SPI_CTRLR0_ADDR_L(bits) (((bits) / 4U) << 2) +#define K230_SSI_SPI_CTRLR0_INST_L_8 (2U << 8) +#define K230_SSI_SPI_CTRLR0_WAIT(v) (((v) & 0x1fU) << 11) +#define K230_SSI_SPI_CTRLR0_SPI_DDR_EN BIT(16) + #define K230_SSI_SR_BUSY BIT(0) #define K230_SSI_SR_TFNF BIT(1) #define K230_SSI_SR_TFE BIT(2) @@ -104,6 +115,8 @@ static const K230SsiInstance k230_ssi_instances[3] =3D { }, }; =20 +#define FLASH_CMD_JEDEC 0x9f + static QTestState *k230_ssi_start(void) { return qtest_init("-machine k230"); @@ -326,6 +339,57 @@ static void test_plic_routing(void) qtest_quit(qts); } =20 +static void assert_enhanced_config_rejected(QTestState *qts, uint32_t frf, + uint32_t extra_spi_ctrlr0) +{ + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + + k230_ssi_configure(qts, K230_SPI0_BASE, K230_SSI_TMOD_RO, 8, 3); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + ctrlr0 |=3D frf << K230_SSI_CTRLR0_SPI_FRF_SHIFT; + spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_INST_L_8 | extra_spi_ctrlr0; + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, ctrlr0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); + k230_ssi_enable_cs(qts, K230_SPI0_BASE, BIT(0)); + k230_ssi_write_frame(qts, K230_SPI0_BASE, FLASH_CMD_JEDEC); + g_assert_cmpuint(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_TXFLR), + =3D=3D, 1); + g_assert_cmpuint(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RXFLR), + =3D=3D, 0); + k230_ssi_disable(qts, K230_SPI0_BASE); +} + +static void test_qspi_config(void) +{ + QTestState *qts =3D k230_ssi_start(); + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + + assert_enhanced_config_rejected(qts, K230_SSI_FRF_OCTAL, 0); + assert_enhanced_config_rejected(qts, K230_SSI_FRF_QUAD, + K230_SSI_SPI_CTRLR0_SPI_DDR_EN); + + k230_ssi_configure(qts, K230_SPI0_BASE, K230_SSI_TMOD_RO, 8, 3); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + ctrlr0 |=3D K230_SSI_FRF_QUAD << K230_SSI_CTRLR0_SPI_FRF_SHIFT; + spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_TRANS_TYPE(1) | + K230_SSI_SPI_CTRLR0_ADDR_L(24) | + K230_SSI_SPI_CTRLR0_INST_L_8 | + K230_SSI_SPI_CTRLR0_WAIT(8); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, ctrlr0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); + k230_ssi_enable_cs(qts, K230_SPI0_BASE, BIT(0)); + k230_ssi_write_frame(qts, K230_SPI0_BASE, FLASH_CMD_JEDEC); + k230_ssi_write_frame(qts, K230_SPI0_BASE, 0x123456); + g_assert_cmpuint(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RXFLR), + =3D=3D, 4); + for (int i =3D 0; i < 4; i++) { + g_assert_cmphex(k230_ssi_read_frame(qts, K230_SPI0_BASE), =3D=3D, = 0); + } + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -335,5 +399,6 @@ int main(int argc, char **argv) qtest_add_func("/k230-dw-ssi/interrupt-controller", test_interrupt_controller); qtest_add_func("/k230-dw-ssi/plic-routing", test_plic_routing); + qtest_add_func("/k230-dw-ssi/qspi-config", test_qspi_config); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785068994; cv=none; d=zohomail.com; s=zohoarc; b=h6WqSgPMOjWx5VhWGTDd2Rd8upwB7F4qCcuvNIawpyxxgqSUsCkKwx+1SdnUG7shh762+rCz6sXa9Fi8QKsH4S0dOoCwjAvgNEAzp1vO05fcSSnh1OY7SNtZE78y+75wCvi+p0EI1Wt9Ux7wz5hvwqHKJ/Yb097PFntkJXsLYqo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785068994; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=QKHz/f2Y+2B+bg+Zr4OrOSfNNtCTzXA5eoPO+6fqgDA=; b=WpWUvEhRj3ymE2+gsbL0307d39l+Bxcsy/UWZ45ViIFmx4APZBp07iHpKCA9M5wb7BvnRpVSxfW+Iz7FbAzQAed6CSuV2K/cy3RNmOuKzA7+fTjgjYVinNbgdxNg/wxcXMi/YiuuESNX6x38wAW7x2QDv95eMpEp3VUub+5t4T0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785068994572550.9041422658706; Sun, 26 Jul 2026 05:29:54 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxzF-0001kA-HY; Sun, 26 Jul 2026 08:29:45 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxzD-0001YK-3S for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:43 -0400 Received: from mail-pf1-x42f.google.com ([2607:f8b0:4864:20::42f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxzA-0003KE-7D for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:42 -0400 Received: by mail-pf1-x42f.google.com with SMTP id d2e1a72fcca58-84a4d8fd6ecso1856147b3a.1 for ; Sun, 26 Jul 2026 05:29:39 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068979; x=1785673779; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=QKHz/f2Y+2B+bg+Zr4OrOSfNNtCTzXA5eoPO+6fqgDA=; b=nr6Hc4uZXV69mdK958WDmE4WgzZ2VewREW7zEvWVJJWk2zJqkYFV76d6aUNSPOFFmh 2d0ImjwwzRucSB0tOcMxd4henz9nvT1xCPRXeD4uteMY+OIdmPbw9MQkLMCJJXMM9StK S7Fm+dISWAX/8tm9mGAqE6dnow1fTPyp2GwSySQdBRzZWi5CIewua7nh0GSl7Cm9lBTV m8Q2JJtzvOspPZMe11Jk/pAWARlROkFBYu/FwGVxn6OZs9QJ8S83qn9FT3OkN230U83E cYjud8BSOwq5ZWW2Toca9tNZ5l4iQn0ZJog8oAcW2/1Qwa9XN5KZ3iJuDHCOwCTRfeJI alXQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068979; x=1785673779; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=QKHz/f2Y+2B+bg+Zr4OrOSfNNtCTzXA5eoPO+6fqgDA=; b=AhLATpGpGa4tOVgvmCtGFPSTu0HRivLDvJmYGaAhxJHlxtOIM2iblR5Ol80omki7im VxNlpe0SeDFNTw3cNuOSgphaZeQ+X9NjRhgZnzvj/sqpv5XBmEBU2UQcxXPgoPcdlap6 IXvpdxdhDMIKztNpHKkcRPtUFsRmua2Bi79tqs/a72creAK4zQ+6c0p7hVDHOXr/dfdR 06S3nkywznZ73shdCCyms7s5ffvqc6rr1I796wXDl0CfqUl8Elo/AIO07ePvhNDNve7I 1NWxCOfwvlwhqu4O5/qsf9ueAnbcVTdevpSLQZFQqrv6EnizHcV8lxS1g4OpSN/7a7tI rdQA== X-Gm-Message-State: AOJu0YyDwtYDJYcOUpso5MkEn1LLCTdQDxvP+MkAAxzr/qPo86alPfOL r49noZFeWhSS0eGpnF8+rAc1pT3ix5AMcqngLAh7bZoEY9RN9zd02swWhAKtpGM8Z+c= X-Gm-Gg: AR+sD12xIeMYSHyC5F1d4RaBTy5hCjlno/RT/V+b9bJiRWprDL2gxpSsXRJhwwuRJQY QvgXLrKqGxXt0a732s7dVJ6a2PXpaxRGUFFLWEhOTFl5nRA/PKWnSxxnbCW5e5gfD96pcuHr8ox +ka7BH+l4GKywZ6BeOqvBrFYjleTHq3RG47cduwnivYe2zH86JBhuWY6DcVTrvcRg2Z1vvnjOpz /VX75pu9UfukgyahT9SBs3e6nRl7eIKLaRs5yMZn9NK56F7JLFsT7NNk0YmJGRr05pYSihmvQxc bqu8Y5BhCCC6J0W4Vfz9KP4/yp/fjR/yOdYZwsG551KQLEqigNCA+PkvqZBcR+JuCxYfxVvI41e 63GwlH/HL6PsW0vE+q1cNI2RTJC0TkaEh18wNNXbnSv7cOlWRCt6klsNYKaBbYa2vPaVLg0H0HR J46nagQryw/xfzuI1iK3GY X-Received: by 2002:a05:6a00:b83:b0:82f:9d21:d352 with SMTP id d2e1a72fcca58-84e59425aa8mr3871874b3a.9.1785068978619; Sun, 26 Jul 2026 05:29:38 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 07/11] hw/riscv/k230: Attach SPI NOR flash to spi0 Date: Sun, 26 Jul 2026 20:28:25 +0800 Message-ID: X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42f; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42f.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785068995172158500 Content-Type: text/plain; charset="utf-8" Add the optional spi-flash machine property and attach the selected m25p80-compatible device to logical spi0 chip select 0. Use an MTD backend when supplied and keep the erased-flash default when no drive is configured. Cover JEDEC identification, standard read and page program, Quad output read, and Quad page program in qtest. Also exercise standard SPI reads and writes from U-Boot and Linux. Also verify the U-Boot-managed boot path by loading the Linux and OpenSBI payloads from the attached SPI flash instead of injecting them via the QEMU command line. The system reaches a Linux shell. Signed-off-by: Kangjie Huang --- docs/system/riscv/k230.rst | 1 + hw/riscv/Kconfig | 1 + hw/riscv/k230.c | 66 ++++++++ include/hw/riscv/k230.h | 2 + tests/qtest/k230-dw-ssi-test.c | 285 +++++++++++++++++++++++++++++++++ 5 files changed, 355 insertions(+) diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index 0358709d2c..0d2617c0f9 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 * 3 K230 SSI controllers for SPI and QSPI +* Optional SPI NOR flash on spi0 CS0 =20 Boot options ------------ diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index 69059ae044..4984b0c9f3 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -163,3 +163,4 @@ config K230 select UNIMP select K230_WDT select K230_DW_SSI + select SSI_M25P80 diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 1863d5ecbb..36fd1eda22 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -21,7 +21,9 @@ #include "system/device_tree.h" #include "system/system.h" #include "system/memory.h" +#include "system/blockdev.h" #include "target/riscv/cpu.h" +#include "hw/block/flash.h" #include "hw/core/loader.h" #include "hw/core/sysbus.h" #include "hw/riscv/k230.h" @@ -31,6 +33,7 @@ #include "hw/intc/sifive_plic.h" #include "hw/char/serial-mm.h" #include "hw/misc/unimp.h" +#include "hw/ssi/ssi.h" =20 /* Align K230_SDK k230_canmv_defconfig */ #define K230_DIRECT_OPENSBI_ADDR 0x8000000 @@ -512,6 +515,49 @@ static void k230_firmware_boot(K230MachineState *s, Ma= chineState *machine) memmap[K230_DEV_BOOTROM].size, 0, 0); } =20 +static char *k230_machine_get_spi_flash(Object *obj, Error **errp) +{ + K230MachineState *s =3D RISCV_K230_MACHINE(obj); + + return g_strdup(s->spi_flash_model); +} + +static void k230_machine_set_spi_flash(Object *obj, const char *value, + Error **errp) +{ + K230MachineState *s =3D RISCV_K230_MACHINE(obj); + + g_free(s->spi_flash_model); + s->spi_flash_model =3D g_strdup(value); +} + +static void k230_connect_spi_flash(K230DwSsiState *ssi, unsigned int cs, + const char *flash_type, DriveInfo *dinf= o) +{ + ObjectClass *flash_class; + DeviceState *flash; + qemu_irq flash_cs; + + flash_class =3D module_object_class_by_name(flash_type); + if (!flash_class || object_class_is_abstract(flash_class) || + !object_class_dynamic_cast(flash_class, TYPE_M25P80)) { + error_report("'%s' is either abstract or not a subtype of m25p80", + flash_type); + exit(EXIT_FAILURE); + } + + flash =3D qdev_new(flash_type); + + if (dinfo) { + qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo)); + } + + qdev_realize_and_unref(flash, BUS(ssi->spi), &error_fatal); + + flash_cs =3D qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0); + qdev_connect_gpio_out_named(DEVICE(ssi), "cs", cs, flash_cs); +} + static void k230_machine_done(Notifier *notifier, void *data) { K230MachineState *s =3D container_of(notifier, K230MachineState, @@ -543,6 +589,12 @@ static void k230_machine_init(MachineState *machine) TYPE_RISCV_K230_SOC); qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); =20 + if (s->spi_flash_model) { + k230_connect_spi_flash(&s->soc.dw_ssi[2], 0, + s->spi_flash_model, + drive_get(IF_MTD, 0, 0)); + } + /* Data Memory */ memory_region_add_subregion(sys_mem, memmap[K230_DEV_DDRC].base, machine->ram); @@ -555,6 +607,13 @@ static void k230_machine_instance_init(Object *obj) { } =20 +static void k230_machine_instance_finalize(Object *obj) +{ + K230MachineState *s =3D RISCV_K230_MACHINE(obj); + + g_clear_pointer(&s->spi_flash_model, g_free); +} + static void k230_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc =3D MACHINE_CLASS(oc); @@ -564,6 +623,12 @@ static void k230_machine_class_init(ObjectClass *oc, c= onst void *data) mc->default_cpus =3D 1; mc->default_ram_id =3D "riscv.K230.ram"; /* DDR */ mc->default_ram_size =3D memmap[K230_DEV_DDRC].size; + + object_class_property_add_str(oc, "spi-flash", + k230_machine_get_spi_flash, + k230_machine_set_spi_flash); + object_class_property_set_description( + oc, "spi-flash", "Attach an M25P80-compatible flash to spi0 CS0"); } =20 static const TypeInfo k230_machine_typeinfo =3D { @@ -571,6 +636,7 @@ static const TypeInfo k230_machine_typeinfo =3D { .parent =3D TYPE_MACHINE, .class_init =3D k230_machine_class_init, .instance_init =3D k230_machine_instance_init, + .instance_finalize =3D k230_machine_instance_finalize, .instance_size =3D sizeof(K230MachineState), .interfaces =3D riscv64_machine_interfaces, }; diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index cfe30590cc..493d739aa4 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -52,6 +52,8 @@ typedef struct K230MachineState { /*< public >*/ K230SoCState soc; Notifier machine_done; + + char *spi_flash_model; } K230MachineState; =20 enum { diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index a507814b66..658b1f4cbf 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qemu/bitops.h" +#include "qemu/units.h" =20 #define K230_SPI0_BASE 0x91584000ULL #define K230_SPI1_BASE 0x91582000ULL @@ -68,9 +69,11 @@ =20 #define K230_SSI_SPI_CTRLR0_TRANS_TYPE(v) ((v) & 0x3U) #define K230_SSI_SPI_CTRLR0_ADDR_L(bits) (((bits) / 4U) << 2) +#define K230_SSI_SPI_CTRLR0_XIP_MD_EN BIT(7) #define K230_SSI_SPI_CTRLR0_INST_L_8 (2U << 8) #define K230_SSI_SPI_CTRLR0_WAIT(v) (((v) & 0x1fU) << 11) #define K230_SSI_SPI_CTRLR0_SPI_DDR_EN BIT(16) +#define K230_SSI_SPI_CTRLR0_XIP_MBL_8 (2U << 26) =20 #define K230_SSI_SR_BUSY BIT(0) #define K230_SSI_SR_TFNF BIT(1) @@ -88,6 +91,10 @@ #define K230_SSI_IRQ_AXIE 8 =20 #define K230_SSI_FIFO_DEPTH 256 +#define K230_SSI_FLASH_IMAGE_SIZE (32 * MiB) +#define K230_SSI_FLASH_PATTERN_ADDR 0x100 +#define K230_SSI_FLASH_HIGH_ADDR 0x1000100 +#define K230_SSI_FLASH_PROGRAM_ADDR 0x22000 =20 typedef struct K230SsiInstance { uint64_t base; @@ -96,6 +103,10 @@ typedef struct K230SsiInstance { uint32_t first_irq; } K230SsiInstance; =20 +typedef struct K230SsiFlashImage { + char *path; +} K230SsiFlashImage; + static const K230SsiInstance k230_ssi_instances[3] =3D { { .base =3D K230_SPI0_BASE, @@ -117,6 +128,17 @@ static const K230SsiInstance k230_ssi_instances[3] =3D= { =20 #define FLASH_CMD_JEDEC 0x9f =20 +#define FLASH_CMD_WREN 0x06 +#define FLASH_CMD_RDSR 0x05 +#define FLASH_CMD_READ 0x03 +#define FLASH_CMD_READ4 0x13 +#define FLASH_CMD_QUAD_OUT 0x6b +#define FLASH_CMD_QUAD_IO 0xeb +#define FLASH_CMD_PP 0x02 +#define FLASH_SR_WIP BIT(0) + +#define FLASH_CMD_QUAD_PP 0x32 + static QTestState *k230_ssi_start(void) { return qtest_init("-machine k230"); @@ -210,6 +232,188 @@ static bool k230_ssi_plic_pending(QTestState *qts, ui= nt32_t irq) return qtest_readl(qts, addr) & BIT(irq % 32); } =20 +static void write_exact(int fd, const void *buf, size_t len, off_t offset) +{ + ssize_t ret =3D pwrite(fd, buf, len, offset); + + g_assert_cmpint(ret, =3D=3D, len); +} + +static void k230_ssi_flash_image_init(K230SsiFlashImage *image) +{ + static const uint8_t low_pattern[] =3D { + 0xa5, 0x5a, 0x3c, 0xc3, 0x11, 0x22, 0x33, 0x44, + }; + static const uint8_t high_pattern[] =3D { 0x71, 0x72, 0x73, 0x74 }; + uint8_t erased[4096]; + int fd; + + memset(erased, 0xff, sizeof(erased)); + image->path =3D NULL; + fd =3D g_file_open_tmp("qtest.k230.w25q256.XXXXXX", &image->path, NULL= ); + g_assert_cmpint(fd, >=3D, 0); + g_assert_cmpint(ftruncate(fd, K230_SSI_FLASH_IMAGE_SIZE), =3D=3D, 0); + write_exact(fd, low_pattern, sizeof(low_pattern), + K230_SSI_FLASH_PATTERN_ADDR); + write_exact(fd, high_pattern, sizeof(high_pattern), + K230_SSI_FLASH_HIGH_ADDR); + write_exact(fd, erased, sizeof(erased), K230_SSI_FLASH_PROGRAM_ADDR); + close(fd); +} + +static void k230_ssi_flash_image_clear(K230SsiFlashImage *image) +{ + if (image->path) { + unlink(image->path); + g_clear_pointer(&image->path, g_free); + } +} + +static QTestState *k230_ssi_start_with_flash(K230SsiFlashImage *image) +{ + k230_ssi_flash_image_init(image); + return qtest_initf("-machine k230,spi-flash=3Dw25q256 " + "-drive file=3D%s,format=3Draw,if=3Dmtd", + image->path); +} + +static void flash_write_transaction(QTestState *qts, + const uint8_t *command, + size_t command_len) +{ + g_assert_nonnull(command); + g_assert_cmpuint(command_len, >, 0); + g_assert_cmpuint(command_len, <=3D, K230_SSI_FIFO_DEPTH); + + k230_ssi_configure(qts, K230_SPI0_BASE, K230_SSI_TMOD_TO, 8, 0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, 0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SSIENR, 1); + for (size_t i =3D 0; i < command_len; i++) { + k230_ssi_write_frame(qts, K230_SPI0_BASE, command[i]); + } + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, BIT(0)); + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_TXFLR, + UINT32_MAX, 0); + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_SR, + K230_SSI_SR_BUSY, 0); + k230_ssi_disable(qts, K230_SPI0_BASE); +} + +static void flash_read_transaction(QTestState *qts, + const uint8_t *command, + size_t command_len, + uint8_t *data, size_t data_len) +{ + g_assert_nonnull(command); + g_assert_nonnull(data); + g_assert_cmpuint(command_len, >, 0); + g_assert_cmpuint(data_len, >, 0); + g_assert_cmpuint(data_len, <, K230_SSI_FIFO_DEPTH); + + k230_ssi_configure(qts, K230_SPI0_BASE, + K230_SSI_TMOD_EEPROM_READ, 8, data_len - 1); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, 0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SSIENR, 1); + for (size_t i =3D 0; i < command_len; i++) { + k230_ssi_write_frame(qts, K230_SPI0_BASE, command[i]); + } + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, BIT(0)); + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_RXFLR, + UINT32_MAX, data_len); + for (size_t i =3D 0; i < data_len; i++) { + data[i] =3D k230_ssi_read_frame(qts, K230_SPI0_BASE); + } + k230_ssi_disable(qts, K230_SPI0_BASE); +} + +static void flash_read(QTestState *qts, uint8_t opcode, uint32_t address, + unsigned int addr_bytes, uint8_t *data, size_t len) +{ + uint8_t command[5]; + + g_assert_cmpuint(addr_bytes, <=3D, 4); + command[0] =3D opcode; + for (unsigned int i =3D 0; i < addr_bytes; i++) { + command[1 + i] =3D address >> (8 * (addr_bytes - i - 1)); + } + flash_read_transaction(qts, command, 1 + addr_bytes, data, len); +} + +static uint8_t flash_read_status(QTestState *qts) +{ + uint8_t command =3D FLASH_CMD_RDSR; + uint8_t status; + + flash_read_transaction(qts, &command, 1, &status, 1); + return status; +} + +static void flash_wait_ready(QTestState *qts) +{ + for (int i =3D 0; i < 1000; i++) { + if (!(flash_read_status(qts) & FLASH_SR_WIP)) { + return; + } + qtest_clock_step(qts, 1000000); + } + g_assert_cmphex(flash_read_status(qts) & FLASH_SR_WIP, =3D=3D, 0); +} + +static void flash_write_enable(QTestState *qts) +{ + uint8_t command =3D FLASH_CMD_WREN; + + flash_write_transaction(qts, &command, 1); +} + +static void configure_enhanced_transfer(QTestState *qts, uint32_t tmod, + uint32_t frf, uint32_t trans_type, + uint32_t wait_cycles, + bool mode_bits_enabled, + size_t data_frames) +{ + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + + k230_ssi_configure(qts, K230_SPI0_BASE, tmod, 8, data_frames - 1); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, 0); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + ctrlr0 &=3D ~K230_SSI_CTRLR0_SPI_FRF_MASK; + ctrlr0 |=3D frf << K230_SSI_CTRLR0_SPI_FRF_SHIFT; + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, ctrlr0); + + spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_TRANS_TYPE(trans_type) | + K230_SSI_SPI_CTRLR0_ADDR_L(24) | + K230_SSI_SPI_CTRLR0_INST_L_8 | + K230_SSI_SPI_CTRLR0_WAIT(wait_cycles); + if (mode_bits_enabled) { + spi_ctrlr0 |=3D K230_SSI_SPI_CTRLR0_XIP_MD_EN | + K230_SSI_SPI_CTRLR0_XIP_MBL_8; + } + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); +} + +static void start_enhanced_transfer(QTestState *qts, uint8_t opcode, + uint32_t address) +{ + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SSIENR, 1); + k230_ssi_write_frame(qts, K230_SPI0_BASE, opcode); + k230_ssi_write_frame(qts, K230_SPI0_BASE, address); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, BIT(0)); +} + +static void read_enhanced_result(QTestState *qts, uint8_t *data, size_t le= n) +{ + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_RXFLR, + UINT32_MAX, len); + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_SR, + K230_SSI_SR_BUSY, 0); + for (size_t i =3D 0; i < len; i++) { + data[i] =3D k230_ssi_read_frame(qts, K230_SPI0_BASE); + } + k230_ssi_disable(qts, K230_SPI0_BASE); +} + static void test_register_contract(void) { QTestState *qts =3D k230_ssi_start(); @@ -390,6 +594,85 @@ static void test_qspi_config(void) qtest_quit(qts); } =20 +static void test_spi_nor(void) +{ + static const uint8_t expected[] =3D { + 0xa5, 0x5a, 0x3c, 0xc3, 0x11, 0x22, 0x33, 0x44, + }; + static const uint8_t payload[] =3D { 0xde, 0xad, 0xbe, 0xef }; + K230SsiFlashImage image; + QTestState *qts =3D k230_ssi_start_with_flash(&image); + uint8_t command =3D FLASH_CMD_JEDEC; + uint8_t id[3]; + uint8_t actual[ARRAY_SIZE(expected)]; + uint8_t program[4 + ARRAY_SIZE(payload)]; + uint32_t addr =3D K230_SSI_FLASH_PROGRAM_ADDR; + + flash_read_transaction(qts, &command, 1, id, sizeof(id)); + g_assert_cmphex(id[0], =3D=3D, 0xef); + g_assert_cmphex(id[1], =3D=3D, 0x40); + g_assert_cmphex(id[2], =3D=3D, 0x19); + flash_read(qts, FLASH_CMD_READ, K230_SSI_FLASH_PATTERN_ADDR, + 3, actual, sizeof(actual)); + g_assert_cmpmem(actual, sizeof(actual), expected, sizeof(expected)); + + flash_write_enable(qts); + program[0] =3D FLASH_CMD_PP; + program[1] =3D addr >> 16; + program[2] =3D addr >> 8; + program[3] =3D addr; + memcpy(program + 4, payload, sizeof(payload)); + flash_write_transaction(qts, program, sizeof(program)); + flash_wait_ready(qts); + flash_read(qts, FLASH_CMD_READ, addr, 3, actual, sizeof(payload)); + g_assert_cmpmem(actual, sizeof(payload), payload, sizeof(payload)); + + qtest_quit(qts); + k230_ssi_flash_image_clear(&image); +} + +static void test_qspi_sdr(void) +{ + static const uint8_t expected[] =3D { 0xa5, 0x5a, 0x3c, 0xc3 }; + static const uint8_t payload[] =3D { 0x12, 0x34, 0x56, 0x78 }; + K230SsiFlashImage image; + QTestState *qts =3D k230_ssi_start_with_flash(&image); + uint8_t actual[ARRAY_SIZE(expected)]; + + configure_enhanced_transfer(qts, K230_SSI_TMOD_RO, + K230_SSI_FRF_QUAD, 0, 8, false, + ARRAY_SIZE(actual)); + start_enhanced_transfer(qts, FLASH_CMD_QUAD_OUT, + K230_SSI_FLASH_PATTERN_ADDR); + read_enhanced_result(qts, actual, sizeof(actual)); + g_assert_cmpmem(actual, sizeof(actual), expected, sizeof(expected)); + + flash_write_enable(qts); + configure_enhanced_transfer(qts, K230_SSI_TMOD_TO, + K230_SSI_FRF_QUAD, 0, 0, false, + ARRAY_SIZE(payload)); + start_enhanced_transfer(qts, FLASH_CMD_QUAD_PP, + K230_SSI_FLASH_PROGRAM_ADDR); + for (int i =3D 0; i < ARRAY_SIZE(payload); i++) { + k230_ssi_write_frame(qts, K230_SPI0_BASE, payload[i]); + } + k230_ssi_wait_mask(qts, K230_SPI0_BASE, K230_SSI_SR, + K230_SSI_SR_BUSY, 0); + k230_ssi_disable(qts, K230_SPI0_BASE); + flash_wait_ready(qts); + + configure_enhanced_transfer(qts, K230_SSI_TMOD_RO, + K230_SSI_FRF_QUAD, 0, 8, false, + ARRAY_SIZE(actual)); + start_enhanced_transfer(qts, FLASH_CMD_QUAD_OUT, + K230_SSI_FLASH_PROGRAM_ADDR); + read_enhanced_result(qts, actual, sizeof(actual)); + g_assert_cmpmem(actual, sizeof(payload), payload, sizeof(payload)); + + qtest_quit(qts); + k230_ssi_flash_image_clear(&image); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -400,5 +683,7 @@ int main(int argc, char **argv) test_interrupt_controller); qtest_add_func("/k230-dw-ssi/plic-routing", test_plic_routing); qtest_add_func("/k230-dw-ssi/qspi-config", test_qspi_config); + qtest_add_func("/k230-dw-ssi/spi-nor", test_spi_nor); + qtest_add_func("/k230-dw-ssi/qspi-sdr", test_qspi_sdr); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069073; cv=none; d=zohomail.com; s=zohoarc; b=eTtw8vxGoreYxA+SsR80iV/SOVuCqadWoTa3wzjHVtxwy0ci1XxV4B5FYMzRj5oubCWvO2NNqjRy+1pSVXVykK3oGKivaQ+9JDGJDTLaLpSkEfj9t2Tdj8c6/wy3A80LwG5XEnuq6WxjSbcWNT71HAOATyQBZlTxzhaZqqwIRe0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069073; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=lGCkWJzQd7BJMWOS08X/BktnfKt61NKByqz9ghUWBQs=; b=QAciq9zOuAI7gr33tJtNXvLckn4iHGhy542HxsYdEVghLm+yS1QjeUFPw0WikeUdjnyWs+5nQLJapQLC0Z53AaODVD+mWTXAqNl4T8YYPImMNOw/8A08p33i2IvvUgJk3SD6d6L/tz3yZHgRcx0J1iOhIQVo8ETmUepsky45vHM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069073478876.8057256043099; Sun, 26 Jul 2026 05:31:13 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxzT-0002Ix-VF; Sun, 26 Jul 2026 08:30:00 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxzJ-00023i-Jt for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:51 -0400 Received: from mail-pf1-x42f.google.com ([2607:f8b0:4864:20::42f]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxzG-0003Kf-0x for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:48 -0400 Received: by mail-pf1-x42f.google.com with SMTP id d2e1a72fcca58-84862b0d5f8so1399304b3a.3 for ; Sun, 26 Jul 2026 05:29:45 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.40 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068985; x=1785673785; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=lGCkWJzQd7BJMWOS08X/BktnfKt61NKByqz9ghUWBQs=; b=YYZD/bSwyLBQ5gPOwYonQaKzKSR8s6ge/8DmM6SXa61KAQLOZdmp4gLKmhaVDHRfYe 9hyIhbKLz5m3jgypNcJhgp3d17TyRay8t4VFyfC7d1ywS8bruZxlzMYvDdMdqofWuJeO qEidkEzOdBrPog/BnQrvcDgUhWTiOuFwnq8au1hKlex2shUPqLqbh1/GGzHbJP16P3gx Oye/gEW6mwE5HR+q88xnFDcXbb8MhSZKCkmBe/QJ68NGEk/JE02ktPXb3YuGccQJ9qI1 Dy0bcRIqsK0OrbWd47PaKeW/DlcJGITX6FMzOqwM71arhRs8lr2CCB3xC4pVq692qRV2 1Fsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068985; x=1785673785; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=lGCkWJzQd7BJMWOS08X/BktnfKt61NKByqz9ghUWBQs=; b=Kmq53pQlHGBZdqnWsbw91RnKF3RDXfMrdXRDdcAIRhmoSkx6txXne50kB3s/g/S/mv dJ5fYWp8SNJv90qUmk2QHt9LnVCZlPZvgub44SOv3PUT6fQUTErwM1geaE5obWFk+mWF aBUjYpQG6rI0vlXDr+XOHskveZq/C2c0UoPmdiyrugvEvkjWrDaR+LKLJdZe9TZ3inUP LE/OOu7Xgp5GzzeJDv7m5n4Zi77LFupRYld2XA8CHcCO5n3Q3ZRwW2s/3clQjXnSr5KM KLecOyBlTbNzKG/S8YE2bVOqQhGzEP3ykx3t0Wcgw5ojDZ5A8EX5GrppBo09SUDWbNVz VN7A== X-Gm-Message-State: AOJu0YxCyxs1VaboCeMxVlhzFG3prw+JRSErsqYmB4pP452RzSs1jqIJ wvIdjRk3cjpocxUoPYCifKJKTNvMflQQA0ysZiA8/XldhVGIluai1aQM94pC+FjawBg= X-Gm-Gg: AR+sD12t6sWu/Z7h9U65JmkzzPnRXlaoIMWUee5PGSDqAYHNQjwpz/mke2J2n9AsRSd r3IrzElIqiIXR/nXn6R9UEG8kUZsLHBPShEeOIXoM0+kbaMWw/9PYRnuMLmytNnpBtLFekWCiiJ nAEB88intwvCKppEdWMzpc5naJdwLbXdZJAK+c6fESzUy45dZHDLh4D+dg/FsLlgVZwyOWzcknj K+da8sXLQWNqlUM8/7Vsf3TiMgoU5hxqS/vW88xHYxvMCUSjE9+iD7wPk+uQURCtGZhmf+A07S5 pFFpLJg+JqJLgrQpjUSHjJqC1Xt9b32CfmFXA9Q1ScVtA0CXvsRl2nJmMsDAbpX1wg04ypIOFiz T47jlaeyZIoF9dNG4kqS6RU42bJNDv0LBMfzCHpQW+EcjCeJ4XZ2zM0Hymd9FZ1/axStMf7Ui31 q1CO+DwSK7D8NW6nTv4LHV X-Received: by 2002:a05:6a00:6c95:b0:847:c014:864e with SMTP id d2e1a72fcca58-84e594969bfmr3791282b3a.30.1785068984445; Sun, 26 Jul 2026 05:29:44 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 08/11] hw/ssi: Implement K230 SSI internal DMA transfers Date: Sun, 26 Jul 2026 20:28:26 +0800 Message-ID: <8f3b84c222bc1d4cc5d5704b13cd70e9af39cce4.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42f; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42f.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069075431158500 Content-Type: text/plain; charset="utf-8" Implement synchronous internal DMA for 8-bit SDR Dual and Quad transfer modes. The K230 SDK driver polls DONE before touching the buffer, so a synchronous model is sufficient and avoids racing guest memory. Only the 8-bit SDR Dual and Quad modes are supported, matching what that driver uses. Build enhanced commands from SPIDR and SPIAR, move data through AXIAR0/1, and report completion or guest-memory failures through the DONE and AXIE interrupt causes. Keep DR accesses out of the FIFO while IDMA is enabled. Implement the read-clear status registers, terminate each transaction with SSI disabled and chip select inactive, and migrate the completed-frame count. Cover a Quad read into guest RAM, completed-frame reporting, DONE routing and clearing, and the AXIE path for an invalid guest address. Exercise QSPI read and write commands from U-Boot and Linux. With spi0 configured for QSPI in the device tree, boot a Linux image from QSPI flash. Signed-off-by: Kangjie Huang --- docs/system/riscv/k230.rst | 1 + hw/ssi/k230_dw_ssi.c | 203 +++++++++++++++++++++++++++++++-- include/hw/ssi/k230_dw_ssi.h | 1 + tests/qtest/k230-dw-ssi-test.c | 95 +++++++++++++++ 4 files changed, 290 insertions(+), 10 deletions(-) diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index 0d2617c0f9..3067c1b5d7 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -22,6 +22,7 @@ The ``k230`` machine supports the following devices: * 5 UART * 3 K230 SSI controllers for SPI and QSPI * Optional SPI NOR flash on spi0 CS0 +* QSPI IDMA transfers for 8-bit SDR Dual and Quad modes =20 Boot options ------------ diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index 31da3a06a3..65b2a8c245 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -25,6 +25,7 @@ #include "qemu/bitops.h" #include "qemu/log.h" #include "qemu/module.h" +#include "system/dma.h" =20 #define K230_DW_SSI_FIFO_CAPACITY 256 =20 @@ -331,7 +332,7 @@ static uint32_t k230_dw_ssi_frame_masked(K230DwSsiState= *s) return bits =3D=3D 32 ? UINT32_MAX : MAKE_64BIT_MASK(0, bits); } =20 -static bool k230_dw_ssi_enabled(K230DwSsiState *s) +static bool k230_dw_ssi_enabled(const K230DwSsiState *s) { return FIELD_EX32(s->regs[R_SSIENR], SSIENR, SSIC_EN); } @@ -408,6 +409,7 @@ static uint32_t k230_dw_ssi_status(K230DwSsiState *s) sr =3D FIELD_DP32(sr, SR, RFNE, rx_used !=3D 0); sr =3D FIELD_DP32(sr, SR, RFF, rx_used =3D=3D K230_DW_SSI_FIFO_CAPACITY); + sr =3D FIELD_DP32(sr, SR, CMPLTD_DF, s->idma_completed_frames); =20 return sr; } @@ -623,6 +625,172 @@ static uint32_t k230_dw_ssi_dummy_bytes(uint32_t spi_= frf, return DIV_ROUND_UP(wait_cycles * lines, 8); } =20 +static bool k230_dw_ssi_idma_enabled(const K230DwSsiState *s) +{ + return FIELD_EX32(s->regs[R_DMACR], DMACR, IDMAE); +} + +static uint64_t k230_dw_ssi_idma_address(const K230DwSsiState *s) +{ + return s->regs[R_AXIAR0] | ((uint64_t)s->regs[R_AXIAR1] << 32); +} + +static bool k230_dw_ssi_idma_triggered(const K230DwSsiState *s) +{ + uint32_t ser =3D s->regs[R_SER]; + + return k230_dw_ssi_idma_enabled(s) && + k230_dw_ssi_enabled(s) && ser && + !(ser & (ser - 1)) && + s->phase =3D=3D K230_DW_SSI_PHASE_IDLE; +} + +static void k230_dw_ssi_idma_end(K230DwSsiState *s, uint32_t cause) +{ + s->regs[R_SSIENR] =3D 0; + s->phase =3D K230_DW_SSI_PHASE_IDLE; + s->remaining_frames =3D 0; + k230_dw_ssi_deselect(s); + s->irq_latched |=3D cause; + k230_dw_ssi_update_irq(s); +} + +static void k230_dw_ssi_idma_fail(K230DwSsiState *s, const char *operation) +{ + qemu_log_mask(LOG_GUEST_ERROR, + "%s: IDMA %s memory access failed\n", + DEVICE(s)->canonical_path, operation); + s->idma_completed_frames =3D 0; + k230_dw_ssi_idma_end(s, R_RISR_AXIER_MASK); +} + +/* + * Supported SDK paths observe the final memory contents and DONE/AXIE, + * so complete IDMA synchronously without modeling AXI timing or FIFO + * backpressure. + */ +static void k230_dw_ssi_try_idma(K230DwSsiState *s) +{ + K230DwSsiEnhancedCommand command =3D { 0 }; + g_autofree uint8_t *buffer =3D NULL; + uint64_t address; + uint32_t dummy_bytes; + uint32_t length; + MemTxResult result; + + if (!k230_dw_ssi_idma_triggered(s)) { + return; + } + + if (!FIELD_EX32(s->regs[R_DMACR], DMACR, AINC)) { + qemu_log_mask(LOG_UNIMP, + "%s: fixed-address IDMA is unsupported\n", + DEVICE(s)->canonical_path); + s->idma_completed_frames =3D 0; + k230_dw_ssi_idma_end(s, 0); + return; + } + + if (!fifo32_is_empty(&s->tx_fifo) || + !fifo32_is_empty(&s->rx_fifo)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: IDMA requires empty TX and RX FIFOs\n", + DEVICE(s)->canonical_path); + s->idma_completed_frames =3D 0; + k230_dw_ssi_idma_end(s, 0); + return; + } + + if (FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, DFS) !=3D 7) { + qemu_log_mask(LOG_UNIMP, + "%s: IDMA only supports 8-bit data frames\n", + DEVICE(s)->canonical_path); + s->idma_completed_frames =3D 0; + k230_dw_ssi_idma_end(s, 0); + return; + } + + if (!k230_dw_ssi_decode_enhanced_command(s, &command)) { + s->idma_completed_frames =3D 0; + k230_dw_ssi_idma_end(s, 0); + return; + } + + command.instruction =3D s->regs[R_SPIDR] & + (uint32_t)MAKE_64BIT_MASK(0, command.instruction_bits); + command.address =3D s->regs[R_SPIAR] & + (uint32_t)MAKE_64BIT_MASK(0, command.address_bits); + length =3D command.data_frames; + address =3D k230_dw_ssi_idma_address(s); + s->idma_completed_frames =3D 0; + + if (address > UINT64_MAX - (length - 1)) { + k230_dw_ssi_idma_fail(s, "address range"); + return; + } + + buffer =3D g_malloc(length); + if (command.tmod =3D=3D K230_DW_SSI_TMOD_TO) { + result =3D dma_memory_read(&address_space_memory, address, buffer, + length, MEMTXATTRS_UNSPECIFIED); + if (result !=3D MEMTX_OK) { + k230_dw_ssi_idma_fail(s, "source"); + return; + } + } + + k230_dw_ssi_update_cs(s); + if (s->active_cs < 0) { + k230_dw_ssi_idma_end(s, 0); + return; + } + + if (command.instruction_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field(s, command.instruction, + command.instruction_bits); + } + if (command.address_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field(s, command.address, + command.address_bits); + } + if (command.mode_bits_enabled) { + k230_dw_ssi_send_enhanced_field(s, command.mode, + command.mode_bits); + } else if (command.trans_type =3D=3D 1 && command.wait_cycles >=3D 2) { + /* + * The SDK's 1-4-4 read supplies its mode byte through XIP_MODE_BI= TS + * without XIP_MD_BIT_EN; one Quad byte consumes two wait cycles. + */ + k230_dw_ssi_send_enhanced_field(s, s->regs[R_XIP_MODE_BITS], 8); + command.wait_cycles -=3D 2; + } + + dummy_bytes =3D k230_dw_ssi_dummy_bytes( + command.spi_frf, command.trans_type, command.wait_cycles); + for (uint32_t i =3D 0; i < dummy_bytes; i++) { + ssi_transfer(s->spi, 0); + } + + if (command.tmod =3D=3D K230_DW_SSI_TMOD_RO) { + for (uint32_t i =3D 0; i < length; i++) { + buffer[i] =3D ssi_transfer(s->spi, 0); + } + result =3D dma_memory_write(&address_space_memory, address, buffer, + length, MEMTXATTRS_UNSPECIFIED); + if (result !=3D MEMTX_OK) { + k230_dw_ssi_idma_fail(s, "destination"); + return; + } + } else { + for (uint32_t i =3D 0; i < length; i++) { + ssi_transfer(s->spi, buffer[i]); + } + } + + s->idma_completed_frames =3D length; + k230_dw_ssi_idma_end(s, R_RISR_DONER_MASK); +} + static void k230_dw_ssi_run_enhanced_rx_data(K230DwSsiState *s) { while (!fifo32_is_full(&s->rx_fifo) && @@ -878,6 +1046,10 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) uint32_t value =3D 0; =20 if (k230_dw_ssi_is_dr(addr)) { + if (k230_dw_ssi_idma_enabled(s)) { + return 0; + } + if (!fifo32_is_empty(&s->rx_fifo)) { value =3D fifo32_pop(&s->rx_fifo) & k230_dw_ssi_frame_masked(s= ); } else { @@ -966,8 +1138,10 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) R_RISR_RXOIR_MASK | R_RISR_MSTIR_MASK); break; case A_AXIECR: + value =3D k230_dw_ssi_irq_read_clear(s, R_RISR_AXIER_MASK); + break; case A_DONECR: - value =3D 0; + value =3D k230_dw_ssi_irq_read_clear(s, R_RISR_DONER_MASK); break; default: if (addr >=3D K230_DW_SSI_REGS_SIZE || (addr & 0x3) !=3D 0) { @@ -987,6 +1161,10 @@ static void k230_dw_ssi_write(void *opaque, hwaddr ad= dr, K230DwSsiState *s =3D K230_DW_SSI(opaque); =20 if (k230_dw_ssi_is_dr(addr)) { + if (k230_dw_ssi_idma_enabled(s)) { + return; + } + k230_dw_ssi_push_tx(s, value); return; } @@ -1028,7 +1206,11 @@ static void k230_dw_ssi_write(void *opaque, hwaddr a= ddr, } =20 k230_dw_ssi_update_cs(s); - k230_dw_ssi_run_transfer(s); + if (k230_dw_ssi_idma_enabled(s)) { + k230_dw_ssi_try_idma(s); + } else { + k230_dw_ssi_run_transfer(s); + } k230_dw_ssi_update_irq(s); break; } @@ -1046,7 +1228,11 @@ static void k230_dw_ssi_write(void *opaque, hwaddr a= ddr, } =20 k230_dw_ssi_update_cs(s); - k230_dw_ssi_run_transfer(s); + if (k230_dw_ssi_idma_enabled(s)) { + k230_dw_ssi_try_idma(s); + } else { + k230_dw_ssi_run_transfer(s); + } k230_dw_ssi_update_irq(s); break; } @@ -1085,12 +1271,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr a= ddr, case A_DMACR: k230_dw_ssi_write_masked(s, R_DMACR, value, K230_DW_SSI_DMACR_WRITABLE_MASK); - if (FIELD_EX32(s->regs[R_DMACR], DMACR, IDMAE)) { - qemu_log_mask(LOG_UNIMP, - "%s: DMACR.IDMAE enabled, internal DMA is not " - "implemented\n", - DEVICE(s)->canonical_path); - } + k230_dw_ssi_try_idma(s); break; case A_AXIAWLEN: k230_dw_ssi_write_masked(s, R_AXIAWLEN, value, @@ -1188,6 +1369,7 @@ static void k230_dw_ssi_enter_reset(Object *obj, Rese= tType type) s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; s->irq_latched =3D 0; + s->idma_completed_frames =3D 0; memset(&s->enhanced, 0, sizeof(s->enhanced)); =20 s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; @@ -1250,6 +1432,7 @@ static const VMStateDescription vmstate_k230_dw_ssi = =3D { VMSTATE_FIFO32(tx_fifo, K230DwSsiState), VMSTATE_FIFO32(rx_fifo, K230DwSsiState), VMSTATE_UINT32(irq_latched, K230DwSsiState), + VMSTATE_UINT32(idma_completed_frames, K230DwSsiState), VMSTATE_UINT32(phase, K230DwSsiState), VMSTATE_UINT32(remaining_frames, K230DwSsiState), VMSTATE_UINT32(enhanced.instruction, K230DwSsiState), diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index b692a616d6..3a1b2c08cd 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -87,6 +87,7 @@ struct K230DwSsiState { uint32_t regs[K230_DW_SSI_NUM_REGS]; =20 uint32_t irq_latched; + uint32_t idma_completed_frames; =20 uint32_t phase; uint32_t remaining_frames; diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index 658b1f4cbf..ddf5e13145 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -79,12 +79,18 @@ #define K230_SSI_SR_TFNF BIT(1) #define K230_SSI_SR_TFE BIT(2) #define K230_SSI_SR_RFNE BIT(3) +#define K230_SSI_SR_CMPLTD_DF_SHIFT 15 +#define K230_SSI_SR_CMPLTD_DF_MASK (0x1ffffU << 15) =20 #define K230_SSI_INT_TXE BIT(0) #define K230_SSI_INT_RXU BIT(2) #define K230_SSI_INT_AXIE BIT(8) #define K230_SSI_INT_DONE BIT(11) =20 +#define K230_SSI_IDMAE BIT(2) +#define K230_SSI_AINC BIT(6) +#define K230_SSI_DMA_ADDR 0x80201000ULL + #define K230_SSI_IRQ_TXE 0 #define K230_SSI_IRQ_RXU 5 #define K230_SSI_IRQ_DONE 7 @@ -414,6 +420,44 @@ static void read_enhanced_result(QTestState *qts, uint= 8_t *data, size_t len) k230_ssi_disable(qts, K230_SPI0_BASE); } =20 +static void configure_idma(QTestState *qts, uint32_t tmod, + uint8_t opcode, uint32_t flash_address, + uint64_t dma_address, size_t length) +{ + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + + k230_ssi_configure(qts, K230_SPI0_BASE, tmod, 8, length - 1); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, 0); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + ctrlr0 |=3D K230_SSI_FRF_QUAD << K230_SSI_CTRLR0_SPI_FRF_SHIFT; + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, ctrlr0); + spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_TRANS_TYPE(0) | + K230_SSI_SPI_CTRLR0_ADDR_L(24) | + K230_SSI_SPI_CTRLR0_INST_L_8 | + K230_SSI_SPI_CTRLR0_WAIT(8); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_DMACR, + K230_SSI_IDMAE | K230_SSI_AINC); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPIDR, opcode); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPIAR, flash_address); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_AXIAR0, dma_address); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_AXIAR1, + dma_address >> 32); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SSIENR, 1); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SER, BIT(0)); +} + +static void assert_idma_stopped(QTestState *qts, size_t completed) +{ + uint32_t status =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_SR); + + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_SSIENR), + =3D=3D, 0); + g_assert_cmpuint((status & K230_SSI_SR_CMPLTD_DF_MASK) >> + K230_SSI_SR_CMPLTD_DF_SHIFT, =3D=3D, completed); +} + static void test_register_contract(void) { QTestState *qts =3D k230_ssi_start(); @@ -673,6 +717,56 @@ static void test_qspi_sdr(void) k230_ssi_flash_image_clear(&image); } =20 +static void test_idma(void) +{ + static const uint8_t expected[] =3D { 0xa5, 0x5a, 0x3c, 0xc3 }; + K230SsiFlashImage image; + QTestState *qts =3D k230_ssi_start_with_flash(&image); + uint8_t actual[ARRAY_SIZE(expected)]; + + qtest_memset(qts, K230_SSI_DMA_ADDR, 0, sizeof(actual)); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_IMR, 0); + configure_idma(qts, K230_SSI_TMOD_RO, FLASH_CMD_QUAD_OUT, + K230_SSI_FLASH_PATTERN_ADDR, K230_SSI_DMA_ADDR, + sizeof(actual)); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RISR) & + K230_SSI_INT_DONE, =3D=3D, K230_SSI_INT_DONE); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_ISR) & + K230_SSI_INT_DONE, =3D=3D, 0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_IMR, + K230_SSI_INT_DONE); + g_assert_true(k230_ssi_plic_pending( + qts, k230_ssi_instances[0].first_irq + K230_SSI_IRQ_DONE)); + assert_idma_stopped(qts, sizeof(actual)); + qtest_memread(qts, K230_SSI_DMA_ADDR, actual, sizeof(actual)); + g_assert_cmpmem(actual, sizeof(actual), expected, sizeof(expected)); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_DONECR, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_DONECR), + =3D=3D, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RISR) & + K230_SSI_INT_DONE, =3D=3D, 0); + + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_IMR, 0); + configure_idma(qts, K230_SSI_TMOD_RO, FLASH_CMD_QUAD_OUT, + K230_SSI_FLASH_PATTERN_ADDR, 0x100000000ULL, + sizeof(actual)); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RISR) & + K230_SSI_INT_AXIE, =3D=3D, K230_SSI_INT_AXIE); + assert_idma_stopped(qts, 0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_IMR, + K230_SSI_INT_AXIE); + g_assert_true(k230_ssi_plic_pending( + qts, k230_ssi_instances[0].first_irq + K230_SSI_IRQ_AXIE)); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_AXIECR, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_AXIECR), + =3D=3D, 1); + g_assert_cmphex(k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_RISR) & + K230_SSI_INT_AXIE, =3D=3D, 0); + + qtest_quit(qts); + k230_ssi_flash_image_clear(&image); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -685,5 +779,6 @@ int main(int argc, char **argv) qtest_add_func("/k230-dw-ssi/qspi-config", test_qspi_config); qtest_add_func("/k230-dw-ssi/spi-nor", test_spi_nor); qtest_add_func("/k230-dw-ssi/qspi-sdr", test_qspi_sdr); + qtest_add_func("/k230-dw-ssi/idma", test_idma); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069100; cv=none; d=zohomail.com; s=zohoarc; b=EtN3iwnHNARdUxdICaiGKMIXLXG0EwQtBNX5PDog+dW4WKJEruGlCWbnlsYtZxuGCOiUKCwTQHTFyZZRoi16vG8ELybhfb7hK7MpZMqJIdULs0dVdf9XAQYNMU5SHf5oqXc6SFVAEtEvnuWomKmrEcOP3xx1zEIpIkQNWHTPG+Y= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069100; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=o25qBdrbB0cCxrdOmXBhSEsyH8Q+W880TO/mlWSZJuU=; b=DWxQD+RxT5pFIZ+mINFoOyJfjVPuSg8h9ZuVNVnzdibxMaw8GiUdaOKctSIzkpeipqF8t6VpVeG2UdtCipu3H+GJWPrkCm0lnTjzbsG6sLGcR0WDQ4ciTHwaA4RTXv6xhlKIw17+uN/soHRijvhTax+/UTrokmlo7TtrVxMZAnE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069100162793.8952581623421; Sun, 26 Jul 2026 05:31:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxzi-0002rJ-5X; Sun, 26 Jul 2026 08:30:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxzQ-0002Hm-Hv for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:57 -0400 Received: from mail-pf1-x42b.google.com ([2607:f8b0:4864:20::42b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxzM-0003MC-SI for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:29:56 -0400 Received: by mail-pf1-x42b.google.com with SMTP id d2e1a72fcca58-84a2c90e383so1858407b3a.0 for ; Sun, 26 Jul 2026 05:29:52 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068992; x=1785673792; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=o25qBdrbB0cCxrdOmXBhSEsyH8Q+W880TO/mlWSZJuU=; b=ZM+YHXYD+X0Z+A5MFlV7heJjb+G4CUR4eeQOvazH7UdUnbEp4b3WfvoejhDE4qe30t swNZEddxsaHLpXWqa1izQkPKe4bHFqnJ7YfiY8Dwz0fMWzBApCVdZrGst34YT2xJMH79 ckPQUUuPQez1AQuuY/xpm1cki/r8lfgONpyuC5R3aa2l6ocNQGv6V+adWG4RlV6AVe/e zN64PHMAN4nGnbUyNaI0eR5Ilhll/iTNvq5pKesScPxLwWQhPxDrzuaOwC5ZeJv9bHwy qqFtLY2rnBV4pg08dpuMwLKn/wk04DuGxJWs1AB/xnl7M2lEpfE4IiQZh29igMk/o5CP nEAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068992; x=1785673792; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=o25qBdrbB0cCxrdOmXBhSEsyH8Q+W880TO/mlWSZJuU=; b=QzF5cQQ/ywMhxPrfdfVSFJeXbl6qCq9Yai/dOsdSPT7VXfhQYtIDIgQEXtjXoC+3vh tbodSeKmyvp2vqbvXWAgWD1FRMqvdmsPZ9yQ3JMo1k5IrfZt0jG4iTPoX2Va12CADMbl jYzuVhtHIcGe9rYa03FCq7UZZag9QKk8My2cAeh6q4tcylOejNkAFyOrFOcHrZcZLC6h k4ecve1lDSQsiME71gIxgZnBV6yxsxEgBmef0PPcIxK436lp6CQqbjTDRQx47EwIx21a dsmhoIWVQ18G8+gn3hk5ao7FHJ98d26lnmpUDbq6HihZYvv/fArQJGtcnJnWCtuWYThZ 1kKQ== X-Gm-Message-State: AOJu0YyRWsLDSObfdZ7IJrjUHtZ52A3mMyVsRdFHkC8eB4JtynsJOTmk jjqmi/BeGS7wJ4D8qesdCLQXnhkA0U5/g7GlNlS72VkZi/9r47dUtuHU8hll6vBMko0= X-Gm-Gg: AR+sD11O7iLzTs5uvYv4hp5Uolu3RCDD5zpgWxwQuH1f6VfhHgaeY1wldJg25qqynuP 6CvBCdyGVY1+b1r5vmWjzz7Aj3niXZObbAlsmIswy8Ng+8iryd0TiBy8WQd2+m81BX9tmNRFzwk J1K+GkIT29UEVqXoGyd4S4yqdU0wuc4wI+ihMNU/t9QvuvLWRchOTz0d4cbekMRrE3Wc2/sBIFu MEVn7ARokmErDWznBxIoxbA7PTBFp33IXOl/yzVH0iupr1j68sONQ83xpHy7gvh/Jv22mnFT3Pz oAgQKLD9GbbeoYOHkoPI+LgwQVm7CUtC8FaRWirj9kGZcA4OVBgLSLNa0A+Yir4KfrTEMrHrkkJ yO1ND2Gp9qyXCg8vLsCLb0hBhImyLGHIKoReE+ZDVxTbrtNuBIXTi0b5kO7czLwY6g0bFzDXBw6 UTlKCgG8wH0/D7pRtiotR+ X-Received: by 2002:a05:6a00:414b:b0:848:7bcf:9a9a with SMTP id d2e1a72fcca58-84e5941163fmr3874859b3a.1.1785068991391; Sun, 26 Jul 2026 05:29:51 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 09/11] hw/misc: Add K230 HI_SYS SSI control Date: Sun, 26 Jul 2026 20:28:27 +0800 Message-ID: <4b46245962029fc7e4585aa225c28053929e8a88.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42b; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42b.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069101554158500 Content-Type: text/plain; charset="utf-8" Model the HI_SYS SSI_CTRL wrapper register, including its reset value, write mask, and dynamic mode and sleep status for the three logical SSI controllers. K230 software uses this register to control XIP enable and observe the mode and sleep state of the SSI instances, so controller-local registers alone do not provide the complete guest-visible interface. Reuse the machine SSI routing table to associate logical controller numbers with physical instances. Keep the sleep indication synchronized when IDMA disables SSI after completion or an AXI error. Map the wrapper over the previously unimplemented HI_SYS region and migrate its writable state and the controller sleep state. Cover the register reset value, write mask, and read-back behaviour, three-instance mode routing, and sleep transitions in qtest. Signed-off-by: Kangjie Huang --- hw/misc/k230_hi_sys.c | 164 +++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + hw/riscv/k230.c | 19 +++- hw/ssi/k230_dw_ssi.c | 15 +++ include/hw/misc/k230_hi_sys.h | 54 +++++++++++ include/hw/riscv/k230.h | 2 + include/hw/ssi/k230_dw_ssi.h | 4 + tests/qtest/k230-dw-ssi-test.c | 60 ++++++++++++ 8 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 hw/misc/k230_hi_sys.c create mode 100644 include/hw/misc/k230_hi_sys.h diff --git a/hw/misc/k230_hi_sys.c b/hw/misc/k230_hi_sys.c new file mode 100644 index 0000000000..44f5ac61de --- /dev/null +++ b/hw/misc/k230_hi_sys.c @@ -0,0 +1,164 @@ +/* + * Kendryte K230 HI_SYS + * + * Copyright (c) 2026 Kangjie Huang + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Models the HI_SYS system control block, including the SSI_CTRL wrapper + * register that K230 firmware uses for XIP enable and SSI mode/sleep + * status. + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Tec= hnical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/misc/k230_hi_sys.h" +#include "migration/vmstate.h" + +/* + * The TRM places SSI_CTRL at HI_SYS_CONFIG + 0x068, while the DWC SSI + * register table uses controller offset 0x068 for DR2. These are separate + * address spaces, so model SSI_CTRL in HI_SYS rather than as an SSI DR al= ias. + */ +static uint32_t k230_hi_sys_ssi_status(const K230HiSysState *s) +{ + static const unsigned int sleep_bits[] =3D { + K230_SSI_CTRL_SPI0_SLEEP, + K230_SSI_CTRL_SPI1_SLEEP, + K230_SSI_CTRL_SPI2_SLEEP, + }; + static const unsigned int mode_shifts[] =3D { + K230_SSI_CTRL_SPI0_MODE_SHIFT, + K230_SSI_CTRL_SPI1_MODE_SHIFT, + K230_SSI_CTRL_SPI2_MODE_SHIFT, + }; + uint32_t value =3D s->ssi_ctrl; + + for (unsigned int i =3D 0; i < ARRAY_SIZE(s->ssi); i++) { + if (!s->ssi[i]) { + continue; + } + + value |=3D k230_dw_ssi_get_spi_mode(s->ssi[i]) << mode_shifts[i]; + if (k230_dw_ssi_is_sleeping(s->ssi[i])) { + value |=3D sleep_bits[i]; + } + } + + return value; +} + +static uint64_t k230_hi_sys_read(void *opaque, hwaddr addr, unsigned size) +{ + K230HiSysState *s =3D opaque; + + if (addr =3D=3D K230_HI_SYS_SSI_CTRL_OFFSET) { + return k230_hi_sys_ssi_status(s); + } + + if (addr < K230_HI_SYS_MMIO_SIZE) { + return 0; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_HI_SYS, addr); + return 0; +} + +static void k230_hi_sys_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) +{ + K230HiSysState *s =3D opaque; + + if (addr =3D=3D K230_HI_SYS_SSI_CTRL_OFFSET) { + s->ssi_ctrl =3D (s->ssi_ctrl & ~K230_SSI_CTRL_WRITABLE_MASK) | + ((uint32_t)value & K230_SSI_CTRL_WRITABLE_MASK); + s->ssi_ctrl &=3D K230_SSI_CTRL_IMPLEMENTED_MASK; + return; + } + + if (addr < K230_HI_SYS_MMIO_SIZE) { + return; + } + + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", + TYPE_K230_HI_SYS, addr); +} + +static const MemoryRegionOps k230_hi_sys_ops =3D { + .read =3D k230_hi_sys_read, + .write =3D k230_hi_sys_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, +}; + +void k230_hi_sys_set_ssi(K230HiSysState *s, unsigned int index, + K230DwSsiState *ssi) +{ + g_assert(index < ARRAY_SIZE(s->ssi)); + s->ssi[index] =3D ssi; +} + +static void k230_hi_sys_reset(Object *obj, ResetType type) +{ + K230HiSysState *s =3D K230_HI_SYS(obj); + + s->ssi_ctrl =3D K230_SSI_CTRL_RESET; +} + +static void k230_hi_sys_init(Object *obj) +{ + K230HiSysState *s =3D K230_HI_SYS(obj); + + memory_region_init_io(&s->mmio, obj, &k230_hi_sys_ops, s, + TYPE_K230_HI_SYS, K230_HI_SYS_MMIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static const VMStateDescription vmstate_k230_hi_sys =3D { + .name =3D TYPE_K230_HI_SYS, + .fields =3D (const VMStateField[]) { + VMSTATE_UINT32(ssi_ctrl, K230HiSysState), + VMSTATE_END_OF_LIST() + }, +}; + +static void k230_hi_sys_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + + dc->vmsd =3D &vmstate_k230_hi_sys; + rc->phases.enter =3D k230_hi_sys_reset; +} + +static const TypeInfo k230_hi_sys_type_info =3D { + .name =3D TYPE_K230_HI_SYS, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(K230HiSysState), + .instance_init =3D k230_hi_sys_init, + .class_init =3D k230_hi_sys_class_init, +}; + +static void k230_hi_sys_register_types(void) +{ + type_register_static(&k230_hi_sys_type_info); +} + +type_init(k230_hi_sys_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 23265f6035..aaae64b33c 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -28,6 +28,7 @@ system_ss.add(when: 'CONFIG_IOSB', if_true: files('iosb.c= ')) system_ss.add(when: 'CONFIG_VIRT_CTRL', if_true: files('virt_ctrl.c')) =20 # RISC-V devices +system_ss.add(when: 'CONFIG_K230', if_true: files('k230_hi_sys.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_DMC', if_true: files('mchp_pfsoc_dm= c.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_= ioscb.c')) system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc= _sysreg.c')) diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index 36fd1eda22..a666a41c59 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -134,6 +134,8 @@ static void k230_soc_init(Object *obj) TYPE_K230_DW_SSI); object_initialize_child(obj, "k230-spi-opi", &s->dw_ssi[2], TYPE_K230_DW_SSI); + object_initialize_child(obj, "k230-hi-sys", &s->hi_sys, + TYPE_K230_HI_SYS); =20 qdev_prop_set_uint32(DEVICE(cpu0), "hartid-base", 0); qdev_prop_set_string(DEVICE(cpu0), "cpu-type", TYPE_RISCV_CPU_THEAD_C9= 08); @@ -254,6 +256,18 @@ static void k230_soc_realize(DeviceState *dev, Error *= *errp) } } =20 + for (size_t logical_index =3D 0; + logical_index < ARRAY_SIZE(k230_ssi_routes); logical_index++) { + const K230SsiRoute *route =3D &k230_ssi_routes[logical_index]; + + k230_hi_sys_set_ssi(&s->hi_sys, logical_index, + &s->dw_ssi[route->ssi_index]); + } + + if (!sysbus_realize(SYS_BUS_DEVICE(&s->hi_sys), errp)) { + return; + } + k230_connect_ssi_irqs(s); =20 sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[0]), 0, memmap[K230_DEV_WDT0].b= ase); @@ -270,6 +284,8 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) memmap[K230_DEV_QSPI1].base); sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[2]), 0, memmap[K230_DEV_SPI].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->hi_sys), 0, + memmap[K230_DEV_HI_SYS_CFG].base); =20 /* unimplemented devices */ create_unimplemented_device("kpu.l2-cache", @@ -414,9 +430,6 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) create_unimplemented_device("sd1", memmap[K230_DEV_SD1].base, memmap[K230_DEV_SD1].size); =20 - create_unimplemented_device("hi_sys_cfg", memmap[K230_DEV_HI_SYS_CFG].= base, - memmap[K230_DEV_HI_SYS_CFG].size); - create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base, memmap[K230_DEV_DDRC_CFG].size); =20 diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index 65b2a8c245..c1c4f40e07 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -337,6 +337,16 @@ static bool k230_dw_ssi_enabled(const K230DwSsiState *= s) return FIELD_EX32(s->regs[R_SSIENR], SSIENR, SSIC_EN); } =20 +uint32_t k230_dw_ssi_get_spi_mode(const K230DwSsiState *s) +{ + return FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); +} + +bool k230_dw_ssi_is_sleeping(const K230DwSsiState *s) +{ + return s->sleep_status; +} + static void k230_dw_ssi_deselect(K230DwSsiState *s) { if (s->active_cs < 0) { @@ -648,6 +658,7 @@ static bool k230_dw_ssi_idma_triggered(const K230DwSsiS= tate *s) static void k230_dw_ssi_idma_end(K230DwSsiState *s, uint32_t cause) { s->regs[R_SSIENR] =3D 0; + s->sleep_status =3D true; s->phase =3D K230_DW_SSI_PHASE_IDLE; s->remaining_frames =3D 0; k230_dw_ssi_deselect(s); @@ -1202,9 +1213,11 @@ static void k230_dw_ssi_write(void *opaque, hwaddr a= ddr, s->regs[R_SSIENR] =3D FIELD_DP32(0, SSIENR, SSIC_EN, new_enabled); if (!new_enabled) { k230_dw_ssi_abort_transfer(s); + s->sleep_status =3D true; return; } =20 + s->sleep_status =3D false; k230_dw_ssi_update_cs(s); if (k230_dw_ssi_idma_enabled(s)) { k230_dw_ssi_try_idma(s); @@ -1371,6 +1384,7 @@ static void k230_dw_ssi_enter_reset(Object *obj, Rese= tType type) s->irq_latched =3D 0; s->idma_completed_frames =3D 0; memset(&s->enhanced, 0, sizeof(s->enhanced)); + s->sleep_status =3D false; =20 s->regs[R_CTRLR0] =3D K230_DW_SSI_CTRLR0_RESET; s->regs[R_SR] =3D K230_DW_SSI_SR_RESET; @@ -1448,6 +1462,7 @@ static const VMStateDescription vmstate_k230_dw_ssi = =3D { VMSTATE_UINT32(enhanced.tmod, K230DwSsiState), VMSTATE_BOOL(enhanced.mode_bits_enabled, K230DwSsiState), VMSTATE_INT32(active_cs, K230DwSsiState), + VMSTATE_BOOL(sleep_status, K230DwSsiState), VMSTATE_END_OF_LIST() }, }; diff --git a/include/hw/misc/k230_hi_sys.h b/include/hw/misc/k230_hi_sys.h new file mode 100644 index 0000000000..4c85da531e --- /dev/null +++ b/include/hw/misc/k230_hi_sys.h @@ -0,0 +1,54 @@ +/* + * Kendryte K230 HI_SYS + * + * Copyright (c) 2026 Kangjie Huang + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Models the HI_SYS system control block, including the SSI_CTRL wrapper + * register that K230 firmware uses for XIP enable and SSI mode/sleep + * status. + * + * K230 Technical Reference Manual V0.3.1 (2024-11-18): + * https://github.com/revyos/external-docs/blob/master/K230/en-us/K230_Tec= hnical_Reference_Manual_V0.3.1_20241118.pdf + * + * For more information, see + */ + +#ifndef HW_MISC_K230_HI_SYS_H +#define HW_MISC_K230_HI_SYS_H + +#include "hw/core/sysbus.h" +#include "hw/ssi/k230_dw_ssi.h" +#include "qom/object.h" + +#define TYPE_K230_HI_SYS "riscv.k230.hi-sys" +OBJECT_DECLARE_SIMPLE_TYPE(K230HiSysState, K230_HI_SYS) + +#define K230_HI_SYS_MMIO_SIZE 0x400 +#define K230_HI_SYS_SSI_CTRL_OFFSET 0x068 + +#define K230_SSI_CTRL_RESET 0x00004000U +#define K230_SSI_CTRL_IMPLEMENTED_MASK 0x0003fff1U +#define K230_SSI_CTRL_WRITABLE_MASK 0x0003e001U + +#define K230_SSI_CTRL_XIP_EN (1U << 0) +#define K230_SSI_CTRL_SPI0_SLEEP (1U << 4) +#define K230_SSI_CTRL_SPI0_MODE_SHIFT 5 +#define K230_SSI_CTRL_SPI1_SLEEP (1U << 7) +#define K230_SSI_CTRL_SPI1_MODE_SHIFT 8 +#define K230_SSI_CTRL_SPI2_SLEEP (1U << 10) +#define K230_SSI_CTRL_SPI2_MODE_SHIFT 11 + +struct K230HiSysState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + uint32_t ssi_ctrl; + K230DwSsiState *ssi[3]; +}; + +void k230_hi_sys_set_ssi(K230HiSysState *s, unsigned int index, + K230DwSsiState *ssi); + +#endif /* HW_MISC_K230_HI_SYS_H */ diff --git a/include/hw/riscv/k230.h b/include/hw/riscv/k230.h index 493d739aa4..f280c3bebe 100644 --- a/include/hw/riscv/k230.h +++ b/include/hw/riscv/k230.h @@ -16,6 +16,7 @@ #define HW_K230_H =20 #include "hw/core/boards.h" +#include "hw/misc/k230_hi_sys.h" #include "hw/riscv/riscv_hart.h" #include "hw/ssi/k230_dw_ssi.h" #include "hw/watchdog/k230_wdt.h" @@ -35,6 +36,7 @@ typedef struct K230SoCState { =20 K230WdtState wdt[2]; K230DwSsiState dw_ssi[3]; + K230HiSysState hi_sys; MemoryRegion sram; MemoryRegion bootrom; =20 diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index 3a1b2c08cd..49c137d73f 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -96,6 +96,10 @@ struct K230DwSsiState { uint32_t num_cs; uint32_t max_lines; int active_cs; + bool sleep_status; }; =20 +uint32_t k230_dw_ssi_get_spi_mode(const K230DwSsiState *s); +bool k230_dw_ssi_is_sleeping(const K230DwSsiState *s); + #endif /* HW_SSI_K230_DW_SSI_H */ diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index ddf5e13145..6bae56b1a8 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -14,6 +14,8 @@ #define K230_SPI0_BASE 0x91584000ULL #define K230_SPI1_BASE 0x91582000ULL #define K230_SPI2_BASE 0x91583000ULL +#define K230_HI_SYS_BASE 0x91585000ULL +#define K230_SSI_CTRL_ADDR (K230_HI_SYS_BASE + 0x68) #define K230_PLIC_BASE 0xf00000000ULL #define K230_PLIC_PENDING_BASE 0x1000 #define K230_SSI_CTRLR0 0x000 @@ -53,6 +55,15 @@ #define K230_SSI_CTRLR0_WRITABLE_MASK 0x01cf7f1fU #define K230_SSI_BAUDR_WRITABLE_MASK 0x0000fffeU =20 +#define K230_SSI_CTRL_RESET 0x00004000U +#define K230_SSI_CTRL_IMPLEMENTED_MASK 0x0003fff1U +#define K230_SSI_CTRL_WRITABLE_MASK 0x0003e001U +#define K230_SSI_CTRL_XIP_EN BIT(0) +#define K230_SSI_CTRL_SPI0_SLEEP BIT(4) +#define K230_SSI_CTRL_SPI0_MODE_SHIFT 5 +#define K230_SSI_CTRL_SPI1_MODE_SHIFT 8 +#define K230_SSI_CTRL_SPI2_MODE_SHIFT 11 + #define K230_SSI_CTRLR0_DFS_MASK 0x1fU #define K230_SSI_CTRLR0_TMOD_SHIFT 10 #define K230_SSI_CTRLR0_SRL BIT(13) @@ -767,6 +778,54 @@ static void test_idma(void) k230_ssi_flash_image_clear(&image); } =20 +static void test_hi_sys(void) +{ + QTestState *qts =3D k230_ssi_start(); + uint32_t ctrlr0; + uint32_t expected_modes; + + g_assert_cmphex(qtest_readl(qts, K230_SSI_CTRL_ADDR), + =3D=3D, K230_SSI_CTRL_RESET); + qtest_writel(qts, K230_SSI_CTRL_ADDR, UINT32_MAX); + g_assert_cmphex(qtest_readl(qts, K230_SSI_CTRL_ADDR) & + K230_SSI_CTRL_IMPLEMENTED_MASK, + =3D=3D, (K230_SSI_CTRL_RESET & + ~K230_SSI_CTRL_WRITABLE_MASK) | + K230_SSI_CTRL_WRITABLE_MASK); + qtest_system_reset(qts); + + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, + ctrlr0 | (K230_SSI_FRF_QUAD << + K230_SSI_CTRLR0_SPI_FRF_SHIFT)); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI1_BASE, K230_SSI_CTRLR0); + k230_ssi_writel(qts, K230_SPI1_BASE, K230_SSI_CTRLR0, + ctrlr0 | (1U << K230_SSI_CTRLR0_SPI_FRF_SHIFT)); + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI2_BASE, K230_SSI_CTRLR0); + k230_ssi_writel(qts, K230_SPI2_BASE, K230_SSI_CTRLR0, + ctrlr0 | (K230_SSI_FRF_OCTAL << + K230_SSI_CTRLR0_SPI_FRF_SHIFT)); + expected_modes =3D (K230_SSI_FRF_QUAD << + K230_SSI_CTRL_SPI0_MODE_SHIFT) | + (1U << K230_SSI_CTRL_SPI1_MODE_SHIFT) | + (K230_SSI_FRF_OCTAL << + K230_SSI_CTRL_SPI2_MODE_SHIFT); + g_assert_cmphex(qtest_readl(qts, K230_SSI_CTRL_ADDR) & + ((3U << K230_SSI_CTRL_SPI0_MODE_SHIFT) | + (3U << K230_SSI_CTRL_SPI1_MODE_SHIFT) | + (3U << K230_SSI_CTRL_SPI2_MODE_SHIFT)), + =3D=3D, expected_modes); + + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SSIENR, 1); + g_assert_cmphex(qtest_readl(qts, K230_SSI_CTRL_ADDR) & + K230_SSI_CTRL_SPI0_SLEEP, =3D=3D, 0); + k230_ssi_disable(qts, K230_SPI0_BASE); + g_assert_cmphex(qtest_readl(qts, K230_SSI_CTRL_ADDR) & + K230_SSI_CTRL_SPI0_SLEEP, + =3D=3D, K230_SSI_CTRL_SPI0_SLEEP); + qtest_quit(qts); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -780,5 +839,6 @@ int main(int argc, char **argv) qtest_add_func("/k230-dw-ssi/spi-nor", test_spi_nor); qtest_add_func("/k230-dw-ssi/qspi-sdr", test_qspi_sdr); qtest_add_func("/k230-dw-ssi/idma", test_idma); + qtest_add_func("/k230-dw-ssi/hi-sys", test_hi_sys); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069146; cv=none; d=zohomail.com; s=zohoarc; b=hSZlMwnymv8S+2ZhSt6cvCJBn8i8sWjX2JhaI1jCfbLsSXo2kwTVoWuOscbC95gQKZ6f2lqrd1Q2PYib3ibhpS8O8qK99EzKeNS1kWyOgsCkBK1ARYiBoqrFIIyV9B9gs5GjPZ+d82IQIU8mkyrgY/8uXfP5AKEERRGBkU5ZxTs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069146; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=27REuxPMNv1Fu0LlZWSG8x5hpH2IrRj0GYRJSZduzAA=; b=bosb5wE/vwYf/Tl8RLP3KOTO/V4ngNbj6tjCM6Ia9QKUGQpvyGJJ3hGvx+UoMXNy3bgssn4ERXjFwZnmUsJ4B90wwNhwEPtHjs2aUqjHmZMPY5fZsuhsNgjbQlzXbF42RLBN+I5dQEY1oWXm9+KVklr6QMCrMTBKS7uWVYmIx2c= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069146776877.3782577828486; Sun, 26 Jul 2026 05:32:26 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxzi-0002se-6a; Sun, 26 Jul 2026 08:30:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxzW-0002X1-2i for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:30:05 -0400 Received: from mail-pf1-x431.google.com ([2607:f8b0:4864:20::431]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxzS-0003Mi-Mf for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:30:01 -0400 Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-848643382fcso1938533b3a.1 for ; Sun, 26 Jul 2026 05:29:58 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:29:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785068997; x=1785673797; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=27REuxPMNv1Fu0LlZWSG8x5hpH2IrRj0GYRJSZduzAA=; b=aIA8ylz+6NNZHAc7z9HaA0hVqYDyHkB6yNrlsr/4vpu8qi6FMJ5BoLm/S0r++Gr5ad jnhIPX+FlsnyrHzA3j4pvF5cyhcR8Aa9+iV8jdWy9jyL6Ci+pepDlt7nc1LaolVCutMT nKYAKxX3vHcKuqkFpQM/MbefJI1CaC9jEpjMTddeWRib/LlMm0IW7yjLD6bop7RocY8G z3gMNAZ2hIGDPyq3O7xwgnvTY10yzYzCmK3HZk1ReHXhBI0tPnp7dtLV7GtCMpTa700e j2gMQe2uFZYqE/RAu3464mTKXK6K1WgvnLuQUTBnX9HCm/YboTGYymIj+Mfbeq/mpq3E R9UA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785068997; x=1785673797; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=27REuxPMNv1Fu0LlZWSG8x5hpH2IrRj0GYRJSZduzAA=; b=szsETXh4haL7SNHWF+KTrV+ErVaudXAH6h5npeiS1/7TU+DtG7uicaPGOw3ai8qJEC bfJxrXprLm5/xoIXlcauJ/B1pf3cwQuIFdTLTFXKrtZ7ROSU4tvbNhAFiPIX9bnHjM76 +KsSJ6y7QSoaFaRMULeEa2dnVbdXFdAXDKYgf++HrE0hWCvfE24FGapoghK7J7hzyNCc 7df68Iy2qhLdO+iL+2XzQwjXdOat+4y1w9XGiUx7NfXysNIkbyzy7ZGiw3T4JtTF8VCW CGbEqO6cekChOpO5JeJRi1Au/RvxmB4ANLDWUbanctJs9/28WsR85LF5+shNsvvF/lCs uKdg== X-Gm-Message-State: AOJu0YytV1PDJLE/bxEt+1r1q+3xuuxWtadM6RvsWZb1E66llwZyFoSO qaLfqlk6DINUefFf36Dzvsu1YN3WO9OCEcOk5AGinp6ilrzlS2xKnqTZJlS7v6lMsL0= X-Gm-Gg: AR+sD11eFJ2vlgN6uCNeEpTj3k6vuClTqtqUADRkVf1+zY4wXIxd3VCRMKpLWv8a2P4 JvRKSPS62KvA6p+aRftw0H8PNfnbxIrdd3WkxOVirM5LXolmTIDQW3t66vQRwHKzJ3tNbwfbHSI WWchgeljZQW97Ocufh/HimZc3f9v23Wpfce4ozRQMQ+BoxuiScXvDe1qXSV1Rr4Raj88T6gdaor g/d7BfnLOWXk5rjzOVG5kY9tjCnIbvW9CaYuME4av/1e1VhwvCgR5DpBXAmF3iJayhZ8QXBm7jA qcsgOREqoDCG/Q7yxOnml8uobecZ5yOWi/RraoNnqSFDEkWlZSv5JtYBECXzdReEHFgRa732kGj jefcNW5hP1g1n4FowSytakc3d4eoYmDKGrNwIaKRvCRIzXsY8kfjZrt4u+FFF8inU+xutS/i4+4 +0VXOQaopKQxR0gnh0oKie X-Received: by 2002:a05:6a00:438f:b0:843:468d:7804 with SMTP id d2e1a72fcca58-84e5949e987mr3923093b3a.34.1785068997038; Sun, 26 Jul 2026 05:29:57 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 10/11] hw/ssi: Add K230 SSI XIP read window Date: Sun, 26 Jul 2026 20:28:28 +0800 Message-ID: <3556bbff1b11bcebe0a4ea40c81ce0b859fcbe15.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::431; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x431.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069147950158500 Content-Type: text/plain; charset="utf-8" Expose logical spi0's 128 MiB flash window as a second SSI MMIO region. Gate accesses through HI_SYS XIP_EN and build Standard, Dual, or Quad SDR read commands from the XIP instruction, address, mode, and dummy-cycle registers. PIO and IDMA cover explicit SPI transactions, while K230 firmware also uses memory-mapped accesses to read spi0 flash. Model the XIP aperture so this interface is visible to the guest. Keep the window read-only, end each access with chip select inactive, and discard stale PIO state before issuing an XIP command. Cover the XIP gate, write rejection, 24-bit and 32-bit addressing, Quad mode and dummy cycles, and PIO/XIP sharing in qtest. Also verify that U-Boot can boot Linux through the XIP read path. Signed-off-by: Kangjie Huang --- docs/system/riscv/k230.rst | 1 + hw/misc/k230_hi_sys.c | 5 + hw/riscv/k230.c | 9 +- hw/ssi/k230_dw_ssi.c | 176 +++++++++++++++++++++++++++++++++ include/hw/misc/k230_hi_sys.h | 1 + include/hw/ssi/k230_dw_ssi.h | 6 ++ tests/qtest/k230-dw-ssi-test.c | 73 ++++++++++++++ 7 files changed, 268 insertions(+), 3 deletions(-) diff --git a/docs/system/riscv/k230.rst b/docs/system/riscv/k230.rst index 3067c1b5d7..e92387ad98 100644 --- a/docs/system/riscv/k230.rst +++ b/docs/system/riscv/k230.rst @@ -23,6 +23,7 @@ The ``k230`` machine supports the following devices: * 3 K230 SSI controllers for SPI and QSPI * Optional SPI NOR flash on spi0 CS0 * QSPI IDMA transfers for 8-bit SDR Dual and Quad modes +* Read-only 128 MiB XIP window for spi0 flash =20 Boot options ------------ diff --git a/hw/misc/k230_hi_sys.c b/hw/misc/k230_hi_sys.c index 44f5ac61de..1514f28fac 100644 --- a/hw/misc/k230_hi_sys.c +++ b/hw/misc/k230_hi_sys.c @@ -115,6 +115,11 @@ void k230_hi_sys_set_ssi(K230HiSysState *s, unsigned i= nt index, s->ssi[index] =3D ssi; } =20 +bool k230_hi_sys_xip_enabled(const K230HiSysState *s) +{ + return !!(s->ssi_ctrl & K230_SSI_CTRL_XIP_EN); +} + static void k230_hi_sys_reset(Object *obj, ResetType type) { K230HiSysState *s =3D K230_HI_SYS(obj); diff --git a/hw/riscv/k230.c b/hw/riscv/k230.c index a666a41c59..e8f3ea499e 100644 --- a/hw/riscv/k230.c +++ b/hw/riscv/k230.c @@ -262,6 +262,10 @@ static void k230_soc_realize(DeviceState *dev, Error *= *errp) =20 k230_hi_sys_set_ssi(&s->hi_sys, logical_index, &s->dw_ssi[route->ssi_index]); + if (logical_index =3D=3D 0) { + k230_dw_ssi_set_hi_sys(&s->dw_ssi[route->ssi_index], + &s->hi_sys); + } } =20 if (!sysbus_realize(SYS_BUS_DEVICE(&s->hi_sys), errp)) { @@ -284,6 +288,8 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) memmap[K230_DEV_QSPI1].base); sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[2]), 0, memmap[K230_DEV_SPI].base); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->dw_ssi[2]), 1, + memmap[K230_DEV_FLASH].base); sysbus_mmio_map(SYS_BUS_DEVICE(&s->hi_sys), 0, memmap[K230_DEV_HI_SYS_CFG].base); =20 @@ -432,9 +438,6 @@ static void k230_soc_realize(DeviceState *dev, Error **= errp) =20 create_unimplemented_device("ddrc_cfg", memmap[K230_DEV_DDRC_CFG].base, memmap[K230_DEV_DDRC_CFG].size); - - create_unimplemented_device("flash", memmap[K230_DEV_FLASH].base, - memmap[K230_DEV_FLASH].size); } =20 static void k230_soc_class_init(ObjectClass *oc, const void *data) diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index c1c4f40e07..d187e3345a 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -19,6 +19,7 @@ #include "hw/core/registerfields.h" #include "hw/core/qdev-properties.h" #include "hw/core/irq.h" +#include "hw/misc/k230_hi_sys.h" #include "hw/ssi/k230_dw_ssi.h" #include "migration/vmstate.h" #include "qapi/error.h" @@ -347,6 +348,11 @@ bool k230_dw_ssi_is_sleeping(const K230DwSsiState *s) return s->sleep_status; } =20 +void k230_dw_ssi_set_hi_sys(K230DwSsiState *s, K230HiSysState *hi_sys) +{ + s->hi_sys =3D hi_sys; +} + static void k230_dw_ssi_deselect(K230DwSsiState *s) { if (s->active_cs < 0) { @@ -635,6 +641,171 @@ static uint32_t k230_dw_ssi_dummy_bytes(uint32_t spi_= frf, return DIV_ROUND_UP(wait_cycles * lines, 8); } =20 +static bool k230_dw_ssi_xip_config_supported(K230DwSsiState *s) +{ + uint32_t spi_frf =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); + uint32_t spi_ctrlr0 =3D s->regs[R_SPI_CTRLR0]; + uint32_t required_lines; + + switch (spi_frf) { + case 0: + required_lines =3D 1; + break; + case 1: + required_lines =3D 2; + break; + case 2: + required_lines =3D 4; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: XIP does not support SPI_FRF=3D%u\n", + DEVICE(s)->canonical_path, spi_frf); + return false; + } + + if (required_lines > s->max_lines || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, TRANS_TYPE) > 2) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported XIP line or transfer type\n", + DEVICE(s)->canonical_path); + return false; + } + + if (FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_DDR_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, INST_DDR_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_RXDS_EN) || + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, SPI_RXDS_SIG_EN)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: XIP DDR/RXDS mode is unsupported\n", + DEVICE(s)->canonical_path); + return false; + } + + return true; +} + +static bool k230_dw_ssi_prepare_xip_command( + K230DwSsiState *s, hwaddr address, K230DwSsiEnhancedCommand *command) +{ + uint32_t spi_ctrlr0 =3D s->regs[R_SPI_CTRLR0]; + uint32_t inst_l =3D FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, INST_L); + uint32_t addr_l =3D FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, ADDR_L); + + command->instruction_bits =3D + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, XIP_INST_EN) && inst_l ? + 1U << (inst_l + 1) : 0; + command->address_bits =3D addr_l << 2; + if (command->address_bits > 32) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: unsupported XIP address length %u bits\n", + DEVICE(s)->canonical_path, command->address_bits); + return false; + } + + command->mode_bits_enabled =3D + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, XIP_MD_BIT_EN); + if (command->mode_bits_enabled) { + uint32_t mode_length_encoding =3D + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, XIP_MBL); + + command->mode_bits =3D 1U << (mode_length_encoding + 1); + } + + command->instruction =3D s->regs[R_XIP_INCR_INST] & + (uint32_t)MAKE_64BIT_MASK(0, command->instruction_bits); + command->address =3D (uint32_t)address & + (uint32_t)MAKE_64BIT_MASK(0, command->address_bits); + command->mode =3D s->regs[R_XIP_MODE_BITS] & + (uint32_t)MAKE_64BIT_MASK(0, command->mode_bits); + command->wait_cycles =3D + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, WAIT_CYCLES); + command->spi_frf =3D FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF); + command->trans_type =3D + FIELD_EX32(spi_ctrlr0, SPI_CTRLR0, TRANS_TYPE); + + return true; +} + +static uint64_t k230_dw_ssi_xip_read(void *opaque, hwaddr address, + unsigned int size) +{ + K230DwSsiState *s =3D K230_DW_SSI(opaque); + K230DwSsiEnhancedCommand command =3D { 0 }; + uint64_t value =3D 0; + uint32_t dummy_bytes; + + if (!s->hi_sys || !k230_hi_sys_xip_enabled(s->hi_sys)) { + return 0; + } + + if (!k230_dw_ssi_xip_config_supported(s) || + !k230_dw_ssi_prepare_xip_command(s, address, &command)) { + return 0; + } + + if (s->active_cs >=3D 0 || s->phase !=3D K230_DW_SSI_PHASE_IDLE) { + k230_dw_ssi_abort_transfer(s); + } + + k230_dw_ssi_select(s, 0); + if (s->active_cs !=3D 0) { + return 0; + } + + if (command.instruction_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field(s, command.instruction, + command.instruction_bits); + } + if (command.address_bits !=3D 0) { + k230_dw_ssi_send_enhanced_field(s, command.address, + command.address_bits); + } + if (command.mode_bits_enabled) { + k230_dw_ssi_send_enhanced_field(s, command.mode, + command.mode_bits); + } + + dummy_bytes =3D k230_dw_ssi_dummy_bytes( + command.spi_frf, command.trans_type, command.wait_cycles); + for (uint32_t i =3D 0; i < dummy_bytes; i++) { + ssi_transfer(s->spi, 0); + } + + for (unsigned int i =3D 0; i < size; i++) { + value |=3D (uint64_t)(ssi_transfer(s->spi, 0) & 0xff) << (8 * i); + } + + k230_dw_ssi_deselect(s); + return value; +} + +static void k230_dw_ssi_xip_write(void *opaque, hwaddr address, + uint64_t value, unsigned int size) +{ + K230DwSsiState *s =3D K230_DW_SSI(opaque); + + qemu_log_mask(LOG_GUEST_ERROR, + "%s: XIP write at 0x%" HWADDR_PRIx " is unsupported\n", + DEVICE(s)->canonical_path, address); +} + +static const MemoryRegionOps k230_dw_ssi_xip_ops =3D { + .read =3D k230_dw_ssi_xip_read, + .write =3D k230_dw_ssi_xip_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + .unaligned =3D true, + }, + .impl =3D { + .min_access_size =3D 1, + .max_access_size =3D 8, + .unaligned =3D true, + }, +}; + static bool k230_dw_ssi_idma_enabled(const K230DwSsiState *s) { return FIELD_EX32(s->regs[R_DMACR], DMACR, IDMAE); @@ -1479,6 +1650,11 @@ static void k230_dw_ssi_init(Object *obj) TYPE_K230_DW_SSI, K230_DW_SSI_MMIO_SIZE); sysbus_init_mmio(sbd, &s->mmio); =20 + memory_region_init_io(&s->xip, obj, &k230_dw_ssi_xip_ops, s, + TYPE_K230_DW_SSI ".xip", + K230_DW_SSI_XIP_WINDOW_SIZE); + sysbus_init_mmio(sbd, &s->xip); + for (int i =3D 0; i < K230_DW_SSI_IRQ_COUNT; i++) { sysbus_init_irq(sbd, &s->irqs[i]); } diff --git a/include/hw/misc/k230_hi_sys.h b/include/hw/misc/k230_hi_sys.h index 4c85da531e..5ef5d64543 100644 --- a/include/hw/misc/k230_hi_sys.h +++ b/include/hw/misc/k230_hi_sys.h @@ -50,5 +50,6 @@ struct K230HiSysState { =20 void k230_hi_sys_set_ssi(K230HiSysState *s, unsigned int index, K230DwSsiState *ssi); +bool k230_hi_sys_xip_enabled(const K230HiSysState *s); =20 #endif /* HW_MISC_K230_HI_SYS_H */ diff --git a/include/hw/ssi/k230_dw_ssi.h b/include/hw/ssi/k230_dw_ssi.h index 49c137d73f..892384f8fb 100644 --- a/include/hw/ssi/k230_dw_ssi.h +++ b/include/hw/ssi/k230_dw_ssi.h @@ -30,6 +30,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(K230DwSsiState, K230_DW_SSI) #define K230_DW_SSI_REGS_SIZE 0x14c #define K230_DW_SSI_NUM_REGS \ (K230_DW_SSI_REGS_SIZE / sizeof(uint32_t)) +#define K230_DW_SSI_XIP_WINDOW_SIZE 0x08000000 + +typedef struct K230HiSysState K230HiSysState; =20 /* SSI GPIO output ordering differs from RISR/ISR bit ordering. */ typedef enum K230DwSsiIrq { @@ -78,7 +81,9 @@ struct K230DwSsiState { SysBusDevice parent_obj; =20 MemoryRegion mmio; + MemoryRegion xip; SSIBus *spi; + K230HiSysState *hi_sys; qemu_irq *cs_lines; qemu_irq irqs[K230_DW_SSI_IRQ_COUNT]; =20 @@ -101,5 +106,6 @@ struct K230DwSsiState { =20 uint32_t k230_dw_ssi_get_spi_mode(const K230DwSsiState *s); bool k230_dw_ssi_is_sleeping(const K230DwSsiState *s); +void k230_dw_ssi_set_hi_sys(K230DwSsiState *s, K230HiSysState *hi_sys); =20 #endif /* HW_SSI_K230_DW_SSI_H */ diff --git a/tests/qtest/k230-dw-ssi-test.c b/tests/qtest/k230-dw-ssi-test.c index 6bae56b1a8..e69215ff6c 100644 --- a/tests/qtest/k230-dw-ssi-test.c +++ b/tests/qtest/k230-dw-ssi-test.c @@ -16,6 +16,7 @@ #define K230_SPI2_BASE 0x91583000ULL #define K230_HI_SYS_BASE 0x91585000ULL #define K230_SSI_CTRL_ADDR (K230_HI_SYS_BASE + 0x68) +#define K230_FLASH_BASE 0xc0000000ULL #define K230_PLIC_BASE 0xf00000000ULL #define K230_PLIC_PENDING_BASE 0x1000 #define K230_SSI_CTRLR0 0x000 @@ -83,6 +84,7 @@ #define K230_SSI_SPI_CTRLR0_XIP_MD_EN BIT(7) #define K230_SSI_SPI_CTRLR0_INST_L_8 (2U << 8) #define K230_SSI_SPI_CTRLR0_WAIT(v) (((v) & 0x1fU) << 11) +#define K230_SSI_SPI_CTRLR0_XIP_INST_EN BIT(20) #define K230_SSI_SPI_CTRLR0_SPI_DDR_EN BIT(16) #define K230_SSI_SPI_CTRLR0_XIP_MBL_8 (2U << 26) =20 @@ -469,6 +471,24 @@ static void assert_idma_stopped(QTestState *qts, size_= t completed) K230_SSI_SR_CMPLTD_DF_SHIFT, =3D=3D, completed); } =20 +static void enable_xip(QTestState *qts) +{ + qtest_writel(qts, K230_SSI_CTRL_ADDR, + K230_SSI_CTRL_RESET | K230_SSI_CTRL_XIP_EN); +} + +static void configure_xip_read(QTestState *qts, uint8_t opcode, + unsigned int address_bits) +{ + uint32_t spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_TRANS_TYPE(0) | + K230_SSI_SPI_CTRLR0_ADDR_L(address_bits) | + K230_SSI_SPI_CTRLR0_INST_L_8 | + K230_SSI_SPI_CTRLR0_XIP_INST_EN; + + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_XIP_INCR_INST, opcode); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); +} + static void test_register_contract(void) { QTestState *qts =3D k230_ssi_start(); @@ -826,6 +846,58 @@ static void test_hi_sys(void) qtest_quit(qts); } =20 +static void test_xip_read_window(void) +{ + static const uint8_t id_expected[] =3D { 0xef, 0x40, 0x19 }; + K230SsiFlashImage image; + QTestState *qts =3D k230_ssi_start_with_flash(&image); + uint64_t low_addr =3D K230_FLASH_BASE + K230_SSI_FLASH_PATTERN_ADDR; + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + uint8_t command =3D FLASH_CMD_JEDEC; + uint8_t id[ARRAY_SIZE(id_expected)]; + + g_assert_cmphex(qtest_readl(qts, low_addr), =3D=3D, 0); + configure_xip_read(qts, FLASH_CMD_READ, 24); + enable_xip(qts); + g_assert_cmphex(qtest_readl(qts, low_addr), =3D=3D, 0xc33c5aa5); + qtest_writeb(qts, low_addr, 0); + g_assert_cmphex(qtest_readb(qts, low_addr), =3D=3D, 0xa5); + + configure_xip_read(qts, FLASH_CMD_READ4, 32); + g_assert_cmphex(qtest_readl(qts, K230_FLASH_BASE + + K230_SSI_FLASH_HIGH_ADDR), + =3D=3D, 0x74737271); + + ctrlr0 =3D k230_ssi_readl(qts, K230_SPI0_BASE, K230_SSI_CTRLR0); + ctrlr0 &=3D ~K230_SSI_CTRLR0_SPI_FRF_MASK; + ctrlr0 |=3D K230_SSI_FRF_QUAD << K230_SSI_CTRLR0_SPI_FRF_SHIFT; + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_CTRLR0, ctrlr0); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_XIP_INCR_INST, + FLASH_CMD_QUAD_IO); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_XIP_MODE_BITS, 0xff); + spi_ctrlr0 =3D K230_SSI_SPI_CTRLR0_TRANS_TYPE(1) | + K230_SSI_SPI_CTRLR0_ADDR_L(24) | + K230_SSI_SPI_CTRLR0_INST_L_8 | + K230_SSI_SPI_CTRLR0_XIP_INST_EN | + K230_SSI_SPI_CTRLR0_XIP_MD_EN | + K230_SSI_SPI_CTRLR0_XIP_MBL_8 | + K230_SSI_SPI_CTRLR0_WAIT(4); + k230_ssi_writel(qts, K230_SPI0_BASE, K230_SSI_SPI_CTRLR0, spi_ctrlr0); + g_assert_cmphex(qtest_readl(qts, low_addr), =3D=3D, 0xc33c5aa5); + + k230_ssi_configure(qts, K230_SPI0_BASE, K230_SSI_TMOD_TR, 8, 0); + configure_xip_read(qts, FLASH_CMD_READ, 24); + k230_ssi_enable_cs(qts, K230_SPI0_BASE, BIT(0)); + g_assert_cmphex(qtest_readb(qts, low_addr), =3D=3D, 0xa5); + flash_read_transaction(qts, &command, 1, id, sizeof(id)); + g_assert_cmpmem(id, sizeof(id), id_expected, sizeof(id_expected)); + g_assert_cmphex(qtest_readb(qts, low_addr), =3D=3D, 0xa5); + + qtest_quit(qts); + k230_ssi_flash_image_clear(&image); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -840,5 +912,6 @@ int main(int argc, char **argv) qtest_add_func("/k230-dw-ssi/qspi-sdr", test_qspi_sdr); qtest_add_func("/k230-dw-ssi/idma", test_idma); qtest_add_func("/k230-dw-ssi/hi-sys", test_hi_sys); + qtest_add_func("/k230-dw-ssi/xip-read-window", test_xip_read_window); return g_test_run(); } --=20 2.43.0 From nobody Mon Sep 28 02:05:28 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1785069088; cv=none; d=zohomail.com; s=zohoarc; b=Y3hMIdxh5VE5+oIOIMH36KsyBmkbQanGqrsr+VWSFegs9NDqCFhIrEMo0ST9PXc9o0xvFKgzXTjnziOiFDARHr3MWEZgmvk934rpSA0s/+XUNUITKZ8FpdhnyLQkpi5SttkrCthOoegqKVmzQwddFa2VyOAhk9Y/OpBoNEORK0g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1785069088; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=nifUjXxe5QBh90mmQAcHpR7NxfbtG30uL6XuUSI3+P8=; b=IVce2vQ35TReAzEEKKp+9zE5y28LsRV7wOmwfshmAp+d8nS5t+KVfCTrs/Zzw+W44/sTR1Qo3Vvd/fcn5pcdQAWOIVWH34hHTOiWMv38LlSaqhrMNpieKuAFytxHJqDDjwSxvZGZZh5S2aUi5RLuRaufNNSrsWcSwNGy8CV1Weo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1785069088734443.320546816097; Sun, 26 Jul 2026 05:31:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wnxzj-00033b-Kn; Sun, 26 Jul 2026 08:30:15 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wnxzb-0002eY-Mk for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:30:09 -0400 Received: from mail-pf1-x42d.google.com ([2607:f8b0:4864:20::42d]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1wnxzZ-0003W8-Jr for qemu-devel@nongnu.org; Sun, 26 Jul 2026 08:30:07 -0400 Received: by mail-pf1-x42d.google.com with SMTP id d2e1a72fcca58-84e04df8c46so1936982b3a.2 for ; Sun, 26 Jul 2026 05:30:04 -0700 (PDT) Received: from localhost.localdomain ([111.201.4.215]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-84e53426628sm1861647b3a.49.2026.07.26.05.29.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 26 Jul 2026 05:30:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1785069003; x=1785673803; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=nifUjXxe5QBh90mmQAcHpR7NxfbtG30uL6XuUSI3+P8=; b=rQYWrLVJHqCsr+C6G2ybDVQFloqksQN5LP/7obN6lRqH11dCF1ItjcUiKFAT05FlE5 ko4BdiJBxOoLhCi2Gx5++T/eJYqNXeP7/AcVv6t/DopEGuNSCs5qs5t+Bc0fTkCN+mOE vmdKJ5jZPFOpWR+LemHDFvPOP2kD9eSO6LgUfnt3h/C1Q3yAiQ1HIQFUip5TbX/6OmGr 9gSJzNxiacYke+WSmsHXxRFw/LaSg9Q0eaFE3QWUCXbRnMye8ZKYcTVd79YEITb8L9VU 0YBaO4Q6KsBvN5QfQOTLWkiBRkkCffo9bBH1H5GXBOo1K5aNwAm23GrpCRFPjwEQuC1m x78w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1785069003; x=1785673803; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=nifUjXxe5QBh90mmQAcHpR7NxfbtG30uL6XuUSI3+P8=; b=oYS7Y3he/hJmHB7YOciPiBuuHLw6l3lrieApL/GcErxU3Yu4vHBOtLViMMo1CnZFSk m4dMDQCFFDGGdnEsQ2YD4Dmfs2RKRUSzOKOLfyD9my3ClhGIvuEyVi5oAqXvdiUSN0vf SZFnn+Lhur2YxpUBoM97wwCfF5nmXt9bHlLTtcRUCKR629zqBr2a7NJVcDGQsJmj70a2 y3QeV9Gw1LGyMOmRbO2wQtjCvA0qY+EudMBLcyosRWmDbUxqlhRI0l4rDRPU87LNNWZf TXWR5r+VOQFGFBi/7BtpaS9j9OWbo/rM17YJ1qdR1utJVoh2U7CNwDJ5Ha7/32DZcWhd M2JA== X-Gm-Message-State: AOJu0YzcH0P8KrZafe2X5IA8bfGK0qF6kFRaFlXTKGeLC/fq/ystMWcL F0xHEaXMO+am1vJUZMISCa5+VEVeNeqsJownQP9CTdvBAaU726kbnfIYQCyGXofw5Kw= X-Gm-Gg: AR+sD11xGbO+pyAaGkOHDJA2LezPAp4BvTW4NxQKxB5BdmlkdjI+MHjq/0hjdylXbDY tIjFWUlQgNMHhaVwIl355gclbagdhgR6W5xvhg00mwRfZFwSkwnr/tCBCYyrZGwpNbvema7KgiG Y73J2uhpYzjDRPOji7Zu/4GBGowJioFeECjGPVLngFY0V2xT4RIcl+JepYJffJP4SN6IkWXGFkU wQGN5Gan3n8IFRhvyuSpCj0AXMpmS/boHWCzWLgi2qZJTdWBwnJtoVpqoHotqfRW00doBOKn2Q7 7NAVcnouU7VxW5aI/+H/b1hw6IT4FJU5r6kP+U8fXHTBSu3JfCPtTVhB6LWl478FNqwf6bF6Uei gK7DP4mzJO6Y+FC80IzgpKQvbsmlm3d+U0uThQj/G44uVUSEyFRhr2VNg/1kAszGksX040sLdws gGqq/m9v1wyS/F3v10TijD X-Received: by 2002:a05:6a00:218f:b0:848:86cf:267b with SMTP id d2e1a72fcca58-84e5959cb48mr4190608b3a.61.1785069003535; Sun, 26 Jul 2026 05:30:03 -0700 (PDT) From: Kangjie Huang To: qemu-devel@nongnu.org Cc: Kangjie Huang , qemu-riscv@nongnu.org, Chao Liu , Alistair Francis , Alistair Francis , Palmer Dabbelt , Weiwei Li , Daniel Henrique Barboza , Liu Zhiwei , Pierrick Bouvier , Paolo Bonzini , Fabiano Rosas , Laurent Vivier Subject: [PATCH 11/11] hw/ssi: Add trace events for K230 DesignWare SSI Date: Sun, 26 Jul 2026 20:28:29 +0800 Message-ID: <1342f13a6ea463424245534989fab7840c13eb3f.1785064313.git.flamboyant.h.01@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::42d; envelope-from=flamboyant.h.01@gmail.com; helo=mail-pf1-x42d.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1785069089339158500 Content-Type: text/plain; charset="utf-8" Add trace events for the K230 DesignWare SSI model covering: - register accesses using the effective masked or returned value; - transaction boundaries for standard and enhanced PIO transfers; - interrupt output levels driven toward the PLIC; - IDMA start, completion, and AXI error paths; and - XIP reads with the final returned value. DR accesses are excluded from register tracing: every FIFO frame passes through DR, so logging it would drown out everything else during U-Boot and Linux SPI NOR traffic. The events avoid tracing individual FIFO data frames so that traces remain usable during U-Boot and Linux SPI NOR transfers. The events only observe model behaviour and do not change it. Signed-off-by: Kangjie Huang --- hw/ssi/k230_dw_ssi.c | 26 ++++++++++++++++++++++++++ hw/ssi/trace-events | 11 +++++++++++ 2 files changed, 37 insertions(+) diff --git a/hw/ssi/k230_dw_ssi.c b/hw/ssi/k230_dw_ssi.c index d187e3345a..2bd84a4723 100644 --- a/hw/ssi/k230_dw_ssi.c +++ b/hw/ssi/k230_dw_ssi.c @@ -27,6 +27,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "system/dma.h" +#include "trace.h" =20 #define K230_DW_SSI_FIFO_CAPACITY 256 =20 @@ -285,6 +286,7 @@ static void k230_dw_ssi_write_masked(K230DwSsiState *s,= unsigned int reg, uint32_t value, uint32_t mask) { s->regs[reg] =3D (s->regs[reg] & ~mask) | (value & mask); + trace_k230_dw_ssi_reg_write(s, reg * sizeof(uint32_t), s->regs[reg]); } =20 static uint32_t k230_dw_ssi_irq_raw_status(K230DwSsiState *s) @@ -312,6 +314,8 @@ static void k230_dw_ssi_update_irq(K230DwSsiState *s) s->regs[R_IMR] & K230_DW_SSI_IRQ_VALID_MASK; =20 for (int i =3D 0; i < K230_DW_SSI_IRQ_COUNT; i++) { + trace_k230_dw_ssi_irq_update( + s, i, !!(status & k230_dw_ssi_irq_status_mask[i])); qemu_set_irq(s->irqs[i], !!(status & k230_dw_ssi_irq_status_mask[i= ])); } } @@ -359,6 +363,11 @@ static void k230_dw_ssi_deselect(K230DwSsiState *s) return; } =20 + trace_k230_dw_ssi_transaction_end( + s, s->active_cs, + FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, TMOD), + FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF), + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); qemu_irq_raise(s->cs_lines[s->active_cs]); s->active_cs =3D -1; } @@ -380,6 +389,10 @@ static void k230_dw_ssi_select(K230DwSsiState *s, unsi= gned cs) k230_dw_ssi_deselect(s); qemu_irq_lower(s->cs_lines[cs]); s->active_cs =3D cs; + trace_k230_dw_ssi_transaction_start( + s, cs, FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, TMOD), + FIELD_EX32(s->regs[R_CTRLR0], CTRLR0, SPI_FRF), + fifo32_num_used(&s->tx_fifo), fifo32_num_used(&s->rx_fifo)); } =20 static void k230_dw_ssi_update_cs(K230DwSsiState *s) @@ -776,6 +789,7 @@ static uint64_t k230_dw_ssi_xip_read(void *opaque, hwad= dr address, value |=3D (uint64_t)(ssi_transfer(s->spi, 0) & 0xff) << (8 * i); } =20 + trace_k230_dw_ssi_xip_read(s, address, size, value); k230_dw_ssi_deselect(s); return value; } @@ -907,15 +921,19 @@ static void k230_dw_ssi_try_idma(K230DwSsiState *s) s->idma_completed_frames =3D 0; =20 if (address > UINT64_MAX - (length - 1)) { + trace_k230_dw_ssi_idma_error(s, 0, address); k230_dw_ssi_idma_fail(s, "address range"); return; } =20 + trace_k230_dw_ssi_idma_start( + s, command.tmod, command.spi_frf, length, address, command.address= ); buffer =3D g_malloc(length); if (command.tmod =3D=3D K230_DW_SSI_TMOD_TO) { result =3D dma_memory_read(&address_space_memory, address, buffer, length, MEMTXATTRS_UNSPECIFIED); if (result !=3D MEMTX_OK) { + trace_k230_dw_ssi_idma_error(s, 1, address); k230_dw_ssi_idma_fail(s, "source"); return; } @@ -960,6 +978,7 @@ static void k230_dw_ssi_try_idma(K230DwSsiState *s) result =3D dma_memory_write(&address_space_memory, address, buffer, length, MEMTXATTRS_UNSPECIFIED); if (result !=3D MEMTX_OK) { + trace_k230_dw_ssi_idma_error(s, 2, address); k230_dw_ssi_idma_fail(s, "destination"); return; } @@ -970,6 +989,7 @@ static void k230_dw_ssi_try_idma(K230DwSsiState *s) } =20 s->idma_completed_frames =3D length; + trace_k230_dw_ssi_idma_done(s, address, length); k230_dw_ssi_idma_end(s, R_RISR_DONER_MASK); } =20 @@ -1245,6 +1265,7 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) } =20 if (k230_dw_ssi_is_razwi(addr)) { + trace_k230_dw_ssi_reg_read(s, addr, 0); return 0; } =20 @@ -1334,6 +1355,7 @@ static uint64_t k230_dw_ssi_read(void *opaque, hwaddr= addr, unsigned int size) break; } =20 + trace_k230_dw_ssi_reg_read(s, addr, value); return value; } =20 @@ -1352,6 +1374,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr ad= dr, } =20 if (k230_dw_ssi_is_razwi(addr)) { + trace_k230_dw_ssi_reg_write(s, addr, 0); return; } =20 @@ -1378,10 +1401,12 @@ static void k230_dw_ssi_write(void *opaque, hwaddr = addr, bool new_enabled =3D value & R_SSIENR_SSIC_EN_MASK; =20 if (old_enabled =3D=3D new_enabled) { + trace_k230_dw_ssi_reg_write(s, addr, s->regs[R_SSIENR]); return; } =20 s->regs[R_SSIENR] =3D FIELD_DP32(0, SSIENR, SSIC_EN, new_enabled); + trace_k230_dw_ssi_reg_write(s, addr, s->regs[R_SSIENR]); if (!new_enabled) { k230_dw_ssi_abort_transfer(s); s->sleep_status =3D true; @@ -1406,6 +1431,7 @@ static void k230_dw_ssi_write(void *opaque, hwaddr ad= dr, uint32_t old_ser =3D s->regs[R_SER]; =20 s->regs[R_SER] =3D value & MAKE_64BIT_MASK(0, s->num_cs); + trace_k230_dw_ssi_reg_write(s, addr, s->regs[R_SER]); if (old_ser && !s->regs[R_SER]) { k230_dw_ssi_abort_transfer(s); break; diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events index b9d8648297..873ca3ba51 100644 --- a/hw/ssi/trace-events +++ b/hw/ssi/trace-events @@ -62,3 +62,14 @@ allwinner_a10_spi_tx(uint8_t byte) "write 0x%02x" allwinner_a10_spi_rx(uint8_t byte) "read 0x%02x" allwinner_a10_spi_read(const char* regname, uint32_t value) "reg[%s] =3D> = 0x%08x" allwinner_a10_spi_write(const char* regname, uint32_t value) "reg[%s] <=3D= 0x%08x" + +# k230_dw_ssi.c +k230_dw_ssi_reg_read(void *dev, uint64_t addr, uint32_t value) "dev=3D%p a= ddr=3D0x%" PRIx64 " value=3D0x%" PRIx32 +k230_dw_ssi_reg_write(void *dev, uint64_t addr, uint32_t value) "dev=3D%p = addr=3D0x%" PRIx64 " value=3D0x%" PRIx32 +k230_dw_ssi_irq_update(void *dev, uint32_t irq, uint32_t level) "dev=3D%p = irq=3D%" PRIu32 " level=3D%" PRIu32 +k230_dw_ssi_transaction_start(void *dev, uint32_t cs, uint32_t tmod, uint3= 2_t frf, uint32_t tx, uint32_t rx) "dev=3D%p cs=3D%" PRIu32 " tmod=3D%" PRI= u32 " frf=3D%" PRIu32 " tx=3D%" PRIu32 " rx=3D%" PRIu32 +k230_dw_ssi_transaction_end(void *dev, uint32_t cs, uint32_t tmod, uint32_= t frf, uint32_t tx, uint32_t rx) "dev=3D%p cs=3D%" PRIu32 " tmod=3D%" PRIu3= 2 " frf=3D%" PRIu32 " tx=3D%" PRIu32 " rx=3D%" PRIu32 +k230_dw_ssi_idma_start(void *dev, uint32_t tmod, uint32_t frf, uint32_t fr= ames, uint64_t guest_addr, uint32_t flash_addr) "dev=3D%p tmod=3D%" PRIu32 = " frf=3D%" PRIu32 " frames=3D%" PRIu32 " guest=3D0x%" PRIx64 " flash=3D0x%"= PRIx32 +k230_dw_ssi_idma_done(void *dev, uint64_t guest_addr, uint32_t frames) "de= v=3D%p guest=3D0x%" PRIx64 " frames=3D%" PRIu32 +k230_dw_ssi_idma_error(void *dev, uint32_t stage, uint64_t guest_addr) "de= v=3D%p stage=3D%" PRIu32 " guest=3D0x%" PRIx64 +k230_dw_ssi_xip_read(void *dev, uint64_t addr, uint32_t size, uint64_t val= ue) "dev=3D%p addr=3D0x%" PRIx64 " size=3D%" PRIu32 " value=3D0x%" PRIx64 --=20 2.43.0