From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2D6ED3C3451; Tue, 26 May 2026 21:52:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779832340; cv=none; b=dxwApRefs1wgpK0YWa9l2H/8Un/8i8rpuonxUtFdNBdSS1dAhjq1lTacrjumP6dt4enKBl0olnBNI94taXZStje9G9xdpejlYBFTgvXf5AljV0EYKaplZxZmelLRMINJ3Flvy00hTyOL7eSvukh0Qx4P7cl6sf2YM3ft2a2hyHo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779832340; c=relaxed/simple; bh=xwlNj4vnY4PMilpPWPjQt3BWMmoV1vdfGS56+EF0V7A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=NoRdQi/OY642f44lEAm08Bj5LrBZ7QtYXQwR30lI/KDaXiij2mgrs7GUlj87Z/iRF21ESynzhDiCs6Qm/k6AJC+nrh2kYeGKy/SA3EOJHhgmUErIkYvjfgo+Byr4/gK6ldBjI9z1H24UkZ8WMZ4GNyLF14wCqivhT+6kGwQd0Tw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LlJkpjRG; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="LlJkpjRG" Received: by smtp.kernel.org (Postfix) with ESMTPS id BFFD3C2BCC6; Tue, 26 May 2026 21:52:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1779832339; bh=xwlNj4vnY4PMilpPWPjQt3BWMmoV1vdfGS56+EF0V7A=; h=From:Date:Subject:References:In-Reply-To:To:Cc:Reply-To:From; b=LlJkpjRGyQG42KfCw0/3lYsCw5zUHXdGXgzrkXr/5v71BdhF2ouSrxg1bqY45H+JP n/+7npwtCajB7Up1KLOOTwBzjej/8KY+vOSrlrJXDmEhzlk10DgvT9BOazpNlcoCJQ Rqd9d/NRl1Doa19lij4Ls4v8rnXTFwMsAYb5/Vt4pNElSAjOQcp9ND04SVme/unfoc 8AbHfxtka/qa+7fXvl5V5bewPNYwuEzaRhoiSkrLb3BvhOKhkbh40ieSaFtJ4XbbBW v3Iu9ZqrXvURbzX01T+qwByd5E/wDLYbFqWAI/8K9g6laOvdAesqfxg9q0E8JJGBTA 1oDphWaeYiBXg== Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id AC417CD6E40; Tue, 26 May 2026 21:52:19 +0000 (UTC) From: Ciprian Regus via B4 Relay Date: Wed, 27 May 2026 00:51:47 +0300 Subject: [PATCH net-next v2 02/10] net: ethernet: oa_tc6: Handle the OA TC6 SPI protected mode Precedence: bulk X-Mailing-List: linux-doc@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20260527-adin1140-driver-v2-2-37e5c8d4e0a0@analog.com> References: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com> In-Reply-To: <20260527-adin1140-driver-v2-0-37e5c8d4e0a0@analog.com> To: Parthiban Veerasooran , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Simon Horman , Jonathan Corbet , Shuah Khan , Andrew Lunn , Heiner Kallweit , Russell King , Rob Herring , Krzysztof Kozlowski , Conor Dooley Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, devicetree@vger.kernel.org, Ciprian Regus X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1779832338; l=8718; i=ciprian.regus@analog.com; s=20260330; h=from:subject:message-id; bh=IW5iHGuR2REc3ASn2CJYH948sOEAxihITnt2zuS2lDc=; b=GZ9SjxpvVEYWTtGJIKfEZHoH1r/uuoNmOA8R7MvvjSlU7NJozXJKIAv6SDLq4gDDRmTTpyML1 QgTf2MaWGx6BcrZSzGRZ4Aiqv4Gh+SdZpl2eeWVMotOoBPjY2KMBYTx X-Developer-Key: i=ciprian.regus@analog.com; a=ed25519; pk=8WoNhI0kQcQUl8YqJO5ZevROYk9HP8lOIeIgIYgjfbc= X-Endpoint-Received: by B4 Relay for ciprian.regus@analog.com/20260330 with auth_id=703 X-Original-From: Ciprian Regus Reply-To: ciprian.regus@analog.com From: Ciprian Regus Implement the OA TC6 standard defined protected mode for control (register access) transactions. In addition to the current register access formats the oa_tc6 driver handles, 1's complement values of the data field are included (by both the host and the MACPHY) in the SPI transfer frames. This feature acts as an integrity check. Control write transactions look like this: |<- 32 bits ->|<--- data_size --->|<- 32 bits ->| MOSI: | ctrl header | reg write data | ignored | MISO: | (discard) | echoed ctrl hdr | echoed data | data_size (LEN = number of registers to read in a sequence): Unprotected: 32 x (LEN + 1) bits Protected: 2 x 32 x (LEN + 1) bits Control read transaction: |<- 32 bits ->|<--- 32 bits --> |<- data_size ->| MOSI: | ctrl header | ignored ... | MISO: | (discard) | echoed ctrl hdr | reg read data | data_size (LEN = number of registers to read in a sequence): Unprotected: 32 x (LEN + 1) bits Protected: 2 x 32 x (LEN + 1) bits Register data format ("reg write data" and "reg read data"): Unprotected: | W1 (normal) | W2 (normal) | ... | Wx (normal) | Protected: | W1 (normal) | W1 (complement) | ... | Wx (normal) | Wx (complement)| The protected mode state can be read from the bit 5 of CONFIG0 (0x4) register, and this setting is usually only configured during the MACPHY's reset (depending on the device it can be done by setting the state of a pin). We can read the protected mode configuration before any other register access and since the SPI transfer is initially sized for an unprotected read, the MACPHY's complement words are never clocked out and no checking is required. The data transactions (Ethernet frames) remain unchanged. Signed-off-by: Ciprian Regus --- v2 changelog: - Updated OA_TC6_CTRL_SPI_BUF_SIZE to always alloc the control transaction buffer size required by the protected mode, instead of calling krealloc if the PROTE bit is set. - Formatting to fit the 80 character column limit. --- drivers/net/ethernet/oa_tc6.c | 93 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 91a906a7918a..baba5aad84df 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -24,6 +24,7 @@ #define OA_TC6_REG_CONFIG0 0x0004 #define CONFIG0_SYNC BIT(15) #define CONFIG0_ZARFE_ENABLE BIT(12) +#define CONFIG0_PROTE BIT(5) /* Status Register #0 */ #define OA_TC6_REG_STATUS0 0x0008 @@ -87,14 +88,17 @@ #define OA_TC6_PHY_C45_AUTO_NEG_MMS5 5 /* MMD 7 */ #define OA_TC6_PHY_C45_POWER_UNIT_MMS6 6 /* MMD 13 */ +#define OA_TC6_CTRL_PROT_REPLY_SIZE 4 #define OA_TC6_CTRL_HEADER_SIZE 4 #define OA_TC6_CTRL_REG_VALUE_SIZE 4 #define OA_TC6_CTRL_IGNORED_SIZE 4 #define OA_TC6_CTRL_MAX_REGISTERS 128 -#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ - (OA_TC6_CTRL_MAX_REGISTERS *\ - OA_TC6_CTRL_REG_VALUE_SIZE) +\ - OA_TC6_CTRL_IGNORED_SIZE) +#define OA_TC6_CTRL_SPI_BUF_SIZE (OA_TC6_CTRL_HEADER_SIZE +\ + (OA_TC6_CTRL_MAX_REGISTERS *\ + (OA_TC6_CTRL_REG_VALUE_SIZE +\ + OA_TC6_CTRL_PROT_REPLY_SIZE)) +\ + OA_TC6_CTRL_IGNORED_SIZE) + #define OA_TC6_CHUNK_PAYLOAD_SIZE 64 #define OA_TC6_DATA_HEADER_SIZE 4 #define OA_TC6_CHUNK_SIZE (OA_TC6_DATA_HEADER_SIZE +\ @@ -129,6 +133,7 @@ struct oa_tc6 { u8 rx_chunks_available; bool rx_buf_overflow; bool int_flag; + bool prot_ctrl; }; enum oa_tc6_header_type { @@ -212,25 +217,36 @@ static void oa_tc6_update_ctrl_write_data(struct oa_tc6 *tc6, u32 value[], { __be32 *tx_buf = tc6->spi_ctrl_tx_buf + OA_TC6_CTRL_HEADER_SIZE; - for (int i = 0; i < length; i++) + for (int i = 0; i < length; i++) { *tx_buf++ = cpu_to_be32(value[i]); + if (tc6->prot_ctrl) + *tx_buf++ = cpu_to_be32(~value[i]); + } } -static u16 oa_tc6_calculate_ctrl_buf_size(u8 length) +static u16 oa_tc6_calculate_ctrl_buf_size(u8 length, bool ctrl_prot) { + u32 reply_size = OA_TC6_CTRL_REG_VALUE_SIZE; + + if (ctrl_prot) + reply_size += OA_TC6_CTRL_PROT_REPLY_SIZE; + /* Control command consists 4 bytes header + 4 bytes register value for - * each register + 4 bytes ignored value. + * each register (+ 4 bytes for the register value complement in case + * protected mode is used) + 4 bytes ignored value. */ - return OA_TC6_CTRL_HEADER_SIZE + OA_TC6_CTRL_REG_VALUE_SIZE * length + + return OA_TC6_CTRL_HEADER_SIZE + reply_size * length + OA_TC6_CTRL_IGNORED_SIZE; } static void oa_tc6_prepare_ctrl_spi_buf(struct oa_tc6 *tc6, u32 address, u32 value[], u8 length, - enum oa_tc6_register_op reg_op) + enum oa_tc6_register_op reg_op, + u16 buf_size) { __be32 *tx_buf = tc6->spi_ctrl_tx_buf; + memset(tx_buf, 0, buf_size); *tx_buf = oa_tc6_prepare_ctrl_header(address, length, reg_op); if (reg_op == OA_TC6_CTRL_REG_WRITE) @@ -253,10 +269,12 @@ static int oa_tc6_check_ctrl_write_reply(struct oa_tc6 *tc6, u8 size) return 0; } -static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size) +static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 length) { - u32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE; - u32 *tx_buf = tc6->spi_ctrl_tx_buf; + __be32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE; + __be32 *tx_buf = tc6->spi_ctrl_tx_buf; + u32 complement; + u32 reply; /* The echoed control read header must match with the one that was * transmitted. @@ -264,6 +282,20 @@ static int oa_tc6_check_ctrl_read_reply(struct oa_tc6 *tc6, u8 size) if (*tx_buf != *rx_buf) return -EPROTO; + if (tc6->prot_ctrl) { + /* Skip past the echoed header to the value/complement pairs */ + rx_buf += 1; + for (int i = 0; i < length; i++) { + reply = be32_to_cpu(rx_buf[0]); + complement = be32_to_cpu(rx_buf[1]); + + if (complement != ~reply) + return -EPROTO; + + rx_buf += 2; + } + } + return 0; } @@ -273,8 +305,13 @@ static void oa_tc6_copy_ctrl_read_data(struct oa_tc6 *tc6, u32 value[], __be32 *rx_buf = tc6->spi_ctrl_rx_buf + OA_TC6_CTRL_IGNORED_SIZE + OA_TC6_CTRL_HEADER_SIZE; - for (int i = 0; i < length; i++) + for (int i = 0; i < length; i++) { value[i] = be32_to_cpu(*rx_buf++); + + /* skip complement word */ + if (tc6->prot_ctrl) + rx_buf++; + } } static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[], @@ -283,10 +320,10 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[], u16 size; int ret; - /* Prepare control command and copy to SPI control buffer */ - oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op); + size = oa_tc6_calculate_ctrl_buf_size(length, tc6->prot_ctrl); - size = oa_tc6_calculate_ctrl_buf_size(length); + /* Prepare control command and copy to SPI control buffer */ + oa_tc6_prepare_ctrl_spi_buf(tc6, address, value, length, reg_op, size); /* Perform SPI transfer */ ret = oa_tc6_spi_transfer(tc6, OA_TC6_CTRL_HEADER, size); @@ -301,7 +338,7 @@ static int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 address, u32 value[], return oa_tc6_check_ctrl_write_reply(tc6, size); /* Check echoed/received control read command reply for errors */ - ret = oa_tc6_check_ctrl_read_reply(tc6, size); + ret = oa_tc6_check_ctrl_read_reply(tc6, length); if (ret) return ret; @@ -1224,6 +1261,20 @@ netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb) } EXPORT_SYMBOL_GPL(oa_tc6_start_xmit); +static int oa_tc6_check_ctrl_protection(struct oa_tc6 *tc6) +{ + u32 regval; + int ret; + + ret = oa_tc6_read_register(tc6, OA_TC6_REG_CONFIG0, ®val); + if (ret) + return ret; + + tc6->prot_ctrl = FIELD_GET(CONFIG0_PROTE, regval); + + return 0; +} + /** * oa_tc6_init - allocates and initializes oa_tc6 structure. * @spi: device with which data will be exchanged. @@ -1276,6 +1327,14 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) if (!tc6->spi_data_rx_buf) return NULL; + /* Check the PROTE bit status so that we can reset the device */ + ret = oa_tc6_check_ctrl_protection(tc6); + if (ret) { + dev_err(&tc6->spi->dev, + "Failed to check the protection mode: %d\n", ret); + return NULL; + } + ret = oa_tc6_sw_reset_macphy(tc6); if (ret) { dev_err(&tc6->spi->dev, -- 2.43.0