]>
Commit | Line | Data |
---|---|---|
56b106ae GC |
1 | /******************************************************************************* |
2 | This contains the functions to handle the normal descriptors. | |
3 | ||
4 | Copyright (C) 2007-2009 STMicroelectronics Ltd | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify it | |
7 | under the terms and conditions of the GNU General Public License, | |
8 | version 2, as published by the Free Software Foundation. | |
9 | ||
10 | This program is distributed in the hope it will be useful, but WITHOUT | |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | more details. | |
14 | ||
15 | You should have received a copy of the GNU General Public License along with | |
16 | this program; if not, write to the Free Software Foundation, Inc., | |
17 | 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | |
18 | ||
19 | The full GNU General Public License is included in this distribution in | |
20 | the file called "COPYING". | |
21 | ||
22 | Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> | |
23 | *******************************************************************************/ | |
24 | ||
38912bdb | 25 | #include <linux/stmmac.h> |
56b106ae | 26 | #include "common.h" |
286a8372 | 27 | #include "descs_com.h" |
56b106ae GC |
28 | |
29 | static int ndesc_get_tx_status(void *data, struct stmmac_extra_stats *x, | |
ad01b7d4 | 30 | struct dma_desc *p, void __iomem *ioaddr) |
56b106ae | 31 | { |
56b106ae | 32 | struct net_device_stats *stats = (struct net_device_stats *)data; |
293e4365 | 33 | unsigned int tdes0 = p->des0; |
c363b658 FG |
34 | unsigned int tdes1 = p->des1; |
35 | int ret = tx_done; | |
36 | ||
37 | /* Get tx owner first */ | |
38 | if (unlikely(tdes0 & TDES0_OWN)) | |
39 | return tx_dma_own; | |
40 | ||
41 | /* Verify tx error by looking at the last segment. */ | |
42 | if (likely(!(tdes1 & TDES1_LAST_SEGMENT))) | |
43 | return tx_not_ls; | |
56b106ae | 44 | |
293e4365 GC |
45 | if (unlikely(tdes0 & TDES0_ERROR_SUMMARY)) { |
46 | if (unlikely(tdes0 & TDES0_UNDERFLOW_ERROR)) { | |
56b106ae GC |
47 | x->tx_underflow++; |
48 | stats->tx_fifo_errors++; | |
49 | } | |
293e4365 | 50 | if (unlikely(tdes0 & TDES0_NO_CARRIER)) { |
56b106ae GC |
51 | x->tx_carrier++; |
52 | stats->tx_carrier_errors++; | |
53 | } | |
293e4365 | 54 | if (unlikely(tdes0 & TDES0_LOSS_CARRIER)) { |
56b106ae GC |
55 | x->tx_losscarrier++; |
56 | stats->tx_carrier_errors++; | |
57 | } | |
293e4365 GC |
58 | if (unlikely((tdes0 & TDES0_EXCESSIVE_DEFERRAL) || |
59 | (tdes0 & TDES0_EXCESSIVE_COLLISIONS) || | |
60 | (tdes0 & TDES0_LATE_COLLISION))) { | |
61 | unsigned int collisions; | |
62 | ||
63 | collisions = (tdes0 & TDES0_COLLISION_COUNT_MASK) >> 3; | |
64 | stats->collisions += collisions; | |
65 | } | |
c363b658 | 66 | ret = tx_err; |
56b106ae | 67 | } |
3c20f72f | 68 | |
293e4365 | 69 | if (tdes0 & TDES0_VLAN_FRAME) |
3c20f72f | 70 | x->tx_vlan++; |
3c20f72f | 71 | |
293e4365 | 72 | if (unlikely(tdes0 & TDES0_DEFERRED)) |
56b106ae GC |
73 | x->tx_deferred++; |
74 | ||
75 | return ret; | |
76 | } | |
77 | ||
78 | static int ndesc_get_tx_len(struct dma_desc *p) | |
79 | { | |
293e4365 | 80 | return (p->des1 & RDES1_BUFFER1_SIZE_MASK); |
56b106ae GC |
81 | } |
82 | ||
83 | /* This function verifies if each incoming frame has some errors | |
84 | * and, if required, updates the multicast statistics. | |
3c20f72f GC |
85 | * In case of success, it returns good_frame because the GMAC device |
86 | * is supposed to be able to compute the csum in HW. */ | |
56b106ae GC |
87 | static int ndesc_get_rx_status(void *data, struct stmmac_extra_stats *x, |
88 | struct dma_desc *p) | |
89 | { | |
3c20f72f | 90 | int ret = good_frame; |
293e4365 | 91 | unsigned int rdes0 = p->des0; |
56b106ae GC |
92 | struct net_device_stats *stats = (struct net_device_stats *)data; |
93 | ||
c1fa3212 FG |
94 | if (unlikely(rdes0 & RDES0_OWN)) |
95 | return dma_own; | |
96 | ||
293e4365 | 97 | if (unlikely(!(rdes0 & RDES0_LAST_DESCRIPTOR))) { |
ceb69499 GC |
98 | pr_warn("%s: Oversized frame spanned multiple buffers\n", |
99 | __func__); | |
56b106ae GC |
100 | stats->rx_length_errors++; |
101 | return discard_frame; | |
102 | } | |
103 | ||
293e4365 GC |
104 | if (unlikely(rdes0 & RDES0_ERROR_SUMMARY)) { |
105 | if (unlikely(rdes0 & RDES0_DESCRIPTOR_ERROR)) | |
56b106ae | 106 | x->rx_desc++; |
293e4365 | 107 | if (unlikely(rdes0 & RDES0_SA_FILTER_FAIL)) |
3c20f72f | 108 | x->sa_filter_fail++; |
293e4365 | 109 | if (unlikely(rdes0 & RDES0_OVERFLOW_ERROR)) |
3c20f72f | 110 | x->overflow_error++; |
293e4365 | 111 | if (unlikely(rdes0 & RDES0_IPC_CSUM_ERROR)) |
3c20f72f | 112 | x->ipc_csum_error++; |
293e4365 | 113 | if (unlikely(rdes0 & RDES0_COLLISION)) { |
56b106ae GC |
114 | x->rx_collision++; |
115 | stats->collisions++; | |
116 | } | |
293e4365 | 117 | if (unlikely(rdes0 & RDES0_CRC_ERROR)) { |
56b106ae GC |
118 | x->rx_crc++; |
119 | stats->rx_crc_errors++; | |
120 | } | |
121 | ret = discard_frame; | |
122 | } | |
293e4365 | 123 | if (unlikely(rdes0 & RDES0_DRIBBLING)) |
1cc5a735 | 124 | x->dribbling_bit++; |
56b106ae | 125 | |
293e4365 | 126 | if (unlikely(rdes0 & RDES0_LENGTH_ERROR)) { |
56b106ae GC |
127 | x->rx_length++; |
128 | ret = discard_frame; | |
129 | } | |
293e4365 | 130 | if (unlikely(rdes0 & RDES0_MII_ERROR)) { |
56b106ae GC |
131 | x->rx_mii++; |
132 | ret = discard_frame; | |
133 | } | |
3c20f72f | 134 | #ifdef STMMAC_VLAN_TAG_USED |
293e4365 | 135 | if (rdes0 & RDES0_VLAN_TAG) |
3c20f72f GC |
136 | x->vlan_tag++; |
137 | #endif | |
56b106ae GC |
138 | return ret; |
139 | } | |
140 | ||
c24602ef GC |
141 | static void ndesc_init_rx_desc(struct dma_desc *p, int disable_rx_ic, int mode, |
142 | int end) | |
56b106ae | 143 | { |
293e4365 GC |
144 | p->des0 |= RDES0_OWN; |
145 | p->des1 |= (BUF_SIZE_2KiB - 1) & RDES1_BUFFER1_SIZE_MASK; | |
c24602ef GC |
146 | |
147 | if (mode == STMMAC_CHAIN_MODE) | |
148 | ndesc_rx_set_on_chain(p, end); | |
149 | else | |
150 | ndesc_rx_set_on_ring(p, end); | |
151 | ||
152 | if (disable_rx_ic) | |
293e4365 | 153 | p->des1 |= RDES1_DISABLE_IC; |
56b106ae GC |
154 | } |
155 | ||
c24602ef | 156 | static void ndesc_init_tx_desc(struct dma_desc *p, int mode, int end) |
56b106ae | 157 | { |
293e4365 | 158 | p->des0 &= ~TDES0_OWN; |
c24602ef | 159 | if (mode == STMMAC_CHAIN_MODE) |
293e4365 | 160 | ndesc_tx_set_on_chain(p); |
c24602ef | 161 | else |
293e4365 | 162 | ndesc_end_tx_desc_on_ring(p, end); |
56b106ae GC |
163 | } |
164 | ||
165 | static int ndesc_get_tx_owner(struct dma_desc *p) | |
166 | { | |
293e4365 | 167 | return (p->des0 & TDES0_OWN) >> 31; |
56b106ae GC |
168 | } |
169 | ||
56b106ae GC |
170 | static void ndesc_set_tx_owner(struct dma_desc *p) |
171 | { | |
293e4365 | 172 | p->des0 |= TDES0_OWN; |
56b106ae GC |
173 | } |
174 | ||
175 | static void ndesc_set_rx_owner(struct dma_desc *p) | |
176 | { | |
293e4365 | 177 | p->des0 |= RDES0_OWN; |
56b106ae GC |
178 | } |
179 | ||
180 | static int ndesc_get_tx_ls(struct dma_desc *p) | |
181 | { | |
293e4365 | 182 | return (p->des1 & TDES1_LAST_SEGMENT) >> 30; |
56b106ae GC |
183 | } |
184 | ||
4a7d666a | 185 | static void ndesc_release_tx_desc(struct dma_desc *p, int mode) |
56b106ae | 186 | { |
293e4365 | 187 | int ter = (p->des1 & TDES1_END_RING) >> 25; |
56b106ae | 188 | |
b71c7aaa | 189 | memset(p, 0, offsetof(struct dma_desc, des2)); |
4a7d666a | 190 | if (mode == STMMAC_CHAIN_MODE) |
293e4365 | 191 | ndesc_tx_set_on_chain(p); |
4a7d666a GC |
192 | else |
193 | ndesc_end_tx_desc_on_ring(p, ter); | |
56b106ae GC |
194 | } |
195 | ||
196 | static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, | |
be434d50 | 197 | bool csum_flag, int mode, bool tx_own, |
0e80bdc9 | 198 | bool ls) |
56b106ae | 199 | { |
293e4365 GC |
200 | unsigned int tdes1 = p->des1; |
201 | ||
0e80bdc9 GC |
202 | if (mode == STMMAC_CHAIN_MODE) |
203 | norm_set_tx_desc_len_on_chain(p, len); | |
204 | else | |
205 | norm_set_tx_desc_len_on_ring(p, len); | |
206 | ||
293e4365 GC |
207 | if (is_fs) |
208 | tdes1 |= TDES1_FIRST_SEGMENT; | |
209 | else | |
210 | tdes1 &= ~TDES1_FIRST_SEGMENT; | |
211 | ||
212 | if (likely(csum_flag)) | |
213 | tdes1 |= (TX_CIC_FULL) << TDES1_CHECKSUM_INSERTION_SHIFT; | |
214 | else | |
215 | tdes1 &= ~(TX_CIC_FULL << TDES1_CHECKSUM_INSERTION_SHIFT); | |
216 | ||
0e80bdc9 GC |
217 | if (ls) |
218 | tdes1 |= TDES1_LAST_SEGMENT; | |
219 | ||
be434d50 GC |
220 | if (tx_own) |
221 | tdes1 |= TDES0_OWN; | |
222 | ||
293e4365 | 223 | p->des1 = tdes1; |
56b106ae GC |
224 | } |
225 | ||
0e80bdc9 | 226 | static void ndesc_set_tx_ic(struct dma_desc *p) |
56b106ae | 227 | { |
0e80bdc9 | 228 | p->des1 |= TDES1_INTERRUPT; |
56b106ae GC |
229 | } |
230 | ||
38912bdb | 231 | static int ndesc_get_rx_frame_len(struct dma_desc *p, int rx_coe_type) |
56b106ae | 232 | { |
293e4365 GC |
233 | unsigned int csum = 0; |
234 | ||
38912bdb DS |
235 | /* The type-1 checksum offload engines append the checksum at |
236 | * the end of frame and the two bytes of checksum are added in | |
237 | * the length. | |
238 | * Adjust for that in the framelen for type-1 checksum offload | |
293e4365 GC |
239 | * engines |
240 | */ | |
38912bdb | 241 | if (rx_coe_type == STMMAC_RX_COE_TYPE1) |
293e4365 GC |
242 | csum = 2; |
243 | ||
244 | return (((p->des0 & RDES0_FRAME_LEN_MASK) >> RDES0_FRAME_LEN_SHIFT) - | |
245 | csum); | |
246 | ||
56b106ae GC |
247 | } |
248 | ||
891434b1 RK |
249 | static void ndesc_enable_tx_timestamp(struct dma_desc *p) |
250 | { | |
293e4365 | 251 | p->des1 |= TDES1_TIME_STAMP_ENABLE; |
891434b1 RK |
252 | } |
253 | ||
254 | static int ndesc_get_tx_timestamp_status(struct dma_desc *p) | |
255 | { | |
293e4365 | 256 | return (p->des0 & TDES0_TIME_STAMP_STATUS) >> 17; |
891434b1 RK |
257 | } |
258 | ||
259 | static u64 ndesc_get_timestamp(void *desc, u32 ats) | |
260 | { | |
261 | struct dma_desc *p = (struct dma_desc *)desc; | |
262 | u64 ns; | |
263 | ||
264 | ns = p->des2; | |
265 | /* convert high/sec time stamp value to nanosecond */ | |
266 | ns += p->des3 * 1000000000ULL; | |
267 | ||
268 | return ns; | |
269 | } | |
270 | ||
271 | static int ndesc_get_rx_timestamp_status(void *desc, u32 ats) | |
272 | { | |
273 | struct dma_desc *p = (struct dma_desc *)desc; | |
274 | ||
275 | if ((p->des2 == 0xffffffff) && (p->des3 == 0xffffffff)) | |
276 | /* timestamp is corrupted, hence don't store it */ | |
277 | return 0; | |
278 | else | |
279 | return 1; | |
280 | } | |
281 | ||
cadb7924 | 282 | const struct stmmac_desc_ops ndesc_ops = { |
56b106ae GC |
283 | .tx_status = ndesc_get_tx_status, |
284 | .rx_status = ndesc_get_rx_status, | |
285 | .get_tx_len = ndesc_get_tx_len, | |
286 | .init_rx_desc = ndesc_init_rx_desc, | |
287 | .init_tx_desc = ndesc_init_tx_desc, | |
288 | .get_tx_owner = ndesc_get_tx_owner, | |
56b106ae GC |
289 | .release_tx_desc = ndesc_release_tx_desc, |
290 | .prepare_tx_desc = ndesc_prepare_tx_desc, | |
0e80bdc9 | 291 | .set_tx_ic = ndesc_set_tx_ic, |
56b106ae GC |
292 | .get_tx_ls = ndesc_get_tx_ls, |
293 | .set_tx_owner = ndesc_set_tx_owner, | |
294 | .set_rx_owner = ndesc_set_rx_owner, | |
295 | .get_rx_frame_len = ndesc_get_rx_frame_len, | |
891434b1 RK |
296 | .enable_tx_timestamp = ndesc_enable_tx_timestamp, |
297 | .get_tx_timestamp_status = ndesc_get_tx_timestamp_status, | |
298 | .get_timestamp = ndesc_get_timestamp, | |
299 | .get_rx_timestamp_status = ndesc_get_rx_timestamp_status, | |
56b106ae | 300 | }; |