Hi William, Thank you for reviewing the patch. On 11/09/23 6:29 pm, Ziyang Xuan (William) wrote: > [Some people who received this message don't often get email from william.xuanziyang@xxxxxxxxxx. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe > >> 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 <Parthiban.Veerasooran@xxxxxxxxxxxxx> >> --- >> 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 <parthiban.veerasooran@xxxxxxxxxxxxx> >> */ >> >> +#include <linux/etherdevice.h> >> #include <linux/bitfield.h> >> #include <linux/interrupt.h> >> #include <linux/oa_tc6.h> >> @@ -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; > Unnecessary initialization for skb. Ah yes, will remove it in the next revision. > >> + >> + /* 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); > netif_rx() may fail, I think it is good to add check and statistics. Sure, will do it in the next revision. Best Regards, Parthiban V > >> + } >> +} >> + >> +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 <linux/spi/spi.h> >> +#include <linux/netdevice.h> >> >> /* 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); >>