]>
Commit | Line | Data |
---|---|---|
945af8d7 | 1 | /* |
5e5f9ed2 | 2 | * (C) Copyright 2003-2005 |
945af8d7 WD |
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. |
4 | * | |
5 | * This file is based on mpc4200fec.c, | |
6 | * (C) Copyright Motorola, Inc., 2000 | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
10 | #include <mpc5xxx.h> | |
80b00af0 | 11 | #include <mpc5xxx_sdma.h> |
945af8d7 WD |
12 | #include <malloc.h> |
13 | #include <net.h> | |
e1d7480b | 14 | #include <netdev.h> |
945af8d7 | 15 | #include <miiphy.h> |
80b00af0 | 16 | #include "mpc5xxx_fec.h" |
945af8d7 | 17 | |
d87080b7 WD |
18 | DECLARE_GLOBAL_DATA_PTR; |
19 | ||
77846748 | 20 | /* #define DEBUG 0x28 */ |
945af8d7 | 21 | |
4431283c | 22 | #if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) |
63ff004c MB |
23 | #error "CONFIG_MII has to be defined!" |
24 | #endif | |
25 | ||
945af8d7 | 26 | #if (DEBUG & 0x60) |
63ff004c MB |
27 | static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec); |
28 | static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec); | |
945af8d7 WD |
29 | #endif /* DEBUG */ |
30 | ||
31 | #if (DEBUG & 0x40) | |
32 | static uint32 local_crc32(char *string, unsigned int crc_value, int len); | |
33 | #endif | |
34 | ||
77846748 WD |
35 | typedef struct { |
36 | uint8 data[1500]; /* actual data */ | |
37 | int length; /* actual length */ | |
38 | int used; /* buffer in use or not */ | |
39 | uint8 head[16]; /* MAC header(6 + 6 + 2) + 2(aligned) */ | |
40 | } NBUF; | |
41 | ||
63ff004c MB |
42 | int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal); |
43 | int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data); | |
44 | ||
f5cf2ef2 SH |
45 | static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis); |
46 | ||
d4ca31c4 WD |
47 | /********************************************************************/ |
48 | #if (DEBUG & 0x2) | |
63ff004c | 49 | static void mpc5xxx_fec_phydump (char *devname) |
d4ca31c4 WD |
50 | { |
51 | uint16 phyStatus, i; | |
52 | uint8 phyAddr = CONFIG_PHY_ADDR; | |
53 | uint8 reg_mask[] = { | |
54 | #if CONFIG_PHY_TYPE == 0x79c874 /* AMD Am79C874 */ | |
55 | /* regs to print: 0...7, 16...19, 21, 23, 24 */ | |
56 | 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |
57 | 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, | |
58 | #else | |
59 | /* regs to print: 0...8, 16...20 */ | |
60 | 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | |
61 | 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
62 | #endif | |
63 | }; | |
64 | ||
65 | for (i = 0; i < 32; i++) { | |
66 | if (reg_mask[i]) { | |
63ff004c | 67 | miiphy_read(devname, phyAddr, i, &phyStatus); |
d4ca31c4 WD |
68 | printf("Mii reg %d: 0x%04x\n", i, phyStatus); |
69 | } | |
70 | } | |
71 | } | |
72 | #endif | |
73 | ||
945af8d7 WD |
74 | /********************************************************************/ |
75 | static int mpc5xxx_fec_rbd_init(mpc5xxx_fec_priv *fec) | |
76 | { | |
77 | int ix; | |
78 | char *data; | |
77846748 | 79 | static int once = 0; |
945af8d7 | 80 | |
945af8d7 | 81 | for (ix = 0; ix < FEC_RBD_NUM; ix++) { |
77846748 WD |
82 | if (!once) { |
83 | data = (char *)malloc(FEC_MAX_PKT_SIZE); | |
84 | if (data == NULL) { | |
85 | printf ("RBD INIT FAILED\n"); | |
86 | return -1; | |
87 | } | |
88 | fec->rbdBase[ix].dataPointer = (uint32)data; | |
945af8d7 WD |
89 | } |
90 | fec->rbdBase[ix].status = FEC_RBD_EMPTY; | |
91 | fec->rbdBase[ix].dataLength = 0; | |
945af8d7 | 92 | } |
77846748 | 93 | once ++; |
945af8d7 WD |
94 | |
95 | /* | |
96 | * have the last RBD to close the ring | |
97 | */ | |
98 | fec->rbdBase[ix - 1].status |= FEC_RBD_WRAP; | |
99 | fec->rbdIndex = 0; | |
100 | ||
101 | return 0; | |
102 | } | |
103 | ||
104 | /********************************************************************/ | |
105 | static void mpc5xxx_fec_tbd_init(mpc5xxx_fec_priv *fec) | |
106 | { | |
107 | int ix; | |
108 | ||
109 | for (ix = 0; ix < FEC_TBD_NUM; ix++) { | |
110 | fec->tbdBase[ix].status = 0; | |
111 | } | |
112 | ||
113 | /* | |
114 | * Have the last TBD to close the ring | |
115 | */ | |
116 | fec->tbdBase[ix - 1].status |= FEC_TBD_WRAP; | |
117 | ||
118 | /* | |
119 | * Initialize some indices | |
120 | */ | |
121 | fec->tbdIndex = 0; | |
122 | fec->usedTbdIndex = 0; | |
123 | fec->cleanTbdNum = FEC_TBD_NUM; | |
124 | } | |
125 | ||
126 | /********************************************************************/ | |
151ab83a | 127 | static void mpc5xxx_fec_rbd_clean(mpc5xxx_fec_priv *fec, volatile FEC_RBD * pRbd) |
945af8d7 WD |
128 | { |
129 | /* | |
130 | * Reset buffer descriptor as empty | |
131 | */ | |
132 | if ((fec->rbdIndex) == (FEC_RBD_NUM - 1)) | |
133 | pRbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY); | |
134 | else | |
135 | pRbd->status = FEC_RBD_EMPTY; | |
136 | ||
137 | pRbd->dataLength = 0; | |
138 | ||
139 | /* | |
140 | * Now, we have an empty RxBD, restart the SmartDMA receive task | |
141 | */ | |
142 | SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); | |
143 | ||
144 | /* | |
145 | * Increment BD count | |
146 | */ | |
147 | fec->rbdIndex = (fec->rbdIndex + 1) % FEC_RBD_NUM; | |
148 | } | |
149 | ||
150 | /********************************************************************/ | |
151 | static void mpc5xxx_fec_tbd_scrub(mpc5xxx_fec_priv *fec) | |
152 | { | |
151ab83a | 153 | volatile FEC_TBD *pUsedTbd; |
945af8d7 WD |
154 | |
155 | #if (DEBUG & 0x1) | |
156 | printf ("tbd_scrub: fec->cleanTbdNum = %d, fec->usedTbdIndex = %d\n", | |
157 | fec->cleanTbdNum, fec->usedTbdIndex); | |
158 | #endif | |
159 | ||
160 | /* | |
161 | * process all the consumed TBDs | |
162 | */ | |
163 | while (fec->cleanTbdNum < FEC_TBD_NUM) { | |
164 | pUsedTbd = &fec->tbdBase[fec->usedTbdIndex]; | |
165 | if (pUsedTbd->status & FEC_TBD_READY) { | |
166 | #if (DEBUG & 0x20) | |
167 | printf("Cannot clean TBD %d, in use\n", fec->cleanTbdNum); | |
168 | #endif | |
169 | return; | |
170 | } | |
171 | ||
172 | /* | |
173 | * clean this buffer descriptor | |
174 | */ | |
175 | if (fec->usedTbdIndex == (FEC_TBD_NUM - 1)) | |
176 | pUsedTbd->status = FEC_TBD_WRAP; | |
177 | else | |
178 | pUsedTbd->status = 0; | |
179 | ||
180 | /* | |
181 | * update some indeces for a correct handling of the TBD ring | |
182 | */ | |
183 | fec->cleanTbdNum++; | |
184 | fec->usedTbdIndex = (fec->usedTbdIndex + 1) % FEC_TBD_NUM; | |
185 | } | |
186 | } | |
187 | ||
188 | /********************************************************************/ | |
189 | static void mpc5xxx_fec_set_hwaddr(mpc5xxx_fec_priv *fec, char *mac) | |
190 | { | |
191 | uint8 currByte; /* byte for which to compute the CRC */ | |
192 | int byte; /* loop - counter */ | |
193 | int bit; /* loop - counter */ | |
194 | uint32 crc = 0xffffffff; /* initial value */ | |
195 | ||
196 | /* | |
197 | * The algorithm used is the following: | |
198 | * we loop on each of the six bytes of the provided address, | |
199 | * and we compute the CRC by left-shifting the previous | |
200 | * value by one position, so that each bit in the current | |
201 | * byte of the address may contribute the calculation. If | |
202 | * the latter and the MSB in the CRC are different, then | |
203 | * the CRC value so computed is also ex-ored with the | |
204 | * "polynomium generator". The current byte of the address | |
205 | * is also shifted right by one bit at each iteration. | |
206 | * This is because the CRC generatore in hardware is implemented | |
207 | * as a shift-register with as many ex-ores as the radixes | |
208 | * in the polynomium. This suggests that we represent the | |
209 | * polynomiumm itself as a 32-bit constant. | |
210 | */ | |
211 | for (byte = 0; byte < 6; byte++) { | |
212 | currByte = mac[byte]; | |
213 | for (bit = 0; bit < 8; bit++) { | |
214 | if ((currByte & 0x01) ^ (crc & 0x01)) { | |
215 | crc >>= 1; | |
216 | crc = crc ^ 0xedb88320; | |
217 | } else { | |
218 | crc >>= 1; | |
219 | } | |
220 | currByte >>= 1; | |
221 | } | |
222 | } | |
223 | ||
224 | crc = crc >> 26; | |
225 | ||
226 | /* | |
227 | * Set individual hash table register | |
228 | */ | |
229 | if (crc >= 32) { | |
230 | fec->eth->iaddr1 = (1 << (crc - 32)); | |
231 | fec->eth->iaddr2 = 0; | |
232 | } else { | |
233 | fec->eth->iaddr1 = 0; | |
234 | fec->eth->iaddr2 = (1 << crc); | |
235 | } | |
236 | ||
237 | /* | |
238 | * Set physical address | |
239 | */ | |
240 | fec->eth->paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3]; | |
241 | fec->eth->paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808; | |
242 | } | |
243 | ||
244 | /********************************************************************/ | |
245 | static int mpc5xxx_fec_init(struct eth_device *dev, bd_t * bis) | |
246 | { | |
247 | mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; | |
248 | struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; | |
945af8d7 WD |
249 | |
250 | #if (DEBUG & 0x1) | |
251 | printf ("mpc5xxx_fec_init... Begin\n"); | |
252 | #endif | |
253 | ||
f5cf2ef2 SH |
254 | mpc5xxx_fec_init_phy(dev, bis); |
255 | ||
945af8d7 WD |
256 | /* |
257 | * Initialize RxBD/TxBD rings | |
258 | */ | |
259 | mpc5xxx_fec_rbd_init(fec); | |
260 | mpc5xxx_fec_tbd_init(fec); | |
261 | ||
945af8d7 WD |
262 | /* |
263 | * Clear FEC-Lite interrupt event register(IEVENT) | |
264 | */ | |
265 | fec->eth->ievent = 0xffffffff; | |
266 | ||
267 | /* | |
268 | * Set interrupt mask register | |
269 | */ | |
270 | fec->eth->imask = 0x00000000; | |
271 | ||
272 | /* | |
273 | * Set FEC-Lite receive control register(R_CNTRL): | |
274 | */ | |
275 | if (fec->xcv_type == SEVENWIRE) { | |
276 | /* | |
277 | * Frame length=1518; 7-wire mode | |
278 | */ | |
279 | fec->eth->r_cntrl = 0x05ee0020; /*0x05ee0000;FIXME */ | |
280 | } else { | |
281 | /* | |
282 | * Frame length=1518; MII mode; | |
283 | */ | |
284 | fec->eth->r_cntrl = 0x05ee0024; /*0x05ee0004;FIXME */ | |
285 | } | |
286 | ||
7e780369 | 287 | fec->eth->x_cntrl = 0x00000000; /* half-duplex, heartbeat disabled */ |
945af8d7 WD |
288 | |
289 | /* | |
290 | * Set Opcode/Pause Duration Register | |
291 | */ | |
6341d9d7 | 292 | fec->eth->op_pause = 0x00010020; /*FIXME 0xffff0020; */ |
945af8d7 WD |
293 | |
294 | /* | |
295 | * Set Rx FIFO alarm and granularity value | |
296 | */ | |
c44ffb9e WD |
297 | fec->eth->rfifo_cntrl = 0x0c000000 |
298 | | (fec->eth->rfifo_cntrl & ~0x0f000000); | |
945af8d7 WD |
299 | fec->eth->rfifo_alarm = 0x0000030c; |
300 | #if (DEBUG & 0x22) | |
301 | if (fec->eth->rfifo_status & 0x00700000 ) { | |
302 | printf("mpc5xxx_fec_init() RFIFO error\n"); | |
303 | } | |
304 | #endif | |
305 | ||
306 | /* | |
307 | * Set Tx FIFO granularity value | |
308 | */ | |
c44ffb9e WD |
309 | fec->eth->tfifo_cntrl = 0x0c000000 |
310 | | (fec->eth->tfifo_cntrl & ~0x0f000000); | |
945af8d7 WD |
311 | #if (DEBUG & 0x2) |
312 | printf("tfifo_status: 0x%08x\n", fec->eth->tfifo_status); | |
313 | printf("tfifo_alarm: 0x%08x\n", fec->eth->tfifo_alarm); | |
314 | #endif | |
315 | ||
316 | /* | |
317 | * Set transmit fifo watermark register(X_WMRK), default = 64 | |
318 | */ | |
319 | fec->eth->tfifo_alarm = 0x00000080; | |
320 | fec->eth->x_wmrk = 0x2; | |
321 | ||
322 | /* | |
323 | * Set individual address filter for unicast address | |
324 | * and set physical address registers. | |
325 | */ | |
77ddac94 | 326 | mpc5xxx_fec_set_hwaddr(fec, (char *)dev->enetaddr); |
945af8d7 WD |
327 | |
328 | /* | |
329 | * Set multicast address filter | |
330 | */ | |
331 | fec->eth->gaddr1 = 0x00000000; | |
332 | fec->eth->gaddr2 = 0x00000000; | |
333 | ||
334 | /* | |
335 | * Turn ON cheater FSM: ???? | |
336 | */ | |
337 | fec->eth->xmit_fsm = 0x03000000; | |
338 | ||
945af8d7 | 339 | /* |
fd428c05 | 340 | * Turn off COMM bus prefetch in the MPC5200 BestComm. It doesn't |
945af8d7 WD |
341 | * work w/ the current receive task. |
342 | */ | |
343 | sdma->PtdCntrl |= 0x00000001; | |
945af8d7 WD |
344 | |
345 | /* | |
346 | * Set priority of different initiators | |
347 | */ | |
348 | sdma->IPR0 = 7; /* always */ | |
349 | sdma->IPR3 = 6; /* Eth RX */ | |
350 | sdma->IPR4 = 5; /* Eth Tx */ | |
351 | ||
352 | /* | |
353 | * Clear SmartDMA task interrupt pending bits | |
354 | */ | |
355 | SDMA_CLEAR_IEVENT(FEC_RECV_TASK_NO); | |
356 | ||
945af8d7 WD |
357 | /* |
358 | * Initialize SmartDMA parameters stored in SRAM | |
359 | */ | |
151ab83a WD |
360 | *(volatile int *)FEC_TBD_BASE = (int)fec->tbdBase; |
361 | *(volatile int *)FEC_RBD_BASE = (int)fec->rbdBase; | |
362 | *(volatile int *)FEC_TBD_NEXT = (int)fec->tbdBase; | |
363 | *(volatile int *)FEC_RBD_NEXT = (int)fec->rbdBase; | |
945af8d7 | 364 | |
6c1362cf WD |
365 | /* |
366 | * Enable FEC-Lite controller | |
367 | */ | |
368 | fec->eth->ecntrl |= 0x00000006; | |
369 | ||
370 | #if (DEBUG & 0x2) | |
371 | if (fec->xcv_type != SEVENWIRE) | |
6dedf3d4 | 372 | mpc5xxx_fec_phydump (dev->name); |
6c1362cf WD |
373 | #endif |
374 | ||
375 | /* | |
376 | * Enable SmartDMA receive task | |
377 | */ | |
378 | SDMA_TASK_ENABLE(FEC_RECV_TASK_NO); | |
379 | ||
380 | #if (DEBUG & 0x1) | |
381 | printf("mpc5xxx_fec_init... Done \n"); | |
382 | #endif | |
383 | ||
384 | return 1; | |
385 | } | |
386 | ||
387 | /********************************************************************/ | |
388 | static int mpc5xxx_fec_init_phy(struct eth_device *dev, bd_t * bis) | |
389 | { | |
6c1362cf WD |
390 | mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; |
391 | const uint8 phyAddr = CONFIG_PHY_ADDR; /* Only one PHY */ | |
f5cf2ef2 SH |
392 | static int initialized = 0; |
393 | ||
394 | if(initialized) | |
395 | return 0; | |
396 | initialized = 1; | |
6c1362cf WD |
397 | |
398 | #if (DEBUG & 0x1) | |
399 | printf ("mpc5xxx_fec_init_phy... Begin\n"); | |
400 | #endif | |
401 | ||
402 | /* | |
403 | * Initialize GPIO pins | |
404 | */ | |
405 | if (fec->xcv_type == SEVENWIRE) { | |
406 | /* 10MBit with 7-wire operation */ | |
6c7a1408 WD |
407 | #if defined(CONFIG_TOTAL5200) |
408 | /* 7-wire and USB2 on Ethernet */ | |
409 | *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00030000; | |
410 | #else /* !CONFIG_TOTAL5200 */ | |
411 | /* 7-wire only */ | |
6c1362cf | 412 | *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00020000; |
6c7a1408 | 413 | #endif /* CONFIG_TOTAL5200 */ |
6c1362cf WD |
414 | } else { |
415 | /* 100MBit with MD operation */ | |
416 | *(vu_long *)MPC5XXX_GPS_PORT_CONFIG |= 0x00050000; | |
417 | } | |
418 | ||
419 | /* | |
420 | * Clear FEC-Lite interrupt event register(IEVENT) | |
421 | */ | |
422 | fec->eth->ievent = 0xffffffff; | |
423 | ||
424 | /* | |
425 | * Set interrupt mask register | |
426 | */ | |
427 | fec->eth->imask = 0x00000000; | |
428 | ||
008861a2 BS |
429 | /* |
430 | * In original Promess-provided code PHY initialization is disabled with the | |
431 | * following comment: "Phy initialization is DISABLED for now. There was a | |
432 | * problem with running 100 Mbps on PRO board". Thus we temporarily disable | |
433 | * PHY initialization for the Motion-PRO board, until a proper fix is found. | |
434 | */ | |
435 | ||
6c1362cf WD |
436 | if (fec->xcv_type != SEVENWIRE) { |
437 | /* | |
438 | * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock | |
439 | * and do not drop the Preamble. | |
440 | */ | |
441 | fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ | |
442 | } | |
443 | ||
945af8d7 WD |
444 | if (fec->xcv_type != SEVENWIRE) { |
445 | /* | |
446 | * Initialize PHY(LXT971A): | |
447 | * | |
448 | * Generally, on power up, the LXT971A reads its configuration | |
449 | * pins to check for forced operation, If not cofigured for | |
450 | * forced operation, it uses auto-negotiation/parallel detection | |
451 | * to automatically determine line operating conditions. | |
452 | * If the PHY device on the other side of the link supports | |
453 | * auto-negotiation, the LXT971A auto-negotiates with it | |
454 | * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not | |
455 | * support auto-negotiation, the LXT971A automatically detects | |
456 | * the presence of either link pulses(10Mbps PHY) or Idle | |
457 | * symbols(100Mbps) and sets its operating conditions accordingly. | |
458 | * | |
459 | * When auto-negotiation is controlled by software, the following | |
460 | * steps are recommended. | |
461 | * | |
462 | * Note: | |
463 | * The physical address is dependent on hardware configuration. | |
464 | * | |
465 | */ | |
466 | int timeout = 1; | |
467 | uint16 phyStatus; | |
468 | ||
469 | /* | |
470 | * Reset PHY, then delay 300ns | |
471 | */ | |
63ff004c | 472 | miiphy_write(dev->name, phyAddr, 0x0, 0x8000); |
945af8d7 WD |
473 | udelay(1000); |
474 | ||
258c37b1 HS |
475 | #if defined(CONFIG_UC101) || defined(CONFIG_MUCMC52) |
476 | /* Set the LED configuration Register for the UC101 | |
477 | and MUCMC52 Board */ | |
37403005 HS |
478 | miiphy_write(dev->name, phyAddr, 0x14, 0x4122); |
479 | #endif | |
945af8d7 WD |
480 | if (fec->xcv_type == MII10) { |
481 | /* | |
482 | * Force 10Base-T, FDX operation | |
483 | */ | |
a57106fc | 484 | #if (DEBUG & 0x2) |
945af8d7 | 485 | printf("Forcing 10 Mbps ethernet link... "); |
a57106fc | 486 | #endif |
63ff004c | 487 | miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); |
945af8d7 | 488 | /* |
63ff004c | 489 | miiphy_write(dev->name, fec, phyAddr, 0x0, 0x0100); |
945af8d7 | 490 | */ |
63ff004c | 491 | miiphy_write(dev->name, phyAddr, 0x0, 0x0180); |
945af8d7 WD |
492 | |
493 | timeout = 20; | |
494 | do { /* wait for link status to go down */ | |
495 | udelay(10000); | |
496 | if ((timeout--) == 0) { | |
497 | #if (DEBUG & 0x2) | |
498 | printf("hmmm, should not have waited..."); | |
499 | #endif | |
500 | break; | |
501 | } | |
63ff004c | 502 | miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); |
945af8d7 WD |
503 | #if (DEBUG & 0x2) |
504 | printf("="); | |
505 | #endif | |
506 | } while ((phyStatus & 0x0004)); /* !link up */ | |
507 | ||
508 | timeout = 1000; | |
509 | do { /* wait for link status to come back up */ | |
510 | udelay(10000); | |
511 | if ((timeout--) == 0) { | |
512 | printf("failed. Link is down.\n"); | |
513 | break; | |
514 | } | |
63ff004c | 515 | miiphy_read(dev->name, phyAddr, 0x1, &phyStatus); |
945af8d7 WD |
516 | #if (DEBUG & 0x2) |
517 | printf("+"); | |
518 | #endif | |
519 | } while (!(phyStatus & 0x0004)); /* !link up */ | |
520 | ||
ab209d51 | 521 | #if (DEBUG & 0x2) |
945af8d7 | 522 | printf ("done.\n"); |
ab209d51 | 523 | #endif |
945af8d7 WD |
524 | } else { /* MII100 */ |
525 | /* | |
526 | * Set the auto-negotiation advertisement register bits | |
527 | */ | |
63ff004c | 528 | miiphy_write(dev->name, phyAddr, 0x4, 0x01e1); |
945af8d7 WD |
529 | |
530 | /* | |
531 | * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation | |
532 | */ | |
63ff004c | 533 | miiphy_write(dev->name, phyAddr, 0x0, 0x1200); |
945af8d7 WD |
534 | |
535 | /* | |
536 | * Wait for AN completion | |
537 | */ | |
538 | timeout = 5000; | |
539 | do { | |
540 | udelay(1000); | |
541 | ||
542 | if ((timeout--) == 0) { | |
543 | #if (DEBUG & 0x2) | |
544 | printf("PHY auto neg 0 failed...\n"); | |
545 | #endif | |
546 | return -1; | |
547 | } | |
548 | ||
63ff004c | 549 | if (miiphy_read(dev->name, phyAddr, 0x1, &phyStatus) != 0) { |
945af8d7 WD |
550 | #if (DEBUG & 0x2) |
551 | printf("PHY auto neg 1 failed 0x%04x...\n", phyStatus); | |
552 | #endif | |
553 | return -1; | |
554 | } | |
7e780369 | 555 | } while (!(phyStatus & 0x0004)); |
945af8d7 WD |
556 | |
557 | #if (DEBUG & 0x2) | |
558 | printf("PHY auto neg complete! \n"); | |
559 | #endif | |
560 | } | |
561 | ||
562 | } | |
563 | ||
945af8d7 | 564 | #if (DEBUG & 0x2) |
d4ca31c4 | 565 | if (fec->xcv_type != SEVENWIRE) |
63ff004c | 566 | mpc5xxx_fec_phydump (dev->name); |
945af8d7 | 567 | #endif |
d4ca31c4 | 568 | |
945af8d7 WD |
569 | |
570 | #if (DEBUG & 0x1) | |
6c1362cf | 571 | printf("mpc5xxx_fec_init_phy... Done \n"); |
945af8d7 WD |
572 | #endif |
573 | ||
013dc8d9 | 574 | return 1; |
945af8d7 WD |
575 | } |
576 | ||
577 | /********************************************************************/ | |
578 | static void mpc5xxx_fec_halt(struct eth_device *dev) | |
579 | { | |
945af8d7 | 580 | struct mpc5xxx_sdma *sdma = (struct mpc5xxx_sdma *)MPC5XXX_SDMA; |
77846748 | 581 | mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; |
945af8d7 WD |
582 | int counter = 0xffff; |
583 | ||
584 | #if (DEBUG & 0x2) | |
d4ca31c4 | 585 | if (fec->xcv_type != SEVENWIRE) |
6dedf3d4 | 586 | mpc5xxx_fec_phydump (dev->name); |
945af8d7 WD |
587 | #endif |
588 | ||
945af8d7 WD |
589 | /* |
590 | * mask FEC chip interrupts | |
591 | */ | |
592 | fec->eth->imask = 0; | |
593 | ||
594 | /* | |
595 | * issue graceful stop command to the FEC transmitter if necessary | |
596 | */ | |
597 | fec->eth->x_cntrl |= 0x00000001; | |
598 | ||
599 | /* | |
600 | * wait for graceful stop to register | |
601 | */ | |
602 | while ((counter--) && (!(fec->eth->ievent & 0x10000000))) ; | |
603 | ||
945af8d7 WD |
604 | /* |
605 | * Disable SmartDMA tasks | |
606 | */ | |
607 | SDMA_TASK_DISABLE (FEC_XMIT_TASK_NO); | |
608 | SDMA_TASK_DISABLE (FEC_RECV_TASK_NO); | |
609 | ||
945af8d7 | 610 | /* |
fd428c05 | 611 | * Turn on COMM bus prefetch in the MPC5200 BestComm after we're |
945af8d7 WD |
612 | * done. It doesn't work w/ the current receive task. |
613 | */ | |
614 | sdma->PtdCntrl &= ~0x00000001; | |
945af8d7 WD |
615 | |
616 | /* | |
617 | * Disable the Ethernet Controller | |
618 | */ | |
619 | fec->eth->ecntrl &= 0xfffffffd; | |
620 | ||
621 | /* | |
622 | * Clear FIFO status registers | |
623 | */ | |
624 | fec->eth->rfifo_status &= 0x00700000; | |
625 | fec->eth->tfifo_status &= 0x00700000; | |
626 | ||
627 | fec->eth->reset_cntrl = 0x01000000; | |
628 | ||
629 | /* | |
630 | * Issue a reset command to the FEC chip | |
631 | */ | |
632 | fec->eth->ecntrl |= 0x1; | |
633 | ||
634 | /* | |
635 | * wait at least 16 clock cycles | |
636 | */ | |
637 | udelay(10); | |
638 | ||
f949bd8d JS |
639 | /* don't leave the MII speed set to zero */ |
640 | if (fec->xcv_type != SEVENWIRE) { | |
641 | /* | |
642 | * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock | |
643 | * and do not drop the Preamble. | |
644 | */ | |
645 | fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ | |
646 | } | |
647 | ||
945af8d7 WD |
648 | #if (DEBUG & 0x3) |
649 | printf("Ethernet task stopped\n"); | |
650 | #endif | |
651 | } | |
652 | ||
653 | #if (DEBUG & 0x60) | |
654 | /********************************************************************/ | |
655 | ||
63ff004c | 656 | static void tfifo_print(char *devname, mpc5xxx_fec_priv *fec) |
945af8d7 | 657 | { |
d4ca31c4 | 658 | uint16 phyAddr = CONFIG_PHY_ADDR; |
945af8d7 WD |
659 | uint16 phyStatus; |
660 | ||
661 | if ((fec->eth->tfifo_lrf_ptr != fec->eth->tfifo_lwf_ptr) | |
662 | || (fec->eth->tfifo_rdptr != fec->eth->tfifo_wrptr)) { | |
663 | ||
63ff004c | 664 | miiphy_read(devname, phyAddr, 0x1, &phyStatus); |
945af8d7 WD |
665 | printf("\nphyStatus: 0x%04x\n", phyStatus); |
666 | printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); | |
667 | printf("ievent: 0x%08x\n", fec->eth->ievent); | |
668 | printf("x_status: 0x%08x\n", fec->eth->x_status); | |
669 | printf("tfifo: status 0x%08x\n", fec->eth->tfifo_status); | |
670 | ||
671 | printf(" control 0x%08x\n", fec->eth->tfifo_cntrl); | |
672 | printf(" lrfp 0x%08x\n", fec->eth->tfifo_lrf_ptr); | |
673 | printf(" lwfp 0x%08x\n", fec->eth->tfifo_lwf_ptr); | |
674 | printf(" alarm 0x%08x\n", fec->eth->tfifo_alarm); | |
675 | printf(" readptr 0x%08x\n", fec->eth->tfifo_rdptr); | |
676 | printf(" writptr 0x%08x\n", fec->eth->tfifo_wrptr); | |
677 | } | |
678 | } | |
679 | ||
63ff004c | 680 | static void rfifo_print(char *devname, mpc5xxx_fec_priv *fec) |
945af8d7 | 681 | { |
d4ca31c4 | 682 | uint16 phyAddr = CONFIG_PHY_ADDR; |
945af8d7 WD |
683 | uint16 phyStatus; |
684 | ||
685 | if ((fec->eth->rfifo_lrf_ptr != fec->eth->rfifo_lwf_ptr) | |
686 | || (fec->eth->rfifo_rdptr != fec->eth->rfifo_wrptr)) { | |
687 | ||
63ff004c | 688 | miiphy_read(devname, phyAddr, 0x1, &phyStatus); |
945af8d7 WD |
689 | printf("\nphyStatus: 0x%04x\n", phyStatus); |
690 | printf("ecntrl: 0x%08x\n", fec->eth->ecntrl); | |
691 | printf("ievent: 0x%08x\n", fec->eth->ievent); | |
692 | printf("x_status: 0x%08x\n", fec->eth->x_status); | |
693 | printf("rfifo: status 0x%08x\n", fec->eth->rfifo_status); | |
694 | ||
695 | printf(" control 0x%08x\n", fec->eth->rfifo_cntrl); | |
696 | printf(" lrfp 0x%08x\n", fec->eth->rfifo_lrf_ptr); | |
697 | printf(" lwfp 0x%08x\n", fec->eth->rfifo_lwf_ptr); | |
698 | printf(" alarm 0x%08x\n", fec->eth->rfifo_alarm); | |
699 | printf(" readptr 0x%08x\n", fec->eth->rfifo_rdptr); | |
700 | printf(" writptr 0x%08x\n", fec->eth->rfifo_wrptr); | |
701 | } | |
702 | } | |
703 | #endif /* DEBUG */ | |
704 | ||
705 | /********************************************************************/ | |
706 | ||
707 | static int mpc5xxx_fec_send(struct eth_device *dev, volatile void *eth_data, | |
708 | int data_length) | |
709 | { | |
710 | /* | |
711 | * This routine transmits one frame. This routine only accepts | |
712 | * 6-byte Ethernet addresses. | |
713 | */ | |
714 | mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; | |
151ab83a | 715 | volatile FEC_TBD *pTbd; |
945af8d7 WD |
716 | |
717 | #if (DEBUG & 0x20) | |
718 | printf("tbd status: 0x%04x\n", fec->tbdBase[0].status); | |
63ff004c | 719 | tfifo_print(dev->name, fec); |
945af8d7 WD |
720 | #endif |
721 | ||
722 | /* | |
723 | * Clear Tx BD ring at first | |
724 | */ | |
725 | mpc5xxx_fec_tbd_scrub(fec); | |
726 | ||
727 | /* | |
728 | * Check for valid length of data. | |
729 | */ | |
730 | if ((data_length > 1500) || (data_length <= 0)) { | |
731 | return -1; | |
732 | } | |
733 | ||
734 | /* | |
735 | * Check the number of vacant TxBDs. | |
736 | */ | |
737 | if (fec->cleanTbdNum < 1) { | |
738 | #if (DEBUG & 0x20) | |
739 | printf("No available TxBDs ...\n"); | |
740 | #endif | |
741 | return -1; | |
742 | } | |
743 | ||
744 | /* | |
745 | * Get the first TxBD to send the mac header | |
746 | */ | |
747 | pTbd = &fec->tbdBase[fec->tbdIndex]; | |
748 | pTbd->dataLength = data_length; | |
749 | pTbd->dataPointer = (uint32)eth_data; | |
77846748 | 750 | pTbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; |
945af8d7 WD |
751 | fec->tbdIndex = (fec->tbdIndex + 1) % FEC_TBD_NUM; |
752 | ||
753 | #if (DEBUG & 0x100) | |
754 | printf("SDMA_TASK_ENABLE, fec->tbdIndex = %d \n", fec->tbdIndex); | |
755 | #endif | |
756 | ||
757 | /* | |
758 | * Kick the MII i/f | |
759 | */ | |
760 | if (fec->xcv_type != SEVENWIRE) { | |
761 | uint16 phyStatus; | |
63ff004c | 762 | miiphy_read(dev->name, 0, 0x1, &phyStatus); |
945af8d7 WD |
763 | } |
764 | ||
765 | /* | |
766 | * Enable SmartDMA transmit task | |
767 | */ | |
768 | ||
769 | #if (DEBUG & 0x20) | |
63ff004c | 770 | tfifo_print(dev->name, fec); |
945af8d7 WD |
771 | #endif |
772 | SDMA_TASK_ENABLE (FEC_XMIT_TASK_NO); | |
773 | #if (DEBUG & 0x20) | |
63ff004c | 774 | tfifo_print(dev->name, fec); |
945af8d7 WD |
775 | #endif |
776 | #if (DEBUG & 0x8) | |
777 | printf( "+" ); | |
778 | #endif | |
779 | ||
780 | fec->cleanTbdNum -= 1; | |
781 | ||
782 | #if (DEBUG & 0x129) && (DEBUG & 0x80000000) | |
783 | printf ("smartDMA ethernet Tx task enabled\n"); | |
784 | #endif | |
785 | /* | |
786 | * wait until frame is sent . | |
787 | */ | |
788 | while (pTbd->status & FEC_TBD_READY) { | |
789 | udelay(10); | |
790 | #if (DEBUG & 0x8) | |
791 | printf ("TDB status = %04x\n", pTbd->status); | |
792 | #endif | |
793 | } | |
794 | ||
795 | return 0; | |
796 | } | |
797 | ||
798 | ||
799 | /********************************************************************/ | |
800 | static int mpc5xxx_fec_recv(struct eth_device *dev) | |
801 | { | |
802 | /* | |
803 | * This command pulls one frame from the card | |
804 | */ | |
805 | mpc5xxx_fec_priv *fec = (mpc5xxx_fec_priv *)dev->priv; | |
151ab83a | 806 | volatile FEC_RBD *pRbd = &fec->rbdBase[fec->rbdIndex]; |
945af8d7 | 807 | unsigned long ievent; |
77846748 WD |
808 | int frame_length, len = 0; |
809 | NBUF *frame; | |
77ddac94 | 810 | uchar buff[FEC_MAX_PKT_SIZE]; |
945af8d7 WD |
811 | |
812 | #if (DEBUG & 0x1) | |
813 | printf ("mpc5xxx_fec_recv %d Start...\n", fec->rbdIndex); | |
814 | #endif | |
815 | #if (DEBUG & 0x8) | |
816 | printf( "-" ); | |
817 | #endif | |
818 | ||
819 | /* | |
820 | * Check if any critical events have happened | |
821 | */ | |
822 | ievent = fec->eth->ievent; | |
823 | fec->eth->ievent = ievent; | |
824 | if (ievent & 0x20060000) { | |
825 | /* BABT, Rx/Tx FIFO errors */ | |
826 | mpc5xxx_fec_halt(dev); | |
827 | mpc5xxx_fec_init(dev, NULL); | |
828 | return 0; | |
829 | } | |
830 | if (ievent & 0x80000000) { | |
831 | /* Heartbeat error */ | |
832 | fec->eth->x_cntrl |= 0x00000001; | |
833 | } | |
834 | if (ievent & 0x10000000) { | |
835 | /* Graceful stop complete */ | |
836 | if (fec->eth->x_cntrl & 0x00000001) { | |
837 | mpc5xxx_fec_halt(dev); | |
838 | fec->eth->x_cntrl &= ~0x00000001; | |
839 | mpc5xxx_fec_init(dev, NULL); | |
840 | } | |
841 | } | |
842 | ||
77846748 WD |
843 | if (!(pRbd->status & FEC_RBD_EMPTY)) { |
844 | if ((pRbd->status & FEC_RBD_LAST) && !(pRbd->status & FEC_RBD_ERR) && | |
845 | ((pRbd->dataLength - 4) > 14)) { | |
945af8d7 | 846 | |
77846748 WD |
847 | /* |
848 | * Get buffer address and size | |
849 | */ | |
850 | frame = (NBUF *)pRbd->dataPointer; | |
851 | frame_length = pRbd->dataLength - 4; | |
852 | ||
853 | #if (DEBUG & 0x20) | |
854 | { | |
855 | int i; | |
856 | printf("recv data hdr:"); | |
857 | for (i = 0; i < 14; i++) | |
858 | printf("%x ", *(frame->head + i)); | |
859 | printf("\n"); | |
860 | } | |
945af8d7 | 861 | #endif |
77846748 WD |
862 | /* |
863 | * Fill the buffer and pass it to upper layers | |
864 | */ | |
865 | memcpy(buff, frame->head, 14); | |
866 | memcpy(buff + 14, frame->data, frame_length); | |
867 | NetReceive(buff, frame_length); | |
868 | len = frame_length; | |
869 | } | |
870 | /* | |
871 | * Reset buffer descriptor as empty | |
872 | */ | |
873 | mpc5xxx_fec_rbd_clean(fec, pRbd); | |
945af8d7 | 874 | } |
77846748 WD |
875 | SDMA_CLEAR_IEVENT (FEC_RECV_TASK_NO); |
876 | return len; | |
945af8d7 WD |
877 | } |
878 | ||
879 | ||
880 | /********************************************************************/ | |
881 | int mpc5xxx_fec_initialize(bd_t * bis) | |
882 | { | |
883 | mpc5xxx_fec_priv *fec; | |
884 | struct eth_device *dev; | |
12f34241 WD |
885 | char *tmp, *end; |
886 | char env_enetaddr[6]; | |
887 | int i; | |
945af8d7 WD |
888 | |
889 | fec = (mpc5xxx_fec_priv *)malloc(sizeof(*fec)); | |
890 | dev = (struct eth_device *)malloc(sizeof(*dev)); | |
53677ef1 | 891 | memset(dev, 0, sizeof *dev); |
945af8d7 WD |
892 | |
893 | fec->eth = (ethernet_regs *)MPC5XXX_FEC; | |
894 | fec->tbdBase = (FEC_TBD *)FEC_BD_BASE; | |
895 | fec->rbdBase = (FEC_RBD *)(FEC_BD_BASE + FEC_TBD_NUM * sizeof(FEC_TBD)); | |
86321fc1 | 896 | #if defined(CONFIG_MPC5xxx_FEC_MII100) |
945af8d7 | 897 | fec->xcv_type = MII100; |
86321fc1 | 898 | #elif defined(CONFIG_MPC5xxx_FEC_MII10) |
a57106fc | 899 | fec->xcv_type = MII10; |
86321fc1 | 900 | #elif defined(CONFIG_MPC5xxx_FEC_SEVENWIRE) |
6c7a1408 | 901 | fec->xcv_type = SEVENWIRE; |
a57106fc WD |
902 | #else |
903 | #error fec->xcv_type not initialized. | |
945af8d7 | 904 | #endif |
f949bd8d JS |
905 | if (fec->xcv_type != SEVENWIRE) { |
906 | /* | |
907 | * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock | |
908 | * and do not drop the Preamble. | |
909 | */ | |
910 | fec->eth->mii_speed = (((gd->ipb_clk >> 20) / 5) << 1); /* No MII for 7-wire mode */ | |
911 | } | |
945af8d7 WD |
912 | |
913 | dev->priv = (void *)fec; | |
914 | dev->iobase = MPC5XXX_FEC; | |
915 | dev->init = mpc5xxx_fec_init; | |
916 | dev->halt = mpc5xxx_fec_halt; | |
917 | dev->send = mpc5xxx_fec_send; | |
918 | dev->recv = mpc5xxx_fec_recv; | |
919 | ||
77846748 | 920 | sprintf(dev->name, "FEC ETHERNET"); |
945af8d7 WD |
921 | eth_register(dev); |
922 | ||
4431283c | 923 | #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) |
63ff004c MB |
924 | miiphy_register (dev->name, |
925 | fec5xxx_miiphy_read, fec5xxx_miiphy_write); | |
926 | #endif | |
927 | ||
12f34241 WD |
928 | /* |
929 | * Try to set the mac address now. The fec mac address is | |
42d1f039 | 930 | * a garbage after reset. When not using fec for booting |
12f34241 WD |
931 | * the Linux fec driver will try to work with this garbage. |
932 | */ | |
933 | tmp = getenv("ethaddr"); | |
934 | if (tmp) { | |
935 | for (i=0; i<6; i++) { | |
936 | env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; | |
937 | if (tmp) | |
938 | tmp = (*end) ? end+1 : end; | |
939 | } | |
940 | mpc5xxx_fec_set_hwaddr(fec, env_enetaddr); | |
941 | } | |
942 | ||
945af8d7 WD |
943 | return 1; |
944 | } | |
945 | ||
946 | /* MII-interface related functions */ | |
947 | /********************************************************************/ | |
63ff004c | 948 | int fec5xxx_miiphy_read(char *devname, uint8 phyAddr, uint8 regAddr, uint16 * retVal) |
945af8d7 WD |
949 | { |
950 | ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; | |
951 | uint32 reg; /* convenient holder for the PHY register */ | |
952 | uint32 phy; /* convenient holder for the PHY */ | |
953 | int timeout = 0xffff; | |
954 | ||
955 | /* | |
956 | * reading from any PHY's register is done by properly | |
957 | * programming the FEC's MII data register. | |
958 | */ | |
959 | reg = regAddr << FEC_MII_DATA_RA_SHIFT; | |
960 | phy = phyAddr << FEC_MII_DATA_PA_SHIFT; | |
961 | ||
962 | eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | phy | reg); | |
963 | ||
964 | /* | |
965 | * wait for the related interrupt | |
966 | */ | |
967 | while ((timeout--) && (!(eth->ievent & 0x00800000))) ; | |
968 | ||
969 | if (timeout == 0) { | |
970 | #if (DEBUG & 0x2) | |
971 | printf ("Read MDIO failed...\n"); | |
972 | #endif | |
973 | return -1; | |
974 | } | |
975 | ||
976 | /* | |
977 | * clear mii interrupt bit | |
978 | */ | |
979 | eth->ievent = 0x00800000; | |
980 | ||
981 | /* | |
982 | * it's now safe to read the PHY's register | |
983 | */ | |
984 | *retVal = (uint16) eth->mii_data; | |
985 | ||
986 | return 0; | |
987 | } | |
988 | ||
989 | /********************************************************************/ | |
63ff004c | 990 | int fec5xxx_miiphy_write(char *devname, uint8 phyAddr, uint8 regAddr, uint16 data) |
945af8d7 WD |
991 | { |
992 | ethernet_regs *eth = (ethernet_regs *)MPC5XXX_FEC; | |
993 | uint32 reg; /* convenient holder for the PHY register */ | |
994 | uint32 phy; /* convenient holder for the PHY */ | |
995 | int timeout = 0xffff; | |
996 | ||
997 | reg = regAddr << FEC_MII_DATA_RA_SHIFT; | |
998 | phy = phyAddr << FEC_MII_DATA_PA_SHIFT; | |
999 | ||
1000 | eth->mii_data = (FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | | |
1001 | FEC_MII_DATA_TA | phy | reg | data); | |
1002 | ||
1003 | /* | |
1004 | * wait for the MII interrupt | |
1005 | */ | |
1006 | while ((timeout--) && (!(eth->ievent & 0x00800000))) ; | |
1007 | ||
1008 | if (timeout == 0) { | |
1009 | #if (DEBUG & 0x2) | |
1010 | printf ("Write MDIO failed...\n"); | |
1011 | #endif | |
1012 | return -1; | |
1013 | } | |
1014 | ||
1015 | /* | |
1016 | * clear MII interrupt bit | |
1017 | */ | |
1018 | eth->ievent = 0x00800000; | |
1019 | ||
1020 | return 0; | |
1021 | } | |
1022 | ||
1023 | #if (DEBUG & 0x40) | |
1024 | static uint32 local_crc32(char *string, unsigned int crc_value, int len) | |
1025 | { | |
1026 | int i; | |
1027 | char c; | |
1028 | unsigned int crc, count; | |
1029 | ||
1030 | /* | |
1031 | * crc32 algorithm | |
1032 | */ | |
1033 | /* | |
1034 | * crc = 0xffffffff; * The initialized value should be 0xffffffff | |
1035 | */ | |
1036 | crc = crc_value; | |
1037 | ||
1038 | for (i = len; --i >= 0;) { | |
1039 | c = *string++; | |
1040 | for (count = 0; count < 8; count++) { | |
1041 | if ((c & 0x01) ^ (crc & 0x01)) { | |
1042 | crc >>= 1; | |
1043 | crc = crc ^ 0xedb88320; | |
1044 | } else { | |
1045 | crc >>= 1; | |
1046 | } | |
1047 | c >>= 1; | |
1048 | } | |
1049 | } | |
1050 | ||
1051 | /* | |
1052 | * In big endian system, do byte swaping for crc value | |
1053 | */ | |
1054 | /**/ return crc; | |
1055 | } | |
1056 | #endif /* DEBUG */ |