From nobody Fri Dec 19 14:28:35 2025 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=linaro.org ARC-Seal: i=1; a=rsa-sha256; t=1613494598; cv=none; d=zohomail.com; s=zohoarc; b=IlmidmDSda+dT9i0SYAC5azySpGrfnl7caaVPS97bnFNL5a6bSIzscJb4MB0f8ezVePTBNyLSjjjcWst2B6pzvObn6pFmIajP0yo7Mff6IN67kP8DMDxgTjktEwVzI1f2YeOrYZ2Dulf/rVwHmcpNfuxCQGKyCvWGyEQY0tf+NI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1613494598; h=Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=gXo9vPoaGS+Kt0SAuDUqe7xzg+ZPyuQ7xrZkIvCpO/M=; b=cnvmaranCKSMBMV6Cg1m7mmLZw+EgCSwkVlMZlscdrjmTyaRqre9IT5eqNsKxEn2/PVbEvFO3OLpo9NDiMWuLTZJHs9OvWucd4CqzkEDE5AeI9IPCUaZLj5525iINC9FTmJ2YuLt/cxIFA9a+E8F+JOgn3vS7joyzNkBXYqZgoA= 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) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1613494598545240.1845372963753; Tue, 16 Feb 2021 08:56:38 -0800 (PST) Received: from localhost ([::1]:39172 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1lC3eP-0007yP-Cy for importer@patchew.org; Tue, 16 Feb 2021 11:56:37 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40914) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1lC33M-0000KT-C4 for qemu-devel@nongnu.org; Tue, 16 Feb 2021 11:18:20 -0500 Received: from mail-wr1-x42f.google.com ([2a00:1450:4864:20::42f]:37925) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1lC32W-0002Y5-Ne for qemu-devel@nongnu.org; Tue, 16 Feb 2021 11:18:20 -0500 Received: by mail-wr1-x42f.google.com with SMTP id b3so13780657wrj.5 for ; Tue, 16 Feb 2021 08:17:28 -0800 (PST) Received: from orth.archaic.org.uk (orth.archaic.org.uk. [81.2.115.148]) by smtp.gmail.com with ESMTPSA id d5sm30630482wrb.14.2021.02.16.08.17.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 16 Feb 2021 08:17:26 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:subject:date:message-id:in-reply-to:references:mime-version :content-transfer-encoding; bh=gXo9vPoaGS+Kt0SAuDUqe7xzg+ZPyuQ7xrZkIvCpO/M=; b=G0+j+4QzJZx68BTqrVZf51Keb6+oy0jz7sH9gewwKZTCO7z60RVxro/UkVHdlAIqk9 gc4AejfNzBNpDjC4IwJxAsICadphsMDEkxOumgAz/ycLnV3i29RIwKRUbewEYuJodmB4 Ohxg3KR2C0LbprF29gzjOIKKVv791olvE8T3AB8jmhbwhYX/vYRk1dJM4lfUfCy59HDK Es6ozlcUoa2j/0YLS/PURib3VgSD+OLbRRNFL9qXZIkHqljuuTF52fwi0OtOA97Mnz4D PryLG5MPrdb+CKrtdqNZkHcoD8U2Ox9lPjnvFt/vEA6HiueIVjxtkI2G0D7xXx6lsAiN 070w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gXo9vPoaGS+Kt0SAuDUqe7xzg+ZPyuQ7xrZkIvCpO/M=; b=p5zcA7DPSCjEnfswpn90DVGy4Wjk10Gq4OgfksOc0q/4BRtchDn0c1bVDknZ59758s oLYw0ghQKTiA7eQ8qJqfZ9Kv5aVE07koOzdyz8MT1BdvS5y77HGnpse8jc9/kWsXExsA lXVOOyPrvIR7JHBpmelpNzHKZPX8xS4xa8slXt83EDa12EhkN9oulkTQQtPAnGI5IaU9 JPwzlSCwoGYjQqXOlguzDE/CLWlQmJWwT3oCasHb2MC/3EzhNIqKKYjraNySwGDqhVGr URARK4RXduPp6gAk+GZIMfeV/w24YsAW0Vnm0EWCTt6R2POqyxEt6oJG2OLfzpS6XuZJ FKJQ== X-Gm-Message-State: AOAM531VW2zLkq8q4KtHFunYn1bSJUVUOOdDpKEqUD8TPpL7JwyqcMD0 lwGZ2gTonGIkO1wcFd29Qik1w9wOI8Enug== X-Google-Smtp-Source: ABdhPJyJnLjYNu9fSXgB4ZjgjoIE/Ewuvfq/HDQH3c761z4D8WJixFK8XP5PmnmglcL0ielob4sT7w== X-Received: by 2002:a5d:6883:: with SMTP id h3mr24144242wru.90.1613492246866; Tue, 16 Feb 2021 08:17:26 -0800 (PST) From: Peter Maydell To: qemu-devel@nongnu.org Subject: [PULL 36/40] hw/i2c: Implement NPCM7XX SMBus Module FIFO Mode Date: Tue, 16 Feb 2021 16:16:54 +0000 Message-Id: <20210216161658.29881-37-peter.maydell@linaro.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210216161658.29881-1-peter.maydell@linaro.org> References: <20210216161658.29881-1-peter.maydell@linaro.org> 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=lists.gnu.org; Received-SPF: pass client-ip=2a00:1450:4864:20::42f; envelope-from=peter.maydell@linaro.org; helo=mail-wr1-x42f.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 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, 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.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: pass (identity @linaro.org) Content-Type: text/plain; charset="utf-8" From: Hao Wu This patch implements the FIFO mode of the SMBus module. In FIFO, the user transmits or receives at most 16 bytes at a time. The FIFO mode allows the module to transmit large amount of data faster than single byte mode. Since we only added the device in a patch that is only a few commits away in the same patch set. We do not increase the VMstate version number in this special case. Reviewed-by: Doug Evans Reviewed-by: Tyrong Ting Signed-off-by: Hao Wu Reviewed-by: Corey Minyard Message-id: 20210210220426.3577804-6-wuhaotsh@google.com Acked-by: Corey Minyard Signed-off-by: Peter Maydell --- include/hw/i2c/npcm7xx_smbus.h | 25 +++ hw/i2c/npcm7xx_smbus.c | 342 +++++++++++++++++++++++++++++-- tests/qtest/npcm7xx_smbus-test.c | 149 +++++++++++++- hw/i2c/trace-events | 1 + 4 files changed, 501 insertions(+), 16 deletions(-) diff --git a/include/hw/i2c/npcm7xx_smbus.h b/include/hw/i2c/npcm7xx_smbus.h index b9761a69932..7d59ee917eb 100644 --- a/include/hw/i2c/npcm7xx_smbus.h +++ b/include/hw/i2c/npcm7xx_smbus.h @@ -27,6 +27,9 @@ */ #define NPCM7XX_SMBUS_NR_ADDRS 10 =20 +/* Size of the FIFO buffer. */ +#define NPCM7XX_SMBUS_FIFO_SIZE 16 + typedef enum NPCM7xxSMBusStatus { NPCM7XX_SMBUS_STATUS_IDLE, NPCM7XX_SMBUS_STATUS_SENDING, @@ -53,6 +56,16 @@ typedef enum NPCM7xxSMBusStatus { * @addr: The SMBus module's own addresses on the I2C bus. * @scllt: The SCL low time register. * @sclht: The SCL high time register. + * @fif_ctl: The FIFO control register. + * @fif_cts: The FIFO control status register. + * @fair_per: The fair preriod register. + * @txf_ctl: The transmit FIFO control register. + * @t_out: The SMBus timeout register. + * @txf_sts: The transmit FIFO status register. + * @rxf_sts: The receive FIFO status register. + * @rxf_ctl: The receive FIFO control register. + * @rx_fifo: The FIFO buffer for receiving in FIFO mode. + * @rx_cur: The current position of rx_fifo. * @status: The current status of the SMBus. */ typedef struct NPCM7xxSMBusState { @@ -78,6 +91,18 @@ typedef struct NPCM7xxSMBusState { uint8_t scllt; uint8_t sclht; =20 + uint8_t fif_ctl; + uint8_t fif_cts; + uint8_t fair_per; + uint8_t txf_ctl; + uint8_t t_out; + uint8_t txf_sts; + uint8_t rxf_sts; + uint8_t rxf_ctl; + + uint8_t rx_fifo[NPCM7XX_SMBUS_FIFO_SIZE]; + uint8_t rx_cur; + NPCM7xxSMBusStatus status; } NPCM7xxSMBusState; =20 diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index a465740623f..6b2f9e1aaad 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -129,14 +129,45 @@ enum NPCM7xxSMBusBank1Register { #define NPCM7XX_ADDR_EN BIT(7) #define NPCM7XX_ADDR_A(rv) extract8((rv), 0, 6) =20 +/* FIFO Mode Register Fields */ +/* FIF_CTL fields */ +#define NPCM7XX_SMBFIF_CTL_FIFO_EN BIT(4) +#define NPCM7XX_SMBFIF_CTL_FAIR_RDY_IE BIT(2) +#define NPCM7XX_SMBFIF_CTL_FAIR_RDY BIT(1) +#define NPCM7XX_SMBFIF_CTL_FAIR_BUSY BIT(0) +/* FIF_CTS fields */ +#define NPCM7XX_SMBFIF_CTS_STR BIT(7) +#define NPCM7XX_SMBFIF_CTS_CLR_FIFO BIT(6) +#define NPCM7XX_SMBFIF_CTS_RFTE_IE BIT(3) +#define NPCM7XX_SMBFIF_CTS_RXF_TXE BIT(1) +/* TXF_CTL fields */ +#define NPCM7XX_SMBTXF_CTL_THR_TXIE BIT(6) +#define NPCM7XX_SMBTXF_CTL_TX_THR(rv) extract8((rv), 0, 5) +/* T_OUT fields */ +#define NPCM7XX_SMBT_OUT_ST BIT(7) +#define NPCM7XX_SMBT_OUT_IE BIT(6) +#define NPCM7XX_SMBT_OUT_CLKDIV(rv) extract8((rv), 0, 6) +/* TXF_STS fields */ +#define NPCM7XX_SMBTXF_STS_TX_THST BIT(6) +#define NPCM7XX_SMBTXF_STS_TX_BYTES(rv) extract8((rv), 0, 5) +/* RXF_STS fields */ +#define NPCM7XX_SMBRXF_STS_RX_THST BIT(6) +#define NPCM7XX_SMBRXF_STS_RX_BYTES(rv) extract8((rv), 0, 5) +/* RXF_CTL fields */ +#define NPCM7XX_SMBRXF_CTL_THR_RXIE BIT(6) +#define NPCM7XX_SMBRXF_CTL_LAST BIT(5) +#define NPCM7XX_SMBRXF_CTL_RX_THR(rv) extract8((rv), 0, 5) + #define KEEP_OLD_BIT(o, n, b) (((n) & (~(b))) | ((o) & (b))) #define WRITE_ONE_CLEAR(o, n, b) ((n) & (b) ? (o) & (~(b)) : (o)) =20 #define NPCM7XX_SMBUS_ENABLED(s) ((s)->ctl2 & NPCM7XX_SMBCTL2_ENABLE) +#define NPCM7XX_SMBUS_FIFO_ENABLED(s) ((s)->fif_ctl & \ + NPCM7XX_SMBFIF_CTL_FIFO_EN) =20 /* VERSION fields values, read-only. */ #define NPCM7XX_SMBUS_VERSION_NUMBER 1 -#define NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED 0 +#define NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED 1 =20 /* Reset values */ #define NPCM7XX_SMB_ST_INIT_VAL 0x00 @@ -151,6 +182,14 @@ enum NPCM7xxSMBusBank1Register { #define NPCM7XX_SMB_ADDR_INIT_VAL 0x00 #define NPCM7XX_SMB_SCLLT_INIT_VAL 0x00 #define NPCM7XX_SMB_SCLHT_INIT_VAL 0x00 +#define NPCM7XX_SMB_FIF_CTL_INIT_VAL 0x00 +#define NPCM7XX_SMB_FIF_CTS_INIT_VAL 0x00 +#define NPCM7XX_SMB_FAIR_PER_INIT_VAL 0x00 +#define NPCM7XX_SMB_TXF_CTL_INIT_VAL 0x00 +#define NPCM7XX_SMB_T_OUT_INIT_VAL 0x3f +#define NPCM7XX_SMB_TXF_STS_INIT_VAL 0x00 +#define NPCM7XX_SMB_RXF_STS_INIT_VAL 0x00 +#define NPCM7XX_SMB_RXF_CTL_INIT_VAL 0x01 =20 static uint8_t npcm7xx_smbus_get_version(void) { @@ -171,7 +210,13 @@ static void npcm7xx_smbus_update_irq(NPCM7xxSMBusState= *s) (s->ctl1 & NPCM7XX_SMBCTL1_STASTRE && s->st & NPCM7XX_SMBST_SDAST) || (s->ctl1 & NPCM7XX_SMBCTL1_EOBINTE && - s->cst3 & NPCM7XX_SMBCST3_EO_BUSY)); + s->cst3 & NPCM7XX_SMBCST3_EO_BUSY) || + (s->rxf_ctl & NPCM7XX_SMBRXF_CTL_THR_RXIE && + s->rxf_sts & NPCM7XX_SMBRXF_STS_RX_THST) || + (s->txf_ctl & NPCM7XX_SMBTXF_CTL_THR_TXIE && + s->txf_sts & NPCM7XX_SMBTXF_STS_TX_THST) || + (s->fif_cts & NPCM7XX_SMBFIF_CTS_RFTE_IE && + s->fif_cts & NPCM7XX_SMBFIF_CTS_RXF_TXE)); =20 if (level) { s->cst2 |=3D NPCM7XX_SMBCST2_INTSTS; @@ -189,6 +234,13 @@ static void npcm7xx_smbus_nack(NPCM7xxSMBusState *s) s->status =3D NPCM7XX_SMBUS_STATUS_NEGACK; } =20 +static void npcm7xx_smbus_clear_buffer(NPCM7xxSMBusState *s) +{ + s->fif_cts &=3D ~NPCM7XX_SMBFIF_CTS_RXF_TXE; + s->txf_sts =3D 0; + s->rxf_sts =3D 0; +} + static void npcm7xx_smbus_send_byte(NPCM7xxSMBusState *s, uint8_t value) { int rv =3D i2c_send(s->bus, value); @@ -197,6 +249,15 @@ static void npcm7xx_smbus_send_byte(NPCM7xxSMBusState = *s, uint8_t value) npcm7xx_smbus_nack(s); } else { s->st |=3D NPCM7XX_SMBST_SDAST; + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + s->fif_cts |=3D NPCM7XX_SMBFIF_CTS_RXF_TXE; + if (NPCM7XX_SMBTXF_STS_TX_BYTES(s->txf_sts) =3D=3D + NPCM7XX_SMBTXF_CTL_TX_THR(s->txf_ctl)) { + s->txf_sts =3D NPCM7XX_SMBTXF_STS_TX_THST; + } else { + s->txf_sts =3D 0; + } + } } trace_npcm7xx_smbus_send_byte((DEVICE(s)->canonical_path), value, !rv); npcm7xx_smbus_update_irq(s); @@ -215,6 +276,67 @@ static void npcm7xx_smbus_recv_byte(NPCM7xxSMBusState = *s) npcm7xx_smbus_update_irq(s); } =20 +static void npcm7xx_smbus_recv_fifo(NPCM7xxSMBusState *s) +{ + uint8_t expected_bytes =3D NPCM7XX_SMBRXF_CTL_RX_THR(s->rxf_ctl); + uint8_t received_bytes =3D NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts); + uint8_t pos; + + if (received_bytes =3D=3D expected_bytes) { + return; + } + + while (received_bytes < expected_bytes && + received_bytes < NPCM7XX_SMBUS_FIFO_SIZE) { + pos =3D (s->rx_cur + received_bytes) % NPCM7XX_SMBUS_FIFO_SIZE; + s->rx_fifo[pos] =3D i2c_recv(s->bus); + trace_npcm7xx_smbus_recv_byte((DEVICE(s)->canonical_path), + s->rx_fifo[pos]); + ++received_bytes; + } + + trace_npcm7xx_smbus_recv_fifo((DEVICE(s)->canonical_path), + received_bytes, expected_bytes); + s->rxf_sts =3D received_bytes; + if (unlikely(received_bytes < expected_bytes)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid rx_thr value: 0x%02x\n", + DEVICE(s)->canonical_path, expected_bytes); + return; + } + + s->rxf_sts |=3D NPCM7XX_SMBRXF_STS_RX_THST; + if (s->rxf_ctl & NPCM7XX_SMBRXF_CTL_LAST) { + trace_npcm7xx_smbus_nack(DEVICE(s)->canonical_path); + i2c_nack(s->bus); + s->rxf_ctl &=3D ~NPCM7XX_SMBRXF_CTL_LAST; + } + if (received_bytes =3D=3D NPCM7XX_SMBUS_FIFO_SIZE) { + s->st |=3D NPCM7XX_SMBST_SDAST; + s->fif_cts |=3D NPCM7XX_SMBFIF_CTS_RXF_TXE; + } else if (!(s->rxf_ctl & NPCM7XX_SMBRXF_CTL_THR_RXIE)) { + s->st |=3D NPCM7XX_SMBST_SDAST; + } else { + s->st &=3D ~NPCM7XX_SMBST_SDAST; + } + npcm7xx_smbus_update_irq(s); +} + +static void npcm7xx_smbus_read_byte_fifo(NPCM7xxSMBusState *s) +{ + uint8_t received_bytes =3D NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts); + + if (received_bytes =3D=3D 0) { + npcm7xx_smbus_recv_fifo(s); + return; + } + + s->sda =3D s->rx_fifo[s->rx_cur]; + s->rx_cur =3D (s->rx_cur + 1u) % NPCM7XX_SMBUS_FIFO_SIZE; + --s->rxf_sts; + npcm7xx_smbus_update_irq(s); +} + static void npcm7xx_smbus_start(NPCM7xxSMBusState *s) { /* @@ -228,6 +350,9 @@ static void npcm7xx_smbus_start(NPCM7xxSMBusState *s) if (available) { s->st |=3D NPCM7XX_SMBST_MODE | NPCM7XX_SMBST_XMIT | NPCM7XX_SMBST= _SDAST; s->cst |=3D NPCM7XX_SMBCST_BUSY; + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + s->fif_cts |=3D NPCM7XX_SMBFIF_CTS_RXF_TXE; + } } else { s->st &=3D ~NPCM7XX_SMBST_MODE; s->cst &=3D ~NPCM7XX_SMBCST_BUSY; @@ -279,7 +404,15 @@ static void npcm7xx_smbus_send_address(NPCM7xxSMBusSta= te *s, uint8_t value) s->st |=3D NPCM7XX_SMBST_SDAST; } } else if (recv) { - npcm7xx_smbus_recv_byte(s); + s->st |=3D NPCM7XX_SMBST_SDAST; + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + npcm7xx_smbus_recv_fifo(s); + } else { + npcm7xx_smbus_recv_byte(s); + } + } else if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + s->st |=3D NPCM7XX_SMBST_SDAST; + s->fif_cts |=3D NPCM7XX_SMBFIF_CTS_RXF_TXE; } npcm7xx_smbus_update_irq(s); } @@ -322,11 +455,31 @@ static uint8_t npcm7xx_smbus_read_sda(NPCM7xxSMBusSta= te *s) =20 switch (s->status) { case NPCM7XX_SMBUS_STATUS_STOPPING_LAST_RECEIVE: - npcm7xx_smbus_execute_stop(s); + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + if (NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts) <=3D 1) { + npcm7xx_smbus_execute_stop(s); + } + if (NPCM7XX_SMBRXF_STS_RX_BYTES(s->rxf_sts) =3D=3D 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read to SDA with an empty rx-fifo buffe= r, " + "result undefined: %u\n", + DEVICE(s)->canonical_path, s->sda); + break; + } + npcm7xx_smbus_read_byte_fifo(s); + value =3D s->sda; + } else { + npcm7xx_smbus_execute_stop(s); + } break; =20 case NPCM7XX_SMBUS_STATUS_RECEIVING: - npcm7xx_smbus_recv_byte(s); + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + npcm7xx_smbus_read_byte_fifo(s); + value =3D s->sda; + } else { + npcm7xx_smbus_recv_byte(s); + } break; =20 default: @@ -372,8 +525,12 @@ static void npcm7xx_smbus_write_st(NPCM7xxSMBusState *= s, uint8_t value) } =20 if (value & NPCM7XX_SMBST_STASTR && - s->status =3D=3D NPCM7XX_SMBUS_STATUS_RECEIVING) { - npcm7xx_smbus_recv_byte(s); + s->status =3D=3D NPCM7XX_SMBUS_STATUS_RECEIVING) { + if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) { + npcm7xx_smbus_recv_fifo(s); + } else { + npcm7xx_smbus_recv_byte(s); + } } =20 npcm7xx_smbus_update_irq(s); @@ -419,6 +576,7 @@ static void npcm7xx_smbus_write_ctl2(NPCM7xxSMBusState = *s, uint8_t value) s->st =3D 0; s->cst3 =3D s->cst3 & (~NPCM7XX_SMBCST3_EO_BUSY); s->cst =3D 0; + npcm7xx_smbus_clear_buffer(s); } } =20 @@ -431,6 +589,70 @@ static void npcm7xx_smbus_write_ctl3(NPCM7xxSMBusState= *s, uint8_t value) NPCM7XX_SMBCTL3_SCL_LVL | NPCM7XX_SMBCTL3_SDA_= LVL); } =20 +static void npcm7xx_smbus_write_fif_ctl(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + uint8_t new_ctl =3D value; + + new_ctl =3D KEEP_OLD_BIT(s->fif_ctl, new_ctl, NPCM7XX_SMBFIF_CTL_FAIR_= RDY); + new_ctl =3D WRITE_ONE_CLEAR(new_ctl, value, NPCM7XX_SMBFIF_CTL_FAIR_RD= Y); + new_ctl =3D KEEP_OLD_BIT(s->fif_ctl, new_ctl, NPCM7XX_SMBFIF_CTL_FAIR_= BUSY); + s->fif_ctl =3D new_ctl; +} + +static void npcm7xx_smbus_write_fif_cts(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + s->fif_cts =3D WRITE_ONE_CLEAR(s->fif_cts, value, NPCM7XX_SMBFIF_CTS_S= TR); + s->fif_cts =3D WRITE_ONE_CLEAR(s->fif_cts, value, NPCM7XX_SMBFIF_CTS_R= XF_TXE); + s->fif_cts =3D KEEP_OLD_BIT(value, s->fif_cts, NPCM7XX_SMBFIF_CTS_RFTE= _IE); + + if (value & NPCM7XX_SMBFIF_CTS_CLR_FIFO) { + npcm7xx_smbus_clear_buffer(s); + } +} + +static void npcm7xx_smbus_write_txf_ctl(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + s->txf_ctl =3D value; +} + +static void npcm7xx_smbus_write_t_out(NPCM7xxSMBusState *s, uint8_t value) +{ + uint8_t new_t_out =3D value; + + if ((value & NPCM7XX_SMBT_OUT_ST) || (!(s->t_out & NPCM7XX_SMBT_OUT_ST= ))) { + new_t_out &=3D ~NPCM7XX_SMBT_OUT_ST; + } else { + new_t_out |=3D NPCM7XX_SMBT_OUT_ST; + } + + s->t_out =3D new_t_out; +} + +static void npcm7xx_smbus_write_txf_sts(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + s->txf_sts =3D WRITE_ONE_CLEAR(s->txf_sts, value, NPCM7XX_SMBTXF_STS_T= X_THST); +} + +static void npcm7xx_smbus_write_rxf_sts(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + if (value & NPCM7XX_SMBRXF_STS_RX_THST) { + s->rxf_sts &=3D ~NPCM7XX_SMBRXF_STS_RX_THST; + if (s->status =3D=3D NPCM7XX_SMBUS_STATUS_RECEIVING) { + npcm7xx_smbus_recv_fifo(s); + } + } +} + +static void npcm7xx_smbus_write_rxf_ctl(NPCM7xxSMBusState *s, uint8_t valu= e) +{ + uint8_t new_ctl =3D value; + + if (!(value & NPCM7XX_SMBRXF_CTL_LAST)) { + new_ctl =3D KEEP_OLD_BIT(s->rxf_ctl, new_ctl, NPCM7XX_SMBRXF_CTL_L= AST); + } + s->rxf_ctl =3D new_ctl; +} + static uint64_t npcm7xx_smbus_read(void *opaque, hwaddr offset, unsigned s= ize) { NPCM7xxSMBusState *s =3D opaque; @@ -487,9 +709,41 @@ static uint64_t npcm7xx_smbus_read(void *opaque, hwadd= r offset, unsigned size) default: if (bank) { /* Bank 1 */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: read from invalid offset 0x%" HWADDR_PRIx "\n", - DEVICE(s)->canonical_path, offset); + switch (offset) { + case NPCM7XX_SMB_FIF_CTS: + value =3D s->fif_cts; + break; + + case NPCM7XX_SMB_FAIR_PER: + value =3D s->fair_per; + break; + + case NPCM7XX_SMB_TXF_CTL: + value =3D s->txf_ctl; + break; + + case NPCM7XX_SMB_T_OUT: + value =3D s->t_out; + break; + + case NPCM7XX_SMB_TXF_STS: + value =3D s->txf_sts; + break; + + case NPCM7XX_SMB_RXF_STS: + value =3D s->rxf_sts; + break; + + case NPCM7XX_SMB_RXF_CTL: + value =3D s->rxf_ctl; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n= ", + DEVICE(s)->canonical_path, offset); + break; + } } else { /* Bank 0 */ switch (offset) { @@ -537,6 +791,10 @@ static uint64_t npcm7xx_smbus_read(void *opaque, hwadd= r offset, unsigned size) value =3D s->scllt; break; =20 + case NPCM7XX_SMB_FIF_CTL: + value =3D s->fif_ctl; + break; + case NPCM7XX_SMB_SCLHT: value =3D s->sclht; break; @@ -618,9 +876,41 @@ static void npcm7xx_smbus_write(void *opaque, hwaddr o= ffset, uint64_t value, default: if (bank) { /* Bank 1 */ - qemu_log_mask(LOG_GUEST_ERROR, - "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", - DEVICE(s)->canonical_path, offset); + switch (offset) { + case NPCM7XX_SMB_FIF_CTS: + npcm7xx_smbus_write_fif_cts(s, value); + break; + + case NPCM7XX_SMB_FAIR_PER: + s->fair_per =3D value; + break; + + case NPCM7XX_SMB_TXF_CTL: + npcm7xx_smbus_write_txf_ctl(s, value); + break; + + case NPCM7XX_SMB_T_OUT: + npcm7xx_smbus_write_t_out(s, value); + break; + + case NPCM7XX_SMB_TXF_STS: + npcm7xx_smbus_write_txf_sts(s, value); + break; + + case NPCM7XX_SMB_RXF_STS: + npcm7xx_smbus_write_rxf_sts(s, value); + break; + + case NPCM7XX_SMB_RXF_CTL: + npcm7xx_smbus_write_rxf_ctl(s, value); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: write to invalid offset 0x%" HWADDR_PRIx "\n", + DEVICE(s)->canonical_path, offset); + break; + } } else { /* Bank 0 */ switch (offset) { @@ -668,6 +958,10 @@ static void npcm7xx_smbus_write(void *opaque, hwaddr o= ffset, uint64_t value, s->scllt =3D value; break; =20 + case NPCM7XX_SMB_FIF_CTL: + npcm7xx_smbus_write_fif_ctl(s, value); + break; + case NPCM7XX_SMB_SCLHT: s->sclht =3D value; break; @@ -714,7 +1008,18 @@ static void npcm7xx_smbus_enter_reset(Object *obj, Re= setType type) s->scllt =3D NPCM7XX_SMB_SCLLT_INIT_VAL; s->sclht =3D NPCM7XX_SMB_SCLHT_INIT_VAL; =20 + s->fif_ctl =3D NPCM7XX_SMB_FIF_CTL_INIT_VAL; + s->fif_cts =3D NPCM7XX_SMB_FIF_CTS_INIT_VAL; + s->fair_per =3D NPCM7XX_SMB_FAIR_PER_INIT_VAL; + s->txf_ctl =3D NPCM7XX_SMB_TXF_CTL_INIT_VAL; + s->t_out =3D NPCM7XX_SMB_T_OUT_INIT_VAL; + s->txf_sts =3D NPCM7XX_SMB_TXF_STS_INIT_VAL; + s->rxf_sts =3D NPCM7XX_SMB_RXF_STS_INIT_VAL; + s->rxf_ctl =3D NPCM7XX_SMB_RXF_CTL_INIT_VAL; + + npcm7xx_smbus_clear_buffer(s); s->status =3D NPCM7XX_SMBUS_STATUS_IDLE; + s->rx_cur =3D 0; } =20 static void npcm7xx_smbus_hold_reset(Object *obj) @@ -756,6 +1061,17 @@ static const VMStateDescription vmstate_npcm7xx_smbus= =3D { VMSTATE_UINT8_ARRAY(addr, NPCM7xxSMBusState, NPCM7XX_SMBUS_NR_ADDR= S), VMSTATE_UINT8(scllt, NPCM7xxSMBusState), VMSTATE_UINT8(sclht, NPCM7xxSMBusState), + VMSTATE_UINT8(fif_ctl, NPCM7xxSMBusState), + VMSTATE_UINT8(fif_cts, NPCM7xxSMBusState), + VMSTATE_UINT8(fair_per, NPCM7xxSMBusState), + VMSTATE_UINT8(txf_ctl, NPCM7xxSMBusState), + VMSTATE_UINT8(t_out, NPCM7xxSMBusState), + VMSTATE_UINT8(txf_sts, NPCM7xxSMBusState), + VMSTATE_UINT8(rxf_sts, NPCM7xxSMBusState), + VMSTATE_UINT8(rxf_ctl, NPCM7xxSMBusState), + VMSTATE_UINT8_ARRAY(rx_fifo, NPCM7xxSMBusState, + NPCM7XX_SMBUS_FIFO_SIZE), + VMSTATE_UINT8(rx_cur, NPCM7xxSMBusState), VMSTATE_END_OF_LIST(), }, }; diff --git a/tests/qtest/npcm7xx_smbus-test.c b/tests/qtest/npcm7xx_smbus-t= est.c index 4594b107df1..4f9f493872a 100644 --- a/tests/qtest/npcm7xx_smbus-test.c +++ b/tests/qtest/npcm7xx_smbus-test.c @@ -132,6 +132,44 @@ enum NPCM7xxSMBusBank1Register { #define ADDR_EN BIT(7) #define ADDR_A(rv) extract8((rv), 0, 6) =20 +/* FIF_CTL fields */ +#define FIF_CTL_FIFO_EN BIT(4) + +/* FIF_CTS fields */ +#define FIF_CTS_CLR_FIFO BIT(6) +#define FIF_CTS_RFTE_IE BIT(3) +#define FIF_CTS_RXF_TXE BIT(1) + +/* TXF_CTL fields */ +#define TXF_CTL_THR_TXIE BIT(6) +#define TXF_CTL_TX_THR(rv) extract8((rv), 0, 5) + +/* TXF_STS fields */ +#define TXF_STS_TX_THST BIT(6) +#define TXF_STS_TX_BYTES(rv) extract8((rv), 0, 5) + +/* RXF_CTL fields */ +#define RXF_CTL_THR_RXIE BIT(6) +#define RXF_CTL_LAST BIT(5) +#define RXF_CTL_RX_THR(rv) extract8((rv), 0, 5) + +/* RXF_STS fields */ +#define RXF_STS_RX_THST BIT(6) +#define RXF_STS_RX_BYTES(rv) extract8((rv), 0, 5) + + +static void choose_bank(QTestState *qts, uint64_t base_addr, uint8_t bank) +{ + uint8_t ctl3 =3D qtest_readb(qts, base_addr + OFFSET_CTL3); + + if (bank) { + ctl3 |=3D CTL3_BNK_SEL; + } else { + ctl3 &=3D ~CTL3_BNK_SEL; + } + + qtest_writeb(qts, base_addr + OFFSET_CTL3, ctl3); +} =20 static void check_running(QTestState *qts, uint64_t base_addr) { @@ -203,10 +241,33 @@ static void send_byte(QTestState *qts, uint64_t base_= addr, uint8_t byte) qtest_writeb(qts, base_addr + OFFSET_SDA, byte); } =20 +static bool check_recv(QTestState *qts, uint64_t base_addr) +{ + uint8_t st, fif_ctl, rxf_ctl, rxf_sts; + bool fifo; + + st =3D qtest_readb(qts, base_addr + OFFSET_ST); + choose_bank(qts, base_addr, 0); + fif_ctl =3D qtest_readb(qts, base_addr + OFFSET_FIF_CTL); + fifo =3D fif_ctl & FIF_CTL_FIFO_EN; + if (!fifo) { + return st =3D=3D (ST_MODE | ST_SDAST); + } + + choose_bank(qts, base_addr, 1); + rxf_ctl =3D qtest_readb(qts, base_addr + OFFSET_RXF_CTL); + rxf_sts =3D qtest_readb(qts, base_addr + OFFSET_RXF_STS); + + if ((rxf_ctl & RXF_CTL_THR_RXIE) && RXF_STS_RX_BYTES(rxf_sts) < 16) { + return st =3D=3D ST_MODE; + } else { + return st =3D=3D (ST_MODE | ST_SDAST); + } +} + static uint8_t recv_byte(QTestState *qts, uint64_t base_addr) { - g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), =3D=3D, - ST_MODE | ST_SDAST); + g_assert_true(check_recv(qts, base_addr)); return qtest_readb(qts, base_addr + OFFSET_SDA); } =20 @@ -229,7 +290,7 @@ static void send_address(QTestState *qts, uint64_t base= _addr, uint8_t addr, qtest_writeb(qts, base_addr + OFFSET_ST, ST_STASTR); st =3D qtest_readb(qts, base_addr + OFFSET_ST); if (recv) { - g_assert_cmphex(st, =3D=3D, ST_MODE | ST_SDAST); + g_assert_true(check_recv(qts, base_addr)); } else { g_assert_cmphex(st, =3D=3D, ST_MODE | ST_XMIT | ST_SDAST); } @@ -251,6 +312,29 @@ static void send_nack(QTestState *qts, uint64_t base_a= ddr) qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1); } =20 +static void start_fifo_mode(QTestState *qts, uint64_t base_addr) +{ + choose_bank(qts, base_addr, 0); + qtest_writeb(qts, base_addr + OFFSET_FIF_CTL, FIF_CTL_FIFO_EN); + g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTL) & + FIF_CTL_FIFO_EN); + choose_bank(qts, base_addr, 1); + qtest_writeb(qts, base_addr + OFFSET_FIF_CTS, + FIF_CTS_CLR_FIFO | FIF_CTS_RFTE_IE); + g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_FIF_CTS), =3D=3D, + FIF_CTS_RFTE_IE); + g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_TXF_STS), =3D=3D, = 0); + g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_RXF_STS), =3D=3D, = 0); +} + +static void start_recv_fifo(QTestState *qts, uint64_t base_addr, uint8_t b= ytes) +{ + choose_bank(qts, base_addr, 1); + qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, 0); + qtest_writeb(qts, base_addr + OFFSET_RXF_CTL, + RXF_CTL_THR_RXIE | RXF_CTL_LAST | bytes); +} + /* Check the SMBus's status is set correctly when disabled. */ static void test_disable_bus(gconstpointer data) { @@ -324,6 +408,64 @@ static void test_single_mode(gconstpointer data) qtest_quit(qts); } =20 +/* Check the SMBus can send and receive bytes in FIFO mode. */ +static void test_fifo_mode(gconstpointer data) +{ + intptr_t index =3D (intptr_t)data; + uint64_t base_addr =3D SMBUS_ADDR(index); + int irq =3D SMBUS_IRQ(index); + uint8_t value =3D 0x60; + QTestState *qts =3D qtest_init("-machine npcm750-evb"); + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + enable_bus(qts, base_addr); + start_fifo_mode(qts, base_addr); + g_assert_false(qtest_get_irq(qts, irq)); + + /* Sending */ + start_transfer(qts, base_addr); + send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true); + choose_bank(qts, base_addr, 1); + g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) & + FIF_CTS_RXF_TXE); + qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, TXF_CTL_THR_TXIE); + send_byte(qts, base_addr, TMP105_REG_CONFIG); + send_byte(qts, base_addr, value); + g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) & + FIF_CTS_RXF_TXE); + g_assert_true(qtest_readb(qts, base_addr + OFFSET_TXF_STS) & + TXF_STS_TX_THST); + g_assert_cmpuint(TXF_STS_TX_BYTES( + qtest_readb(qts, base_addr + OFFSET_TXF_STS)), =3D= =3D, 0); + g_assert_true(qtest_get_irq(qts, irq)); + stop_transfer(qts, base_addr); + check_stopped(qts, base_addr); + + /* Receiving */ + start_fifo_mode(qts, base_addr); + start_transfer(qts, base_addr); + send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true); + send_byte(qts, base_addr, TMP105_REG_CONFIG); + start_transfer(qts, base_addr); + qtest_writeb(qts, base_addr + OFFSET_FIF_CTS, FIF_CTS_RXF_TXE); + start_recv_fifo(qts, base_addr, 1); + send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true); + g_assert_false(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) & + FIF_CTS_RXF_TXE); + g_assert_true(qtest_readb(qts, base_addr + OFFSET_RXF_STS) & + RXF_STS_RX_THST); + g_assert_cmpuint(RXF_STS_RX_BYTES( + qtest_readb(qts, base_addr + OFFSET_RXF_STS)), =3D= =3D, 1); + send_nack(qts, base_addr); + stop_transfer(qts, base_addr); + check_running(qts, base_addr); + g_assert_cmphex(recv_byte(qts, base_addr), =3D=3D, value); + g_assert_cmpuint(RXF_STS_RX_BYTES( + qtest_readb(qts, base_addr + OFFSET_RXF_STS)), =3D= =3D, 0); + check_stopped(qts, base_addr); + qtest_quit(qts); +} + static void smbus_add_test(const char *name, int index, GTestDataFunc fn) { g_autofree char *full_name =3D g_strdup_printf( @@ -346,6 +488,7 @@ int main(int argc, char **argv) =20 for (i =3D 0; i < ARRAY_SIZE(evb_bus_list); ++i) { add_test(single_mode, evb_bus_list[i]); + add_test(fifo_mode, evb_bus_list[i]); } =20 return g_test_run(); diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index c3bb70ad045..82fe6f965f4 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -25,3 +25,4 @@ npcm7xx_smbus_send_byte(const char *id, uint8_t value, in= t success) "%s send byt npcm7xx_smbus_recv_byte(const char *id, uint8_t value) "%s recv byte: 0x%0= 2x" npcm7xx_smbus_stop(const char *id) "%s stopping" npcm7xx_smbus_nack(const char *id) "%s nacking" +npcm7xx_smbus_recv_fifo(const char *id, uint8_t received, uint8_t expected= ) "%s recv fifo: received %u, expected %u" --=20 2.20.1