From patchwork Fri Apr 28 12:15:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Valentin Caron X-Patchwork-Id: 677857 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 42339C77B60 for ; Fri, 28 Apr 2023 12:17:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345978AbjD1MR5 (ORCPT ); Fri, 28 Apr 2023 08:17:57 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56912 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345984AbjD1MRz (ORCPT ); Fri, 28 Apr 2023 08:17:55 -0400 Received: from mx07-00178001.pphosted.com (mx07-00178001.pphosted.com [185.132.182.106]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 53D5E26BE; Fri, 28 Apr 2023 05:17:34 -0700 (PDT) Received: from pps.filterd (m0288072.ppops.net [127.0.0.1]) by mx07-00178001.pphosted.com (8.17.1.19/8.17.1.19) with ESMTP id 33SBAUGu025079; Fri, 28 Apr 2023 14:17:17 +0200 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=foss.st.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=selector1; bh=BUhV9MaeZLuBC+HDtvlz4ndLWeEyEoqV+IK2po+lKQE=; b=eb47z588iLjb22zKpMGE6cKY8QjtqfnEvflIYzDjogWq0RLuMQg7sLMh6U8Q2kAoP8or UP+WTfalnc//fhRcd3tUdP4SLhlVWccTi2iQUKJiAzV5BXAZZTktrLMMKcKngOlRoUHG tiyfJc6BDAZCk66HuilnDF2fUJG/XZE2k8vKRexQxi6J2qKxZ1wcp1WlOdNXGeN6t9h0 DdtGu6F98fjWuMHOCbxFTlayphfq+jwa/zz6OQGp6S2puNmkWd3TGlunIumoOUCLRr6b gCOMQIyfrNBZQ8uLL4912nMU+vWciqOdBXl55T/Q+3wRdLeg1xPOYx/fawT0dmoq9xZs Kw== Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx07-00178001.pphosted.com (PPS) with ESMTPS id 3q8a80suva-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Fri, 28 Apr 2023 14:17:17 +0200 Received: from euls16034.sgp.st.com (euls16034.sgp.st.com [10.75.44.20]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id 9052910002A; Fri, 28 Apr 2023 14:17:16 +0200 (CEST) Received: from Webmail-eu.st.com (shfdag1node1.st.com [10.75.129.69]) by euls16034.sgp.st.com (STMicroelectronics) with ESMTP id 878C6227EF6; Fri, 28 Apr 2023 14:17:16 +0200 (CEST) Received: from localhost (10.201.20.168) by SHFDAG1NODE1.st.com (10.75.129.69) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 28 Apr 2023 14:17:16 +0200 From: Valentin Caron To: Mark Brown , Rob Herring , Krzysztof Kozlowski , Alexandre Torgue , Alain Volmat CC: , , , , , Valentin Caron Subject: [PATCH 7/7] spi: stm32: add support for stm32h7 SPI slave underrun detection Date: Fri, 28 Apr 2023 14:15:24 +0200 Message-ID: <20230428121524.2125832-8-valentin.caron@foss.st.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230428121524.2125832-1-valentin.caron@foss.st.com> References: <20230428121524.2125832-1-valentin.caron@foss.st.com> MIME-Version: 1.0 X-Originating-IP: [10.201.20.168] X-ClientProxiedBy: SHFCAS1NODE2.st.com (10.75.129.73) To SHFDAG1NODE1.st.com (10.75.129.69) X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.942,Hydra:6.0.573,FMLib:17.11.170.22 definitions=2023-04-28_04,2023-04-27_01,2023-02-09_01 Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org If stm32h7 SPI controller is in slave role and TX FIFO is abnormally empty during transaction, controller is able to automatically send either a pattern, the last transmitted data, or the last received data. Signed-off-by: Valentin Caron Acked-by: Alain Volmat --- drivers/spi/spi-stm32.c | 112 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c index 2db6f93654d7..0063c2f69265 100644 --- a/drivers/spi/spi-stm32.c +++ b/drivers/spi/spi-stm32.c @@ -18,6 +18,7 @@ #include #include #include +#include #define DRIVER_NAME "spi_stm32" @@ -84,6 +85,7 @@ #define STM32H7_SPI_IFCR 0x18 #define STM32H7_SPI_TXDR 0x20 #define STM32H7_SPI_RXDR 0x30 +#define STM32H7_SPI_UDRDR 0x4C #define STM32H7_SPI_I2SCFGR 0x50 /* STM32H7_SPI_CR1 bit fields */ @@ -101,6 +103,14 @@ /* STM32H7_SPI_CFG1 bit fields */ #define STM32H7_SPI_CFG1_DSIZE GENMASK(4, 0) #define STM32H7_SPI_CFG1_FTHLV GENMASK(8, 5) +#define STM32H7_SPI_CFG1_UDRDET GENMASK(12, 11) +#define STM32H7_SPI_CFG1_UDRDET_BEGIN 0 +#define STM32H7_SPI_CFG1_UDRDET_LAST 1 +#define STM32H7_SPI_CFG1_UDRDET_SS 2 +#define STM32H7_SPI_CFG1_UDRCFG GENMASK(10, 9) +#define STM32H7_SPI_CFG1_UDRCFG_PTRN 0 +#define STM32H7_SPI_CFG1_UDRCFG_LAST_R 1 +#define STM32H7_SPI_CFG1_UDRCFG_LAST_T 2 #define STM32H7_SPI_CFG1_RXDMAEN BIT(14) #define STM32H7_SPI_CFG1_TXDMAEN BIT(15) #define STM32H7_SPI_CFG1_MBR GENMASK(30, 28) @@ -126,6 +136,7 @@ #define STM32H7_SPI_IER_DXPIE BIT(2) #define STM32H7_SPI_IER_EOTIE BIT(3) #define STM32H7_SPI_IER_TXTFIE BIT(4) +#define STM32H7_SPI_IER_UDRIE BIT(5) #define STM32H7_SPI_IER_OVRIE BIT(6) #define STM32H7_SPI_IER_MODFIE BIT(9) #define STM32H7_SPI_IER_ALL GENMASK(10, 0) @@ -134,6 +145,7 @@ #define STM32H7_SPI_SR_RXP BIT(0) #define STM32H7_SPI_SR_TXP BIT(1) #define STM32H7_SPI_SR_EOT BIT(3) +#define STM32H7_SPI_SR_UDR BIT(5) #define STM32H7_SPI_SR_OVR BIT(6) #define STM32H7_SPI_SR_MODF BIT(9) #define STM32H7_SPI_SR_SUSP BIT(11) @@ -239,6 +251,8 @@ struct stm32_spi; * @baud_rate_div_max: maximum baud rate divisor * @has_fifo: boolean to know if fifo is used for driver * @flags: compatible specific SPI controller flags used at registration time + * @set_slave_udr: routine to configure registers to desired slave underrun + * behavior (if driver has this functionality) */ struct stm32_spi_cfg { const struct stm32_spi_regspec *regs; @@ -260,6 +274,7 @@ struct stm32_spi_cfg { unsigned int baud_rate_div_max; bool has_fifo; u16 flags; + void (*set_slave_udr)(struct stm32_spi *spi); }; /** @@ -288,6 +303,8 @@ struct stm32_spi_cfg { * @dma_rx: dma channel for RX transfer * @phys_addr: SPI registers physical base address * @slave_mode: the controller is configured as SPI slave + * @slave_udr_mode: slave underrun behavior + * @slave_udr_pattern: slave underrun pattern parameter */ struct stm32_spi { struct device *dev; @@ -317,6 +334,8 @@ struct stm32_spi { dma_addr_t phys_addr; bool slave_mode; + u32 slave_udr_mode; + u32 slave_udr_pattern; }; static const struct stm32_spi_regspec stm32f4_spi_regspec = { @@ -921,6 +940,14 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id) end = true; } + if (sr & STM32H7_SPI_SR_UDR) { + static DEFINE_RATELIMIT_STATE(rs, + DEFAULT_RATELIMIT_INTERVAL * 10, + 1); + if (__ratelimit(&rs)) + dev_dbg_ratelimited(spi->dev, "Underrun detected\n"); + } + if (sr & STM32H7_SPI_SR_EOT) { if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0))) stm32h7_spi_read_rxfifo(spi); @@ -1178,6 +1205,9 @@ static int stm32h7_spi_transfer_one_irq(struct stm32_spi *spi) if (spi->tx_buf) stm32h7_spi_write_txfifo(spi); + if (STM32_SPI_SLAVE_MODE(spi) && spi->slave_udr_mode != SPI_NO_ACTION) + ier |= STM32H7_SPI_IER_UDRIE; + if (STM32_SPI_MASTER_MODE(spi)) stm32_spi_set_bits(spi, STM32H7_SPI_CR1, STM32H7_SPI_CR1_CSTART); @@ -1222,6 +1252,9 @@ static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi) if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX) ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE; + if (STM32_SPI_SLAVE_MODE(spi) && spi->slave_udr_mode != SPI_NO_ACTION) + ier |= STM32H7_SPI_IER_UDRIE; + stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier); stm32_spi_enable(spi); @@ -1530,6 +1563,53 @@ static int stm32h7_spi_number_of_data(struct stm32_spi *spi, u32 nb_words) return 0; } +/** + * stm32h7_spi_set_slave_udr - configure slave underrun detection and reaction + * @spi: pointer to the spi controller data structure + */ +static void stm32h7_spi_set_slave_udr(struct stm32_spi *spi) +{ + u32 max_udr_ptrn, udr_ptrn, cfg1_setb = 0; + + if (spi->slave_udr_mode == SPI_NO_ACTION) + return; + + switch (spi->slave_udr_mode) { + case SPI_SEND_PATTERN: + max_udr_ptrn = (1 << spi->cur_bpw) - 1; + if (spi->slave_udr_pattern > max_udr_ptrn) { + udr_ptrn = spi->slave_udr_pattern & max_udr_ptrn; + dev_warn(spi->dev, + "force slave underrun pattern to data width (> 0x%x, set 0x%x)\n", + max_udr_ptrn, udr_ptrn); + } else { + udr_ptrn = spi->slave_udr_pattern; + dev_dbg(spi->dev, "spi slave underrun: send pattern (0x%x)\n", + spi->slave_udr_pattern); + } + writel_relaxed(udr_ptrn, spi->base + STM32H7_SPI_UDRDR); + cfg1_setb |= FIELD_PREP(STM32H7_SPI_CFG1_UDRCFG, STM32H7_SPI_CFG1_UDRCFG_PTRN); + break; + case SPI_REPEAT_LAST_RECEIVED_DATA: + cfg1_setb |= FIELD_PREP(STM32H7_SPI_CFG1_UDRCFG, STM32H7_SPI_CFG1_UDRCFG_LAST_R); + dev_dbg(spi->dev, "spi slave underrun: repeat received data\n"); + break; + case SPI_REPEAT_LAST_TRANSMITTED_DATA: + cfg1_setb |= FIELD_PREP(STM32H7_SPI_CFG1_UDRCFG, STM32H7_SPI_CFG1_UDRCFG_LAST_T); + dev_dbg(spi->dev, "spi slave underrun: repeat transmitted data\n"); + break; + default: + dev_warn(spi->dev, "slave underrun detection disabled\n"); + spi->slave_udr_mode = SPI_NO_ACTION; + } + + if (spi->slave_udr_mode != SPI_NO_ACTION) { + cfg1_setb |= FIELD_PREP(STM32H7_SPI_CFG1_UDRDET, STM32H7_SPI_CFG1_UDRDET_LAST); + + stm32_spi_set_bits(spi, STM32H7_SPI_CFG1, cfg1_setb); + } +} + /** * stm32_spi_transfer_one_setup - common setup to transfer a single * spi_transfer either using DMA or @@ -1591,6 +1671,9 @@ static int stm32_spi_transfer_one_setup(struct stm32_spi *spi, goto out; } + if (STM32_SPI_SLAVE_MODE(spi) && spi->cfg->set_slave_udr) + spi->cfg->set_slave_udr(spi); + dev_dbg(spi->dev, "transfer communication mode set to %d\n", spi->cur_comm); dev_dbg(spi->dev, @@ -1774,6 +1857,7 @@ static const struct stm32_spi_cfg stm32h7_spi_cfg = { .baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN, .baud_rate_div_max = STM32H7_SPI_MBR_DIV_MAX, .has_fifo = true, + .set_slave_udr = stm32h7_spi_set_slave_udr, }; static const struct of_device_id stm32_spi_of_match[] = { @@ -1789,6 +1873,31 @@ static int stm32h7_spi_slave_abort(struct spi_controller *ctrl) return 0; } +static void stm32h7_spi_parse_slave_config(struct stm32_spi *spi, struct device_node *np) +{ + u32 udr_configs[2] = { 0, 0 }; + int count, ret; + + count = of_property_count_elems_of_size(np, "st,spi-slave-underrun", sizeof(u32)); + if (count <= 0) { + if (count != -EINVAL) + dev_err(spi->dev, "Invalid st,spi-slave-underrun property\n"); + return; + } + + ret = of_property_read_u32_array(np, "st,spi-slave-underrun", udr_configs, count); + if (ret) + return; + + spi->slave_udr_mode = udr_configs[0]; + if (spi->slave_udr_mode == SPI_SEND_PATTERN) { + if (count > 1) + spi->slave_udr_pattern = udr_configs[1]; + else + dev_warn(spi->dev, "Missing pattern in st,spi-slave-underrun property\n"); + } +} + static int stm32_spi_probe(struct platform_device *pdev) { struct spi_controller *ctrl; @@ -1842,6 +1951,9 @@ static int stm32_spi_probe(struct platform_device *pdev) return ret; } + if (STM32_SPI_SLAVE_MODE(spi)) + stm32h7_spi_parse_slave_config(spi, np); + spi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(spi->clk)) { ret = PTR_ERR(spi->clk);