1// SPDX-License-Identifier: GPL-2.0-only
2/*******************************************************************************
3 This contains the functions to handle the normal descriptors.
4
5 Copyright (C) 2007-2009 STMicroelectronics Ltd
6
7
8 Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
9*******************************************************************************/
10
11#include <linux/stmmac.h>
12#include "common.h"
13#include "descs_com.h"
14
15static int ndesc_get_tx_status(struct stmmac_extra_stats *x,
16 struct dma_desc *p, void __iomem *ioaddr)
17{
18 unsigned int tdes0 = le32_to_cpu(p->des0);
19 unsigned int tdes1 = le32_to_cpu(p->des1);
20 int ret = tx_done;
21
22 /* Get tx owner first */
23 if (unlikely(tdes0 & TDES0_OWN))
24 return tx_dma_own;
25
26 /* Verify tx error by looking at the last segment. */
27 if (likely(!(tdes1 & TDES1_LAST_SEGMENT)))
28 return tx_not_ls;
29
30 if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) {
31 if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) {
32 x->tx_underflow++;
33 }
34 if (unlikely(tdes0 & TDES0_NO_CARRIER)) {
35 x->tx_carrier++;
36 }
37 if (unlikely(tdes0 & TDES0_LOSS_CARRIER)) {
38 x->tx_losscarrier++;
39 }
40 if (unlikely((tdes0 & TDES0_EXCESSIVE_DEFERRAL) ||
41 (tdes0 & TDES0_EXCESSIVE_COLLISIONS) ||
42 (tdes0 & TDES0_LATE_COLLISION))) {
43 unsigned int collisions;
44
45 collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3;
46 x->tx_collision += collisions;
47 }
48 ret = tx_err;
49 }
50
51 if (tdes0 & TDES0_VLAN_FRAME)
52 x->tx_vlan++;
53
54 if (unlikely(tdes0 & TDES0_DEFERRED))
55 x->tx_deferred++;
56
57 return ret;
58}
59
60static int ndesc_get_tx_len(struct dma_desc *p)
61{
62 return (le32_to_cpu(p->des1) & RDES1_BUFFER1_SIZE_MASK);
63}
64
65/* This function verifies if each incoming frame has some errors
66 * and, if required, updates the multicast statistics.
67 * In case of success, it returns good_frame because the GMAC device
68 * is supposed to be able to compute the csum in HW. */
69static int ndesc_get_rx_status(struct stmmac_extra_stats *x,
70 struct dma_desc *p)
71{
72 int ret = good_frame;
73 unsigned int rdes0 = le32_to_cpu(p->des0);
74
75 if (unlikely(rdes0 & RDES0_OWN))
76 return dma_own;
77
78 if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) {
79 x->rx_length++;
80 return discard_frame;
81 }
82
83 if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) {
84 if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR))
85 x->rx_desc++;
86 if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL))
87 x->sa_filter_fail++;
88 if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR))
89 x->overflow_error++;
90 if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR))
91 x->ipc_csum_error++;
92 if (unlikely(rdes0 & RDES0_COLLISION)) {
93 x->rx_collision++;
94 }
95 if (unlikely(rdes0 & RDES0_CRC_ERROR)) {
96 x->rx_crc_errors++;
97 }
98 ret = discard_frame;
99 }
100 if (unlikely(rdes0 & RDES0_DRIBBLING))
101 x->dribbling_bit++;
102
103 if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) {
104 x->rx_length++;
105 ret = discard_frame;
106 }
107 if (unlikely(rdes0 & RDES0_MII_ERROR)) {
108 x->rx_mii++;
109 ret = discard_frame;
110 }
111#ifdef STMMAC_VLAN_TAG_USED
112 if (rdes0 & RDES0_VLAN_TAG)
113 x->vlan_tag++;
114#endif
115 return ret;
116}
117
118static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode,
119 int end, int bfsize)
120{
121 int bfsize1;
122
123 p->des0 |= cpu_to_le32(RDES0_OWN);
124
125 bfsize1 = min(bfsize, BUF_SIZE_2KiB - 1);
126 p->des1 |= cpu_to_le32(bfsize1 & RDES1_BUFFER1_SIZE_MASK);
127
128 if (mode == STMMAC_CHAIN_MODE)
129 ndesc_rx_set_on_chain(p, end);
130 else
131 ndesc_rx_set_on_ring(p, end, bfsize);
132
133 if (disable_rx_ic)
134 p->des1 |= cpu_to_le32(RDES1_DISABLE_IC);
135}
136
137static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end)
138{
139 p->des0 &= cpu_to_le32(~TDES0_OWN);
140 if (mode == STMMAC_CHAIN_MODE)
141 ndesc_tx_set_on_chain(p);
142 else
143 ndesc_end_tx_desc_on_ring(p, end);
144}
145
146static int ndesc_get_tx_owner(struct dma_desc *p)
147{
148 return (le32_to_cpu(p->des0) & TDES0_OWN) >> 31;
149}
150
151static void ndesc_set_tx_owner(struct dma_desc *p)
152{
153 p->des0 |= cpu_to_le32(TDES0_OWN);
154}
155
156static void ndesc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
157{
158 p->des0 |= cpu_to_le32(RDES0_OWN);
159}
160
161static int ndesc_get_tx_ls(struct dma_desc *p)
162{
163 return (le32_to_cpu(p->des1) & TDES1_LAST_SEGMENT) >> 30;
164}
165
166static void ndesc_release_tx_desc(struct dma_desc *p, int mode)
167{
168 int ter = (le32_to_cpu(p->des1) & TDES1_END_RING) >> 25;
169
170 memset(p, 0, offsetof(struct dma_desc, des2));
171 if (mode == STMMAC_CHAIN_MODE)
172 ndesc_tx_set_on_chain(p);
173 else
174 ndesc_end_tx_desc_on_ring(p, end: ter);
175}
176
177static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len,
178 bool csum_flag, int mode, bool tx_own,
179 bool ls, unsigned int tot_pkt_len)
180{
181 unsigned int tdes1 = le32_to_cpu(p->des1);
182
183 if (is_fs)
184 tdes1 |= TDES1_FIRST_SEGMENT;
185 else
186 tdes1 &= ~TDES1_FIRST_SEGMENT;
187
188 if (likely(csum_flag))
189 tdes1 |= (TX_CIC_FULL) << TDES1_CHECKSUM_INSERTION_SHIFT;
190 else
191 tdes1 &= ~(TX_CIC_FULL << TDES1_CHECKSUM_INSERTION_SHIFT);
192
193 if (ls)
194 tdes1 |= TDES1_LAST_SEGMENT;
195
196 p->des1 = cpu_to_le32(tdes1);
197
198 if (mode == STMMAC_CHAIN_MODE)
199 norm_set_tx_desc_len_on_chain(p, len);
200 else
201 norm_set_tx_desc_len_on_ring(p, len);
202
203 if (tx_own)
204 p->des0 |= cpu_to_le32(TDES0_OWN);
205}
206
207static void ndesc_set_tx_ic(struct dma_desc *p)
208{
209 p->des1 |= cpu_to_le32(TDES1_INTERRUPT);
210}
211
212static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type)
213{
214 unsigned int csum = 0;
215
216 /* The type-1 checksum offload engines append the checksum at
217 * the end of frame and the two bytes of checksum are added in
218 * the length.
219 * Adjust for that in the framelen for type-1 checksum offload
220 * engines
221 */
222 if (rx_coe_type == STMMAC_RX_COE_TYPE1)
223 csum = 2;
224
225 return (((le32_to_cpu(p->des0) & RDES0_FRAME_LEN_MASK)
226 >> RDES0_FRAME_LEN_SHIFT) -
227 csum);
228
229}
230
231static void ndesc_enable_tx_timestamp(struct dma_desc *p)
232{
233 p->des1 |= cpu_to_le32(TDES1_TIME_STAMP_ENABLE);
234}
235
236static int ndesc_get_tx_timestamp_status(struct dma_desc *p)
237{
238 return (le32_to_cpu(p->des0) & TDES0_TIME_STAMP_STATUS) >> 17;
239}
240
241static void ndesc_get_timestamp(void *desc, u32 ats, u64 *ts)
242{
243 struct dma_desc *p = (struct dma_desc *)desc;
244 u64 ns;
245
246 ns = le32_to_cpu(p->des2);
247 /* convert high/sec time stamp value to nanosecond */
248 ns += le32_to_cpu(p->des3) * 1000000000ULL;
249
250 *ts = ns;
251}
252
253static int ndesc_get_rx_timestamp_status(void *desc, void *next_desc, u32 ats)
254{
255 struct dma_desc *p = (struct dma_desc *)desc;
256
257 if ((le32_to_cpu(p->des2) == 0xffffffff) &&
258 (le32_to_cpu(p->des3) == 0xffffffff))
259 /* timestamp is corrupted, hence don't store it */
260 return 0;
261 else
262 return 1;
263}
264
265static void ndesc_display_ring(void *head, unsigned int size, bool rx,
266 dma_addr_t dma_rx_phy, unsigned int desc_size)
267{
268 struct dma_desc *p = (struct dma_desc *)head;
269 dma_addr_t dma_addr;
270 int i;
271
272 pr_info("%s descriptor ring:\n", rx ? "RX" : "TX");
273
274 for (i = 0; i < size; i++) {
275 u64 x;
276 dma_addr = dma_rx_phy + i * sizeof(*p);
277
278 x = *(u64 *)p;
279 pr_info("%03d [%pad]: 0x%x 0x%x 0x%x 0x%x",
280 i, &dma_addr,
281 (unsigned int)x, (unsigned int)(x >> 32),
282 p->des2, p->des3);
283 p++;
284 }
285 pr_info("\n");
286}
287
288static void ndesc_set_addr(struct dma_desc *p, dma_addr_t addr)
289{
290 p->des2 = cpu_to_le32(addr);
291}
292
293static void ndesc_clear(struct dma_desc *p)
294{
295 p->des2 = 0;
296}
297
298const struct stmmac_desc_ops ndesc_ops = {
299 .tx_status = ndesc_get_tx_status,
300 .rx_status = ndesc_get_rx_status,
301 .get_tx_len = ndesc_get_tx_len,
302 .init_rx_desc = ndesc_init_rx_desc,
303 .init_tx_desc = ndesc_init_tx_desc,
304 .get_tx_owner = ndesc_get_tx_owner,
305 .release_tx_desc = ndesc_release_tx_desc,
306 .prepare_tx_desc = ndesc_prepare_tx_desc,
307 .set_tx_ic = ndesc_set_tx_ic,
308 .get_tx_ls = ndesc_get_tx_ls,
309 .set_tx_owner = ndesc_set_tx_owner,
310 .set_rx_owner = ndesc_set_rx_owner,
311 .get_rx_frame_len = ndesc_get_rx_frame_len,
312 .enable_tx_timestamp = ndesc_enable_tx_timestamp,
313 .get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
314 .get_timestamp = ndesc_get_timestamp,
315 .get_rx_timestamp_status = ndesc_get_rx_timestamp_status,
316 .display_ring = ndesc_display_ring,
317 .set_addr = ndesc_set_addr,
318 .clear = ndesc_clear,
319};
320

source code of linux/drivers/net/ethernet/stmicro/stmmac/norm_desc.c