From patchwork Fri Sep 8 14:29:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377537 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 A2A2FF4F5 for ; Fri, 8 Sep 2023 14:30:08 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CB2281BF1; Fri, 8 Sep 2023 07:30:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183405; x=1725719405; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=sTnw4RkAKahri7QiR37TfnSSXBfjikQbnSydeQyhXww=; b=KyiqHqtfPb4VKHTXvfWghkIM/pm9RHyWhuPdrr1CdY2gnvocZHrKclMi VyLo8D/2AH6ObBVDnquz2Xtj+h0eLnDRjBly5dMfN0LruycEWVwxwqWs/ fsXDkmjKcdW6Fc29XWEisvNBBQzdb4X4Dvcu8m1P2RWfNbPOHdIpr6w8N 31Bycolv3v5WdQ1Wpiea9GbXixUSpFwwCDPhi1NdaNwnUbHLaiEUJNa/Y qvjtSrZtOlCm1udhIwQRDh2sWuSNSXsk4OlHdwWM6baQG8YRXBm/jckfD Y79iUcOiaKi2TyJ07ZWFa+//ziwR1xBWbqbWyZWzoNmtxKXSH223laJ79 w==; X-CSE-ConnectionGUID: jZHmu+07Re+nCxNQqEz9lw== X-CSE-MsgGUID: vDQBKQIFR92dm4f8igXjTQ== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="170641697" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:01 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:01 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:29:53 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 1/6] net: ethernet: implement OPEN Alliance control transaction interface Date: Fri, 8 Sep 2023 19:59:14 +0530 Message-ID: <20230908142919.14849-2-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Implement register read/write interface according to the control communication specified in the OPEN Alliance 10BASE-T1x MACPHY Serial Interface document. Control transactions consist of one or more control commands. Control commands are used by the SPI host to read and write registers within the MAC-PHY. Each control commands are composed of a 32-bit control command header followed by register data. Control write commands can write either a single register or multiple consecutive registers. When multiple consecutive registers are written, the address is automatically post-incremented by the MAC-PHY. The write command and data is also echoed from the MAC-PHY back to the SPI host to enable the SPI host to identify which register write failed in the case of any bus errors. Control read commands can read either a single register or multiple consecutive registers. When multiple consecutive registers are read, the address is automatically post-incremented by the MAC-PHY. The register data being read or written can be protected against simple bit errors. When enabled by setting the Protection Enable (PROTE) bit in the CONFIG0 register, protection is accomplished by duplication of each 32-bit word containing register data with its ones’ complement. Errors are detected at the receiver by performing a simple exclusive-OR (XOR) of each received 32-bit word containing register data with its received complement and detecting if there are any zeros in the result. Signed-off-by: Parthiban Veerasooran --- Documentation/networking/oa-tc6-framework.rst | 231 ++++++++++++++++++ MAINTAINERS | 8 + drivers/net/ethernet/oa_tc6.c | 222 +++++++++++++++++ include/linux/oa_tc6.h | 31 +++ 4 files changed, 492 insertions(+) create mode 100644 Documentation/networking/oa-tc6-framework.rst create mode 100644 drivers/net/ethernet/oa_tc6.c create mode 100644 include/linux/oa_tc6.h diff --git a/Documentation/networking/oa-tc6-framework.rst b/Documentation/networking/oa-tc6-framework.rst new file mode 100644 index 000000000000..5a8af2398f3c --- /dev/null +++ b/Documentation/networking/oa-tc6-framework.rst @@ -0,0 +1,231 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +========================================================================= +OPEN Alliance 10BASE-T1x MAC-PHY Serial Interface (TC6) Framework Support +========================================================================= + +:Copyright: |copy| 2023 MICROCHIP + +Introduction +------------ + +The IEEE 802.3cg project defines two 10 Mbit/s PHYs operating over a +single pair of conductors. The 10BASE-T1L (Clause 146) is a long reach +PHY supporting full duplex point-to-point operation over 1 km of single +balanced pair of conductors. The 10BASE-T1S (Clause 147) is a short reach +PHY supporting full / half duplex point-to-point operation over 15 m of +single balanced pair of conductors, or half duplex multidrop bus +operation over 25 m of single balanced pair of conductors. + +Furthermore, the IEEE 802.3cg project defines the new Physical Layer +Collision Avoidance (PLCA) Reconciliation Sublayer (Clause 148) meant to +provide improved determinism to the CSMA/CD media access method. PLCA +works in conjunction with the 10BASE-T1S PHY operating in multidrop mode. + +The aforementioned PHYs are intended to cover the low-speed / low-cost +applications in industrial and automotive environment. The large number +of pins (16) required by the MII interface, which is specified by the +IEEE 802.3 in Clause 22, is one of the major cost factors that need to be +addressed to fulfil this objective. + +The MAC-PHY solution integrates an IEEE Clause 4 MAC and a 10BASE-T1x PHY +exposing a low pin count Serial Peripheral Interface (SPI) to the host +microcontroller. This also enables the addition of Ethernet functionality +to existing low-end microcontrollers which do not integrate a MAC +controller. + +Overview +-------- + +The MAC-PHY is specified to carry both data (Ethernet frames) and control +(register access) transactions over a single full-duplex serial +peripheral interface. + +Protocol Overview +----------------- + +Two types of transactions are defined in the protocol: data transactions +for Ethernet frame transfers and control transactions for register +read/write transfers. A chunk is the basic element of data transactions +and is composed of 4 bytes of overhead plus the configured payload size +for each chunk. Ethernet frames are transferred over one or more data +chunks. Control transactions consist of one or more register read/write +control commands. + +SPI transactions are initiated by the SPI host with the assertion of CSn +low to the MAC-PHY and ends with the deassertion of CSn high. In between +each SPI transaction, the SPI host may need time for additional +processing and to setup the next SPI data or control transaction. + +SPI data transactions consist of an equal number of transmit (TX) and +receive (RX) chunks. Chunks in both transmit and receive directions may +or may not contain valid frame data independent from each other, allowing +for the simultaneous transmission and reception of different length +frames. + +Each transmit data chunk begins with a 32-bit data header followed by a +data chunk payload on MOSI. The data header indicates whether transmit +frame data is present and provides the information to determine which +bytes of the payload contain valid frame data. + +In parallel, receive data chunks are received on MISO. Each receive data +chunk consists of a data chunk payload ending with a 32-bit data footer. +The data footer indicates if there is receive frame data present within +the payload or not and provides the information to determine which bytes +of the payload contain valid frame data. + +Reference +--------- + +10BASE-T1x MAC-PHY Serial Interface Specification, + +Link: https://www.opensig.org/about/specifications/ + +Hardware Architecture +--------------------- + +.. code-block:: none + +-------------------------------------+ + | MAC-PHY | + +----------+ | +-----------+ +-------+ +-------+ | + | SPI Host |<---->| | SPI Slave | | MAC | | PHY | | + +----------+ | +-----------+ +-------+ +-------+ | + +-------------------------------------+ + +Software Architecture +--------------------- + +.. code-block:: none + + +----------------------------------------------------------+ + | Networking Subsystem | + +----------------------------------------------------------+ + | | + | | + +----------------------+ +-----------------------------+ + | MAC Driver |<--->| OPEN Alliance TC6 Framework | + +----------------------+ +-----------------------------+ + | | + | | + +----------------------+ +-----------------------------+ + | PHYLIB | | SPI Subsystem | + +----------------------+ +-----------------------------+ + | + | + +----------------------------------------------------------+ + | 10BASE-T1x MAC-PHY Device | + +----------------------------------------------------------+ + +Implementation +-------------- + +MAC Driver +~~~~~~~~~~ +- Initializes and configures the OA TC6 framework for the MAC-PHY. + +- Initializes PHYLIB interface. + +- Registers and configures the network device. + +- Sends the tx ethernet frame from n/w subsystem to OA TC6 framework. + +OPEN Alliance TC6 Framework +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Registers macphy interrupt which is used to indicate receive data + available, any communication errors and tx credit count availability in + case it was 0 already. + +- Prepares SPI chunks from the tx ethernet frame enqueued by the MAC + driver and sends to MAC-PHY. + +- Receives SPI chunks from MAC-PHY and prepares ethernet frame and sends + to n/w subsystem. + +- Prepares and performs control read/write commands. + +Data Transaction +~~~~~~~~~~~~~~~~ + +Tx ethernet frame from the n/w layer is divided into multiple chunks in +the oa_tc6_prepare_tx_chunks() function. Each tx chunk will have 4 bytes +header and 64/32 bytes chunk payload. + +.. code-block:: none + + +---------------------------------------------------+ + | Tx Chunk | + | +---------------------------+ +----------------+ | MOSI + | | 64/32 bytes chunk payload | | 4 bytes header | |------------> + | +---------------------------+ +----------------+ | + +---------------------------------------------------+ + +The number of buffers available in the MAC-PHY to store the incoming tx +chunk payloads is represented as tx credit count (txc). This txc can be +read either from the Buffer Status Register or footer (Refer below in the +rx case for the footer info) received from the MAC-PHY. The number of txc +is needed to transport the ethernet frame is calculated from the size of +the ethernet frame. The header in the each chunk is updated with the +chunk payload details like data not control, data valid, start valid, +start word offset, end byte offset, end valid and parity bit. + +Once the tx chunks are ready, oa_tc6_handler() task is triggered to +perform SPI transfer. This task checks for the txc availability in the +MAC-PHY and sends the number of chunks equal to the txc. If there is no +txc is available then the task waits for the interrupt to indicate the +txc availability. If the available txc is less than the needed txc then +the SPI transfer is performed for the available txc and the rx chunks +footer is processed for the txc availability again. For every SPI +transfer the received rx chunks will be processed for the rx ethernet +frame (if any), txc and rca. + +Rx ethernet frame from the MAC-PHY is framed from the rx chunks received +from the MAC-PHY and will be transferred to the n/w layer. Each rx chunk +will have 64/32 bytes chunk payload and 4 bytes footer. + +.. code-block:: none + + +---------------------------------------------------+ + | Rx Chunk | + | +----------------+ +---------------------------+ | MISO + | | 4 bytes footer | | 64/32 bytes chunk payload | |------------> + | +----------------+ +---------------------------+ | + +---------------------------------------------------+ + +In case of interrupt, the oa_tc6_handler() task performs an empty chunk +SPI transfer to get the reason for the interrupt in the received chunk +footer. The reason can be receive chunk available (rca) or extended block +status (exst) or txc availability. Based on this the corresponding +operation is performed. If it is for rca then the SPI transfer is +performed with the empty chunks equal to the rca to get the rx chunks. +Rx ethernet frame is framed from the rx chunks received and transferred +to n/w layer. If it is for exst then the STATUS0 register will be read +for the error detail. + +In the beginning the interrupt occurs for indicating the reset complete +from the MAC-PHY and is ready for the configuration. oa_tc6_handler() task +handles this interrupt to get and clear the reset complete status. + +Control Transaction +~~~~~~~~~~~~~~~~~~~ + +Control transactions are performed to read/write the registers in the +MAC-PHY. Each control command headers are 4 bytes length with the +necessary details to read/write the registers. + +In case of a register write command, the write command header has the +information like data not control, write not read, address increment +disable, memory map selector, address, length and parity followed by the +data to be written. If the protected mode is enabled in the CONFIG0 +register then each data to be written will be sent first followed by its +ones' complement value to ensure the error free transfer. For every write +command, both the write command header and the data will be echoed back in +the rx path to confirm the error free transaction. + +In case of a register read command, the read command is preferred with the +above mentioned details and the echoed rx data will be processed for the +register data. In case of protected mode enabled the echoed rx data +contains the ones' complemented data also for verifying the data error. + +oa_tc6_perform_ctrl() function prepares control commands based on the +read/write request, performs SPI transfer, checks for the error and +returns read register data in case of control read command. diff --git a/MAINTAINERS b/MAINTAINERS index 9cc15c50c2c6..c54454c7e7a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15835,6 +15835,14 @@ L: linux-rdma@vger.kernel.org S: Supported F: drivers/infiniband/ulp/opa_vnic +OPEN ALLIANCE 10BASE-T1S MACPHY SERIAL INTERFACE FRAMEWORK +M: Parthiban Veerasooran +L: netdev@vger.kernel.org +S: Maintained +F: Documentation/networking/oa-tc6-framework.rst +F: drivers/include/linux/oa_tc6.h +F: drivers/net/ethernet/oa_tc6.c + OPEN FIRMWARE AND FLATTENED DEVICE TREE M: Rob Herring M: Frank Rowand diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c new file mode 100644 index 000000000000..613cf034430a --- /dev/null +++ b/drivers/net/ethernet/oa_tc6.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework + * + * Author: Parthiban Veerasooran + */ + +#include +#include + +static int oa_tc6_spi_transfer(struct spi_device *spi, u8 *ptx, u8 *prx, + u16 len) +{ + struct spi_transfer xfer = { + .tx_buf = ptx, + .rx_buf = prx, + .len = len, + }; + struct spi_message msg; + + spi_message_init(&msg); + spi_message_add_tail(&xfer, &msg); + + return spi_sync(spi, &msg); +} + +static bool oa_tc6_get_parity(u32 p) +{ + bool parity = true; + + /* This function returns an odd parity bit */ + while (p) { + parity = !parity; + p = p & (p - 1); + } + return parity; +} + +static void oa_tc6_prepare_ctrl_buf(struct oa_tc6 *tc6, u32 addr, u32 val[], + u8 len, bool wnr, u8 *buf, bool ctrl_prot) +{ + u32 hdr; + + /* Prepare the control header with the required details */ + hdr = FIELD_PREP(CTRL_HDR_DNC, 0) | + FIELD_PREP(CTRL_HDR_WNR, wnr) | + FIELD_PREP(CTRL_HDR_AID, 0) | + FIELD_PREP(CTRL_HDR_MMS, addr >> 16) | + FIELD_PREP(CTRL_HDR_ADDR, addr) | + FIELD_PREP(CTRL_HDR_LEN, len - 1); + hdr |= FIELD_PREP(CTRL_HDR_P, oa_tc6_get_parity(hdr)); + *(u32 *)buf = cpu_to_be32(hdr); + + if (wnr) { + for (u8 i = 0; i < len; i++) { + u16 pos; + + if (!ctrl_prot) { + /* Send the value to be written followed by the + * header. + */ + pos = (i + 1) * TC6_HDR_SIZE; + *(u32 *)&buf[pos] = cpu_to_be32(val[i]); + } else { + /* If protected then send complemented value + * also followed by actual value. + */ + pos = TC6_HDR_SIZE + (i * (TC6_HDR_SIZE * 2)); + *(u32 *)&buf[pos] = cpu_to_be32(val[i]); + pos = (i + 1) * (TC6_HDR_SIZE * 2); + *(u32 *)&buf[pos] = cpu_to_be32(~val[i]); + } + } + } +} + +static int oa_tc6_check_control(struct oa_tc6 *tc6, u8 *ptx, u8 *prx, u8 len, + bool wnr, bool ctrl_prot) +{ + /* 1st 4 bytes of rx chunk data can be discarded */ + u32 rx_hdr = *(u32 *)&prx[TC6_HDR_SIZE]; + u32 tx_hdr = *(u32 *)ptx; + u32 rx_data_complement; + u32 tx_data; + u32 rx_data; + u16 pos1; + u16 pos2; + + /* If tx hdr and echoed hdr are not equal then there might be an issue + * with the connection between SPI host and MAC-PHY. Here this case is + * considered as MAC-PHY is not connected. + */ + if (tx_hdr != rx_hdr) + return -ENODEV; + + if (wnr) { + if (!ctrl_prot) { + /* In case of ctrl write, both tx data & echoed + * data are compared for the error. + */ + pos1 = TC6_HDR_SIZE; + pos2 = TC6_HDR_SIZE * 2; + for (u8 i = 0; i < len; i++) { + tx_data = *(u32 *)&ptx[pos1 + (i * TC6_HDR_SIZE)]; + rx_data = *(u32 *)&prx[pos2 + (i * TC6_HDR_SIZE)]; + if (tx_data != rx_data) + return -ENODEV; + } + return 0; + } + } else { + if (!ctrl_prot) + return 0; + } + + /* In case of ctrl read or ctrl write in protected mode, the rx data and + * the complement of rx data are compared for the error. + */ + pos1 = TC6_HDR_SIZE * 2; + pos2 = TC6_HDR_SIZE * 3; + for (u8 i = 0; i < len; i++) { + rx_data = *(u32 *)&prx[pos1 + (i * TC6_HDR_SIZE * 2)]; + rx_data_complement = *(u32 *)&prx[pos2 + (i * TC6_HDR_SIZE * 2)]; + if (rx_data != ~rx_data_complement) + return -ENODEV; + } + + return 0; +} + +int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len, + bool wnr, bool ctrl_prot) +{ + u8 *tx_buf; + u8 *rx_buf; + u16 size; + u16 pos; + int ret; + + if (ctrl_prot) + size = (TC6_HDR_SIZE * 2) + (len * (TC6_HDR_SIZE * 2)); + else + size = (TC6_HDR_SIZE * 2) + (len * TC6_HDR_SIZE); + + tx_buf = kzalloc(size, GFP_KERNEL); + if (!tx_buf) + return -ENOMEM; + + rx_buf = kzalloc(size, GFP_KERNEL); + if (!rx_buf) { + ret = -ENOMEM; + goto err_rx_buf_kzalloc; + } + + /* Prepare control command */ + oa_tc6_prepare_ctrl_buf(tc6, addr, val, len, wnr, tx_buf, ctrl_prot); + + /* Perform SPI transfer */ + ret = oa_tc6_spi_transfer(tc6->spi, tx_buf, rx_buf, size); + if (ret) + goto err_ctrl; + + /* Check echoed/received control reply */ + ret = oa_tc6_check_control(tc6, tx_buf, rx_buf, len, wnr, ctrl_prot); + if (ret) + goto err_ctrl; + + if (!wnr) { + /* Copy read data from the rx data in case of ctrl read */ + for (u8 i = 0; i < len; i++) { + if (!ctrl_prot) { + pos = (TC6_HDR_SIZE * 2) + (i * TC6_HDR_SIZE); + val[i] = be32_to_cpu(*(u32 *)&rx_buf[pos]); + } else { + pos = (TC6_HDR_SIZE * 2) + + (i * (TC6_HDR_SIZE * 2)); + val[i] = be32_to_cpu(*(u32 *)&rx_buf[pos]); + } + } + } + +err_ctrl: + kfree(rx_buf); +err_rx_buf_kzalloc: + kfree(tx_buf); + return ret; +} + +int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len) +{ + return oa_tc6_perform_ctrl(tc6, addr, val, len, true, tc6->ctrl_prot); +} +EXPORT_SYMBOL_GPL(oa_tc6_write_register); + +int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len) +{ + return oa_tc6_perform_ctrl(tc6, addr, val, len, false, tc6->ctrl_prot); +} +EXPORT_SYMBOL_GPL(oa_tc6_read_register); + +struct oa_tc6 *oa_tc6_init(struct spi_device *spi) +{ + struct oa_tc6 *tc6; + + if (!spi) + return NULL; + + tc6 = kzalloc(sizeof(*tc6), GFP_KERNEL); + if (!tc6) + return NULL; + + tc6->spi = spi; + + return tc6; +} +EXPORT_SYMBOL_GPL(oa_tc6_init); + +void oa_tc6_deinit(struct oa_tc6 *tc6) +{ + kfree(tc6); +} +EXPORT_SYMBOL_GPL(oa_tc6_deinit); diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h new file mode 100644 index 000000000000..5e0a58ab1dcd --- /dev/null +++ b/include/linux/oa_tc6.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface framework + * + * Author: Parthiban Veerasooran + */ + +#include + +/* Control header */ +#define CTRL_HDR_DNC BIT(31) /* Data-Not-Control */ +#define CTRL_HDR_HDRB BIT(30) /* Received Header Bad */ +#define CTRL_HDR_WNR BIT(29) /* Write-Not-Read */ +#define CTRL_HDR_AID BIT(28) /* Address Increment Disable */ +#define CTRL_HDR_MMS GENMASK(27, 24) /* Memory Map Selector */ +#define CTRL_HDR_ADDR GENMASK(23, 8) /* Address */ +#define CTRL_HDR_LEN GENMASK(7, 1) /* Length */ +#define CTRL_HDR_P BIT(0) /* Parity Bit */ + +#define TC6_HDR_SIZE 4 /* Ctrl command header size as per OA */ +#define TC6_FTR_SIZE 4 /* Ctrl command footer size ss per OA */ + +struct oa_tc6 { + struct spi_device *spi; + bool ctrl_prot; +}; + +struct oa_tc6 *oa_tc6_init(struct spi_device *spi); +void oa_tc6_deinit(struct oa_tc6 *tc6); +int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); +int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); From patchwork Fri Sep 8 14:29:15 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377538 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 7AA3414AA7 for ; Fri, 8 Sep 2023 14:30:13 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EECDE1BF5; Fri, 8 Sep 2023 07:30:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183410; x=1725719410; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=kmUe7gf4S/VQfRUrjgXkd5uprc3VQ+NXaLDIYMzPFnA=; b=xRKoQMy1CS0cGc1P+2pIuc+uTQAUI3J6SFH16wdwvSLjKHFA9+5JmUNV Dd/AfvTD1sFtxANN9gwhGLp81aohmjtptiCBSv1EP9IQP3qqxIGXzF6bp d7p7006kyGkCdIngVOC3qFp+rkEmKNA1juxX2zOanW4eSlpWGpZKVTejt DYjKFgTtbKKX+AVLf6rNmisVHQMZkCyufnJLnWUX6KIKqkmgjk7psbdVk 9KwrGG04eA6oOX4dlKFj/YYPYCUIBdYq3mJ2UImXJ6N4gTLjptNPqf7lF ZOhfQHX97kI3vq+iLOWM71GMP/8xh6fCs7ZRbZs9Urb0qwhslvs2pqs9H w==; X-CSE-ConnectionGUID: Aywz+yjmSOCB/VIlcerAKg== X-CSE-MsgGUID: zQWazptsS8ukr9RtQEVIDQ== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="3544696" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa4.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:09 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:09 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:30:01 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 2/6] net: ethernet: add mac-phy interrupt support with reset complete handling Date: Fri, 8 Sep 2023 19:59:15 +0530 Message-ID: <20230908142919.14849-3-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Register MAC-PHY interrupt and handle reset complete interrupt. Reset complete bit is set when the MAC-PHY reset complete and ready for configuration. When it is set, it will generate a non-maskable interrupt to alert the SPI host. Additionally reset complete bit in the STS0 register has to be written by one upon reset complete to clear the interrupt. Signed-off-by: Parthiban Veerasooran --- drivers/net/ethernet/oa_tc6.c | 141 ++++++++++++++++++++++++++++++++-- include/linux/oa_tc6.h | 16 +++- 2 files changed, 150 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 613cf034430a..0019f70345b6 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -6,6 +6,7 @@ */ #include +#include #include static int oa_tc6_spi_transfer(struct spi_device *spi, u8 *ptx, u8 *prx, @@ -160,10 +161,16 @@ int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len, if (ret) goto err_ctrl; - /* Check echoed/received control reply */ - ret = oa_tc6_check_control(tc6, tx_buf, rx_buf, len, wnr, ctrl_prot); - if (ret) - goto err_ctrl; + /* In case of reset write, the echoed control command doesn't have any + * valid data. So no need to check for error. + */ + if (addr != OA_TC6_RESET) { + /* Check echoed/received control reply */ + ret = oa_tc6_check_control(tc6, tx_buf, rx_buf, len, wnr, + ctrl_prot); + if (ret) + goto err_ctrl; + } if (!wnr) { /* Copy read data from the rx data in case of ctrl read */ @@ -186,6 +193,88 @@ int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len, return ret; } +static int oa_tc6_handler(void *data) +{ + struct oa_tc6 *tc6 = data; + u32 regval; + int ret; + + while (likely(!kthread_should_stop())) { + wait_event_interruptible(tc6->tc6_wq, tc6->int_flag || + kthread_should_stop()); + if (tc6->int_flag) { + tc6->int_flag = false; + ret = oa_tc6_perform_ctrl(tc6, OA_TC6_STS0, ®val, 1, + false, false); + if (ret) { + dev_err(&tc6->spi->dev, "Failed to read STS0\n"); + continue; + } + /* Check for reset complete interrupt status */ + if (regval & RESETC) { + regval = RESETC; + /* SPI host should write RESETC bit with one to + * clear the reset interrupt status. + */ + ret = oa_tc6_perform_ctrl(tc6, OA_TC6_STS0, + ®val, 1, true, + false); + if (ret) { + dev_err(&tc6->spi->dev, + "Failed to write STS0\n"); + continue; + } + complete(&tc6->rst_complete); + } + } + } + return 0; +} + +static irqreturn_t macphy_irq(int irq, void *dev_id) +{ + struct oa_tc6 *tc6 = dev_id; + + /* Wake tc6 task to perform interrupt action */ + tc6->int_flag = true; + wake_up_interruptible(&tc6->tc6_wq); + + return IRQ_HANDLED; +} + +static int oa_tc6_sw_reset(struct oa_tc6 *tc6) +{ + long timeleft; + u32 regval; + int ret; + + /* Perform software reset with both protected and unprotected control + * commands because the driver doesn't know the current status of the + * MAC-PHY. + */ + regval = SW_RESET; + reinit_completion(&tc6->rst_complete); + ret = oa_tc6_perform_ctrl(tc6, OA_TC6_RESET, ®val, 1, true, false); + if (ret) { + dev_err(&tc6->spi->dev, "RESET register write failed\n"); + return ret; + } + + ret = oa_tc6_perform_ctrl(tc6, OA_TC6_RESET, ®val, 1, true, true); + if (ret) { + dev_err(&tc6->spi->dev, "RESET register write failed\n"); + return ret; + } + timeleft = wait_for_completion_interruptible_timeout(&tc6->rst_complete, + msecs_to_jiffies(1)); + if (timeleft <= 0) { + dev_err(&tc6->spi->dev, "MAC-PHY reset failed\n"); + return -ENODEV; + } + + return 0; +} + int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len) { return oa_tc6_perform_ctrl(tc6, addr, val, len, true, tc6->ctrl_prot); @@ -201,6 +290,7 @@ EXPORT_SYMBOL_GPL(oa_tc6_read_register); struct oa_tc6 *oa_tc6_init(struct spi_device *spi) { struct oa_tc6 *tc6; + int ret; if (!spi) return NULL; @@ -211,12 +301,51 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi) tc6->spi = spi; + /* Used for triggering the OA TC6 task */ + init_waitqueue_head(&tc6->tc6_wq); + + init_completion(&tc6->rst_complete); + + /* This task performs the SPI transfer */ + tc6->tc6_task = kthread_run(oa_tc6_handler, tc6, "OA TC6 Task"); + if (IS_ERR(tc6->tc6_task)) + goto err_tc6_task; + + /* Set the highest priority to the tc6 task as it is time critical */ + sched_set_fifo(tc6->tc6_task); + + /* Register MAC-PHY interrupt service routine */ + ret = devm_request_irq(&spi->dev, spi->irq, macphy_irq, 0, "macphy int", + tc6); + if ((ret != -ENOTCONN) && ret < 0) { + dev_err(&spi->dev, "Error attaching macphy irq %d\n", ret); + goto err_macphy_irq; + } + + /* Perform MAC-PHY software reset */ + if (oa_tc6_sw_reset(tc6)) + goto err_macphy_reset; + return tc6; + +err_macphy_reset: + devm_free_irq(&tc6->spi->dev, tc6->spi->irq, tc6); +err_macphy_irq: + kthread_stop(tc6->tc6_task); +err_tc6_task: + kfree(tc6); + return NULL; } EXPORT_SYMBOL_GPL(oa_tc6_init); -void oa_tc6_deinit(struct oa_tc6 *tc6) +int oa_tc6_deinit(struct oa_tc6 *tc6) { - kfree(tc6); + int ret; + + devm_free_irq(&tc6->spi->dev, tc6->spi->irq, tc6); + ret = kthread_stop(tc6->tc6_task); + if (!ret) + kfree(tc6); + return ret; } EXPORT_SYMBOL_GPL(oa_tc6_deinit); diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h index 5e0a58ab1dcd..315f061c2dfe 100644 --- a/include/linux/oa_tc6.h +++ b/include/linux/oa_tc6.h @@ -17,15 +17,29 @@ #define CTRL_HDR_LEN GENMASK(7, 1) /* Length */ #define CTRL_HDR_P BIT(0) /* Parity Bit */ +/* Open Alliance TC6 Standard Control and Status Registers */ +#define OA_TC6_RESET 0x0003 /* Reset Control and Status Register */ +#define OA_TC6_STS0 0x0008 /* Status Register #0 */ + +/* RESET register field */ +#define SW_RESET BIT(0) /* Software Reset */ + +/* STATUS0 register field */ +#define RESETC BIT(6) /* Reset Complete */ + #define TC6_HDR_SIZE 4 /* Ctrl command header size as per OA */ #define TC6_FTR_SIZE 4 /* Ctrl command footer size ss per OA */ struct oa_tc6 { struct spi_device *spi; bool ctrl_prot; + struct task_struct *tc6_task; + wait_queue_head_t tc6_wq; + bool int_flag; + struct completion rst_complete; }; struct oa_tc6 *oa_tc6_init(struct spi_device *spi); -void oa_tc6_deinit(struct oa_tc6 *tc6); +int oa_tc6_deinit(struct oa_tc6 *tc6); int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); From patchwork Fri Sep 8 14:29:16 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377540 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 601571640B for ; Fri, 8 Sep 2023 14:30:29 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5AFA51FE8; Fri, 8 Sep 2023 07:30:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183422; x=1725719422; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=o/4kIOJ9QiV558+OQwsWtdlftTAaBUFLv7BslVDZoe4=; b=cOvNhMNNqGnG95Cwno7D/374xXDT7shCCSWKWykYCsdzyvZ4PbPgcOSu mbOQtp7jV8hRF9xRa1LB6scSdIhSAlnZ+ZNYN9dAXWTf//68zxocQAz66 ZsqjpO4YyQRdAlqsxc/O0BTB8Gnc8cjDX46ISRcNTfhqoPoGnguL71IzF C3mHNNpRbTdHiZKH0cyAYV3WtLcNiVJnhlmz8pOw3QB+gVm6tWOB+1ytd V9P/MYCW9EvycO725eLuT5wJ0406VyVINgQAN1t88Sw8RtzjCaUZs9Z55 6IgH0wWtsOCy/RbzPFWulqnCN8B+f8o5GqccoRpyDe6LRsstaWNNJHoDY Q==; X-CSE-ConnectionGUID: 0oM0WiPjTKynEvbCqNPq+Q== X-CSE-MsgGUID: Vkq0kZYKRvy3VwWmZvV6yQ== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="170641771" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:20 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex02.mchp-main.com (10.10.85.144) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:16 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:30:09 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 3/6] net: ethernet: implement OA TC6 configuration function Date: Fri, 8 Sep 2023 19:59:16 +0530 Message-ID: <20230908142919.14849-4-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Read STDCAP register for the MAC-PHY capability and check against the configuration parameters such as chunk payload, tx cut through and rx cut through to configure the MAC-PHY. It also configures the control command protected/unprotected mode. In cut through mode configuration the MAC-PHY doesn't buffer the incoming data. In tx case, it passes the data to the network if the configured cps of data available. In rx case, it passes the data to the SPI host if the configured cps of data available from the network. Also disables all the errors mask in the IMASK0 register to check for the errors. Signed-off-by: Parthiban Veerasooran --- drivers/net/ethernet/oa_tc6.c | 39 +++++++++++++++++++++++++++++++++++ include/linux/oa_tc6.h | 28 ++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 0019f70345b6..65a7317f768d 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -287,6 +287,45 @@ int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len) } EXPORT_SYMBOL_GPL(oa_tc6_read_register); +int oa_tc6_configure(struct oa_tc6 *tc6, u8 cps, bool ctrl_prot, bool tx_cut_thr, + bool rx_cut_thr) +{ + u32 regval; + int ret; + + /* Read and configure the IMASK0 register for unmasking the interrupts */ + ret = oa_tc6_read_register(tc6, OA_TC6_IMASK0, ®val, 1); + if (ret) + return ret; + + regval &= TXPEM & TXBOEM & TXBUEM & RXBOEM & LOFEM & HDREM; + ret = oa_tc6_write_register(tc6, OA_TC6_IMASK0, ®val, 1); + if (ret) + return ret; + + /* Configure the CONFIG0 register with the required configurations */ + regval = SYNC; + if (ctrl_prot) + regval |= PROTE; + if (tx_cut_thr) + regval |= TXCTE; + if (rx_cut_thr) + regval |= RXCTE; + regval |= FIELD_PREP(CPS, ilog2(cps) / ilog2(2)); + + ret = oa_tc6_write_register(tc6, OA_TC6_CONFIG0, ®val, 1); + if (ret) + return ret; + + tc6->cps = cps; + tc6->ctrl_prot = ctrl_prot; + tc6->tx_cut_thr = tx_cut_thr; + tc6->rx_cut_thr = rx_cut_thr; + + return 0; +} +EXPORT_SYMBOL_GPL(oa_tc6_configure); + struct oa_tc6 *oa_tc6_init(struct spi_device *spi) { struct oa_tc6 *tc6; diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h index 315f061c2dfe..fa29c4e09720 100644 --- a/include/linux/oa_tc6.h +++ b/include/linux/oa_tc6.h @@ -19,11 +19,28 @@ /* Open Alliance TC6 Standard Control and Status Registers */ #define OA_TC6_RESET 0x0003 /* Reset Control and Status Register */ +#define OA_TC6_CONFIG0 0x0004 /* Configuration Register #0 */ #define OA_TC6_STS0 0x0008 /* Status Register #0 */ +#define OA_TC6_IMASK0 0x000C /* Interrupt Mask Register #0 */ /* RESET register field */ #define SW_RESET BIT(0) /* Software Reset */ +/* CONFIG0 register fields */ +#define SYNC BIT(15) /* Configuration Synchronization */ +#define TXCTE BIT(9) /* Tx cut-through enable */ +#define RXCTE BIT(8) /* Rx cut-through enable */ +#define PROTE BIT(5) /* Ctrl read/write Protection Enable */ +#define CPS GENMASK(2, 0) /* Chunk Payload Size */ + +/* Unmasking interrupt fields in IMASK0 */ +#define HDREM ~BIT(5) /* Header Error Mask */ +#define LOFEM ~BIT(4) /* Loss of Framing Error Mask */ +#define RXBOEM ~BIT(3) /* Rx Buffer Overflow Error Mask */ +#define TXBUEM ~BIT(2) /* Tx Buffer Underflow Error Mask */ +#define TXBOEM ~BIT(1) /* Tx Buffer Overflow Error Mask */ +#define TXPEM ~BIT(0) /* Tx Protocol Error Mask */ + /* STATUS0 register field */ #define RESETC BIT(6) /* Reset Complete */ @@ -31,15 +48,20 @@ #define TC6_FTR_SIZE 4 /* Ctrl command footer size ss per OA */ struct oa_tc6 { - struct spi_device *spi; - bool ctrl_prot; + struct completion rst_complete; struct task_struct *tc6_task; wait_queue_head_t tc6_wq; + struct spi_device *spi; + bool tx_cut_thr; + bool rx_cut_thr; + bool ctrl_prot; bool int_flag; - struct completion rst_complete; + u8 cps; }; struct oa_tc6 *oa_tc6_init(struct spi_device *spi); int oa_tc6_deinit(struct oa_tc6 *tc6); int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); +int oa_tc6_configure(struct oa_tc6 *tc6, u8 cps, bool ctrl_prot, bool tx_cut_thr, + bool rx_cut_thr); From patchwork Fri Sep 8 14:29:17 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377541 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 17B591E533 for ; Fri, 8 Sep 2023 14:31:07 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 85E101FE0; Fri, 8 Sep 2023 07:30:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183440; x=1725719440; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=09CLHJYF06z+jqAq0MqKYKVfwDgOLa0ChlMUjxzbJbk=; b=qnkFg0ia9pO/KAmAAx17x+Vx52UbDC/5j6KHbY1jrBaUS9nLOBGCBUAj nnlosTVfTgHcomqZM50heEDtyUgxlaS1R4zQTTG1jNTJmnXoo66GLY9qb +mSHI/dgUdRu+aQTHUVYAen94Q2ZxlWZ9An2PjOZM8KE/HYDeZVKBBBR+ xZeZjn4QiZ4Thq6myQbEfIfyiAQCa4rJ/BYuRVgu17YdisFK0jZFKOoOH bfeGCqvnAGp+TRT76272H4UBdE1BN53aF7iSm9/kGSuaJnH9/TAF7c7ae ytrEcPnY4eiApTo2e6QhlZOJj3fYskFtMJ0w+AKXZa5BAnuk3sqA2ZQ8y A==; X-CSE-ConnectionGUID: BfdJWj6dSkej858jQph4IQ== X-CSE-MsgGUID: 6GlqnmBWTWe90D4pA6ooVw== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="170641817" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:39 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:24 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:30:17 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 4/6] net: ethernet: implement data transaction interface Date: Fri, 8 Sep 2023 19:59:17 +0530 Message-ID: <20230908142919.14849-5-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC The ethernet frame to be sent to MAC-PHY is converted into multiple transmit data chunks. A transmit data chunk consists of a 4-byte data header followed by the transmit data chunk payload. The received ethernet frame from the network is converted into multiple receive data chunks by the MAC-PHY and a receive data chunk consists of the receive data chunk payload followed by a 4-byte data footer at the end. The MAC-PHY shall support a default data chunk payload size of 64 bytes. Data chunk payload sizes of 32, 16, or 8 bytes may also be supported. The data chunk payload is always a multiple of 4 bytes. The 4-byte data header occurs at the beginning of each transmit data chunk on MOSI and the 4-byte data footer occurs at the end of each receive data chunk on MISO. The data header and footer contain the information needed to determine the validity and location of the transmit and receive frame data within the data chunk payload. Ethernet frames shall be aligned to a 32-bit boundary within the data chunk payload. Signed-off-by: Parthiban Veerasooran --- drivers/net/ethernet/oa_tc6.c | 425 +++++++++++++++++++++++++++++++++- include/linux/oa_tc6.h | 65 +++++- 2 files changed, 485 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/oa_tc6.c b/drivers/net/ethernet/oa_tc6.c index 65a7317f768d..20138b178185 100644 --- a/drivers/net/ethernet/oa_tc6.c +++ b/drivers/net/ethernet/oa_tc6.c @@ -5,6 +5,7 @@ * Author: Parthiban Veerasooran */ +#include #include #include #include @@ -193,17 +194,224 @@ int oa_tc6_perform_ctrl(struct oa_tc6 *tc6, u32 addr, u32 val[], u8 len, return ret; } +static u16 oa_tc6_prepare_empty_chunk(struct oa_tc6 *tc6, u8 *buf, u8 cp_count) +{ + u32 hdr; + + /* Prepare empty chunks used for getting interrupt information or if + * receive data available. + */ + for (u8 i = 0; i < cp_count; i++) { + hdr = FIELD_PREP(DATA_HDR_DNC, 1); + hdr |= FIELD_PREP(DATA_HDR_P, oa_tc6_get_parity(hdr)); + hdr = cpu_to_be32(hdr); + *(u32 *)&buf[i * (tc6->cps + TC6_HDR_SIZE)] = hdr; + memset(&buf[TC6_HDR_SIZE + (i * (tc6->cps + TC6_HDR_SIZE))], 0, + tc6->cps); + } + + return cp_count * (tc6->cps + TC6_HDR_SIZE); +} + +static void oa_tc6_rx_eth_ready(struct oa_tc6 *tc6) +{ + struct sk_buff *skb = NULL; + + /* Send the received ethernet packet to network layer */ + skb = netdev_alloc_skb(tc6->netdev, tc6->rxd_bytes + NET_IP_ALIGN); + if (!skb) { + tc6->netdev->stats.rx_dropped++; + netdev_err(tc6->netdev, "Out of memory for rx'd frame"); + } else { + skb_reserve(skb, NET_IP_ALIGN); + memcpy(skb_put(skb, tc6->rxd_bytes), &tc6->eth_rx_buf[0], + tc6->rxd_bytes); + skb->protocol = eth_type_trans(skb, tc6->netdev); + tc6->netdev->stats.rx_packets++; + tc6->netdev->stats.rx_bytes += tc6->rxd_bytes; + netif_rx(skb); + } +} + +static int oa_tc6_process_exst(struct oa_tc6 *tc6) +{ + u32 regval; + int ret; + + ret = oa_tc6_read_register(tc6, OA_TC6_STS0, ®val, 1); + if (ret) { + netdev_err(tc6->netdev, "STS0 register read failed.\n"); + return ret; + } + if (regval & TXPE) + netdev_err(tc6->netdev, "Transmit protocol error\n"); + if (regval & TXBOE) + netdev_err(tc6->netdev, "Transmit buffer overflow\n"); + if (regval & TXBUE) + netdev_err(tc6->netdev, "Transmit buffer underflow\n"); + if (regval & RXBOE) + netdev_err(tc6->netdev, "Receive buffer overflow\n"); + if (regval & LOFE) + netdev_err(tc6->netdev, "Loss of frame\n"); + if (regval & HDRE) + netdev_err(tc6->netdev, "Header error\n"); + if (regval & TXFCSE) + netdev_err(tc6->netdev, "Transmit Frame Check Sequence Error\n"); + ret = oa_tc6_write_register(tc6, OA_TC6_STS0, ®val, 1); + if (ret) + netdev_err(tc6->netdev, "STS0 register write failed.\n"); + return ret; +} + +static int oa_tc6_process_rx_chunks(struct oa_tc6 *tc6, u8 *buf, u16 len) +{ + u8 cp_count; + u8 *payload; + u32 ftr; + u16 ebo; + u16 sbo; + + /* Calculate the number of chunks received */ + cp_count = len / (tc6->cps + TC6_FTR_SIZE); + + for (u8 i = 0; i < cp_count; i++) { + /* Get the footer and payload */ + ftr = *(u32 *)&buf[tc6->cps + (i * (tc6->cps + TC6_FTR_SIZE))]; + ftr = be32_to_cpu(ftr); + payload = &buf[(i * (tc6->cps + TC6_FTR_SIZE))]; + /* Check for footer parity error */ + if (oa_tc6_get_parity(ftr)) { + netdev_err(tc6->netdev, "Footer: Parity error\n"); + goto err_exit; + } + /* If EXST set in the footer then read STS0 register to get the + * status information. + */ + if (FIELD_GET(DATA_FTR_EXST, ftr)) { + if (oa_tc6_process_exst(tc6)) + netdev_err(tc6->netdev, "Failed to process EXST\n"); + goto err_exit; + } + if (FIELD_GET(DATA_FTR_HDRB, ftr)) { + netdev_err(tc6->netdev, "Footer: Received header bad\n"); + goto err_exit; + } + if (!FIELD_GET(DATA_FTR_SYNC, ftr)) { + netdev_err(tc6->netdev, "Footer: Configuration unsync\n"); + goto err_exit; + } + /* If Frame Drop is set, indicates that the MAC has detected a + * condition for which the SPI host should drop the received + * ethernet frame. + */ + if (FIELD_GET(DATA_FTR_FD, ftr) && FIELD_GET(DATA_FTR_EV, ftr)) { + netdev_warn(tc6->netdev, "Footer: Frame drop\n"); + if (FIELD_GET(DATA_FTR_SV, ftr)) { + goto start_new_frame; + } else { + if (tc6->rx_eth_started) { + tc6->rxd_bytes = 0; + tc6->rx_eth_started = false; + tc6->netdev->stats.rx_dropped++; + } + continue; + } + } + /* Check for data valid */ + if (FIELD_GET(DATA_FTR_DV, ftr)) { + /* Check whether both start valid and end valid are in a + * single chunk payload means a single chunk payload may + * contain an entire ethernet frame. + */ + if (FIELD_GET(DATA_FTR_SV, ftr) && + FIELD_GET(DATA_FTR_EV, ftr)) { + sbo = FIELD_GET(DATA_FTR_SWO, ftr) * 4; + ebo = FIELD_GET(DATA_FTR_EBO, ftr) + 1; + if (ebo <= sbo) { + memcpy(&tc6->eth_rx_buf[tc6->rxd_bytes], + &payload[0], ebo); + tc6->rxd_bytes += ebo; + oa_tc6_rx_eth_ready(tc6); + tc6->rxd_bytes = 0; + memcpy(&tc6->eth_rx_buf[tc6->rxd_bytes], + &payload[sbo], tc6->cps - sbo); + tc6->rxd_bytes += (tc6->cps - sbo); + goto exit; + } else { + memcpy(&tc6->eth_rx_buf[tc6->rxd_bytes], + &payload[sbo], ebo - sbo); + tc6->rxd_bytes += (ebo - sbo); + oa_tc6_rx_eth_ready(tc6); + tc6->rxd_bytes = 0; + goto exit; + } + } +start_new_frame: + /* Check for start valid to start capturing the incoming + * ethernet frame. + */ + if (FIELD_GET(DATA_FTR_SV, ftr) && !tc6->rx_eth_started) { + tc6->rxd_bytes = 0; + tc6->rx_eth_started = true; + sbo = FIELD_GET(DATA_FTR_SWO, ftr) * 4; + memcpy(&tc6->eth_rx_buf[tc6->rxd_bytes], + &payload[sbo], tc6->cps - sbo); + tc6->rxd_bytes += (tc6->cps - sbo); + goto exit; + } + + /* Check for end valid and calculate the copy length */ + if (tc6->rx_eth_started) { + if (FIELD_GET(DATA_FTR_EV, ftr)) + ebo = FIELD_GET(DATA_FTR_EBO, ftr) + 1; + else + ebo = tc6->cps; + + memcpy(&tc6->eth_rx_buf[tc6->rxd_bytes], + &payload[0], ebo); + tc6->rxd_bytes += ebo; + if (FIELD_GET(DATA_FTR_EV, ftr)) { + /* If End Valid set then send the + * received ethernet frame to n/w. + */ + oa_tc6_rx_eth_ready(tc6); + tc6->rxd_bytes = 0; + tc6->rx_eth_started = false; + } + } + } + +exit: + tc6->txc = FIELD_GET(DATA_FTR_TXC, ftr); + tc6->rca = FIELD_GET(DATA_FTR_RCA, ftr); + } + return FTR_OK; + +err_exit: + if (tc6->rx_eth_started) { + tc6->rxd_bytes = 0; + tc6->rx_eth_started = false; + tc6->netdev->stats.rx_dropped++; + } + return FTR_ERR; +} + static int oa_tc6_handler(void *data) { struct oa_tc6 *tc6 = data; + bool txc_wait = false; + u16 tx_pos = 0; u32 regval; + u16 len; int ret; while (likely(!kthread_should_stop())) { - wait_event_interruptible(tc6->tc6_wq, tc6->int_flag || + wait_event_interruptible(tc6->tc6_wq, tc6->tx_flag || + tc6->int_flag || tc6->rca || kthread_should_stop()); - if (tc6->int_flag) { + if (tc6->int_flag && !tc6->reset) { tc6->int_flag = false; + tc6->reset = true; ret = oa_tc6_perform_ctrl(tc6, OA_TC6_STS0, ®val, 1, false, false); if (ret) { @@ -227,10 +435,170 @@ static int oa_tc6_handler(void *data) complete(&tc6->rst_complete); } } + + if (tc6->int_flag || tc6->rca) { + /* If rca is updated from the previous footer then + * prepare the empty chunks equal to rca and perform + * SPI transfer to receive the ethernet frame. + */ + if (tc6->rca) { + len = oa_tc6_prepare_empty_chunk(tc6, + tc6->spi_tx_buf, + tc6->rca); + } else { + /* If there is an interrupt then perform a SPI + * transfer with a empty chunk to get the + * details. + */ + tc6->int_flag = false; + len = oa_tc6_prepare_empty_chunk(tc6, + tc6->spi_tx_buf, + 1); + } + /* Perform SPI transfer */ + ret = oa_tc6_spi_transfer(tc6->spi, tc6->spi_tx_buf, + tc6->spi_rx_buf, len); + if (ret) { + netdev_err(tc6->netdev, "SPI transfer failed\n"); + continue; + } + /* Process the received chunks to get the ethernet frame + * or interrupt details. + */ + if (oa_tc6_process_rx_chunks(tc6, tc6->spi_rx_buf, len)) + continue; + } + + /* If there is a tx ethernet frame available */ + if (tc6->tx_flag || txc_wait) { + tc6->tx_flag = false; + txc_wait = false; + len = 0; + if (!tc6->txc) { + /* If there is no txc available to transport the + * tx ethernet frames then wait for the MAC-PHY + * interrupt to get the txc availability. + */ + txc_wait = true; + continue; + } else if (tc6->txc >= tc6->txc_needed) { + len = tc6->txc_needed * (tc6->cps + TC6_HDR_SIZE); + } else { + len = tc6->txc * (tc6->cps + TC6_HDR_SIZE); + } + memcpy(&tc6->spi_tx_buf[0], &tc6->eth_tx_buf[tx_pos], + len); + ret = oa_tc6_spi_transfer(tc6->spi, tc6->spi_tx_buf, + tc6->spi_rx_buf, len); + if (ret) { + netdev_err(tc6->netdev, "SPI transfer failed\n"); + continue; + } + /* Process the received chunks to get the ethernet frame + * or status. + */ + if (oa_tc6_process_rx_chunks(tc6, tc6->spi_rx_buf, + len)) { + /* In case of error while processing rx chunks + * discard the incomplete tx ethernet frame and + * resend it. + */ + tx_pos = 0; + tc6->txc_needed = tc6->total_txc_needed; + } else { + tx_pos += len; + tc6->txc_needed = tc6->txc_needed - + (len / (tc6->cps + TC6_HDR_SIZE)); + /* If the complete ethernet frame is transmitted + * then return the skb and update the details to + * n/w layer. + */ + if (!tc6->txc_needed) { + tc6->netdev->stats.tx_packets++; + tc6->netdev->stats.tx_bytes += tc6->tx_skb->len; + dev_kfree_skb(tc6->tx_skb); + tx_pos = 0; + tc6->tx_skb = NULL; + if (netif_queue_stopped(tc6->netdev)) + netif_wake_queue(tc6->netdev); + } else if (tc6->txc) { + /* If txc is available again and updated + * from the previous footer then perform + * tx again. + */ + tc6->tx_flag = true; + } else { + /* If there is no txc then wait for the + * interrupt to indicate txc + * availability. + */ + txc_wait = true; + } + } + } } return 0; } +static void oa_tc6_prepare_tx_chunks(struct oa_tc6 *tc6, u8 *buf, + struct sk_buff *skb) +{ + bool frame_started = false; + u16 copied_bytes = 0; + u16 copy_len; + u32 hdr; + + /* Calculate the number tx credit counts needed to transport the tx + * ethernet frame. + */ + tc6->txc_needed = (skb->len / tc6->cps) + ((skb->len % tc6->cps) ? 1 : 0); + tc6->total_txc_needed = tc6->txc_needed; + + for (u8 i = 0; i < tc6->txc_needed; i++) { + /* Prepare the header for each chunks to be transmitted */ + hdr = FIELD_PREP(DATA_HDR_DNC, 1) | + FIELD_PREP(DATA_HDR_DV, 1); + if (!frame_started) { + hdr |= FIELD_PREP(DATA_HDR_SV, 1) | + FIELD_PREP(DATA_HDR_SWO, 0); + frame_started = true; + } + if ((tc6->cps + copied_bytes) >= skb->len) { + copy_len = skb->len - copied_bytes; + hdr |= FIELD_PREP(DATA_HDR_EBO, copy_len - 1) | + FIELD_PREP(DATA_HDR_EV, 1); + } else { + copy_len = tc6->cps; + } + copied_bytes += copy_len; + hdr |= FIELD_PREP(DATA_HDR_P, oa_tc6_get_parity(hdr)); + hdr = cpu_to_be32(hdr); + *(u32 *)&buf[i * (tc6->cps + TC6_HDR_SIZE)] = hdr; + /* Copy the ethernet frame in the chunk payload section */ + memcpy(&buf[TC6_HDR_SIZE + (i * (tc6->cps + TC6_HDR_SIZE))], + &skb->data[copied_bytes - copy_len], copy_len); + } +} + +netdev_tx_t oa_tc6_send_eth_pkt(struct oa_tc6 *tc6, struct sk_buff *skb) +{ + if (tc6->tx_skb) { + netif_stop_queue(tc6->netdev); + return NETDEV_TX_BUSY; + } + + tc6->tx_skb = skb; + /* Prepare tx chunks using the tx ethernet frame */ + oa_tc6_prepare_tx_chunks(tc6, tc6->eth_tx_buf, skb); + + /* Wake tc6 task to perform tx transfer */ + tc6->tx_flag = true; + wake_up_interruptible(&tc6->tc6_wq); + + return NETDEV_TX_OK; +} +EXPORT_SYMBOL_GPL(oa_tc6_send_eth_pkt); + static irqreturn_t macphy_irq(int irq, void *dev_id) { struct oa_tc6 *tc6 = dev_id; @@ -293,6 +661,14 @@ int oa_tc6_configure(struct oa_tc6 *tc6, u8 cps, bool ctrl_prot, bool tx_cut_thr u32 regval; int ret; + /* Read BUFSTS register to get the current txc and rca. */ + ret = oa_tc6_read_register(tc6, OA_TC6_BUFSTS, ®val, 1); + if (ret) + return ret; + + tc6->txc = FIELD_GET(TXC, regval); + tc6->rca = FIELD_GET(RCA, regval); + /* Read and configure the IMASK0 register for unmasking the interrupts */ ret = oa_tc6_read_register(tc6, OA_TC6_IMASK0, ®val, 1); if (ret) @@ -326,7 +702,7 @@ int oa_tc6_configure(struct oa_tc6 *tc6, u8 cps, bool ctrl_prot, bool tx_cut_thr } EXPORT_SYMBOL_GPL(oa_tc6_configure); -struct oa_tc6 *oa_tc6_init(struct spi_device *spi) +struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev) { struct oa_tc6 *tc6; int ret; @@ -334,11 +710,39 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi) if (!spi) return NULL; + if (!netdev) + return NULL; + tc6 = kzalloc(sizeof(*tc6), GFP_KERNEL); if (!tc6) return NULL; tc6->spi = spi; + tc6->netdev = netdev; + + /* Allocate memory for the tx buffer used for SPI transfer. */ + tc6->spi_tx_buf = kzalloc(MAX_ETH_LEN + (OA_TC6_MAX_CPS * TC6_HDR_SIZE), + GFP_KERNEL); + if (!tc6->spi_tx_buf) + goto err_spi_tx_buf_alloc; + + /* Allocate memory for the rx buffer used for SPI transfer. */ + tc6->spi_rx_buf = kzalloc(MAX_ETH_LEN + (OA_TC6_MAX_CPS * TC6_FTR_SIZE), + GFP_KERNEL); + if (!tc6->spi_rx_buf) + goto err_spi_rx_buf_alloc; + + /* Allocate memory for the tx ethernet chunks to transfer on SPI. */ + tc6->eth_tx_buf = kzalloc(MAX_ETH_LEN + (OA_TC6_MAX_CPS * TC6_HDR_SIZE), + GFP_KERNEL); + if (!tc6->eth_tx_buf) + goto err_eth_tx_buf_alloc; + + /* Allocate memory for the rx ethernet packet. */ + tc6->eth_rx_buf = kzalloc(MAX_ETH_LEN + (OA_TC6_MAX_CPS * TC6_FTR_SIZE), + GFP_KERNEL); + if (!tc6->eth_rx_buf) + goto err_eth_rx_buf_alloc; /* Used for triggering the OA TC6 task */ init_waitqueue_head(&tc6->tc6_wq); @@ -372,6 +776,14 @@ struct oa_tc6 *oa_tc6_init(struct spi_device *spi) err_macphy_irq: kthread_stop(tc6->tc6_task); err_tc6_task: + kfree(tc6->eth_rx_buf); +err_eth_rx_buf_alloc: + kfree(tc6->eth_tx_buf); +err_eth_tx_buf_alloc: + kfree(tc6->spi_rx_buf); +err_spi_rx_buf_alloc: + kfree(tc6->spi_tx_buf); +err_spi_tx_buf_alloc: kfree(tc6); return NULL; } @@ -383,8 +795,13 @@ int oa_tc6_deinit(struct oa_tc6 *tc6) devm_free_irq(&tc6->spi->dev, tc6->spi->irq, tc6); ret = kthread_stop(tc6->tc6_task); - if (!ret) + if (!ret) { + kfree(tc6->eth_rx_buf); + kfree(tc6->eth_tx_buf); + kfree(tc6->spi_rx_buf); + kfree(tc6->spi_tx_buf); kfree(tc6); + } return ret; } EXPORT_SYMBOL_GPL(oa_tc6_deinit); diff --git a/include/linux/oa_tc6.h b/include/linux/oa_tc6.h index fa29c4e09720..61ac1cdfa7d6 100644 --- a/include/linux/oa_tc6.h +++ b/include/linux/oa_tc6.h @@ -6,6 +6,7 @@ */ #include +#include /* Control header */ #define CTRL_HDR_DNC BIT(31) /* Data-Not-Control */ @@ -17,10 +18,36 @@ #define CTRL_HDR_LEN GENMASK(7, 1) /* Length */ #define CTRL_HDR_P BIT(0) /* Parity Bit */ +/* Data header */ +#define DATA_HDR_DNC BIT(31) /* Data-Not-Control */ +#define DATA_HDR_SEQ BIT(30) /* Data Chunk Sequence */ +#define DATA_HDR_NORX BIT(29) /* No Receive */ +#define DATA_HDR_DV BIT(21) /* Data Valid */ +#define DATA_HDR_SV BIT(20) /* Start Valid */ +#define DATA_HDR_SWO GENMASK(19, 16) /* Start Word Offset */ +#define DATA_HDR_EV BIT(14) /* End Valid */ +#define DATA_HDR_EBO GENMASK(13, 8) /* End Byte Offset */ +#define DATA_HDR_P BIT(0) /* Header Parity Bit */ + +/* Data footer */ +#define DATA_FTR_EXST BIT(31) /* Extended Status */ +#define DATA_FTR_HDRB BIT(30) /* Received Header Bad */ +#define DATA_FTR_SYNC BIT(29) /* Configuration Synchronized */ +#define DATA_FTR_RCA GENMASK(28, 24) /* Receive Chunks Available */ +#define DATA_FTR_DV BIT(21) /* Data Valid */ +#define DATA_FTR_SV BIT(20) /* Start Valid */ +#define DATA_FTR_SWO GENMASK(19, 16) /* Start Word Offset */ +#define DATA_FTR_FD BIT(15) /* Frame Drop */ +#define DATA_FTR_EV BIT(14) /* End Valid */ +#define DATA_FTR_EBO GENMASK(13, 8) /* End Byte Offset */ +#define DATA_FTR_TXC GENMASK(5, 1) /* Transmit Credits */ +#define DATA_FTR_P BIT(0) /* Footer Parity Bit */ + /* Open Alliance TC6 Standard Control and Status Registers */ #define OA_TC6_RESET 0x0003 /* Reset Control and Status Register */ #define OA_TC6_CONFIG0 0x0004 /* Configuration Register #0 */ #define OA_TC6_STS0 0x0008 /* Status Register #0 */ +#define OA_TC6_BUFSTS 0x000B /* Buffer Status Register */ #define OA_TC6_IMASK0 0x000C /* Interrupt Mask Register #0 */ /* RESET register field */ @@ -33,6 +60,17 @@ #define PROTE BIT(5) /* Ctrl read/write Protection Enable */ #define CPS GENMASK(2, 0) /* Chunk Payload Size */ +/* STATUS0 register fields */ +#define CDPE BIT(12) /* Control Data Protection Error */ +#define TXFCSE BIT(11) /* Transmit Frame Check Sequence Error */ +#define RESETC BIT(6) /* Reset Complete */ +#define HDRE BIT(5) /* Header Error */ +#define LOFE BIT(4) /* Loss of Framing Error */ +#define RXBOE BIT(3) /* Receive Buffer Overflow Error */ +#define TXBUE BIT(2) /* Transmit Buffer Underflow Error */ +#define TXBOE BIT(1) /* Transmit Buffer Overflow Error */ +#define TXPE BIT(0) /* Transmit Protocol Error */ + /* Unmasking interrupt fields in IMASK0 */ #define HDREM ~BIT(5) /* Header Error Mask */ #define LOFEM ~BIT(4) /* Loss of Framing Error Mask */ @@ -44,24 +82,49 @@ /* STATUS0 register field */ #define RESETC BIT(6) /* Reset Complete */ +/* BUFSTS register fields */ +#define TXC GENMASK(15, 8) /* Transmit Credits Available */ +#define RCA GENMASK(7, 0) /* Receive Chunks Available */ + #define TC6_HDR_SIZE 4 /* Ctrl command header size as per OA */ #define TC6_FTR_SIZE 4 /* Ctrl command footer size ss per OA */ +#define FTR_OK 0 +#define FTR_ERR 1 + +#define MAX_ETH_LEN 1536 +#define OA_TC6_MAX_CPS 64 + struct oa_tc6 { struct completion rst_complete; struct task_struct *tc6_task; + struct net_device *netdev; wait_queue_head_t tc6_wq; struct spi_device *spi; + struct sk_buff *tx_skb; + u8 total_txc_needed; + bool rx_eth_started; bool tx_cut_thr; bool rx_cut_thr; bool ctrl_prot; + u8 *spi_tx_buf; + u8 *spi_rx_buf; + u8 *eth_tx_buf; + u8 *eth_rx_buf; bool int_flag; + u16 rxd_bytes; + u8 txc_needed; + bool tx_flag; + bool reset; u8 cps; + u8 txc; + u8 rca; }; -struct oa_tc6 *oa_tc6_init(struct spi_device *spi); +struct oa_tc6 *oa_tc6_init(struct spi_device *spi, struct net_device *netdev); int oa_tc6_deinit(struct oa_tc6 *tc6); int oa_tc6_write_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); int oa_tc6_read_register(struct oa_tc6 *tc6, u32 addr, u32 value[], u8 len); int oa_tc6_configure(struct oa_tc6 *tc6, u8 cps, bool ctrl_prot, bool tx_cut_thr, bool rx_cut_thr); +netdev_tx_t oa_tc6_send_eth_pkt(struct oa_tc6 *tc6, struct sk_buff *skb); From patchwork Fri Sep 8 14:29:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377542 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 77D1F538F for ; Fri, 8 Sep 2023 14:31:30 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DAB5A1FD9; Fri, 8 Sep 2023 07:30:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183451; x=1725719451; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=+rrMlOgsuHLTPFZQ7ei/a1ya5hmurM4G/a3RwuLZ0ko=; b=WJKp03zwzpzJSmdA6YMuy+E1WjghYGTBezD+hZBj4Dl+Tfl2BwWDGxPA ue+ne7jpcMnO2tymOnTAoC8Z6G+lF3cZ/RY9KmHb/DLqyxmRbLokpJfMv MWnKvMbwKR6lzOncP+wDeiJt6g3/GBiPKjqNYzsjxB2cOUr1x3KevD1u7 KN0tqDHBJA/t9dHEe+avi8tvzznMvxXRKCGlT/yHSUTTKB1/wuJ3uJWpx MsKibPsobc/bCK3tmX4q6LWhPG0cn3LcBDLIYkOJJzokC6fFKEC9yapok pz9jQqL12OEz1j9pxe0Q9rDLfsyI08UT5zn4JPt2q838+9fnIqVUDCo5N g==; X-CSE-ConnectionGUID: BfdJWj6dSkej858jQph4IQ== X-CSE-MsgGUID: F1a5nzbsQxmiVGW5yEPuUw== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="170641846" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:45 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:32 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:30:24 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 5/6] microchip: lan865x: add driver support for Microchip's LAN865X MACPHY Date: Fri, 8 Sep 2023 19:59:18 +0530 Message-ID: <20230908142919.14849-6-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC The LAN8650/1 is designed to conform to the OPEN Alliance 10BASE‑T1x MAC‑PHY Serial Interface specification, Version 1.1. The IEEE Clause 4 MAC integration provides the low pin count standard SPI interface to any microcontroller therefore providing Ethernet functionality without requiring MAC integration within the microcontroller. The LAN8650/1 operates as an SPI client supporting SCLK clock rates up to a maximum of 25 MHz. This SPI interface supports the transfer of both data (Ethernet frames) and control (register access). By default, the chunk data payload is 64 bytes in size. A smaller payload data size of 32 bytes is also supported and may be configured in the Chunk Payload Size (CPS) field of the Configuration 0 (OA_CONFIG0) register. Changing the chunk payload size requires the LAN8650/1 be reset and shall not be done during normal operation. The Ethernet Media Access Controller (MAC) module implements a 10 Mbps half duplex Ethernet MAC, compatible with the IEEE 802.3 standard. 10BASE-T1S physical layer transceiver integrated into the LAN8650/1. The PHY and MAC are connected via an internal Media Independent Interface (MII). Signed-off-by: Parthiban Veerasooran --- MAINTAINERS | 6 + drivers/net/ethernet/microchip/Kconfig | 10 + drivers/net/ethernet/microchip/Makefile | 3 + drivers/net/ethernet/microchip/lan865x.c | 589 +++++++++++++++++++++++ 4 files changed, 608 insertions(+) create mode 100644 drivers/net/ethernet/microchip/lan865x.c diff --git a/MAINTAINERS b/MAINTAINERS index c54454c7e7a1..666c042a15b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13879,6 +13879,12 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/microchip/lan743x_* +MICROCHIP LAN8650/1 10BASE-T1S MACPHY ETHERNET DRIVER +M: Parthiban Veerasooran +L: netdev@vger.kernel.org +S: Maintained +F: drivers/net/ethernet/microchip/lan865x.c + MICROCHIP LAN87xx/LAN937x T1 PHY DRIVER M: Arun Ramadoss R: UNGLinuxDriver@microchip.com diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 329e374b9539..d99be51b99f1 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -59,4 +59,14 @@ source "drivers/net/ethernet/microchip/lan966x/Kconfig" source "drivers/net/ethernet/microchip/sparx5/Kconfig" source "drivers/net/ethernet/microchip/vcap/Kconfig" +config LAN865X + tristate "LAN865x support" + depends on SPI + help + Support for the Microchip LAN8650/1 Rev.B0 Ethernet chip. It uses OPEN + Alliance 10BASE-T1x Serial Interface specification. + + To compile this driver as a module, choose M here. The module will be + called lan865x. + endif # NET_VENDOR_MICROCHIP diff --git a/drivers/net/ethernet/microchip/Makefile b/drivers/net/ethernet/microchip/Makefile index bbd349264e6f..315e850b2b26 100644 --- a/drivers/net/ethernet/microchip/Makefile +++ b/drivers/net/ethernet/microchip/Makefile @@ -12,3 +12,6 @@ lan743x-objs := lan743x_main.o lan743x_ethtool.o lan743x_ptp.o obj-$(CONFIG_LAN966X_SWITCH) += lan966x/ obj-$(CONFIG_SPARX5_SWITCH) += sparx5/ obj-$(CONFIG_VCAP) += vcap/ + +obj-$(CONFIG_LAN865X) += lan865x_t1s.o +lan865x_t1s-objs := lan865x.o ../oa_tc6.o diff --git a/drivers/net/ethernet/microchip/lan865x.c b/drivers/net/ethernet/microchip/lan865x.c new file mode 100644 index 000000000000..3c8ebf4c258f --- /dev/null +++ b/drivers/net/ethernet/microchip/lan865x.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Microchip's LAN865x 10BASE-T1S MAC-PHY driver + * + * Author: Parthiban Veerasooran + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "lan865x" +#define DRV_VERSION "0.1" + +#define REG_STDR_RESET 0x00000003 +#define REG_MAC_ADDR_BO 0x00010022 +#define REG_MAC_ADDR_L 0x00010024 +#define REG_MAC_ADDR_H 0x00010025 +#define REG_MAC_NW_CTRL 0x00010000 +#define REG_MAC_NW_CONFIG 0x00010001 +#define REG_MAC_HASHL 0x00010020 +#define REG_MAC_HASHH 0x00010021 +#define REG_MAC_ADDR_BO 0x00010022 +#define REG_MAC_ADDR_L 0x00010024 +#define REG_MAC_ADDR_H 0x00010025 + +#define CCS_Q0_TX_CFG 0x000A0081 +#define CCS_Q0_RX_CFG 0x000A0082 + +/* Buffer configuration for 32-bytes chunk payload */ +#define CCS_Q0_TX_CFG_32 0x70000000 +#define CCS_Q0_RX_CFG_32 0x30000C00 + +#define NW_RX_STATUS BIT(2) +#define NW_TX_STATUS BIT(3) +#define NW_DISABLE 0x0 + +#define MAC_PROMISCUOUS_MODE BIT(4) +#define MAC_MULTICAST_MODE BIT(6) +#define MAC_UNICAST_MODE BIT(7) + +#define TX_TIMEOUT (4 * HZ) +#define LAN865X_MSG_DEFAULT \ + (NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK) + +struct lan865x_priv { + struct net_device *netdev; + struct spi_device *spi; + struct oa_tc6 *tc6; + struct mii_bus *mdiobus; + struct phy_device *phydev; + struct device *dev; + u32 msg_enable; + bool txcte; + bool rxcte; + u32 cps; + bool protected; +}; + +static void lan865x_handle_link_change(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + phy_print_status(priv->phydev); +} + +static int lan865x_mdiobus_read(struct mii_bus *bus, int phy_id, int idx) +{ + struct lan865x_priv *priv = bus->priv; + u32 regval; + bool ret; + + ret = oa_tc6_read_register(priv->tc6, 0xFF00 | (idx & 0xFF), ®val, 1); + if (ret) + return -ENODEV; + + return regval; +} + +static int lan865x_mdiobus_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) +{ + struct lan865x_priv *priv = bus->priv; + u32 value = regval; + bool ret; + + ret = oa_tc6_write_register(priv->tc6, 0xFF00 | (idx & 0xFF), &value, 1); + if (ret) + return -ENODEV; + + return 0; +} + +static int lan865x_phy_init(struct lan865x_priv *priv) +{ + int ret; + + priv->mdiobus = mdiobus_alloc(); + if (!priv->mdiobus) { + netdev_err(priv->netdev, "MDIO bus alloc failed\n"); + return -ENODEV; + } + + priv->mdiobus->phy_mask = ~(u32)BIT(1); + priv->mdiobus->priv = priv; + priv->mdiobus->read = lan865x_mdiobus_read; + priv->mdiobus->write = lan865x_mdiobus_write; + priv->mdiobus->name = "lan865x-mdiobus"; + priv->mdiobus->parent = priv->dev; + + snprintf(priv->mdiobus->id, ARRAY_SIZE(priv->mdiobus->id), + "%s", dev_name(&priv->spi->dev)); + + ret = mdiobus_register(priv->mdiobus); + if (ret) { + netdev_err(priv->netdev, "Could not register MDIO bus\n"); + mdiobus_free(priv->mdiobus); + return ret; + } + priv->phydev = phy_find_first(priv->mdiobus); + if (!priv->phydev) { + netdev_err(priv->netdev, "No PHY found\n"); + mdiobus_unregister(priv->mdiobus); + mdiobus_free(priv->mdiobus); + return -ENODEV; + } + priv->phydev->is_internal = true; + ret = phy_connect_direct(priv->netdev, priv->phydev, + &lan865x_handle_link_change, + PHY_INTERFACE_MODE_INTERNAL); + if (ret) { + netdev_err(priv->netdev, "Can't attach PHY to %s\n", priv->mdiobus->id); + return ret; + } + phy_attached_info(priv->phydev); + return ret; +} + +static int lan865x_set_hw_macaddr(struct net_device *netdev) +{ + u32 regval; + bool ret; + struct lan865x_priv *priv = netdev_priv(netdev); + const u8 *mac = netdev->dev_addr; + + ret = oa_tc6_read_register(priv->tc6, REG_MAC_NW_CTRL, ®val, 1); + if (ret) + goto error_mac; + if ((regval & NW_TX_STATUS) | (regval & NW_RX_STATUS)) { + if (netif_msg_drv(priv)) + netdev_warn(netdev, "Hardware must be disabled for MAC setting\n"); + return -EBUSY; + } + /* MAC address setting */ + regval = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | + mac[0]; + ret = oa_tc6_write_register(priv->tc6, REG_MAC_ADDR_L, ®val, 1); + if (ret) + goto error_mac; + + regval = (mac[5] << 8) | mac[4]; + ret = oa_tc6_write_register(priv->tc6, REG_MAC_ADDR_H, ®val, 1); + if (ret) + goto error_mac; + + regval = (mac[5] << 24) | (mac[4] << 16) | + (mac[3] << 8) | mac[2]; + ret = oa_tc6_write_register(priv->tc6, REG_MAC_ADDR_BO, ®val, 1); + if (ret) + goto error_mac; + + return 0; + +error_mac: + return -ENODEV; +} + +static int +lan865x_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + int ret = 0; + + if (cmd->base.autoneg != AUTONEG_DISABLE || + cmd->base.speed != SPEED_10 || cmd->base.duplex != DUPLEX_HALF) { + if (netif_msg_link(priv)) + netdev_warn(netdev, "Unsupported link setting"); + ret = -EOPNOTSUPP; + } else { + if (netif_msg_link(priv)) + netdev_warn(netdev, "Hardware must be disabled to set link mode"); + ret = -EBUSY; + } + return ret; +} + +static int +lan865x_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + + cmd->base.speed = SPEED_10; + cmd->base.duplex = DUPLEX_HALF; + cmd->base.port = PORT_TP; + cmd->base.autoneg = AUTONEG_DISABLE; + + return 0; +} + +static void lan865x_set_msglevel(struct net_device *netdev, u32 val) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + priv->msg_enable = val; +} + +static u32 lan865x_get_msglevel(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + return priv->msg_enable; +} + +static void +lan865x_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) +{ + strscpy(info->driver, DRV_NAME, sizeof(info->driver)); + strscpy(info->version, DRV_VERSION, sizeof(info->version)); + strscpy(info->bus_info, + dev_name(netdev->dev.parent), sizeof(info->bus_info)); +} + +static const struct ethtool_ops lan865x_ethtool_ops = { + .get_drvinfo = lan865x_get_drvinfo, + .get_msglevel = lan865x_get_msglevel, + .set_msglevel = lan865x_set_msglevel, + .get_link_ksettings = lan865x_get_link_ksettings, + .set_link_ksettings = lan865x_set_link_ksettings, +}; + +static void lan865x_tx_timeout(struct net_device *netdev, unsigned int txqueue) +{ + netdev->stats.tx_errors++; +} + +static int lan865x_set_mac_address(struct net_device *netdev, void *addr) +{ + struct sockaddr *address = addr; + + if (netif_running(netdev)) + return -EBUSY; + if (!is_valid_ether_addr(address->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, address->sa_data); + return lan865x_set_hw_macaddr(netdev); +} + +static u32 lan865x_hash(u8 addr[ETH_ALEN]) +{ + return (ether_crc(ETH_ALEN, addr) >> 26) & 0x3f; +} + +static void lan865x_set_multicast_list(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + u32 regval = 0; + + if (netdev->flags & IFF_PROMISC) { + /* Enabling promiscuous mode */ + regval |= MAC_PROMISCUOUS_MODE; + regval &= (~MAC_MULTICAST_MODE); + regval &= (~MAC_UNICAST_MODE); + } else if (netdev->flags & IFF_ALLMULTI) { + /* Enabling all multicast mode */ + regval &= (~MAC_PROMISCUOUS_MODE); + regval |= MAC_MULTICAST_MODE; + regval &= (~MAC_UNICAST_MODE); + } else if (!netdev_mc_empty(netdev)) { + /* Enabling specific multicast addresses */ + struct netdev_hw_addr *ha; + u32 hash_lo = 0; + u32 hash_hi = 0; + + netdev_for_each_mc_addr(ha, netdev) { + u32 bit_num = lan865x_hash(ha->addr); + u32 mask = 1 << (bit_num & 0x1f); + + if (bit_num & 0x20) + hash_hi |= mask; + else + hash_lo |= mask; + } + if (oa_tc6_write_register(priv->tc6, REG_MAC_HASHH, &hash_hi, 1)) { + if (netif_msg_timer(priv)) + netdev_err(netdev, "Failed to write reg_hashh"); + return; + } + if (oa_tc6_write_register(priv->tc6, REG_MAC_HASHL, &hash_lo, 1)) { + if (netif_msg_timer(priv)) + netdev_err(netdev, "Failed to write reg_hashl"); + return; + } + regval &= (~MAC_PROMISCUOUS_MODE); + regval &= (~MAC_MULTICAST_MODE); + regval |= MAC_UNICAST_MODE; + } else { + /* enabling local mac address only */ + if (oa_tc6_write_register(priv->tc6, REG_MAC_HASHH, ®val, 1)) { + if (netif_msg_timer(priv)) + netdev_err(netdev, "Failed to write reg_hashh"); + return; + } + if (oa_tc6_write_register(priv->tc6, REG_MAC_HASHL, ®val, 1)) { + if (netif_msg_timer(priv)) + netdev_err(netdev, "Failed to write reg_hashl"); + return; + } + } + if (oa_tc6_write_register(priv->tc6, REG_MAC_NW_CONFIG, ®val, 1)) { + if (netif_msg_timer(priv)) + netdev_err(netdev, "Failed to enable promiscuous mode"); + } +} + +static netdev_tx_t lan865x_send_packet(struct sk_buff *skb, + struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + + return oa_tc6_send_eth_pkt(priv->tc6, skb); +} + +static int lan865x_hw_disable(struct lan865x_priv *priv) +{ + u32 regval = NW_DISABLE; + + if (oa_tc6_write_register(priv->tc6, REG_MAC_NW_CTRL, ®val, 1)) + return -ENODEV; + + return 0; +} + +static int lan865x_net_close(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + int ret; + + netif_stop_queue(netdev); + ret = lan865x_hw_disable(priv); + if (ret) { + if (netif_msg_ifup(priv)) + netdev_err(netdev, "Failed to disable the hardware\n"); + return ret; + } + + return 0; +} + +static int lan865x_hw_enable(struct lan865x_priv *priv) +{ + u32 regval = NW_TX_STATUS | NW_RX_STATUS; + + if (oa_tc6_write_register(priv->tc6, REG_MAC_NW_CTRL, ®val, 1)) + return -ENODEV; + + return 0; +} + +static int lan865x_net_open(struct net_device *netdev) +{ + struct lan865x_priv *priv = netdev_priv(netdev); + int ret; + + if (!is_valid_ether_addr(netdev->dev_addr)) { + if (netif_msg_ifup(priv)) + netdev_err(netdev, "Invalid MAC address %pm", netdev->dev_addr); + return -EADDRNOTAVAIL; + } + if (lan865x_hw_disable(priv)) { + if (netif_msg_ifup(priv)) + netdev_err(netdev, "Failed to disable the hardware\n"); + return -ENODEV; + } + ret = lan865x_set_hw_macaddr(netdev); + if (ret != 0) + return ret; + + if (lan865x_hw_enable(priv) != 0) { + if (netif_msg_ifup(priv)) + netdev_err(netdev, "Failed to enable hardware\n"); + return -ENODEV; + } + netif_start_queue(netdev); + + return 0; +} + +static const struct net_device_ops lan865x_netdev_ops = { + .ndo_open = lan865x_net_open, + .ndo_stop = lan865x_net_close, + .ndo_start_xmit = lan865x_send_packet, + .ndo_set_rx_mode = lan865x_set_multicast_list, + .ndo_set_mac_address = lan865x_set_mac_address, + .ndo_tx_timeout = lan865x_tx_timeout, + .ndo_validate_addr = eth_validate_addr, +}; + +static int lan865x_get_dt_data(struct lan865x_priv *priv) +{ + struct spi_device *spi = priv->spi; + int ret; + + if (of_property_present(spi->dev.of_node, "oa-chunk-size")) { + ret = of_property_read_u32(spi->dev.of_node, "oa-chunk-size", + &priv->cps); + if (ret < 0) + return ret; + } else { + priv->cps = 64; + dev_info(&spi->dev, "Property oa-chunk-size is not found in dt and proceeding with the size 64\n"); + } + + if (of_property_present(spi->dev.of_node, "oa-tx-cut-through")) + priv->txcte = true; + else + dev_info(&spi->dev, "Property oa-tx-cut-through is not found in dt and proceeding with tx store and forward mode\n"); + + if (of_property_present(spi->dev.of_node, "oa-rx-cut-through")) + priv->rxcte = true; + else + dev_info(&spi->dev, "Property oa-rx-cut-through is not found in dt and proceeding with rx store and forward mode\n"); + + if (of_property_present(spi->dev.of_node, "oa-protected")) + priv->protected = true; + else + dev_info(&spi->dev, "Property oa-protected is not found in dt and proceeding with protection enabled\n"); + + return 0; +} + +static int lan865x_probe(struct spi_device *spi) +{ + struct net_device *netdev; + struct lan865x_priv *priv; + u32 regval; + int ret; + + netdev = alloc_etherdev(sizeof(struct lan865x_priv)); + if (!netdev) + return -ENOMEM; + + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->spi = spi; + priv->msg_enable = 0; + spi_set_drvdata(spi, priv); + SET_NETDEV_DEV(netdev, &spi->dev); + + ret = lan865x_get_dt_data(priv); + if (ret) + return ret; + + spi->rt = true; + spi_setup(spi); + + priv->tc6 = oa_tc6_init(spi, netdev); + if (!priv->tc6) { + ret = -ENOMEM; + goto error_oa_tc6_init; + } + + if (priv->cps == 32) { + regval = CCS_Q0_TX_CFG_32; + ret = oa_tc6_write_register(priv->tc6, CCS_Q0_TX_CFG, ®val, 1); + if (ret) + return ret; + + regval = CCS_Q0_RX_CFG_32; + ret = oa_tc6_write_register(priv->tc6, CCS_Q0_RX_CFG, ®val, 1); + if (ret) + return ret; + } + + if (oa_tc6_configure(priv->tc6, priv->cps, priv->protected, priv->txcte, + priv->rxcte)) + goto err_macphy_config; + + ret = lan865x_phy_init(priv); + if (ret) + goto error_phy; + + if (device_get_ethdev_address(&spi->dev, netdev)) + eth_hw_addr_random(netdev); + + ret = lan865x_set_hw_macaddr(netdev); + if (ret) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, "Failed to configure MAC"); + goto error_set_mac; + } + + netdev->if_port = IF_PORT_10BASET; + netdev->irq = spi->irq; + netdev->netdev_ops = &lan865x_netdev_ops; + netdev->watchdog_timeo = TX_TIMEOUT; + netdev->ethtool_ops = &lan865x_ethtool_ops; + ret = register_netdev(netdev); + if (ret) { + if (netif_msg_probe(priv)) + dev_err(&spi->dev, "Register netdev failed (ret = %d)", + ret); + goto error_netdev_register; + } + + phy_start(priv->phydev); + return 0; + +error_netdev_register: +error_set_mac: + phy_disconnect(priv->phydev); + mdiobus_unregister(priv->mdiobus); + mdiobus_free(priv->mdiobus); +error_phy: +err_macphy_config: + oa_tc6_deinit(priv->tc6); +error_oa_tc6_init: + free_netdev(priv->netdev); + return ret; +} + +static void lan865x_remove(struct spi_device *spi) +{ + struct lan865x_priv *priv = spi_get_drvdata(spi); + + phy_stop(priv->phydev); + phy_disconnect(priv->phydev); + mdiobus_unregister(priv->mdiobus); + mdiobus_free(priv->mdiobus); + unregister_netdev(priv->netdev); + if (oa_tc6_deinit(priv->tc6)) + dev_err(&spi->dev, "Failed to deinitialize oa tc6\n"); + free_netdev(priv->netdev); +} + +#ifdef CONFIG_OF +static const struct of_device_id lan865x_dt_ids[] = { + { .compatible = "microchip,lan865x" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, lan865x_dt_ids); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id lan865x_acpi_ids[] = { + { .id = "LAN865X", + }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, lan865x_acpi_ids); +#endif + +static struct spi_driver lan865x_driver = { + .driver = { + .name = DRV_NAME, +#ifdef CONFIG_OF + .of_match_table = lan865x_dt_ids, +#endif +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(lan865x_acpi_ids), +#endif + }, + .probe = lan865x_probe, + .remove = lan865x_remove, +}; +module_spi_driver(lan865x_driver); + +MODULE_DESCRIPTION(DRV_NAME " 10Base-T1S MACPHY Ethernet Driver"); +MODULE_AUTHOR("Parthiban Veerasooran "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:" DRV_NAME); From patchwork Fri Sep 8 14:29:19 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Parthiban Veerasooran X-Patchwork-Id: 13377543 X-Patchwork-Delegate: kuba@kernel.org Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net [23.128.96.19]) (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 8C946134BD for ; Fri, 8 Sep 2023 14:31:37 +0000 (UTC) Received: from esa.microchip.iphmx.com (esa.microchip.iphmx.com [68.232.154.123]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D5AB211C; Fri, 8 Sep 2023 07:31:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=microchip.com; i=@microchip.com; q=dns/txt; s=mchp; t=1694183459; x=1725719459; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=TeHB0y9kfzmaAN0kkVn5rf9xamWzRSUzTry5VZYVrXg=; b=yGkGgXfVJ85d/6qW39ne3e/tnsETkfd4NWedguo5PVSbFI5LYW1G793f nBJOAGTu/mSliRhrSxVMjPgwczgqgfeTMvMB3KSV7nPVSlbJyeACzpTE2 b05fcYBsLZ5hVPjFBkt2B4DtDESTlj/SbJim/K5/kUUAP+YUQ0+lCsHCz wuT519fd85JVMGnae5uFjEAka/WNQ3P/CQJeIJdPQZ7xDp1f2h485sr5y BsxhgAZVXfO3tbWJSLk3PULin9j0CfXOkWvoYD87zfEIIcwDWlukVCPdz 4pjT3BXsjdGBGhPuVT6PL2Xap38zJwTaa2Czaa5a4zeTqTiEYPkMbkKRx g==; X-CSE-ConnectionGUID: BfdJWj6dSkej858jQph4IQ== X-CSE-MsgGUID: urbI0jbkRtODixBn6MAIdw== X-ThreatScanner-Verdict: Negative X-IronPort-AV: E=Sophos;i="6.02,237,1688454000"; d="scan'208";a="170641889" X-Amp-Result: SKIPPED(no attachment in message) Received: from unknown (HELO email.microchip.com) ([170.129.1.10]) by esa6.microchip.iphmx.com with ESMTP/TLS/ECDHE-RSA-AES128-GCM-SHA256; 08 Sep 2023 07:30:53 -0700 Received: from chn-vm-ex01.mchp-main.com (10.10.85.143) by chn-vm-ex03.mchp-main.com (10.10.85.151) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.21; Fri, 8 Sep 2023 07:30:39 -0700 Received: from CHE-LT-I17164LX.microchip.com (10.10.85.11) by chn-vm-ex01.mchp-main.com (10.10.85.143) with Microsoft SMTP Server id 15.1.2507.21 via Frontend Transport; Fri, 8 Sep 2023 07:30:32 -0700 From: Parthiban Veerasooran To: , , , , , , , , , , , , CC: , , , , , , , , , Parthiban Veerasooran Subject: [RFC PATCH net-next 6/6] microchip: lan865x: add device-tree support for Microchip's LAN865X MACPHY Date: Fri, 8 Sep 2023 19:59:19 +0530 Message-ID: <20230908142919.14849-7-Parthiban.Veerasooran@microchip.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> References: <20230908142919.14849-1-Parthiban.Veerasooran@microchip.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF, RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H5,RCVD_IN_MSPIKE_WL, SPF_HELO_PASS,SPF_NONE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net X-Patchwork-Delegate: kuba@kernel.org X-Patchwork-State: RFC Add device-tree support for Microchip's LAN865X MACPHY for configuring the OPEN Alliance 10BASE-T1x MACPHY Serial Interface parameters. Signed-off-by: Parthiban Veerasooran --- .../bindings/net/microchip,lan865x.yaml | 54 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 55 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/microchip,lan865x.yaml diff --git a/Documentation/devicetree/bindings/net/microchip,lan865x.yaml b/Documentation/devicetree/bindings/net/microchip,lan865x.yaml new file mode 100644 index 000000000000..3465b2c97690 --- /dev/null +++ b/Documentation/devicetree/bindings/net/microchip,lan865x.yaml @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/microchip,lan865x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip LAN8650/1 10BASE-T1S MACPHY Ethernet Controllers + +maintainers: + - Parthiban Veerasooran + +description: | + Device tree properties for LAN8650/1 10BASE-T1S MACPHY Ethernet + controller. + +allOf: + - $ref: ethernet-controller.yaml# + +properties: + compatible: + items: + - enum: + - microchip,lan865x + reg: + maxItems: 1 + + local-mac-address: true + oa-chunk-size: true + oa-tx-cut-through: true + oa-rx-cut-through: true + oa-protected: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + ethernet@1{ + compatible = "microchip,lan865x"; + reg = <1>; /* CE0 */ + local-mac-address = [04 05 06 01 02 03]; + oa-chunk-size = <64>; + oa-tx-cut-through; + oa-rx-cut-through; + oa-protected; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 666c042a15b2..2bbb7f17d74e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13883,6 +13883,7 @@ MICROCHIP LAN8650/1 10BASE-T1S MACPHY ETHERNET DRIVER M: Parthiban Veerasooran L: netdev@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/net/microchip,lan865x.yaml F: drivers/net/ethernet/microchip/lan865x.c MICROCHIP LAN87xx/LAN937x T1 PHY DRIVER