From patchwork Mon Jul 29 13:05:43 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marc Kleine-Budde X-Patchwork-Id: 13744920 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 83FC3C3DA61 for ; Mon, 29 Jul 2024 13:12:06 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References :Message-Id:Content-Transfer-Encoding:Content-Type:MIME-Version:Subject:Date: From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=zc6/0C5DPZyWbaaWCAfNZ7EgKFdy7N7j4ACreFpOJsM=; b=zW3I3JOs6wqTcOTz3eS3+dhOIs VphZ4+/0+lN5GrCtY27YTXkhx9K+tQCiegVMFLbsJFrwXj19zivKDTzWXm/621No2dvAwsd7TNrZR AiV+UBS+4rbewsqC83Lct5T8h1+nemux/p4CUL1pn/icHmMaWpXjusfUiEZNvd1KAXJQ/3JsJ9dPe zBi7dK3ea+WDO3youU6WYBAjKUA9qIMifB3OUDiUYY6+yVmglblSRC4tXG+e8ELBvdI0mM6FvXp3a dD+WsDLwKsrGhaiUo4lJD9CpG8Org2dlGklAXKY2D4OH55IfrvssqWrOK46tk/vDuU5BG0mV8CoX5 36ctMOlA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1sYQAQ-0000000BNCU-0koo; Mon, 29 Jul 2024 13:11:58 +0000 Received: from metis.whiteo.stw.pengutronix.de ([2a0a:edc0:2:b01:1d::104]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1sYQ5l-0000000BLJc-3iJ8 for linux-arm-kernel@lists.infradead.org; Mon, 29 Jul 2024 13:07:11 +0000 Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1sYQ5k-0000Yb-MF for linux-arm-kernel@lists.infradead.org; Mon, 29 Jul 2024 15:07:08 +0200 Received: from [2a0a:edc0:0:b01:1d::7b] (helo=bjornoya.blackshift.org) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1sYQ5j-0033BK-Rh for linux-arm-kernel@lists.infradead.org; Mon, 29 Jul 2024 15:07:07 +0200 Received: from dspam.blackshift.org (localhost [127.0.0.1]) by bjornoya.blackshift.org (Postfix) with SMTP id 86762310E43 for ; Mon, 29 Jul 2024 13:07:07 +0000 (UTC) Received: from hardanger.blackshift.org (unknown [172.20.34.65]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by bjornoya.blackshift.org (Postfix) with ESMTPS id 098A6310DE7; Mon, 29 Jul 2024 13:06:59 +0000 (UTC) Received: from [172.20.34.65] (localhost [::1]) by hardanger.blackshift.org (OpenSMTPD) with ESMTP id 3e5b42a6; Mon, 29 Jul 2024 13:06:31 +0000 (UTC) From: Marc Kleine-Budde Date: Mon, 29 Jul 2024 15:05:43 +0200 Subject: [PATCH can-next 12/21] can: rockchip_canfd: add TX PATH MIME-Version: 1.0 Message-Id: <20240729-rockchip-canfd-v1-12-fa1250fd6be3@pengutronix.de> References: <20240729-rockchip-canfd-v1-0-fa1250fd6be3@pengutronix.de> In-Reply-To: <20240729-rockchip-canfd-v1-0-fa1250fd6be3@pengutronix.de> To: kernel@pengutronix.de, Vincent Mailhol , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heiko Stuebner , Philipp Zabel , Elaine Zhang , David Jander Cc: linux-can@vger.kernel.org, netdev@vger.kernel.org, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, Marc Kleine-Budde X-Mailer: b4 0.15-dev-37811 X-Developer-Signature: v=1; a=openpgp-sha256; l=9344; i=mkl@pengutronix.de; h=from:subject:message-id; bh=BTDI17d/x6zfl95KLdW0t8KzpPaPurYCbbYkd5icNSA=; b=owEBbQGS/pANAwAKASg4oj56LbxvAcsmYgBmp5PG5zkhSfcYzHZYz83HLgw0D/8l1OJuXHnC9 tCTJ2JxTmCJATMEAAEKAB0WIQRQQLqG4LYE3Sm8Pl8oOKI+ei28bwUCZqeTxgAKCRAoOKI+ei28 bya2B/49wrPGrG5YRKkd/PUjHm6SsIPzMpeqPtiNGQRWJL25iSqCtG/EpjkYNIfnAdiEQaOlEN3 rM4Vz73HsbGNNKuEBpyQpZ/7q9ZthfskSwcEfRFvDlk01I28Q/hQENKtaRV7wMxch25Wsh8oOuy +wHweHpanCD8/zNuUsrsbla2SILgAcCaktoluz7n8DgBBTGaorBAoHR/3aWzFDimeZlf9bm40J8 eFXoLkrSiZsEu3t/QcwkjAoScMgoo17ePNG7HtRRKfqsh5GbRXy3PiCCzrMEMCXZwt45Osz/eRK JBqurLXHeG+h/wNZVTVoSvpJlDZ8uhmYy/r9UnpckRaH7aED X-Developer-Key: i=mkl@pengutronix.de; a=openpgp; fpr=C1400BA0B3989E6FBC7D5B5C2B5EE211C58AEA54 X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: mkl@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-arm-kernel@lists.infradead.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240729_060710_124304_D00E1019 X-CRM114-Status: GOOD ( 23.91 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org The IP core has a TX event FIFO. In other IP cores, this type of FIFO normally contains the event that a CAN frame has been successfully sent. However, the IP core on the rk3568v2 the FIFO also holds events of unsuccessful transmission attempts. It turned out that the best way to work around this problem is to set the IP core to self-receive mode (RXSTX), filter out the self-received frames and insert them into the complete TX path. Signed-off-by: Marc Kleine-Budde --- drivers/net/can/rockchip/rockchip_canfd-core.c | 6 +- drivers/net/can/rockchip/rockchip_canfd-rx.c | 54 +++++++++++++++ drivers/net/can/rockchip/rockchip_canfd-tx.c | 96 ++++++++++++++++++++++++++ drivers/net/can/rockchip/rockchip_canfd.h | 26 +++++++ 4 files changed, 181 insertions(+), 1 deletion(-) diff --git a/drivers/net/can/rockchip/rockchip_canfd-core.c b/drivers/net/can/rockchip/rockchip_canfd-core.c index 02291bd77deb..03ad7936034b 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-core.c +++ b/drivers/net/can/rockchip/rockchip_canfd-core.c @@ -232,6 +232,7 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv) * - CAN_FD: enable CAN-FD * - AUTO_RETX_MODE: auto retransmission on TX error * - COVER_MODE: RX-FIFO overwrite mode, do not send OVERLOAD frames + * - RXSTX_MODE: Receive Self Transmit data mode * - WORK_MODE: transition from reset to working mode */ reg = rkcanfd_read(priv, RKCANFD_REG_MODE); @@ -239,17 +240,20 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv) RKCANFD_REG_MODE_CAN_FD_MODE_ENABLE | RKCANFD_REG_MODE_AUTO_RETX_MODE | RKCANFD_REG_MODE_COVER_MODE | + RKCANFD_REG_MODE_RXSTX_MODE | RKCANFD_REG_MODE_WORK_MODE; /* mask, i.e. ignore: * - TIMESTAMP_COUNTER_OVERFLOW_INT - timestamp counter overflow interrupt * - TX_ARBIT_FAIL_INT - TX arbitration fail interrupt * - OVERLOAD_INT - CAN bus overload interrupt + * - TX_FINISH_INT - Transmit finish interrupt */ priv->reg_int_mask_default = RKCANFD_REG_INT_TIMESTAMP_COUNTER_OVERFLOW_INT | RKCANFD_REG_INT_TX_ARBIT_FAIL_INT | - RKCANFD_REG_INT_OVERLOAD_INT; + RKCANFD_REG_INT_OVERLOAD_INT | + RKCANFD_REG_INT_TX_FINISH_INT; rkcanfd_chip_fifo_setup(priv); rkcanfd_timestamp_init(priv); diff --git a/drivers/net/can/rockchip/rockchip_canfd-rx.c b/drivers/net/can/rockchip/rockchip_canfd-rx.c index df5280375ca9..af90c66de60f 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-rx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-rx.c @@ -4,6 +4,8 @@ // Marc Kleine-Budde // +#include + #include "rockchip_canfd.h" static bool rkcanfd_can_frame_header_equal(const struct canfd_frame *const cfd1, @@ -89,6 +91,48 @@ rkcanfd_fifo_header_to_cfd_header(const struct rkcanfd_priv *priv, return len + cfd->len; } +static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv, + const struct canfd_frame *cfd_rx, const u32 ts, + bool *tx_done) +{ + const struct canfd_frame *cfd_nominal; + const struct sk_buff *skb; + unsigned int tx_tail; + + tx_tail = rkcanfd_get_tx_tail(priv); + skb = priv->can.echo_skb[tx_tail]; + if (!skb) { + netdev_err(priv->ndev, + "%s: echo_skb[%u]=NULL tx_head=0x%08x tx_tail=0x%08x\n", + __func__, tx_tail, + priv->tx_head, priv->tx_tail); + + return -ENOMSG; + } + cfd_nominal = (struct canfd_frame *)skb->data; + + /* We RX'ed a frame identical to our pending TX frame. */ + if (rkcanfd_can_frame_header_equal(cfd_rx, cfd_nominal, + cfd_rx->flags & CANFD_FDF) && + rkcanfd_can_frame_data_equal(cfd_rx, cfd_nominal, + cfd_rx->flags & CANFD_FDF)) { + unsigned int frame_len; + + rkcanfd_handle_tx_done_one(priv, ts, &frame_len); + + WRITE_ONCE(priv->tx_tail, priv->tx_tail + 1); + netif_subqueue_completed_wake(priv->ndev, 0, 1, frame_len, + rkcanfd_get_tx_free(priv), + RKCANFD_TX_START_THRESHOLD); + + *tx_done = true; + + return 0; + } + + return 0; +} + static inline bool rkcanfd_fifo_header_empty(const struct rkcanfd_fifo_header *header) { @@ -131,6 +175,16 @@ static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv) return 0; } + if (rkcanfd_get_tx_pending(priv)) { + bool tx_done = false; + + err = rkcanfd_rxstx_filter(priv, cfd, header->ts, &tx_done); + if (err) + return err; + if (tx_done) + return 0; + } + if (header->frameinfo & RKCANFD_REG_FD_FRAMEINFO_FDF) skb = alloc_canfd_skb(priv->ndev, &skb_cfd); else diff --git a/drivers/net/can/rockchip/rockchip_canfd-tx.c b/drivers/net/can/rockchip/rockchip_canfd-tx.c index 89c65db3b2dc..85daf7b7be8b 100644 --- a/drivers/net/can/rockchip/rockchip_canfd-tx.c +++ b/drivers/net/can/rockchip/rockchip_canfd-tx.c @@ -4,9 +4,105 @@ // Marc Kleine-Budde // +#include + #include "rockchip_canfd.h" +static void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv, + const u32 reg_cmd) +{ + rkcanfd_write(priv, RKCANFD_REG_CMD, reg_cmd); +} + int rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev) { + struct rkcanfd_priv *priv = netdev_priv(ndev); + u32 reg_frameinfo, reg_id, reg_cmd; + unsigned int tx_head, frame_len; + const struct canfd_frame *cfd; + int err; + u8 i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (!netif_subqueue_maybe_stop(priv->ndev, 0, + rkcanfd_get_tx_free(priv), + RKCANFD_TX_STOP_THRESHOLD, + RKCANFD_TX_START_THRESHOLD)) { + if (net_ratelimit()) + netdev_info(priv->ndev, + "Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, tx_pending=%d)\n", + priv->tx_head, priv->tx_tail, + rkcanfd_get_tx_pending(priv)); + + return NETDEV_TX_BUSY; + } + + cfd = (struct canfd_frame *)skb->data; + + if (cfd->can_id & CAN_EFF_FLAG) { + reg_frameinfo = RKCANFD_REG_FD_FRAMEINFO_FRAME_FORMAT; + reg_id = FIELD_PREP(RKCANFD_REG_FD_ID_EFF, cfd->can_id); + } else { + reg_frameinfo = 0; + reg_id = FIELD_PREP(RKCANFD_REG_FD_ID_SFF, cfd->can_id); + } + + if (cfd->can_id & CAN_RTR_FLAG) + reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_RTR; + + if (can_is_canfd_skb(skb)) { + reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_FDF; + + if (cfd->flags & CANFD_BRS) + reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_BRS; + + reg_frameinfo |= FIELD_PREP(RKCANFD_REG_FD_FRAMEINFO_DATA_LENGTH, + can_fd_len2dlc(cfd->len)); + } else { + reg_frameinfo |= FIELD_PREP(RKCANFD_REG_FD_FRAMEINFO_DATA_LENGTH, + cfd->len); + } + + tx_head = rkcanfd_get_tx_head(priv); + reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head); + + rkcanfd_write(priv, RKCANFD_REG_FD_TXFRAMEINFO, reg_frameinfo); + rkcanfd_write(priv, RKCANFD_REG_FD_TXID, reg_id); + for (i = 0; i < cfd->len; i += 4) + rkcanfd_write(priv, RKCANFD_REG_FD_TXDATA0 + i, + *(u32 *)(cfd->data + i)); + + frame_len = can_skb_get_frame_len(skb); + err = can_put_echo_skb(skb, ndev, tx_head, frame_len); + if (!err) + netdev_sent_queue(priv->ndev, frame_len); + + WRITE_ONCE(priv->tx_head, priv->tx_head + 1); + + rkcanfd_start_xmit_write_cmd(priv, reg_cmd); + + netif_subqueue_maybe_stop(priv->ndev, 0, + rkcanfd_get_tx_free(priv), + RKCANFD_TX_STOP_THRESHOLD, + RKCANFD_TX_START_THRESHOLD); + return NETDEV_TX_OK; } + +void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts, + unsigned int *frame_len_p) +{ + struct net_device_stats *stats = &priv->ndev->stats; + unsigned int tx_tail; + struct sk_buff *skb; + + tx_tail = rkcanfd_get_tx_tail(priv); + skb = priv->can.echo_skb[tx_tail]; + stats->tx_bytes += + can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload, + tx_tail, ts, + frame_len_p); + stats->tx_packets++; +} diff --git a/drivers/net/can/rockchip/rockchip_canfd.h b/drivers/net/can/rockchip/rockchip_canfd.h index 09369ea62797..ddc9cba1a095 100644 --- a/drivers/net/can/rockchip/rockchip_canfd.h +++ b/drivers/net/can/rockchip/rockchip_canfd.h @@ -470,10 +470,36 @@ rkcanfd_get_timestamp(const struct rkcanfd_priv *priv) return rkcanfd_read(priv, RKCANFD_REG_TIMESTAMP); } +static inline unsigned int +rkcanfd_get_tx_head(const struct rkcanfd_priv *priv) +{ + return READ_ONCE(priv->tx_head) & (RKCANFD_TXFIFO_DEPTH - 1); +} + +static inline unsigned int +rkcanfd_get_tx_tail(const struct rkcanfd_priv *priv) +{ + return READ_ONCE(priv->tx_tail) & (RKCANFD_TXFIFO_DEPTH - 1); +} + +static inline unsigned int +rkcanfd_get_tx_pending(const struct rkcanfd_priv *priv) +{ + return READ_ONCE(priv->tx_head) - READ_ONCE(priv->tx_tail); +} + +static inline unsigned int +rkcanfd_get_tx_free(const struct rkcanfd_priv *priv) +{ + return RKCANFD_TXFIFO_DEPTH - rkcanfd_get_tx_pending(priv); +} + int rkcanfd_handle_rx_int(struct rkcanfd_priv *priv); void rkcanfd_timestamp_init(struct rkcanfd_priv *priv); int rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts, + unsigned int *frame_len_p); #endif