1 /**********************************************************************
2 * Copyright (C) 2014-2015 Cadence Design Systems, Inc.- http://www.cadence.com
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 ******************************************************************************
17 * edd_tx.c
18 * Ethernet DMA MAC Driver
19 *
20 * Tx-related functions source file
21 *****************************************************************************/
22 /****************************************************************************
23 * Modification by Infineon: To make this file compile with ModusToolbox
24 * toolchain
25 *****************************************************************************/
26
27 #include "cy_device.h"
28
29 #if defined (CY_IP_MXETH)
30
31 #include "cdn_stdint.h"
32 #include "cdn_errno.h"
33 #include "log.h"
34 #include "cps_v2.h"
35 #include "emac_regs.h"
36 #include "cedi.h"
37 #include "edd_int.h"
38
39 #ifdef __cplusplus
40 extern "C" {
41 #endif
42 /******************************************************************************
43 * Private Driver functions
44 *****************************************************************************/
45
46 /* move descriptor pointer bd and virtual address pointer vp on to next in ring.
47 * stat should be the status (word 1) of current descriptor */
inc_txbd(void * pD,uint32_t stat,txDesc ** bd,uintptr_t ** vp,txQueue_t * txQ)48 static void inc_txbd(void *pD, uint32_t stat, txDesc **bd, uintptr_t **vp,
49 txQueue_t *txQ) {
50 if (stat & CEDI_TXD_WRAP) {
51 *bd = txQ->bdBase;
52 *vp = txQ->vAddrList;
53 } else {
54 *bd = (txDesc *) (((uintptr_t)(*bd))+(CEDI_PdVar(txDescriptorSize)));
55 ++(*vp);
56 }
57 }
58
59 /* move descriptor and virtual address pointers back to previous in ring */
dec_txbd(void * pD,txDesc ** bd,uintptr_t ** vp,txQueue_t * txQ)60 static void dec_txbd(void *pD, txDesc **bd, uintptr_t **vp, txQueue_t *txQ) {
61 if (*bd==txQ->bdBase) {
62 *bd = (txDesc *)(((uintptr_t)(*bd))+
63 (txQ->descMax-1)*(CEDI_PdVar(txDescriptorSize)));
64 *vp += (txQ->descMax-1);
65 } else {
66 *bd = (txDesc *)(((uintptr_t)(*bd))-(CEDI_PdVar(txDescriptorSize)));
67 --(*vp);
68 }
69 }
70
71 /******************************************************************************
72 * Driver API functions
73 *****************************************************************************/
74
75 /**
76 * Identify max Tx pkt size for queues. When using full store & forward packet
77 * buffering, this is based on the sram size for each queue, otherwise it is
78 * limited by an internal counter to 16kB.
79 * @param pD - driver private state info specific to this instance
80 * @param maxTxSize - pointer for returning array of sizes for queues
81 * @return 0 if successful
82 * @return EINVAL if any parameter =NULL
83 */
emacCalcMaxTxFrameSize(void * pD,CEDI_FrameSize * maxTxSize)84 uint32_t emacCalcMaxTxFrameSize(void *pD, CEDI_FrameSize *maxTxSize) {
85 uint32_t i, watermark;
86 uint16_t ram_word_size, ram_addr_bits, burst_len;
87 uint16_t ram_size, num_segments, size_per_segment, tx_overhead;
88 uint16_t num_segments_q[CEDI_MAX_TX_QUEUES];
89 uint8_t enabled = 0;
90 uint32_t ret;
91
92 if ((pD==NULL) || (maxTxSize==NULL)) return EINVAL;
93
94 if (0!=(ret = emacGetTxPartialStFwd(pD, &watermark, &enabled)))
95 return ret;
96
97 if (!(enabled) && CEDI_PdVar(hwCfg).tx_pkt_buffer)
98 {
99 // What is word size of SRAM in bytes
100 ram_word_size = (CEDI_PdVar(hwCfg).tx_pbuf_data >> 1)+1;
101 //vDbgMsg(DBG_GEN_MSG, 10, "RAM word size = %u (x32 bits)\n", CEDI_PdVar(hwCfg).tx_pbuf_data);
102 ram_addr_bits = CEDI_PdVar(hwCfg).tx_pbuf_addr;
103 //vDbgMsg(DBG_GEN_MSG, 10, "RAM Tx addr bits = %u\n", ram_addr_bits);
104
105 ram_size = ram_addr_bits + ram_word_size + 1;
106 vDbgMsg(DBG_GEN_MSG, 10, "RAM size = %u\n", 1<<ram_size);
107
108 // how many segments are there ?
109 num_segments = CEDI_PdVar(hwCfg).tx_pbuf_queue_segment_size;
110 /* this is number of address lines used for segment selection,
111 * e.g. if =3, there are 2^3 = 8 segments */
112 vDbgMsg(DBG_GEN_MSG, 10, "Num segments = %u\n", 1<<num_segments);
113
114 size_per_segment = (ram_size - num_segments);
115 /* again, as a power of 2 */
116 vDbgMsg(DBG_GEN_MSG, 10, "RAM Size per segment = %u\n",
117 1<<size_per_segment);
118
119 num_segments_q[0] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q0;
120 num_segments_q[1] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q1;
121 num_segments_q[2] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q2;
122 num_segments_q[3] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q3;
123 num_segments_q[4] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q4;
124 num_segments_q[5] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q5;
125 num_segments_q[6] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q6;
126 num_segments_q[7] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q7;
127 num_segments_q[8] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q8;
128 num_segments_q[9] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q9;
129 num_segments_q[10] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q10;
130 num_segments_q[11] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q11;
131 num_segments_q[12] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q12;
132 num_segments_q[13] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q13;
133 num_segments_q[14] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q14;
134 num_segments_q[15] = CEDI_PdVar(hwCfg).tx_pbuf_num_segments_q15;
135 vDbgMsg(DBG_GEN_MSG, 10,
136 "number segments Q0 = %u, Q1 = %u, Q2 = %u, Q3 = %u,\n",
137 num_segments_q[0], num_segments_q[1], num_segments_q[2],
138 num_segments_q[3]);
139 vDbgMsg(DBG_GEN_MSG, 10,
140 "number segments Q4 = %u, Q5 = %u, Q6 = %u, Q7 = %u,\n",
141 num_segments_q[4], num_segments_q[5], num_segments_q[6],
142 num_segments_q[7]);
143
144 burst_len = EMAC_REGS__DMA_CONFIG__AMBA_BURST_LENGTH__READ(
145 CPS_UncachedRead32(CEDI_RegAddr(dma_config)));
146 switch (burst_len) {
147 case CEDI_AMBD_BURST_LEN_8:
148 tx_overhead = ((CEDI_PdVar(hwCfg).tx_pbuf_data << 5)/8)*26;
149 break;
150 case CEDI_AMBD_BURST_LEN_16:
151 tx_overhead = ((CEDI_PdVar(hwCfg).tx_pbuf_data << 5)/8)*46;
152 break;
153 case CEDI_AMBD_BURST_LEN_1:
154 case CEDI_AMBD_BURST_LEN_4:
155 default:
156 tx_overhead = ((CEDI_PdVar(hwCfg).tx_pbuf_data << 5)/8)*16;
157 }
158
159 for (i=0; i<CEDI_MAX_TX_QUEUES; i++) {
160 if (i<CEDI_PdVar(cfg).txQs) {
161 maxTxSize->FrameSize[i] =
162 (1 << (num_segments_q[i] + size_per_segment)) - tx_overhead;
163 /* add in some extra overhead */
164 maxTxSize->FrameSize[i] = (maxTxSize->FrameSize[i]*9)/10;
165 }
166 else
167 maxTxSize->FrameSize[i] = 0;
168 }
169 }
170 else
171 for (i=0; i<CEDI_MAX_TX_QUEUES; i++)
172 if (i<CEDI_PdVar(cfg).txQs)
173 maxTxSize->FrameSize[i] = CEDI_TXD_LMASK;
174 else
175 maxTxSize->FrameSize[i] = 0;
176
177 vDbgMsg(DBG_GEN_MSG, 10,
178 "max_frm_size_Q0 = %u, Q1 = %u, Q2 = %u, Q3 = %u,\n",
179 maxTxSize->FrameSize[0], maxTxSize->FrameSize[1],
180 maxTxSize->FrameSize[2], maxTxSize->FrameSize[3]);
181 vDbgMsg(DBG_GEN_MSG, 10,
182 "max_frm_size_Q4 = %u, Q5 = %u, Q6 = %u, Q7 = %u,\n",
183 maxTxSize->FrameSize[4], maxTxSize->FrameSize[5],
184 maxTxSize->FrameSize[6], maxTxSize->FrameSize[7]);
185 return 0;
186 }
187
188
189 /* Add a buffer containing Tx data to the end of the transmit queue.
190 * Use repeated calls for multi-buffer frames, setting lastBuffer on the
191 * last call, to indicate the end of the frame.
192 * @param pD - driver private state info specific to this instance
193 * @param queueNum - number of Tx queue
194 * @param bufAdd - pointer to struct for virtual and physical addresses of
195 * start of data buffer
196 * @param length - length of data in buffer
197 * @param flags - bit-flags specifying last buffer/auto CRC/auto-start
198 * @return 0 if successful
199 * @return EINVAL if invalid queueNum, length or buffer alignment, NULL
200 * pointers or buffer addresses
201 * @return ENOENT if no available descriptors
202 */
emacQueueTxBuf(void * pD,uint8_t queueNum,CEDI_BuffAddr * bufAdd,uint32_t length,uint8_t flags)203 uint32_t emacQueueTxBuf(void *pD, uint8_t queueNum, CEDI_BuffAddr *bufAdd,
204 uint32_t length, uint8_t flags)
205 {
206 txQueue_t *txQ;
207 txDesc *freeDesc;
208 txDesc *bd1stBuf;
209 uint32_t stat, ncr;
210 uint16_t nFree;
211
212 if ((pD==NULL) || (queueNum>=CEDI_PdVar(cfg).txQs) || (bufAdd==NULL)
213 || (bufAdd->pAddr==0))
214 return EINVAL;
215
216 // vDbgMsg(DBG_GEN_MSG, 10, "%s entered\n", __func__);
217
218 txQ = &CEDI_PdVar(txQueue)[queueNum];
219 freeDesc = txQ->bdHead;
220 bd1stBuf = txQ->bd1stBuf;
221
222 if (!length || (length > CEDI_TXD_LMASK)) {
223 vDbgMsg(DBG_GEN_MSG, 5, "Error: bad length specified: %u\n", length);
224 return EINVAL;
225 }
226
227 if (emacTxDescFree(pD, queueNum, &nFree)) return EINVAL;
228 if (!nFree) {
229 vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: insufficient buffer descriptors");
230 return ENOENT;
231 }
232
233 /* preserve wrap bit if present in status word */
234 stat = CPS_UncachedRead32(&freeDesc->word[1]) & CEDI_TXD_WRAP;
235 stat |= ((flags & CEDI_TXB_LAST_BUFF)?CEDI_TXD_LAST_BUF:0)
236 | ((flags & CEDI_TXB_NO_AUTO_CRC)?CEDI_TXD_NO_AUTO_CRC:0) | length;
237
238 /* Handle a multi-buffer frame */
239 if (!(flags & CEDI_TXB_LAST_BUFF) && (NULL == bd1stBuf)) {
240 /* This is the 1st buf of several; prevent it from going and remember its BD. */
241 stat |= (uint32_t)CEDI_TXD_USED;
242 txQ->bd1stBuf = freeDesc;
243 }
244
245 *txQ->vHead = bufAdd->vAddr;
246 CPS_UncachedWrite32(&freeDesc->word[0], bufAdd->pAddr & 0xFFFFFFFF);
247 /* upper 32 bits if 64 bit addressing */
248 if (CEDI_PdVar(cfg).dmaAddrBusWidth) {
249 #ifdef CEDI_64B_COMPILE
250 /* 64-bit addressing */
251 CPS_UncachedWrite32(&freeDesc->word[2],
252 (bufAdd->pAddr & 0xFFFFFFFF00000000)>>32);
253 #else
254 /* 32-bit addressing */
255 #endif
256 }
257 CPS_UncachedWrite32(&freeDesc->word[1], stat);
258
259 if ((flags & CEDI_TXB_LAST_BUFF) && (NULL != bd1stBuf)) {
260 /* Last buffer of a multibuffer frame is in place, 1st buffer can go. */
261 CPS_UncachedWrite32(&bd1stBuf->word[1],
262 CPS_UncachedRead32(&bd1stBuf->word[1]) & ~CEDI_TXD_USED);
263 txQ->bd1stBuf = NULL;
264 }
265
266 --txQ->descFree;
267 if (emacTxDescFree(pD, queueNum, &nFree)) return EINVAL;
268 vDbgMsg(DBG_GEN_MSG, 15, "len=%u, queue=%u, txbdHead=%p, buffV=%p, buffP=%p, descFree=%u\n",
269 length, queueNum, freeDesc, (void *)bufAdd->vAddr, (void *)bufAdd->pAddr, nFree);
270 inc_txbd(pD, stat, &freeDesc, &txQ->vHead, txQ);
271 txQ->bdHead = freeDesc;
272
273 /* set going if complete frame queued */
274 if ((flags & CEDI_TXB_LAST_BUFF) && !(flags & CEDI_TXB_NO_AUTO_START)) {
275 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
276 EMAC_REGS__NETWORK_CONTROL__TX_START_PCLK__SET(ncr);
277 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
278 }
279
280 return 0;
281 }
282
283 /* Add a buffer containing Tx data to the end of the transmit queue.
284 * Use repeated calls for multi-buffer frames, setting lastBuffer on the
285 * last call, to indicate the end of the frame.
286 * @param pD - driver private state info specific to this instance
287 * @param prm - pointer to struct of parameters
288 * @return 0 if successful
289 * @return EINVAL if invalid queueNum, length or buffer alignment, NULL
290 * pointers or buffer addresses, or prm->flags specifies
291 * CEDI_TXB_LAST_BUFF as well as CEDI_TXB_TCP_ENCAP or CEDI_TXB_UDP_ENCAP
292 * @return ENOENT if no available descriptors
293 */
emacQTxBuf(void * pD,CEDI_qTxBufParams * prm)294 uint32_t emacQTxBuf(void *pD, CEDI_qTxBufParams *prm)
295 {
296 txQueue_t *txQ;
297 txDesc *freeDesc;
298 txDesc *bd1stBuf;
299 uint32_t stat, ncr;
300 uint16_t nFree;
301
302 if ((pD==NULL) || (prm->queueNum>=CEDI_PdVar(cfg).txQs)
303 || (prm->bufAdd==NULL)
304 || (prm->bufAdd->pAddr==0))
305 return EINVAL;
306
307 // vDbgMsg(DBG_GEN_MSG, 10, "%s entered\n", __func__);
308
309 txQ = &CEDI_PdVar(txQueue)[prm->queueNum];
310 freeDesc = txQ->bdHead;
311 bd1stBuf = txQ->bd1stBuf;
312
313 if (!prm->length || (prm->length > CEDI_TXD_LMASK)) {
314 vDbgMsg(DBG_GEN_MSG, 5, "Error: bad length specified: %u\n",
315 prm->length);
316 return EINVAL;
317 }
318
319 if (emacTxDescFree(pD, prm->queueNum, &nFree)) return EINVAL;
320 if (!nFree) {
321 vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: insufficient buffer descriptors");
322 return ENOENT;
323 }
324
325 if (NULL!=bd1stBuf) { /* inc counter after 1st in frame */
326 txQ->descNum++;
327 }
328
329 /* preserve wrap bit if present in status word */
330 stat = CPS_UncachedRead32(&freeDesc->word[1]) & CEDI_TXD_WRAP;
331 stat |= ((prm->flags & CEDI_TXB_LAST_BUFF)?CEDI_TXD_LAST_BUF:0)
332 | ((prm->flags & CEDI_TXB_NO_AUTO_CRC)?CEDI_TXD_NO_AUTO_CRC:0)
333 | prm->length
334 | ((txQ->descNum>=1)?
335 ((prm->mssMfs << CEDI_TXD_MSSMFS_SHIFT) & CEDI_TXD_MSSMFS_MASK):0);
336 // only set MSS/MFS on second or later descriptor
337 // vDbgMsg(DBG_GEN_MSG, 10, "descNum = %u, desc wd1 = 0x%08X, mss = %u\n",
338 // txQ->descNum, stat, (stat>>16) & 0x3FFF);
339
340 /* Handle a multi-buffer frame */
341 if (!(prm->flags & CEDI_TXB_LAST_BUFF) && (NULL==bd1stBuf)) {
342 /* This is the 1st buf of several; prevent it from going and remember its BD. */
343 stat |= CEDI_TXD_USED
344 /* Also use this condition to set encapsulation flags & TCP stream -
345 * must not set stream if TSO bit clear */
346 | ((prm->flags & CEDI_TXB_TCP_ENCAP)?
347 /* TSO settings */
348 (CEDI_TXD_TSO_ENABLE|
349 (((prm->tcpStream)<<CEDI_TXD_STREAM_SHIFT)
350 & CEDI_TXD_STREAM_MASK)|
351 ((prm->flags & CEDI_TXB_TSO_AUTO_SEQ)?CEDI_TXD_AUTOSEQ_SEL:0)) :
352 /* UFO bit only */
353 ((prm->flags & CEDI_TXB_UDP_ENCAP)?CEDI_TXD_UFO_ENABLE:0));
354 txQ->bd1stBuf = freeDesc;
355 }
356
357 *txQ->vHead = prm->bufAdd->vAddr;
358 CPS_UncachedWrite32(&freeDesc->word[0], prm->bufAdd->pAddr & 0xFFFFFFFF);
359
360 /* upper 32 bits if 64 bit addressing */
361 if (CEDI_PdVar(cfg).dmaAddrBusWidth) {
362 #ifdef CEDI_64B_COMPILE
363 /* 64-bit addressing */
364 CPS_UncachedWrite32(&freeDesc->word[2],
365 (prm->bufAdd->pAddr & 0xFFFFFFFF00000000)>>32);
366 #else
367 #endif
368 }
369 CPS_UncachedWrite32(&freeDesc->word[1], stat);
370
371
372 if ((prm->flags & CEDI_TXB_LAST_BUFF) && (NULL!=bd1stBuf)) {
373 /* Last buffer of a multibuffer frame is in place, 1st buffer can go. */
374 CPS_UncachedWrite32(&bd1stBuf->word[1],
375 CPS_UncachedRead32(&bd1stBuf->word[1]) & ~CEDI_TXD_USED);
376 /* vDbgMsg(DBG_GEN_MSG, 10,
377 "set multi-buffer go: 1stBuf=%p, wd1=%08X, transmit queue ptr=%08X\n",
378 bd1stBuf, CPS_UncachedRead32(&bd1stBuf->word[1]),
379 CPS_UncachedRead32(CEDI_RegAddr(transmit_q_ptr)));*/
380 txQ->bd1stBuf = NULL;
381 txQ->descNum = 0;
382 }
383
384 --txQ->descFree;
385 /* if (emacTxDescFree(pD, prm->queueNum, &nFree)) return EINVAL;
386 vDbgMsg(DBG_GEN_MSG, 10,
387 "len=%u, queue=%u, txbdHead=%p, buffV=%p, buffP=%p, wd1=%08X, descFree=%u\n",
388 prm->length, prm->queueNum, freeDesc,
389 (void *)prm->bufAdd->vAddr,
390 (void *)prm->bufAdd->pAddr,
391 CPS_UncachedRead32(&freeDesc->word[1]), nFree);*/
392 inc_txbd(pD, stat, &freeDesc, &txQ->vHead, txQ);
393 txQ->bdHead = freeDesc;
394
395 /* set going if complete frame queued */
396 if ((prm->flags & CEDI_TXB_LAST_BUFF) && !(prm->flags & CEDI_TXB_NO_AUTO_START)) {
397 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
398 EMAC_REGS__NETWORK_CONTROL__TX_START_PCLK__SET(ncr);
399 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
400 }
401
402 return 0;
403 }
404
405 /* Remove buffer from head of transmit queue in case of error during queueing
406 * and free the corresponding descriptor.
407 * Caller must have knowledge of queueing status, i.e. that frame has not been
408 * completed for transmission (first used bit still set) and how many
409 * descriptors have been queued for untransmitted frame.
410 * @param pD - driver private state info specific to this instance
411 * @param prm - pointer to struct of parameters to return
412 * @return 0 if successful
413 * @return EINVAL if invalid queueNum or NULL parameters
414 * @return ENOENT if no unfree descriptors in queue
415 */
emacDeQTxBuf(void * pD,CEDI_qTxBufParams * prm)416 uint32_t emacDeQTxBuf(void *pD, CEDI_qTxBufParams *prm)
417 {
418 txQueue_t *txQ;
419 txDesc *descToFree;
420 uint32_t stat;
421
422 if ((pD==NULL) || (prm==NULL) || (prm->bufAdd==NULL) ||
423 (prm->queueNum>=CEDI_PdVar(cfg).txQs))
424 return EINVAL;
425
426 // vDbgMsg(DBG_GEN_MSG, 10, "%s entered\n", __func__);
427
428 txQ = &CEDI_PdVar(txQueue)[prm->queueNum];
429 descToFree = txQ->bdHead;
430
431 /* Check if any in queue */
432 if (txQ->bdTail==txQ->bdHead)
433 return ENOENT;
434
435 /* unwind head pointers */
436 dec_txbd(pD, &descToFree, &txQ->vHead, txQ);
437 txQ->bdHead = descToFree;
438
439 /* get virtual address */
440 prm->bufAdd->vAddr = *txQ->vHead;
441
442 /* get phys address */
443 prm->bufAdd->pAddr = CPS_UncachedRead32(&descToFree->word[0]);
444 #ifdef CEDI_64B_COMPILE
445 /* upper 32 bits if 64 bit addressing */
446 if (CEDI_PdVar(cfg).dmaAddrBusWidth) {
447 prm->bufAdd->pAddr |= (CPS_UncachedRead32(&descToFree->word[2])<<32);
448 }
449 #endif
450
451 /* get length */
452 stat = CPS_UncachedRead32(&descToFree->word[1]);
453 prm->length = stat & CEDI_TXD_LEN_MASK;
454 /* set used bit */
455 CPS_UncachedWrite32(&descToFree->word[1], stat | (uint32_t)CEDI_TXD_USED);
456
457 if (txQ->descNum>0)
458 txQ->descNum--;
459
460 ++txQ->descFree;
461
462 return 0;
463 }
464
465 /* Get number of free descriptors in specified Tx queue
466 * @param pD - driver private state info specific to this instance
467 * @param queueNum - number of Tx queue
468 * @param numFree - pointer for returning number of free descriptors
469 * @return 0 if successful
470 * @return EINVAL if invalid parameter
471 */
emacTxDescFree(void * pD,uint8_t queueNum,uint16_t * numFree)472 uint32_t emacTxDescFree(void *pD, uint8_t queueNum, uint16_t *numFree)
473 {
474 if ((pD==NULL) || (numFree==NULL) || (queueNum>=CEDI_PdVar(cfg).txQs))
475 return EINVAL;
476 *numFree = CEDI_PdVar(txQueue)[queueNum].descFree;
477 return 0;
478 }
479
480 /*
481 * Read Tx descriptor queue and free used descriptor.
482 *
483 * @param[in] pD driver private state info specific to this instance
484 * @param[in] queueNum number of Tx queue
485 * $RANGE $FROM 0 $TO CEDI_Config.txQs-1$
486 * @param[out] descData pointer for returning status & descriptor data
487 * Struct fields:
488 *
489 * CEDI_BuffAddr bufAdd - addresses of buffer freed up
490 *
491 * uint32_t txDescStat - descriptor status word. Only valid if first
492 * buffer of frame.
493 *
494 * uint8_t status - descriptor queue status, one of the following values:
495 * CEDI_TXDATA_1ST_NOT_LAST :a first descriptor was freed,
496 * frame not finished:
497 * bufAdd & txDescStat are valid
498 * CEDI_TXDATA_1ST_AND_LAST :a first descriptor was freed,
499 * frame is finished:
500 * bufAdd & txDescStat are valid
501 * CEDI_TXDATA_MID_BUFFER :a descriptor was freed,
502 * (not first in frame),
503 * frame not finished: bufAdd valid,
504 * txDescStat not valid
505 * CEDI_TXDATA_LAST_BUFFER :a descriptor was freed, frame is finished:
506 * bufAdd valid, txDescStat not valid
507 * CEDI_TXDATA_NONE_FREED :no used descriptor to free:
508 * bufAdd & txDescStat not valid
509 *
510 * CEDI_TimeStampData txTsData - Tx descriptor timestamp when valid
511 * (txTsData->tsValid will be set to 1).
512 * @return 0 if successful (and status is set),
513 * @return ENOENT if the queue is empty (status = CEDI_TXDATA_NONE_FREED), or
514 * @return EIO if an incomplete frame was detected (no lastBuffer flag in
515 * queue)
516 * @return EINVAL if any parameter invalid
517 */
emacFreeTxDesc(void * pD,uint8_t queueNum,CEDI_TxDescData * descData)518 uint32_t emacFreeTxDesc(void *pD, uint8_t queueNum, CEDI_TxDescData *descData)
519 {
520 txQueue_t *txQ;
521 // uint16_t nFree = 0;
522 // txDesc *freedDesc;
523 uint8_t wdNum;
524 uint32_t tsLowerWd, tsUpperWd;
525 // uintptr_t *nextV;
526 // txDesc *nextD;
527 // uint32_t wd1_1, wd1_2, wd1_3, wd1_4, wd1_5;
528
529 if ((pD==NULL) || (descData==NULL))
530 return EINVAL;
531 if (queueNum>=CEDI_PdVar(cfg).txQs)
532 return EINVAL;
533
534 // vDbgMsg(DBG_GEN_MSG, 10, "%s entered\n", __func__);
535 txQ = &CEDI_PdVar(txQueue)[queueNum];
536
537 /* Check if any to free */
538 if (txQ->bdTail == txQ->bdHead)
539 {
540 descData->status = CEDI_TXDATA_NONE_FREED;
541 return ENOENT;
542 }
543
544 /* Free next used descriptor in this frame */
545 descData->txDescStat = CPS_UncachedRead32(&(txQ->bdTail->word[1]));
546 if (txQ->firstToFree)
547 {
548 /* look ahead to next desc */
549 /* nextD = txQ->bdTail;
550 nextV = txQ->vTail;
551 inc_txbd(pD, descData->txDescStat, &nextD, &nextV, txQ);
552 wd1_1 = CPS_UncachedRead32(&(nextD->word[1]));
553 inc_txbd(pD, descData->txDescStat, &nextD, &nextV, txQ);
554 wd1_2 = CPS_UncachedRead32(&(nextD->word[1]));
555 inc_txbd(pD, descData->txDescStat, &nextD, &nextV, txQ);
556 wd1_3 = CPS_UncachedRead32(&(nextD->word[1]));
557 inc_txbd(pD, descData->txDescStat, &nextD, &nextV, txQ);
558 wd1_4 = CPS_UncachedRead32(&(nextD->word[1]));
559 inc_txbd(pD, descData->txDescStat, &nextD, &nextV, txQ);
560 wd1_5 = CPS_UncachedRead32(&(nextD->word[1]));
561 vDbgMsg(DBG_GEN_MSG, 10,
562 " testing desc: queue=%u, txBdTail=%p, wd1(0)=%08X,"\
563 " wd1(1)=%08X, wd1(2)=%08X, wd1(3)=%08X, wd1(4)=%08X, wd1(5)=%08X\n",
564 queueNum, txQ->bdTail, descData->txDescStat, wd1_1, wd1_2,
565 wd1_3, wd1_4, wd1_5);*/
566
567 /* Only test used bit state for first buffer in frame. */
568 if(!(descData->txDescStat & (uint32_t)CEDI_TXD_USED)) {
569 descData->status = CEDI_TXDATA_NONE_FREED;
570 return 0;
571 }
572
573 /* extract timestamp if available */
574 if ((CEDI_PdVar(cfg).enTxExtBD) &&
575 (descData->txDescStat & CEDI_TXD_TS_VALID))
576 {
577 uint32_t reg;
578 descData->txTsData.tsValid = 1;
579 // position depends on 32/64 bit addr
580 wdNum = (CEDI_PdVar(cfg).dmaAddrBusWidth)?4:2;
581 tsLowerWd = CPS_UncachedRead32(&(txQ->bdTail->word[wdNum]));
582 tsUpperWd = CPS_UncachedRead32(&(txQ->bdTail->word[wdNum+1]));
583
584 descData->txTsData.tsNanoSec = tsLowerWd & CEDI_TS_NANO_SEC_MASK;
585 descData->txTsData.tsSecs = ((tsUpperWd & CEDI_TS_SEC1_MASK)
586 <<CEDI_TS_SEC1_POS_SHIFT)
587 | (tsLowerWd >> CEDI_TS_SEC0_SHIFT);
588
589 /* The timestamp only contains lower few bits of seconds, so add value from 1588 timer */
590 reg = CPS_UncachedRead32(CEDI_RegAddr(tsu_timer_sec));
591 /* If the top bit is set in the timestamp, but not in 1588 timer, it has rolled over, so subtract max size */
592 if ((descData->txTsData.tsSecs & (CEDI_TS_SEC_TOP>>1)) && !(reg & (CEDI_TS_SEC_TOP>>1))) {
593 descData->txTsData.tsSecs -= CEDI_TS_SEC_TOP;
594 }
595 descData->txTsData.tsSecs += ((~CEDI_TS_SEC_MASK) & EMAC_REGS__TSU_TIMER_SEC__TIMER__READ(reg));
596 }
597 else{
598 descData->txTsData.tsValid = 0;
599
600 }
601
602 if (descData->txDescStat & CEDI_TXD_LAST_BUF)
603 descData->status = CEDI_TXDATA_1ST_AND_LAST;
604 else {
605 txQ->firstToFree = 0;
606 descData->status = CEDI_TXDATA_1ST_NOT_LAST;
607 }
608 }
609 else
610 {
611 /* set later used bits in frame, for consistency */
612 CPS_UncachedWrite32(&(txQ->bdTail->word[1]),
613 descData->txDescStat | (uint32_t)CEDI_TXD_USED);
614 if (descData->txDescStat & CEDI_TXD_LAST_BUF) {
615 descData->status = CEDI_TXDATA_LAST_BUFFER;
616 txQ->firstToFree = 1;
617 }
618 else
619 descData->status = CEDI_TXDATA_MID_BUFFER;
620 }
621
622 descData->bufAdd.pAddr = CPS_UncachedRead32(&(txQ->bdTail->word[0]));
623
624 #ifdef CEDI_64B_COMPILE
625 /* upper 32 bits if 64 bit addressing */
626 if ((CEDI_PdVar(cfg).dmaAddrBusWidth) &&
627 (sizeof(descData->bufAdd.pAddr)==sizeof(uint64_t)))
628 descData->bufAdd.pAddr |=
629 ((uint64_t)CPS_UncachedRead32(&(txQ->bdTail->word[2])))<<32;
630
631 #endif
632
633 descData->bufAdd.vAddr = *txQ->vTail;
634 // freedDesc = txQ->bdTail;
635
636 /* move queue pointers on */
637 inc_txbd(pD, descData->txDescStat, &txQ->bdTail, &txQ->vTail, txQ);
638 ++txQ->descFree;
639 /*if (0==emacTxDescFree(pD, queueNum, &nFree))
640 vDbgMsg(DBG_GEN_MSG, 15,
641 " free desc: queue=%u, txBdTail=%p, buffV=%p, buffP=%p, length=%u, descFree=%u\n",
642 queueNum, freedDesc, (void *)descData->bufAdd.vAddr,
643 (void *)descData->bufAdd.pAddr, descData->txDescStat & CEDI_TXD_LMASK, nFree);*/
644
645 /* paranoid - empty and no last buffer flag (on last freed)? */
646 if ((0==(descData->txDescStat & CEDI_TXD_LAST_BUF)) &&
647 (txQ->descFree==txQ->descMax-CEDI_MIN_TXBD)) {
648 vDbgMsg(DBG_GEN_MSG, 5,
649 "Error: txQueue %u: LAST bit of frame not found!\n", queueNum);
650 txQ->firstToFree = 1;
651 return EIO;
652 }
653
654 return 0;
655 }
656
657 /* Decode the Tx descriptor status into a bit-field struct
658 * @param pD - driver private state info specific to this instance
659 * @param txDStatWord - Tx descriptor status word
660 * @param txDStat - pointer to bit-field struct for decoded status fields
661 */
emacGetTxDescStat(void * pD,uint32_t txDStatWord,CEDI_TxDescStat * txDStat)662 void emacGetTxDescStat(void *pD, uint32_t txDStatWord, CEDI_TxDescStat *txDStat)
663 {
664 uint32_t wd1;
665
666 if ((NULL==pD) || (NULL==txDStat))
667 return;
668
669 wd1 = txDStatWord;
670 txDStat->chkOffErr = (wd1 & CEDI_TXD_CHKOFF_MASK) >> CEDI_TXD_CHKOFF_SHIFT;
671 txDStat->lateColl = (wd1 & CEDI_TXD_LATE_COLL)?1:0;
672 txDStat->frameCorr = (wd1 & CEDI_TXD_FR_CORR)?1:0;
673 txDStat->txUnderrun = (wd1 & CEDI_TXD_UNDERRUN)?1:0;
674 txDStat->retryExc = (wd1 & CEDI_TXD_RETRY_EXC)?1:0;
675 }
676
677 /* Provide the size of descriptor calculated for the current configuration.
678 * @param pD - driver private state info specific to this instance
679 * @param txDescSize - pointer to Tx descriptor Size
680 */
emacGetTxDescSize(void * pD,uint32_t * txDescSize)681 void emacGetTxDescSize(void *pD, uint32_t *txDescSize)
682 {
683 if ((pD==NULL)||(txDescSize==NULL)) return;
684 *txDescSize = CEDI_PdVar(txDescriptorSize);
685 }
686
687 /* Reset transmit buffer queue. Any untransmitted buffer data will be
688 * discarded and must be re-queued. Transmission must be disabled
689 * before calling this function.
690 * @param pD - driver private state info specific to this instance
691 * @param queueNum - number of Tx queue
692 * @return 0 if successful
693 * @return EINVAL if invalid parameter
694 */
emacResetTxQ(void * pD,uint8_t queueNum)695 uint32_t emacResetTxQ(void *pD, uint8_t queueNum)
696 {
697 #define CEDI_WR_TXQ_PTR_REG_N_CASE(Q) case Q:\
698 regTmp = CPS_UncachedRead32(CEDI_RegAddr(transmit_q##Q##_ptr));\
699 EMAC_REGS__TRANSMIT_Q_PTR__DMA_TX_Q_PTR__MODIFY(regTmp, pAddr>>2);\
700 CPS_UncachedWrite32(CEDI_RegAddr(transmit_q##Q##_ptr), regTmp);\
701 break;
702
703 uint32_t result = 0, regTmp;
704 txQueue_t *txQ;
705 txDesc *descStartPerQ;
706 uint32_t pAddr;
707 uintptr_t vAddr;
708 uint16_t q, i;
709
710 if ((pD==NULL) || (queueNum>=CEDI_PdVar(cfg).txQs))
711 return EINVAL;
712
713 txQ = &CEDI_PdVar(txQueue)[queueNum];
714 txQ->descFree = CEDI_PdVar(cfg).txQLen[queueNum];
715 txQ->descMax = txQ->descFree + CEDI_MIN_TXBD;
716 vAddr = CEDI_PdVar(cfg).txQAddr;
717 pAddr = CEDI_PdVar(cfg).txQPhyAddr;
718 q = 0;
719 /* find start addresses for this txQ */
720 if (queueNum>0)
721 txQ->vAddrList = CEDI_PdVar(txQueue)[0].vAddrList;
722 while (q<queueNum) {
723 vAddr += txQ->descMax * (CEDI_PdVar(txDescriptorSize)); //sizeof(txDesc);
724 pAddr += txQ->descMax * (CEDI_PdVar(txDescriptorSize)); //sizeof(txDesc);
725 txQ->vAddrList += txQ->descMax;
726 q++;
727 }
728 vDbgMsg(DBG_GEN_MSG, 10, "%s: base address Q%u virt=%08lX phys=%08X vAddrList=%p\n",
729 __func__, queueNum, vAddr, pAddr, txQ->vAddrList);
730 txQ->bdBase = (txDesc *)vAddr;
731
732 txQ->bdTail = txQ->bdBase;
733 txQ->bdHead = txQ->bdBase;
734 txQ->bd1stBuf = NULL;
735 txQ->vHead = txQ->vAddrList;
736 txQ->vTail = txQ->vAddrList;
737 txQ->firstToFree = 1;
738 txQ->descNum = 0;
739
740 /* set used flags & last wrap flag */
741 descStartPerQ = txQ->bdBase;
742 for (i = 0; i<txQ->descMax; i++) {
743 CPS_UncachedWrite32((uint32_t *)&(descStartPerQ->word[0]), 0);
744 CPS_UncachedWrite32((uint32_t *)&(descStartPerQ->word[1]),
745 CEDI_TXD_USED | (i==(txQ->descMax-1)?CEDI_TXD_WRAP:0));
746
747 descStartPerQ = (txDesc*) (((uintptr_t)(descStartPerQ)) +
748 (CEDI_PdVar(txDescriptorSize)));
749 }
750
751 switch (q) {
752 case 0:
753 regTmp = CPS_UncachedRead32(CEDI_RegAddr(transmit_q_ptr));\
754 EMAC_REGS__TRANSMIT_Q_PTR__DMA_TX_Q_PTR__MODIFY(regTmp, pAddr>>2);
755 CPS_UncachedWrite32(CEDI_RegAddr(transmit_q_ptr), regTmp);
756 break;
757 CEDI_WR_TXQ_PTR_REG_N_CASE(1);
758 CEDI_WR_TXQ_PTR_REG_N_CASE(2);
759 CEDI_WR_TXQ_PTR_REG_N_CASE(3);
760 CEDI_WR_TXQ_PTR_REG_N_CASE(4);
761 CEDI_WR_TXQ_PTR_REG_N_CASE(5);
762 CEDI_WR_TXQ_PTR_REG_N_CASE(6);
763 CEDI_WR_TXQ_PTR_REG_N_CASE(7);
764 CEDI_WR_TXQ_PTR_REG_N_CASE(8);
765 CEDI_WR_TXQ_PTR_REG_N_CASE(9);
766 CEDI_WR_TXQ_PTR_REG_N_CASE(10);
767 CEDI_WR_TXQ_PTR_REG_N_CASE(11);
768 CEDI_WR_TXQ_PTR_REG_N_CASE(12);
769 CEDI_WR_TXQ_PTR_REG_N_CASE(13);
770 CEDI_WR_TXQ_PTR_REG_N_CASE(14);
771 CEDI_WR_TXQ_PTR_REG_N_CASE(15);
772 }
773
774 return result;
775 }
776
777 /* Enable & start the transmit circuit. Not required during normal
778 * operation, as queueTxBuf will automatically start Tx when complete frame
779 * has been queued, but may be used to restart after a Tx error.
780 * @param pD - driver private state info specific to this instance
781 * @return 0 if successful
782 * @return ECANCELED if no entries in buffer
783 * @return EINVAL if invalid parameter
784 */
emacStartTx(void * pD)785 uint32_t emacStartTx(void *pD)
786 {
787 const CEDI_Config *cfg;
788 uint32_t qNum;
789 uint8_t ok = 0;
790 txQueue_t *txQ;
791 uint32_t ncr;
792
793 if (pD==NULL) return EINVAL;
794
795 vDbgMsg(DBG_GEN_MSG, 10, "%s entered\n", __func__);
796
797 cfg = &((CEDI_PrivateData *)pD)->cfg;
798
799 if (!emacGetTxEnabled(pD))
800 emacEnableTx(pD);
801
802 /* if anything to transmit, start transmission */
803 for (qNum = 0; qNum < cfg->txQs; ++qNum) {
804 txQ = &CEDI_PdVar(txQueue)[qNum];
805 if (txQ->bdHead != txQ->bdTail) {
806 ok = 1;
807 break;
808 }
809 }
810 if (!ok)
811 return ECANCELED;
812 else
813 {
814 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
815 EMAC_REGS__NETWORK_CONTROL__TX_START_PCLK__SET(ncr);
816 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
817 }
818 return 0;
819 }
820
821 /* Halt transmission as soon as current frame Tx has finished
822 * @param pD - driver private state info specific to this instance
823 */
emacStopTx(void * pD)824 void emacStopTx(void *pD)
825 {
826 uint32_t ncr;
827
828 if (!pD) return;
829 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
830 EMAC_REGS__NETWORK_CONTROL__TX_HALT_PCLK__SET(ncr);
831 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
832 return;
833 }
834
835 /* Immediately disable transmission without waiting for completion.
836 * Since the EMAC will reset to point to the start of transmit descriptor
837 * list, the buffer queues may have to be reset after this call.
838 * @param pD - driver private state info specific to this instance
839 */
emacAbortTx(void * pD)840 void emacAbortTx(void *pD)
841 {
842 uint32_t ncr;
843
844 if (!pD) return;
845 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
846 EMAC_REGS__NETWORK_CONTROL__ENABLE_TRANSMIT__CLR(ncr);
847 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
848 return;
849 }
850
851 /* Get state of transmitter
852 * @param pD - driver private state info specific to this instance
853 * @return 1 if active
854 * @return 0 if idle or pD==NULL
855 */
emacTransmitting(void * pD)856 uint32_t emacTransmitting(void *pD)
857 {
858 if (pD==NULL) return 0;
859
860 return EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_GO__READ(
861 CPS_UncachedRead32(CEDI_RegAddr(transmit_status)));
862 }
863
864 /**
865 * Enable the transmit circuit. This will be done automatically
866 * when call startTx, but it may be desirable to call this earlier,
867 * since some functionality depends on transmit being enabled.
868 * @param[in] pD driver private state info specific to this instance
869 */
emacEnableTx(void * pD)870 void emacEnableTx(void *pD)
871 {
872 uint32_t ncr;
873 if (pD==NULL) return;
874
875 /* Enable the transmitter */
876 ncr = CPS_UncachedRead32(CEDI_RegAddr(network_control));
877 EMAC_REGS__NETWORK_CONTROL__ENABLE_TRANSMIT__SET(ncr);
878 CPS_UncachedWrite32(CEDI_RegAddr(network_control), ncr);
879 }
880
881 /**
882 * Get state of transmision enabled
883 * @param pD - driver private state info specific to this instance
884 * @return 1 if transmission enabled
885 * @return 0 if transmission disabled or pD==NULL
886 */
emacGetTxEnabled(void * pD)887 uint32_t emacGetTxEnabled(void *pD)
888 {
889 if (pD==NULL) return 0;
890
891 return EMAC_REGS__NETWORK_CONTROL__ENABLE_TRANSMIT__READ(
892 CPS_UncachedRead32(CEDI_RegAddr(network_control)));
893 }
894
895 /* Get the content of EMAC transmit status register
896 * @param pD - driver private state info specific to this instance
897 * @param status - pointer to struct with fields for each flag
898 * @return raw status register value, !=0 if any flags set
899 */
emacGetTxStatus(void * pD,CEDI_TxStatus * status)900 uint32_t emacGetTxStatus(void *pD, CEDI_TxStatus *status)
901 {
902 uint32_t reg;
903 if ((pD==NULL)||(status==NULL))
904 return 0;
905
906 reg = CPS_UncachedRead32(CEDI_RegAddr(transmit_status));
907 // vDbgMsg(DBG_GEN_MSG, 10, "-----getTxStatus reads 0x%08X ------\n", reg);
908
909 status->txComplete =
910 EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_COMPLETE__READ(reg);
911 status->usedBitRead =
912 EMAC_REGS__TRANSMIT_STATUS__USED_BIT_READ__READ(reg);
913 status->collisionOcc =
914 EMAC_REGS__TRANSMIT_STATUS__COLLISION_OCCURRED__READ(reg);
915 status->retryLimExc =
916 EMAC_REGS__TRANSMIT_STATUS__RETRY_LIMIT_EXCEEDED__READ(reg);
917 status->lateCollision =
918 EMAC_REGS__TRANSMIT_STATUS__LATE_COLLISION_OCCURRED__READ(reg);
919 status->txActive =
920 EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_GO__READ(reg);
921 status->txFrameErr =
922 EMAC_REGS__TRANSMIT_STATUS__AMBA_ERROR__READ(reg);
923 status->txUnderRun =
924 EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_UNDER_RUN__READ(reg);
925 status->hRespNotOk =
926 EMAC_REGS__TRANSMIT_STATUS__RESP_NOT_OK__READ(reg);
927
928 return reg;
929 }
930
931 /* Reset the bits of EMAC transmit status register as selected in resetStatus
932 * @param pD - driver private state info specific to this instance
933 * @param resetStatus - OR'd combination of CEDI_TXS_ bit-fields
934 */
emacClearTxStatus(void * pD,uint32_t resetStatus)935 void emacClearTxStatus(void *pD, uint32_t resetStatus)
936 {
937 uint32_t dst = 0;
938
939 if (!pD) return;
940 if (resetStatus & CEDI_TXS_USED_READ)
941 EMAC_REGS__TRANSMIT_STATUS__USED_BIT_READ__MODIFY(dst, 1);
942 if (resetStatus & CEDI_TXS_COLLISION)
943 EMAC_REGS__TRANSMIT_STATUS__COLLISION_OCCURRED__MODIFY(dst, 1);
944 if (resetStatus & CEDI_TXS_RETRY_EXC)
945 EMAC_REGS__TRANSMIT_STATUS__RETRY_LIMIT_EXCEEDED__MODIFY(dst, 1);
946 if (resetStatus & CEDI_TXS_LATE_COLL)
947 EMAC_REGS__TRANSMIT_STATUS__LATE_COLLISION_OCCURRED__MODIFY(dst, 1);
948 /* txActive not resettable */
949 if (resetStatus & CEDI_TXS_FRAME_ERR)
950 EMAC_REGS__TRANSMIT_STATUS__AMBA_ERROR__MODIFY(dst, 1);
951 if (resetStatus & CEDI_TXS_TX_COMPLETE)
952 EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_COMPLETE__MODIFY(dst, 1);
953 if (resetStatus & CEDI_TXS_UNDERRUN)
954 EMAC_REGS__TRANSMIT_STATUS__TRANSMIT_UNDER_RUN__MODIFY(dst, 1);
955 if (resetStatus & CEDI_TXS_HRESP_ERR)
956 EMAC_REGS__TRANSMIT_STATUS__RESP_NOT_OK__MODIFY(dst, 1);
957 if (dst)
958 CPS_UncachedWrite32(CEDI_RegAddr(transmit_status), dst);
959 }
960
emacSetTxPartialStFwd(void * pD,uint32_t watermark,uint8_t enable)961 uint32_t emacSetTxPartialStFwd(void *pD, uint32_t watermark, uint8_t enable)
962 {
963 uint32_t reg;
964 if (!pD) return EINVAL;
965 if (CEDI_PdVar(hwCfg).tx_pkt_buffer==0)
966 return ENOTSUP;
967 if (enable>1) return EINVAL;
968
969 if ((enable==1) &&
970 ((watermark<0x14UL) || (watermark>=(1UL<<CEDI_PdVar(hwCfg).tx_pbuf_addr))))
971 return EINVAL;
972
973 reg = CPS_UncachedRead32(CEDI_RegAddr(pbuf_txcutthru));
974 if (enable) {
975 EMAC_REGS__PBUF_TXCUTTHRU__DMA_TX_CUTTHRU_THRESHOLD__MODIFY(reg,
976 watermark);
977 EMAC_REGS__PBUF_TXCUTTHRU__DMA_TX_CUTTHRU__SET(reg);
978 }
979 else
980 EMAC_REGS__PBUF_TXCUTTHRU__DMA_TX_CUTTHRU__CLR(reg);
981
982 CPS_UncachedWrite32(CEDI_RegAddr(pbuf_txcutthru), reg);
983
984 return 0;
985 }
986
emacGetTxPartialStFwd(void * pD,uint32_t * watermark,uint8_t * enable)987 uint32_t emacGetTxPartialStFwd(void *pD, uint32_t *watermark, uint8_t *enable)
988 {
989 uint32_t reg;
990 if ((pD==0)||(enable==0)||(watermark==0))
991 return EINVAL;
992 if (CEDI_PdVar(hwCfg).tx_pkt_buffer==0)
993 return ENOTSUP;
994
995 reg = CPS_UncachedRead32(CEDI_RegAddr(pbuf_txcutthru));
996 (*enable) = EMAC_REGS__PBUF_TXCUTTHRU__DMA_TX_CUTTHRU__READ(reg);
997 if (*enable)
998 (*watermark) = EMAC_REGS__PBUF_TXCUTTHRU__DMA_TX_CUTTHRU_THRESHOLD__READ(reg);
999
1000 return 0;
1001 }
1002
1003
1004 /**
1005 * Enable credit-based shaping (CBS) on the specified queue. If already
1006 * enabled then first disables, sets a new idle slope value for the queue,
1007 * and re-enables CBS
1008 * @param[in] pD driver private state info specific to this instance
1009 * @param[in] qSel if equal 0 selects highest priority queue (queue A),
1010 * if equal 1 selects next-highest priority queue (queue B)
1011 * $RANGE $FROM 0 $TO 1$
1012 * @param[in] idleSlope new idle slope value (in bytes/sec)
1013 * @return 0 if successful
1014 * @return EINVAL if priority queueing not enabled (i.e. only one Tx queue)
1015 * or invalid parameter
1016 * @return ENOTSUP if CBS has been excluded from h/w config
1017 * $VALIDFAIL if (CEDI_Config.txQs==1) $EXPECT_RETURN EINVAL $
1018 */
emacEnableCbs(void * pD,uint8_t qSel,uint32_t idleSlope)1019 uint32_t emacEnableCbs(void *pD, uint8_t qSel, uint32_t idleSlope)
1020 {
1021 uint32_t tmp, ret;
1022 uint8_t enabled;
1023 uint32_t reg;
1024
1025 if (pD==NULL) return EINVAL;
1026
1027 if (CEDI_PdVar(hwCfg).exclude_cbs==1)
1028 return ENOTSUP;
1029
1030 if (CEDI_PdVar(numQs)==1)
1031 return EINVAL;
1032
1033 ret = emacGetCbsQSetting(pD, qSel, &enabled, &tmp);
1034 if (ret!=0)
1035 return ret;
1036
1037 if (enabled)
1038 emacDisableCbs(pD, qSel);
1039
1040 reg = 0;
1041 if (qSel) { /* i.e. queue B */
1042 EMAC_REGS__CBS_IDLESLOPE_Q_B__IDLESLOPE_B__MODIFY(reg, idleSlope);
1043 CPS_UncachedWrite32(CEDI_RegAddr(cbs_idleslope_q_b), reg);
1044 }
1045 else {
1046 EMAC_REGS__CBS_IDLESLOPE_Q_A__IDLESLOPE_A__MODIFY(reg, idleSlope);
1047 CPS_UncachedWrite32(CEDI_RegAddr(cbs_idleslope_q_a), reg);
1048 }
1049
1050 reg = CPS_UncachedRead32(CEDI_RegAddr(cbs_control));
1051 if (qSel) /* i.e. queue B */
1052 EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_B__MODIFY(reg, 1);
1053 else
1054 EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_A__MODIFY(reg, 1);
1055 CPS_UncachedWrite32(CEDI_RegAddr(cbs_control), reg);
1056
1057 return 0;
1058 }
1059
1060 /* Disable CBS on the specified queue
1061 * @param pD - driver private state info specific to this instance
1062 * @param qSel - if = 0, selects highest priority queue (queue A), else
1063 * selects next-highest priority queue (queue B)
1064 */
emacDisableCbs(void * pD,uint8_t qSel)1065 void emacDisableCbs(void *pD, uint8_t qSel)
1066 {
1067 uint32_t reg;
1068
1069 if ((!pD) || (qSel>1) || CEDI_PdVar(hwCfg).exclude_cbs) return;
1070 reg = CPS_UncachedRead32(CEDI_RegAddr(cbs_control));
1071 if (qSel) /* i.e. queue B */
1072 EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_B__MODIFY(reg, 0);
1073 else
1074 EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_A__MODIFY(reg, 0);
1075 CPS_UncachedWrite32(CEDI_RegAddr(cbs_control), reg);
1076 }
1077
1078 /**
1079 * Read CBS setting for the specified queue.
1080 * @param[in] pD driver private state info specific to this instance
1081 * @param[in] qSel if equal 0 selects highest priority queue (queue A),
1082 * if equal 1 selects next-highest priority queue (queue B)
1083 * $RANGE $FROM 0 $TO 1$
1084 * @param[out] enable returns: 1 if CBS enabled for the specified queue,
1085 * 0 if not enabled
1086 * @param[out] idleSlope pointer for returning the idleSlope value
1087 * for selected queue.
1088 * @return 0 for success.
1089 * @return EINVAL for invalid pointer.
1090 * @return ENOTSUP if CBS has been excluded from h/w config
1091 */
emacGetCbsQSetting(void * pD,uint8_t qSel,uint8_t * enable,uint32_t * idleSlope)1092 uint32_t emacGetCbsQSetting(void *pD, uint8_t qSel,
1093 uint8_t *enable, uint32_t *idleSlope)
1094 {
1095 uint32_t reg, enabled;
1096
1097 if ((pD==0)||(enable==0)||(idleSlope==0))
1098 return EINVAL;
1099 if (CEDI_PdVar(hwCfg).exclude_cbs==1)
1100 return ENOTSUP;
1101 if (qSel>1) return EINVAL;
1102
1103 reg = CPS_UncachedRead32(CEDI_RegAddr(cbs_control));
1104 if (qSel) { /* i.e. queue B */
1105 enabled = EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_B__READ(reg);
1106 if (enabled && (idleSlope!=NULL)) {
1107 reg = CPS_UncachedRead32(CEDI_RegAddr(cbs_idleslope_q_b));
1108 *idleSlope = EMAC_REGS__CBS_IDLESLOPE_Q_B__IDLESLOPE_B__READ(reg);
1109 }
1110 }
1111 else {
1112 enabled = EMAC_REGS__CBS_CONTROL__CBS_ENABLE_QUEUE_A__READ(reg);
1113 if (enabled && (idleSlope!=NULL)) {
1114 reg = CPS_UncachedRead32(CEDI_RegAddr(cbs_idleslope_q_a));
1115 *idleSlope = EMAC_REGS__CBS_IDLESLOPE_Q_A__IDLESLOPE_A__READ(reg);
1116 }
1117 }
1118
1119 *enable=enabled;
1120 return 0;
1121 }
1122
1123 /**
1124 * Enable/disable the inter-packet gap (IPG) stretch function.
1125 * @param[in] pD driver private state info specific to this instance
1126 * @param[in] enable if equal 1 then enable IPG stretch, if 0 then disable.
1127 * $RANGE $FROM 0 $TO 1$
1128 * @param[in] multiplier multiplying factor applied to previous Tx frame
1129 * length. Ignored if enable equal 0.
1130 * @param[in] divisor after multiplying previous frame length, divide by
1131 * (divisor+1) - if result>96 bits, this is used for the Tx IPG.
1132 * Ignored if enable equal 0.
1133 * @return EINVAL if pD equal NULL
1134 * @return 0 if successful.
1135 */
emacSetIpgStretch(void * pD,uint8_t enable,uint8_t multiplier,uint8_t divisor)1136 uint32_t emacSetIpgStretch(void *pD, uint8_t enable, uint8_t multiplier,
1137 uint8_t divisor)
1138 {
1139 uint32_t reg;
1140
1141 if (pD==NULL) return EINVAL;
1142 if (enable>1) return EINVAL;
1143 reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
1144 if (enable) {
1145 EMAC_REGS__NETWORK_CONFIG__IPG_STRETCH_ENABLE__SET(reg);
1146 CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
1147 reg = CPS_UncachedRead32(CEDI_RegAddr(stretch_ratio));
1148 EMAC_REGS__STRETCH_RATIO__IPG_STRETCH__MODIFY(reg,
1149 (divisor << 8) | multiplier);
1150 CPS_UncachedWrite32(CEDI_RegAddr(stretch_ratio), reg);
1151 }
1152 else {
1153 EMAC_REGS__NETWORK_CONFIG__IPG_STRETCH_ENABLE__CLR(reg);
1154 CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
1155 }
1156 return 0;
1157 }
1158
1159 /* Read the inter-packet gap (IPG) stretch settings.
1160 * @param pD - driver private state info specific to this instance
1161 * @param enabled - pointer for returning enabled state: returns 1 if
1162 * IPG stretch enabled, 0 if disabled.
1163 * @param multiplier - pointer for returning IPG multiplying factor.
1164 * @param divisor - pointer for returning IPG divisor.
1165 * @return =0 if successful, EINVAL if any parameter =NULL
1166 */
emacGetIpgStretch(void * pD,uint8_t * enabled,uint8_t * multiplier,uint8_t * divisor)1167 uint32_t emacGetIpgStretch(void *pD, uint8_t *enabled, uint8_t *multiplier,
1168 uint8_t *divisor)
1169 {
1170 uint32_t reg, stretch;
1171
1172 if ((pD==NULL) || (enabled==NULL) || (multiplier==NULL) || (divisor==NULL))
1173 return EINVAL;
1174
1175 reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
1176 if (EMAC_REGS__NETWORK_CONFIG__IPG_STRETCH_ENABLE__READ(reg)) {
1177 *enabled = 1;
1178 reg = CPS_UncachedRead32(CEDI_RegAddr(stretch_ratio));
1179 stretch = EMAC_REGS__STRETCH_RATIO__IPG_STRETCH__READ(reg);
1180 *multiplier = (stretch & 0xFF);
1181 *divisor = (stretch >> 8) & 0xFF;
1182 }
1183 else {
1184 *enabled = 0;
1185 *multiplier = 0;
1186 *divisor = 0;
1187 }
1188 return 0;
1189 }
1190
1191 #ifdef __cplusplus
1192 }
1193 #endif
1194
1195 #endif /* CY_IP_MXETH */
1196
1197 /* [] END OF FILE */
1198
1199