From nobody Wed Dec 17 09:12:41 2025 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A0A97C4167B for ; Tue, 5 Dec 2023 15:22:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1442580AbjLEPWe (ORCPT ); Tue, 5 Dec 2023 10:22:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34750 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1442400AbjLEPV6 (ORCPT ); Tue, 5 Dec 2023 10:21:58 -0500 Received: from relay2-d.mail.gandi.net (relay2-d.mail.gandi.net [IPv6:2001:4b98:dc4:8::222]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5948E10C4 for ; Tue, 5 Dec 2023 07:21:42 -0800 (PST) Received: by mail.gandi.net (Postfix) with ESMTPA id 1A23340014; Tue, 5 Dec 2023 15:21:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=bootlin.com; s=gm1; t=1701789701; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mdA0q+ZRkjXrzwvRJclr5agGrJuV6jx3QazcP12Q7NY=; b=nlPdjxWpCw7qkx8kLJjWcWZn5fnH9D/kG6HfIvmmo/ag9spvhbFXNIPVKEFHViph+OgSI2 9HTcdSOnckA+L9dU4LcPBf76fDBeFBkcmfzCl5OcC688Z89g+26jVgEg9UbpuDLF/yVkEn 9X2F2tHOlu+XW2KxMbJr181Ehgzo2cL3Sx5/EK4FFp++VkhtUiNnTApPixqoU3QvS9HiiC JQ4MV3zU+KxHYMwnKwtsw1vk/2sEIp9JdLvnEOsY+/t/DhiNEzy6Y94ehXvEfHMiUC57Qy aBrl03BeZ/BQo+0mpFkxbcKiZSo84G+tvU8P7XfebA6siQYDEf9w4k8h9Jh/fw== From: Herve Codina To: Herve Codina , Qiang Zhao , Li Yang , Jakub Kicinski , Shengjiu Wang , Xiubo Li , Fabio Estevam , Nicolin Chen , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai , Christophe Leroy Cc: Arnd Bergmann , linuxppc-dev@lists.ozlabs.org, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, alsa-devel@alsa-project.org, Thomas Petazzoni Subject: [PATCH v2 15/17] soc: fsl: cpm1: qmc: Handle timeslot entries at channel start() and stop() Date: Tue, 5 Dec 2023 16:21:12 +0100 Message-ID: <20231205152116.122512-16-herve.codina@bootlin.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231205152116.122512-1-herve.codina@bootlin.com> References: <20231205152116.122512-1-herve.codina@bootlin.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-GND-Sasl: herve.codina@bootlin.com Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" In order to support runtime timeslot route changes, enable the channel timeslot entries at channel start() and disable them at channel stop(). Signed-off-by: Herve Codina Reviewed-by: Christophe Leroy --- drivers/soc/fsl/qe/qmc.c | 241 +++++++++++++++++++++++++++++++-------- 1 file changed, 195 insertions(+), 46 deletions(-) diff --git a/drivers/soc/fsl/qe/qmc.c b/drivers/soc/fsl/qe/qmc.c index e651b3bba1ca..bc72c1bc0ec4 100644 --- a/drivers/soc/fsl/qe/qmc.c +++ b/drivers/soc/fsl/qe/qmc.c @@ -177,6 +177,7 @@ struct qmc_chan { struct qmc *qmc; void __iomem *s_param; enum qmc_mode mode; + spinlock_t ts_lock; /* Protect timeslots */ u64 tx_ts_mask_avail; u64 tx_ts_mask; u64 rx_ts_mask_avail; @@ -265,6 +266,7 @@ static void qmc_setbits32(void __iomem *addr, u32 set) int qmc_chan_get_info(struct qmc_chan *chan, struct qmc_chan_info *info) { struct tsa_serial_info tsa_info; + unsigned long flags; int ret; =20 /* Retrieve info from the TSA related serial */ @@ -272,6 +274,8 @@ int qmc_chan_get_info(struct qmc_chan *chan, struct qmc= _chan_info *info) if (ret) return ret; =20 + spin_lock_irqsave(&chan->ts_lock, flags); + info->mode =3D chan->mode; info->rx_fs_rate =3D tsa_info.rx_fs_rate; info->rx_bit_rate =3D tsa_info.rx_bit_rate; @@ -280,6 +284,8 @@ int qmc_chan_get_info(struct qmc_chan *chan, struct qmc= _chan_info *info) info->tx_bit_rate =3D tsa_info.tx_bit_rate; info->nb_rx_ts =3D hweight64(chan->rx_ts_mask); =20 + spin_unlock_irqrestore(&chan->ts_lock, flags); + return 0; } EXPORT_SYMBOL(qmc_chan_get_info); @@ -683,6 +689,40 @@ static int qmc_chan_setup_tsa_32tx(struct qmc_chan *ch= an, const struct tsa_seria return 0; } =20 +static int qmc_chan_setup_tsa_tx(struct qmc_chan *chan, bool enable) +{ + struct tsa_serial_info info; + int ret; + + /* Retrieve info from the TSA related serial */ + ret =3D tsa_serial_get_info(chan->qmc->tsa_serial, &info); + if (ret) + return ret; + + /* Setup entries */ + if (chan->qmc->is_tsa_64rxtx) + return qmc_chan_setup_tsa_64rxtx(chan, &info, enable); + + return qmc_chan_setup_tsa_32tx(chan, &info, enable); +} + +static int qmc_chan_setup_tsa_rx(struct qmc_chan *chan, bool enable) +{ + struct tsa_serial_info info; + int ret; + + /* Retrieve info from the TSA related serial */ + ret =3D tsa_serial_get_info(chan->qmc->tsa_serial, &info); + if (ret) + return ret; + + /* Setup entries */ + if (chan->qmc->is_tsa_64rxtx) + return qmc_chan_setup_tsa_64rxtx(chan, &info, enable); + + return qmc_chan_setup_tsa_32rx(chan, &info, enable); +} + static int qmc_chan_setup_tsa(struct qmc_chan *chan, bool enable) { struct tsa_serial_info info; @@ -719,6 +759,12 @@ static int qmc_chan_stop_rx(struct qmc_chan *chan) =20 spin_lock_irqsave(&chan->rx_lock, flags); =20 + if (chan->is_rx_stopped) { + /* The channel is already stopped -> simply return ok */ + ret =3D 0; + goto end; + } + /* Send STOP RECEIVE command */ ret =3D qmc_chan_command(chan, 0x0); if (ret) { @@ -729,6 +775,15 @@ static int qmc_chan_stop_rx(struct qmc_chan *chan) =20 chan->is_rx_stopped =3D true; =20 + if (!chan->qmc->is_tsa_64rxtx || chan->is_tx_stopped) { + ret =3D qmc_chan_setup_tsa_rx(chan, false); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Disable tsa entries failed (%d)\n", + chan->id, ret); + goto end; + } + } + end: spin_unlock_irqrestore(&chan->rx_lock, flags); return ret; @@ -741,6 +796,12 @@ static int qmc_chan_stop_tx(struct qmc_chan *chan) =20 spin_lock_irqsave(&chan->tx_lock, flags); =20 + if (chan->is_tx_stopped) { + /* The channel is already stopped -> simply return ok */ + ret =3D 0; + goto end; + } + /* Send STOP TRANSMIT command */ ret =3D qmc_chan_command(chan, 0x1); if (ret) { @@ -751,37 +812,114 @@ static int qmc_chan_stop_tx(struct qmc_chan *chan) =20 chan->is_tx_stopped =3D true; =20 + if (!chan->qmc->is_tsa_64rxtx || chan->is_rx_stopped) { + ret =3D qmc_chan_setup_tsa_tx(chan, false); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Disable tsa entries failed (%d)\n", + chan->id, ret); + goto end; + } + } + end: spin_unlock_irqrestore(&chan->tx_lock, flags); return ret; } =20 +static int qmc_chan_start_rx(struct qmc_chan *chan); + int qmc_chan_stop(struct qmc_chan *chan, int direction) { - int ret; + bool is_rx_rollback_needed =3D false; + unsigned long flags; + int ret =3D 0; + + spin_lock_irqsave(&chan->ts_lock, flags); =20 if (direction & QMC_CHAN_READ) { + is_rx_rollback_needed =3D !chan->is_rx_stopped; ret =3D qmc_chan_stop_rx(chan); if (ret) - return ret; + goto end; } =20 if (direction & QMC_CHAN_WRITE) { ret =3D qmc_chan_stop_tx(chan); - if (ret) - return ret; + if (ret) { + /* Restart rx if needed */ + if (is_rx_rollback_needed) + qmc_chan_start_rx(chan); + goto end; + } } =20 - return 0; +end: + spin_unlock_irqrestore(&chan->ts_lock, flags); + return ret; } EXPORT_SYMBOL(qmc_chan_stop); =20 -static void qmc_chan_start_rx(struct qmc_chan *chan) +static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan) +{ + struct tsa_serial_info info; + u16 first_rx, last_tx; + u16 trnsync; + int ret; + + /* Retrieve info from the TSA related serial */ + ret =3D tsa_serial_get_info(chan->qmc->tsa_serial, &info); + if (ret) + return ret; + + /* Find the first Rx TS allocated to the channel */ + first_rx =3D chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0; + + /* Find the last Tx TS allocated to the channel */ + last_tx =3D fls64(chan->tx_ts_mask); + + trnsync =3D 0; + if (info.nb_rx_ts) + trnsync |=3D QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2); + if (info.nb_tx_ts) + trnsync |=3D QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2); + + qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync); + + dev_dbg(qmc->dev, "chan %u: trnsync=3D0x%04x, rx %u/%u 0x%llx, tx %u/%u 0= x%llx\n", + chan->id, trnsync, + first_rx, info.nb_rx_ts, chan->rx_ts_mask, + last_tx, info.nb_tx_ts, chan->tx_ts_mask); + + return 0; +} + +static int qmc_chan_start_rx(struct qmc_chan *chan) { unsigned long flags; + int ret; =20 spin_lock_irqsave(&chan->rx_lock, flags); =20 + if (!chan->is_rx_stopped) { + /* The channel is already started -> simply return ok */ + ret =3D 0; + goto end; + } + + ret =3D qmc_chan_setup_tsa_rx(chan, true); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Enable tsa entries failed (%d)\n", + chan->id, ret); + goto end; + } + + ret =3D qmc_setup_chan_trnsync(chan->qmc, chan); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: setup TRNSYNC failed (%d)\n", + chan->id, ret); + goto end; + } + /* Restart the receiver */ if (chan->mode =3D=3D QMC_TRANSPARENT) qmc_write32(chan->s_param + QMC_SPE_ZDSTATE, 0x18000080); @@ -792,15 +930,38 @@ static void qmc_chan_start_rx(struct qmc_chan *chan) =20 chan->is_rx_stopped =3D false; =20 +end: spin_unlock_irqrestore(&chan->rx_lock, flags); + return ret; } =20 -static void qmc_chan_start_tx(struct qmc_chan *chan) +static int qmc_chan_start_tx(struct qmc_chan *chan) { unsigned long flags; + int ret; =20 spin_lock_irqsave(&chan->tx_lock, flags); =20 + if (!chan->is_tx_stopped) { + /* The channel is already started -> simply return ok */ + ret =3D 0; + goto end; + } + + ret =3D qmc_chan_setup_tsa_tx(chan, true); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: Enable tsa entries failed (%d)\n", + chan->id, ret); + goto end; + } + + ret =3D qmc_setup_chan_trnsync(chan->qmc, chan); + if (ret) { + dev_err(chan->qmc->dev, "chan %u: setup TRNSYNC failed (%d)\n", + chan->id, ret); + goto end; + } + /* * Enable channel transmitter as it could be disabled if * qmc_chan_reset() was called. @@ -812,18 +973,39 @@ static void qmc_chan_start_tx(struct qmc_chan *chan) =20 chan->is_tx_stopped =3D false; =20 +end: spin_unlock_irqrestore(&chan->tx_lock, flags); + return ret; } =20 int qmc_chan_start(struct qmc_chan *chan, int direction) { - if (direction & QMC_CHAN_READ) - qmc_chan_start_rx(chan); + bool is_rx_rollback_needed =3D false; + unsigned long flags; + int ret =3D 0; =20 - if (direction & QMC_CHAN_WRITE) - qmc_chan_start_tx(chan); + spin_lock_irqsave(&chan->ts_lock, flags); =20 - return 0; + if (direction & QMC_CHAN_READ) { + is_rx_rollback_needed =3D chan->is_rx_stopped; + ret =3D qmc_chan_start_rx(chan); + if (ret) + goto end; + } + + if (direction & QMC_CHAN_WRITE) { + ret =3D qmc_chan_start_tx(chan); + if (ret) { + /* Restop rx if needed */ + if (is_rx_rollback_needed) + qmc_chan_stop_rx(chan); + goto end; + } + } + +end: + spin_unlock_irqrestore(&chan->ts_lock, flags); + return ret; } EXPORT_SYMBOL(qmc_chan_start); =20 @@ -992,6 +1174,7 @@ static int qmc_of_parse_chans(struct qmc *qmc, struct = device_node *np) } =20 chan->id =3D chan_id; + spin_lock_init(&chan->ts_lock); spin_lock_init(&chan->rx_lock); spin_lock_init(&chan->tx_lock); =20 @@ -1129,40 +1312,6 @@ static int qmc_init_tsa(struct qmc *qmc) qmc_init_tsa_32rx_32tx(qmc, &info); } =20 -static int qmc_setup_chan_trnsync(struct qmc *qmc, struct qmc_chan *chan) -{ - struct tsa_serial_info info; - u16 first_rx, last_tx; - u16 trnsync; - int ret; - - /* Retrieve info from the TSA related serial */ - ret =3D tsa_serial_get_info(chan->qmc->tsa_serial, &info); - if (ret) - return ret; - - /* Find the first Rx TS allocated to the channel */ - first_rx =3D chan->rx_ts_mask ? __ffs64(chan->rx_ts_mask) + 1 : 0; - - /* Find the last Tx TS allocated to the channel */ - last_tx =3D fls64(chan->tx_ts_mask); - - trnsync =3D 0; - if (info.nb_rx_ts) - trnsync |=3D QMC_SPE_TRNSYNC_RX((first_rx % info.nb_rx_ts) * 2); - if (info.nb_tx_ts) - trnsync |=3D QMC_SPE_TRNSYNC_TX((last_tx % info.nb_tx_ts) * 2); - - qmc_write16(chan->s_param + QMC_SPE_TRNSYNC, trnsync); - - dev_dbg(qmc->dev, "chan %u: trnsync=3D0x%04x, rx %u/%u 0x%llx, tx %u/%u 0= x%llx\n", - chan->id, trnsync, - first_rx, info.nb_rx_ts, chan->rx_ts_mask, - last_tx, info.nb_tx_ts, chan->tx_ts_mask); - - return 0; -} - static int qmc_setup_chan(struct qmc *qmc, struct qmc_chan *chan) { unsigned int i; --=20 2.43.0