brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · a37c9a4 Raw
274 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright Sunplus Technology Co., Ltd.3 *       All rights reserved.4 */5 6#include <linux/platform_device.h>7#include <linux/etherdevice.h>8#include <linux/netdevice.h>9#include <linux/bitfield.h>10#include <linux/spinlock.h>11#include <linux/of_mdio.h>12 13#include "spl2sw_register.h"14#include "spl2sw_define.h"15#include "spl2sw_int.h"16 17int spl2sw_rx_poll(struct napi_struct *napi, int budget)18{19	struct spl2sw_common *comm = container_of(napi, struct spl2sw_common, rx_napi);20	struct spl2sw_mac_desc *desc, *h_desc;21	struct net_device_stats *stats;22	struct sk_buff *skb, *new_skb;23	struct spl2sw_skb_info *sinfo;24	int budget_left = budget;25	unsigned long flags;26	u32 rx_pos, pkg_len;27	u32 num, rx_count;28	s32 queue;29	u32 mask;30	int port;31	u32 cmd;32	u32 len;33 34	/* Process high-priority queue and then low-priority queue. */35	for (queue = 0; queue < RX_DESC_QUEUE_NUM; queue++) {36		rx_pos = comm->rx_pos[queue];37		rx_count = comm->rx_desc_num[queue];38 39		for (num = 0; num < rx_count && budget_left; num++) {40			sinfo = comm->rx_skb_info[queue] + rx_pos;41			desc = comm->rx_desc[queue] + rx_pos;42			cmd = desc->cmd1;43 44			if (cmd & RXD_OWN)45				break;46 47			port = FIELD_GET(RXD_PKT_SP, cmd);48			if (port < MAX_NETDEV_NUM && comm->ndev[port])49				stats = &comm->ndev[port]->stats;50			else51				goto spl2sw_rx_poll_rec_err;52 53			pkg_len = FIELD_GET(RXD_PKT_LEN, cmd);54			if (unlikely((cmd & RXD_ERR_CODE) || pkg_len < ETH_ZLEN + 4)) {55				stats->rx_length_errors++;56				stats->rx_dropped++;57				goto spl2sw_rx_poll_rec_err;58			}59 60			dma_unmap_single(&comm->pdev->dev, sinfo->mapping,61					 comm->rx_desc_buff_size, DMA_FROM_DEVICE);62 63			skb = sinfo->skb;64			skb_put(skb, pkg_len - 4); /* Minus FCS */65			skb->ip_summed = CHECKSUM_NONE;66			skb->protocol = eth_type_trans(skb, comm->ndev[port]);67			len = skb->len;68			netif_receive_skb(skb);69 70			stats->rx_packets++;71			stats->rx_bytes += len;72 73			/* Allocate a new skb for receiving. */74			new_skb = netdev_alloc_skb(NULL, comm->rx_desc_buff_size);75			if (unlikely(!new_skb)) {76				desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?77					     RXD_EOR : 0;78				sinfo->skb = NULL;79				sinfo->mapping = 0;80				desc->addr1 = 0;81				goto spl2sw_rx_poll_alloc_err;82			}83 84			sinfo->mapping = dma_map_single(&comm->pdev->dev, new_skb->data,85							comm->rx_desc_buff_size,86							DMA_FROM_DEVICE);87			if (dma_mapping_error(&comm->pdev->dev, sinfo->mapping)) {88				dev_kfree_skb_irq(new_skb);89				desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?90					     RXD_EOR : 0;91				sinfo->skb = NULL;92				sinfo->mapping = 0;93				desc->addr1 = 0;94				goto spl2sw_rx_poll_alloc_err;95			}96 97			sinfo->skb = new_skb;98			desc->addr1 = sinfo->mapping;99 100spl2sw_rx_poll_rec_err:101			desc->cmd2 = (rx_pos == comm->rx_desc_num[queue] - 1) ?102				     RXD_EOR | comm->rx_desc_buff_size :103				     comm->rx_desc_buff_size;104 105			wmb();	/* Set RXD_OWN after other fields are effective. */106			desc->cmd1 = RXD_OWN;107 108spl2sw_rx_poll_alloc_err:109			/* Move rx_pos to next position */110			rx_pos = ((rx_pos + 1) == comm->rx_desc_num[queue]) ? 0 : rx_pos + 1;111 112			budget_left--;113 114			/* If there are packets in high-priority queue,115			 * stop processing low-priority queue.116			 */117			if (queue == 1 && !(h_desc->cmd1 & RXD_OWN))118				break;119		}120 121		comm->rx_pos[queue] = rx_pos;122 123		/* Save pointer to last rx descriptor of high-priority queue. */124		if (queue == 0)125			h_desc = comm->rx_desc[queue] + rx_pos;126	}127 128	spin_lock_irqsave(&comm->int_mask_lock, flags);129	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);130	mask &= ~MAC_INT_RX;131	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);132	spin_unlock_irqrestore(&comm->int_mask_lock, flags);133 134	napi_complete(napi);135	return budget - budget_left;136}137 138int spl2sw_tx_poll(struct napi_struct *napi, int budget)139{140	struct spl2sw_common *comm = container_of(napi, struct spl2sw_common, tx_napi);141	struct spl2sw_skb_info *skbinfo;142	struct net_device_stats *stats;143	int budget_left = budget;144	unsigned long flags;145	u32 tx_done_pos;146	u32 mask;147	u32 cmd;148	int i;149 150	spin_lock(&comm->tx_lock);151 152	tx_done_pos = comm->tx_done_pos;153	while (((tx_done_pos != comm->tx_pos) || (comm->tx_desc_full == 1)) && budget_left) {154		cmd = comm->tx_desc[tx_done_pos].cmd1;155		if (cmd & TXD_OWN)156			break;157 158		skbinfo = &comm->tx_temp_skb_info[tx_done_pos];159		if (unlikely(!skbinfo->skb))160			goto spl2sw_tx_poll_next;161 162		i = ffs(FIELD_GET(TXD_VLAN, cmd)) - 1;163		if (i < MAX_NETDEV_NUM && comm->ndev[i])164			stats = &comm->ndev[i]->stats;165		else166			goto spl2sw_tx_poll_unmap;167 168		if (unlikely(cmd & (TXD_ERR_CODE))) {169			stats->tx_errors++;170		} else {171			stats->tx_packets++;172			stats->tx_bytes += skbinfo->len;173		}174 175spl2sw_tx_poll_unmap:176		dma_unmap_single(&comm->pdev->dev, skbinfo->mapping, skbinfo->len,177				 DMA_TO_DEVICE);178		skbinfo->mapping = 0;179		dev_kfree_skb_irq(skbinfo->skb);180		skbinfo->skb = NULL;181 182spl2sw_tx_poll_next:183		/* Move tx_done_pos to next position */184		tx_done_pos = ((tx_done_pos + 1) == TX_DESC_NUM) ? 0 : tx_done_pos + 1;185 186		if (comm->tx_desc_full == 1)187			comm->tx_desc_full = 0;188 189		budget_left--;190	}191 192	comm->tx_done_pos = tx_done_pos;193	if (!comm->tx_desc_full)194		for (i = 0; i < MAX_NETDEV_NUM; i++)195			if (comm->ndev[i])196				if (netif_queue_stopped(comm->ndev[i]))197					netif_wake_queue(comm->ndev[i]);198 199	spin_unlock(&comm->tx_lock);200 201	spin_lock_irqsave(&comm->int_mask_lock, flags);202	mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);203	mask &= ~MAC_INT_TX;204	writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);205	spin_unlock_irqrestore(&comm->int_mask_lock, flags);206 207	napi_complete(napi);208	return budget - budget_left;209}210 211irqreturn_t spl2sw_ethernet_interrupt(int irq, void *dev_id)212{213	struct spl2sw_common *comm = (struct spl2sw_common *)dev_id;214	u32 status;215	u32 mask;216	int i;217 218	status = readl(comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);219	if (unlikely(!status)) {220		dev_dbg(&comm->pdev->dev, "Interrupt status is null!\n");221		goto spl2sw_ethernet_int_out;222	}223	writel(status, comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);224 225	if (status & MAC_INT_RX) {226		/* Disable RX interrupts. */227		spin_lock(&comm->int_mask_lock);228		mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);229		mask |= MAC_INT_RX;230		writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);231		spin_unlock(&comm->int_mask_lock);232 233		if (unlikely(status & MAC_INT_RX_DES_ERR)) {234			for (i = 0; i < MAX_NETDEV_NUM; i++)235				if (comm->ndev[i]) {236					comm->ndev[i]->stats.rx_fifo_errors++;237					break;238				}239			dev_dbg(&comm->pdev->dev, "Illegal RX Descriptor!\n");240		}241 242		napi_schedule(&comm->rx_napi);243	}244 245	if (status & MAC_INT_TX) {246		/* Disable TX interrupts. */247		spin_lock(&comm->int_mask_lock);248		mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);249		mask |= MAC_INT_TX;250		writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);251		spin_unlock(&comm->int_mask_lock);252 253		if (unlikely(status & MAC_INT_TX_DES_ERR)) {254			for (i = 0; i < MAX_NETDEV_NUM; i++)255				if (comm->ndev[i]) {256					comm->ndev[i]->stats.tx_fifo_errors++;257					break;258				}259			dev_dbg(&comm->pdev->dev, "Illegal TX Descriptor Error\n");260 261			spin_lock(&comm->int_mask_lock);262			mask = readl(comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);263			mask &= ~MAC_INT_TX;264			writel(mask, comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);265			spin_unlock(&comm->int_mask_lock);266		} else {267			napi_schedule(&comm->tx_napi);268		}269	}270 271spl2sw_ethernet_int_out:272	return IRQ_HANDLED;273}274