1// SPDX-License-Identifier: GPL-2.0
2/* 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 else
51 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, len: pkg_len - 4); /* Minus FCS */
65 skb->ip_summed = CHECKSUM_NONE;
66 skb->protocol = eth_type_trans(skb, dev: 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, length: 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(dev: &comm->pdev->dev, dma_addr: sinfo->mapping)) {
88 dev_kfree_skb_irq(skb: 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(addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
130 mask &= ~MAC_INT_RX;
131 writel(val: mask, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
132 spin_unlock_irqrestore(lock: &comm->int_mask_lock, flags);
133
134 napi_complete(n: 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(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 else
166 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(skb: 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(dev: comm->ndev[i]))
197 netif_wake_queue(dev: comm->ndev[i]);
198
199 spin_unlock(lock: &comm->tx_lock);
200
201 spin_lock_irqsave(&comm->int_mask_lock, flags);
202 mask = readl(addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
203 mask &= ~MAC_INT_TX;
204 writel(val: mask, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
205 spin_unlock_irqrestore(lock: &comm->int_mask_lock, flags);
206
207 napi_complete(n: 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(addr: 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(val: status, addr: comm->l2sw_reg_base + L2SW_SW_INT_STATUS_0);
224
225 if (status & MAC_INT_RX) {
226 /* Disable RX interrupts. */
227 spin_lock(lock: &comm->int_mask_lock);
228 mask = readl(addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
229 mask |= MAC_INT_RX;
230 writel(val: mask, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
231 spin_unlock(lock: &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(n: &comm->rx_napi);
243 }
244
245 if (status & MAC_INT_TX) {
246 /* Disable TX interrupts. */
247 spin_lock(lock: &comm->int_mask_lock);
248 mask = readl(addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
249 mask |= MAC_INT_TX;
250 writel(val: mask, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
251 spin_unlock(lock: &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(lock: &comm->int_mask_lock);
262 mask = readl(addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
263 mask &= ~MAC_INT_TX;
264 writel(val: mask, addr: comm->l2sw_reg_base + L2SW_SW_INT_MASK_0);
265 spin_unlock(lock: &comm->int_mask_lock);
266 } else {
267 napi_schedule(n: &comm->tx_napi);
268 }
269 }
270
271spl2sw_ethernet_int_out:
272 return IRQ_HANDLED;
273}
274

source code of linux/drivers/net/ethernet/sunplus/spl2sw_int.c