summaryrefslogtreecommitdiff
path: root/board/xilinx/xilinx_enet/xemac_polled.c
blob: 23768bca790d855198ccfc187acc038030e41ea4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
/******************************************************************************
*
*     Author: Xilinx, Inc.
*
*
*     This program is free software; you can redistribute it and/or modify it
*     under the terms of the GNU General Public License as published by the
*     Free Software Foundation; either version 2 of the License, or (at your
*     option) any later version.
*
*
*     XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A
*     COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS
*     ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD,
*     XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE
*     FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING
*     ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION.
*     XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO
*     THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY
*     WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM
*     CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND
*     FITNESS FOR A PARTICULAR PURPOSE.
*
*
*     Xilinx hardware products are not intended for use in life support
*     appliances, devices, or systems. Use in such applications is
*     expressly prohibited.
*
*
*     (c) Copyright 2002-2004 Xilinx Inc.
*     All rights reserved.
*
*
*     You should have received a copy of the GNU General Public License along
*     with this program; if not, write to the Free Software Foundation, Inc.,
*     675 Mass Ave, Cambridge, MA 02139, USA.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file xemac_polled.c
*
* Contains functions used when the driver is in polled mode. Use the
* XEmac_SetOptions() function to put the driver into polled mode.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who  Date     Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a rpm  07/31/01 First release
* 1.00b rpm  02/20/02 Repartitioned files and functions
* 1.00c rpm  12/05/02 New version includes support for simple DMA
* </pre>
*
******************************************************************************/

/***************************** Include Files *********************************/

#include "xbasic_types.h"
#include "xemac_i.h"
#include "xio.h"
#include "xipif_v1_23_b.h"	/* Uses v1.23b of the IPIF */

/************************** Constant Definitions *****************************/

/**************************** Type Definitions *******************************/

/***************** Macros (Inline Functions) Definitions *********************/

/************************** Variable Definitions *****************************/

/************************** Function Prototypes ******************************/

/*****************************************************************************/
/**
*
* Send an Ethernet frame in polled mode.  The device/driver must be in polled
* mode before calling this function. The driver writes the frame directly to
* the MAC's packet FIFO, then enters a loop checking the device status for
* completion or error. Statistics are updated if an error occurs. The buffer
* to be sent must be word-aligned.
*
* It is assumed that the upper layer software supplies a correctly formatted
* Ethernet frame, including the destination and source addresses, the
* type/length field, and the data field.  It is also assumed that upper layer
* software does not append FCS at the end of the frame.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BufPtr is a pointer to a word-aligned buffer containing the Ethernet
*        frame to be sent.
* @param ByteCount is the size of the Ethernet frame.
*
* @return
*
* - XST_SUCCESS if the frame was sent successfully
* - XST_DEVICE_IS_STOPPED if the device has not yet been started
* - XST_NOT_POLLED if the device is not in polled mode
* - XST_FIFO_NO_ROOM if there is no room in the EMAC's length FIFO for this frame
* - XST_FIFO_ERROR if the FIFO was overrun or underrun. This error is critical
*   and requires the caller to reset the device.
* - XST_EMAC_COLLISION if the send failed due to excess deferral or late
*   collision
*
* @note
*
* There is the possibility that this function will not return if the hardware
* is broken (i.e., it never sets the status bit indicating that transmission is
* done). If this is of concern to the user, the user should provide protection
* from this problem - perhaps by using a different timer thread to monitor the
* PollSend thread. On a 10Mbps MAC, it takes about 1.21 msecs to transmit a
* maximum size Ethernet frame (1518 bytes). On a 100Mbps MAC, it takes about
* 121 usecs to transmit a maximum size Ethernet frame.
*
* @internal
*
* The EMAC uses FIFOs behind its length and status registers. For this reason,
* it is important to keep the length, status, and data FIFOs in sync when
* reading or writing to them.
*
******************************************************************************/
XStatus
XEmac_PollSend(XEmac * InstancePtr, u8 * BufPtr, u32 ByteCount)
{
	u32 IntrStatus;
	u32 XmitStatus;
	XStatus Result;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(BufPtr != NULL);
	XASSERT_NONVOID(ByteCount > XEM_HDR_SIZE);	/* send at least 1 byte */
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/*
	 * Be sure the device is configured for polled mode and it is started
	 */
	if (!InstancePtr->IsPolled) {
		return XST_NOT_POLLED;
	}

	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return XST_DEVICE_IS_STOPPED;
	}

	/*
	 * Check for overruns and underruns for the transmit status and length
	 * FIFOs and make sure the send packet FIFO is not deadlocked. Any of these
	 * conditions is bad enough that we do not want to continue. The upper layer
	 * software should reset the device to resolve the error.
	 */
	IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);

	/*
	 * Overrun errors
	 */
	if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK |
			  XEM_EIR_XMIT_LFIFO_OVER_MASK)) {
		InstancePtr->Stats.XmitOverrunErrors++;
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	/*
	 * Underrun errors
	 */
	if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK |
			  XEM_EIR_XMIT_LFIFO_UNDER_MASK)) {
		InstancePtr->Stats.XmitUnderrunErrors++;
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	if (XPF_V100B_IS_DEADLOCKED(&InstancePtr->SendFifo)) {
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	/*
	 * Before writing to the data FIFO, make sure the length FIFO is not
	 * full.  The data FIFO might not be full yet even though the length FIFO
	 * is. This avoids an overrun condition on the length FIFO and keeps the
	 * FIFOs in sync.
	 */
	if (IntrStatus & XEM_EIR_XMIT_LFIFO_FULL_MASK) {
		/*
		 * Clear the latched LFIFO_FULL bit so next time around the most
		 * current status is represented
		 */
		XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
				      XEM_EIR_XMIT_LFIFO_FULL_MASK);
		return XST_FIFO_NO_ROOM;
	}

	/*
	 * This is a non-blocking write. The packet FIFO returns an error if there
	 * is not enough room in the FIFO for this frame.
	 */
	Result =
	    XPacketFifoV100b_Write(&InstancePtr->SendFifo, BufPtr, ByteCount);
	if (Result != XST_SUCCESS) {
		return Result;
	}

	/*
	 * Loop on the MAC's status to wait for any pause to complete.
	 */
	IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);

	while ((IntrStatus & XEM_EIR_XMIT_PAUSE_MASK) != 0) {
		IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
		/*
		   * Clear the pause status from the transmit status register
		 */
		XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
				      IntrStatus & XEM_EIR_XMIT_PAUSE_MASK);
	}

	/*
	 * Set the MAC's transmit packet length register to tell it to transmit
	 */
	XIo_Out32(InstancePtr->BaseAddress + XEM_TPLR_OFFSET, ByteCount);

	/*
	 * Loop on the MAC's status to wait for the transmit to complete. The
	 * transmit status is in the FIFO when the XMIT_DONE bit is set.
	 */
	do {
		IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
	}
	while ((IntrStatus & XEM_EIR_XMIT_DONE_MASK) == 0);

	XmitStatus = XIo_In32(InstancePtr->BaseAddress + XEM_TSR_OFFSET);

	InstancePtr->Stats.XmitFrames++;
	InstancePtr->Stats.XmitBytes += ByteCount;

	/*
	 * Check for various errors, bump statistics, and return an error status.
	 */

	/*
	 * Overrun errors
	 */
	if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK |
			  XEM_EIR_XMIT_LFIFO_OVER_MASK)) {
		InstancePtr->Stats.XmitOverrunErrors++;
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	/*
	 * Underrun errors
	 */
	if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK |
			  XEM_EIR_XMIT_LFIFO_UNDER_MASK)) {
		InstancePtr->Stats.XmitUnderrunErrors++;
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	/*
	 * Clear the interrupt status register of transmit statuses
	 */
	XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
			      IntrStatus & XEM_EIR_XMIT_ALL_MASK);

	/*
	 * Collision errors are stored in the transmit status register
	 * instead of the interrupt status register
	 */
	if (XmitStatus & XEM_TSR_EXCESS_DEFERRAL_MASK) {
		InstancePtr->Stats.XmitExcessDeferral++;
		return XST_EMAC_COLLISION_ERROR;
	}

	if (XmitStatus & XEM_TSR_LATE_COLLISION_MASK) {
		InstancePtr->Stats.XmitLateCollisionErrors++;
		return XST_EMAC_COLLISION_ERROR;
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* Receive an Ethernet frame in polled mode. The device/driver must be in polled
* mode before calling this function. The driver receives the frame directly
* from the MAC's packet FIFO. This is a non-blocking receive, in that if there
* is no frame ready to be received at the device, the function returns with an
* error. The MAC's error status is not checked, so statistics are not updated
* for polled receive. The buffer into which the frame will be received must be
* word-aligned.
*
* @param InstancePtr is a pointer to the XEmac instance to be worked on.
* @param BufPtr is a pointer to a word-aligned buffer into which the received
*        Ethernet frame will be copied.
* @param ByteCountPtr is both an input and an output parameter. It is a pointer
*        to a 32-bit word that contains the size of the buffer on entry into the
*        function and the size the received frame on return from the function.
*
* @return
*
* - XST_SUCCESS if the frame was sent successfully
* - XST_DEVICE_IS_STOPPED if the device has not yet been started
* - XST_NOT_POLLED if the device is not in polled mode
* - XST_NO_DATA if there is no frame to be received from the FIFO
* - XST_BUFFER_TOO_SMALL if the buffer to receive the frame is too small for
*   the frame waiting in the FIFO.
*
* @note
*
* Input buffer must be big enough to hold the largest Ethernet frame. Buffer
* must also be 32-bit aligned.
*
* @internal
*
* The EMAC uses FIFOs behind its length and status registers. For this reason,
* it is important to keep the length, status, and data FIFOs in sync when
* reading or writing to them.
*
******************************************************************************/
XStatus
XEmac_PollRecv(XEmac * InstancePtr, u8 * BufPtr, u32 * ByteCountPtr)
{
	XStatus Result;
	u32 PktLength;
	u32 IntrStatus;

	XASSERT_NONVOID(InstancePtr != NULL);
	XASSERT_NONVOID(BufPtr != NULL);
	XASSERT_NONVOID(ByteCountPtr != NULL);
	XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY);

	/*
	 * Be sure the device is configured for polled mode and it is started
	 */
	if (!InstancePtr->IsPolled) {
		return XST_NOT_POLLED;
	}

	if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) {
		return XST_DEVICE_IS_STOPPED;
	}

	/*
	 * Make sure the buffer is big enough to hold the maximum frame size.
	 * We need to do this because as soon as we read the MAC's packet length
	 * register, which is actually a FIFO, we remove that length from the
	 * FIFO.  We do not want to read the length FIFO without also reading the
	 * data FIFO since this would get the FIFOs out of sync.  So we have to
	 * make this restriction.
	 */
	if (*ByteCountPtr < XEM_MAX_FRAME_SIZE) {
		return XST_BUFFER_TOO_SMALL;
	}

	/*
	 * First check for packet FIFO deadlock and return an error if it has
	 * occurred. A reset by the caller is necessary to correct this problem.
	 */
	if (XPF_V100B_IS_DEADLOCKED(&InstancePtr->RecvFifo)) {
		InstancePtr->Stats.FifoErrors++;
		return XST_FIFO_ERROR;
	}

	/*
	 * Get the interrupt status to know what happened (whether an error occurred
	 * and/or whether frames have been received successfully). When clearing the
	 * intr status register, clear only statuses that pertain to receive.
	 */
	IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress);
	XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress,
			      IntrStatus & XEM_EIR_RECV_ALL_MASK);

	/*
	 * Check receive errors and bump statistics so the caller will have a clue
	 * as to why data may not have been received. We continue on if an error
	 * occurred since there still may be frames that were received successfully.
	 */
	if (IntrStatus & (XEM_EIR_RECV_LFIFO_OVER_MASK |
			  XEM_EIR_RECV_DFIFO_OVER_MASK)) {
		InstancePtr->Stats.RecvOverrunErrors++;
		InstancePtr->Stats.FifoErrors++;
	}

	if (IntrStatus & XEM_EIR_RECV_LFIFO_UNDER_MASK) {
		InstancePtr->Stats.RecvUnderrunErrors++;
		InstancePtr->Stats.FifoErrors++;
	}

	/*
	 * General receive errors
	 */
	if (IntrStatus & XEM_EIR_RECV_ERROR_MASK) {
		if (IntrStatus & XEM_EIR_RECV_MISSED_FRAME_MASK) {
			InstancePtr->Stats.RecvMissedFrameErrors =
			    XIo_In32(InstancePtr->BaseAddress +
				     XEM_RMFC_OFFSET);
		}

		if (IntrStatus & XEM_EIR_RECV_COLLISION_MASK) {
			InstancePtr->Stats.RecvCollisionErrors =
			    XIo_In32(InstancePtr->BaseAddress + XEM_RCC_OFFSET);
		}

		if (IntrStatus & XEM_EIR_RECV_FCS_ERROR_MASK) {
			InstancePtr->Stats.RecvFcsErrors =
			    XIo_In32(InstancePtr->BaseAddress +
				     XEM_RFCSEC_OFFSET);
		}

		if (IntrStatus & XEM_EIR_RECV_LEN_ERROR_MASK) {
			InstancePtr->Stats.RecvLengthFieldErrors++;
		}

		if (IntrStatus & XEM_EIR_RECV_SHORT_ERROR_MASK) {
			InstancePtr->Stats.RecvShortErrors++;
		}

		if (IntrStatus & XEM_EIR_RECV_LONG_ERROR_MASK) {
			InstancePtr->Stats.RecvLongErrors++;
		}

		if (IntrStatus & XEM_EIR_RECV_ALIGN_ERROR_MASK) {
			InstancePtr->Stats.RecvAlignmentErrors =
			    XIo_In32(InstancePtr->BaseAddress +
				     XEM_RAEC_OFFSET);
		}
	}

	/*
	 * Before reading from the length FIFO, make sure the length FIFO is not
	 * empty. We could cause an underrun error if we try to read from an
	 * empty FIFO.
	 */
	if ((IntrStatus & XEM_EIR_RECV_DONE_MASK) == 0) {
		return XST_NO_DATA;
	}

	/*
	 * Determine, from the MAC, the length of the next packet available
	 * in the data FIFO (there should be a non-zero length here)
	 */
	PktLength = XIo_In32(InstancePtr->BaseAddress + XEM_RPLR_OFFSET);
	if (PktLength == 0) {
		return XST_NO_DATA;
	}

	/*
	 * Write the RECV_DONE bit in the status register to clear it. This bit
	 * indicates the RPLR is non-empty, and we know it's set at this point.
	 * We clear it so that subsequent entry into this routine will reflect the
	 * current status. This is done because the non-empty bit is latched in the
	 * IPIF, which means it may indicate a non-empty condition even though
	 * there is something in the FIFO.
	 */
	XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, XEM_EIR_RECV_DONE_MASK);

	/*
	 * We assume that the MAC never has a length bigger than the largest
	 * Ethernet frame, so no need to make another check here.
	 */

	/*
	 * This is a non-blocking read. The FIFO returns an error if there is
	 * not at least the requested amount of data in the FIFO.
	 */
	Result =
	    XPacketFifoV100b_Read(&InstancePtr->RecvFifo, BufPtr, PktLength);
	if (Result != XST_SUCCESS) {
		return Result;
	}

	InstancePtr->Stats.RecvFrames++;
	InstancePtr->Stats.RecvBytes += PktLength;

	*ByteCountPtr = PktLength;

	return XST_SUCCESS;
}