]>
Commit | Line | Data |
---|---|---|
b4489621 SP |
1 | /* |
2 | * (C) Copyright 2007 | |
3 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
4 | * | |
5 | * Author: Igor Lisitsin <[email protected]> | |
6 | * | |
7 | * See file CREDITS for list of people who contributed to this | |
8 | * project. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License as | |
12 | * published by the Free Software Foundation; either version 2 of | |
13 | * the License, or (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
23 | * MA 02111-1307 USA | |
24 | */ | |
25 | ||
26 | #include <common.h> | |
27 | ||
28 | /* | |
29 | * Ethernet test | |
30 | * | |
31 | * The Ethernet Media Access Controllers (EMAC) are tested in the | |
32 | * internal loopback mode. | |
33 | * The controllers are configured accordingly and several packets | |
34 | * are transmitted. The configurable test parameters are: | |
35 | * MIN_PACKET_LENGTH - minimum size of packet to transmit | |
36 | * MAX_PACKET_LENGTH - maximum size of packet to transmit | |
37 | * TEST_NUM - number of tests | |
38 | */ | |
39 | ||
b4489621 SP |
40 | #include <post.h> |
41 | ||
6d0f6bcf | 42 | #if CONFIG_POST & CONFIG_SYS_POST_ETHER |
b4489621 SP |
43 | |
44 | #include <asm/cache.h> | |
45 | #include <asm/io.h> | |
46 | #include <asm/processor.h> | |
b36df561 SR |
47 | #include <asm/ppc4xx-mal.h> |
48 | #include <asm/ppc4xx-emac.h> | |
b4489621 SP |
49 | #include <malloc.h> |
50 | ||
51 | DECLARE_GLOBAL_DATA_PTR; | |
52 | ||
7d47cee2 SR |
53 | /* |
54 | * Get count of EMAC devices (doesn't have to be the max. possible number | |
55 | * supported by the cpu) | |
56 | * | |
57 | * CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the | |
58 | * EMAC count is possible. As it is needed for the Kilauea/Haleakala | |
59 | * 405EX/405EXr eval board, using the same binary. | |
60 | */ | |
61 | #if defined(CONFIG_BOARD_EMAC_COUNT) | |
62 | #define LAST_EMAC_NUM board_emac_count() | |
63 | #else /* CONFIG_BOARD_EMAC_COUNT */ | |
64 | #if defined(CONFIG_HAS_ETH3) | |
65 | #define LAST_EMAC_NUM 4 | |
66 | #elif defined(CONFIG_HAS_ETH2) | |
67 | #define LAST_EMAC_NUM 3 | |
68 | #elif defined(CONFIG_HAS_ETH1) | |
69 | #define LAST_EMAC_NUM 2 | |
70 | #else | |
71 | #define LAST_EMAC_NUM 1 | |
72 | #endif | |
73 | #endif /* CONFIG_BOARD_EMAC_COUNT */ | |
74 | ||
b4489621 SP |
75 | #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) |
76 | #define SDR0_MFR_ETH_CLK_SEL_V(n) ((0x01<<27) / (n+1)) | |
77 | #endif | |
78 | ||
79 | #define MIN_PACKET_LENGTH 64 | |
80 | #define MAX_PACKET_LENGTH 256 | |
81 | #define TEST_NUM 1 | |
82 | ||
83 | static volatile mal_desc_t tx __cacheline_aligned; | |
84 | static volatile mal_desc_t rx __cacheline_aligned; | |
85 | static char *tx_buf; | |
86 | static char *rx_buf; | |
87 | ||
7d47cee2 SR |
88 | int board_emac_count(void); |
89 | ||
b4489621 SP |
90 | static void ether_post_init (int devnum, int hw_addr) |
91 | { | |
92 | int i; | |
b4489621 SP |
93 | #if defined(CONFIG_440GX) || \ |
94 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ | |
95 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) | |
c5a172a5 | 96 | unsigned mode_reg; |
b4489621 SP |
97 | sys_info_t sysinfo; |
98 | #endif | |
99 | #if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || defined(CONFIG_440SPE) | |
100 | unsigned long mfr; | |
101 | #endif | |
102 | ||
103 | #if defined(CONFIG_440GX) || \ | |
104 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ | |
105 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) | |
106 | /* Need to get the OPB frequency so we can access the PHY */ | |
107 | get_sys_info (&sysinfo); | |
108 | #endif | |
109 | ||
110 | #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) | |
111 | /* provide clocks for EMAC internal loopback */ | |
d1c3b275 | 112 | mfsdr (SDR0_MFR, mfr); |
b4489621 | 113 | mfr |= SDR0_MFR_ETH_CLK_SEL_V(devnum); |
d1c3b275 | 114 | mtsdr (SDR0_MFR, mfr); |
b4489621 SP |
115 | sync (); |
116 | #endif | |
117 | /* reset emac */ | |
ddc922ff | 118 | out_be32 ((void*)(EMAC0_MR0 + hw_addr), EMAC_MR0_SRST); |
b4489621 SP |
119 | sync (); |
120 | ||
121 | for (i = 0;; i++) { | |
ddc922ff | 122 | if (!(in_be32 ((void*)(EMAC0_MR0 + hw_addr)) & EMAC_MR0_SRST)) |
b4489621 SP |
123 | break; |
124 | if (i >= 1000) { | |
125 | printf ("Timeout resetting EMAC\n"); | |
126 | break; | |
127 | } | |
128 | udelay (1000); | |
129 | } | |
130 | #if defined(CONFIG_440GX) || \ | |
131 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ | |
132 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) | |
133 | /* Whack the M1 register */ | |
134 | mode_reg = 0x0; | |
135 | if (sysinfo.freqOPB <= 50000000); | |
136 | else if (sysinfo.freqOPB <= 66666667) | |
ddc922ff | 137 | mode_reg |= EMAC_MR1_OBCI_66; |
b4489621 | 138 | else if (sysinfo.freqOPB <= 83333333) |
ddc922ff | 139 | mode_reg |= EMAC_MR1_OBCI_83; |
b4489621 | 140 | else if (sysinfo.freqOPB <= 100000000) |
ddc922ff | 141 | mode_reg |= EMAC_MR1_OBCI_100; |
b4489621 | 142 | else |
ddc922ff | 143 | mode_reg |= EMAC_MR1_OBCI_GT100; |
b4489621 | 144 | |
ddc922ff | 145 | out_be32 ((void*)(EMAC0_MR1 + hw_addr), mode_reg); |
b4489621 SP |
146 | |
147 | #endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */ | |
148 | ||
149 | /* set the Mal configuration reg */ | |
150 | #if defined(CONFIG_440GX) || \ | |
151 | defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ | |
152 | defined(CONFIG_440SP) || defined(CONFIG_440SPE) | |
d1c3b275 | 153 | mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | |
b4489621 SP |
154 | MAL_CR_PLBLT_DEFAULT | 0x00330000); |
155 | #else | |
d1c3b275 | 156 | mtdcr (MAL0_CFG, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT); |
b4489621 SP |
157 | /* Errata 1.12: MAL_1 -- Disable MAL bursting */ |
158 | if (get_pvr() == PVR_440GP_RB) { | |
d1c3b275 | 159 | mtdcr (MAL0_CFG, mfdcr(MAL0_CFG) & ~MAL_CR_PLBB); |
b4489621 SP |
160 | } |
161 | #endif | |
162 | /* setup buffer descriptors */ | |
163 | tx.ctrl = MAL_TX_CTRL_WRAP; | |
164 | tx.data_len = 0; | |
165 | tx.data_ptr = (char*)L1_CACHE_ALIGN((u32)tx_buf); | |
166 | ||
167 | rx.ctrl = MAL_TX_CTRL_WRAP | MAL_RX_CTRL_EMPTY; | |
168 | rx.data_len = 0; | |
169 | rx.data_ptr = (char*)L1_CACHE_ALIGN((u32)rx_buf); | |
f71b2888 SR |
170 | flush_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); |
171 | flush_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); | |
b4489621 SP |
172 | |
173 | switch (devnum) { | |
174 | case 1: | |
175 | /* setup MAL tx & rx channel pointers */ | |
176 | #if defined (CONFIG_405EP) || defined (CONFIG_440EP) || defined (CONFIG_440GR) | |
d1c3b275 | 177 | mtdcr (MAL0_TXCTP2R, &tx); |
b4489621 | 178 | #else |
d1c3b275 | 179 | mtdcr (MAL0_TXCTP1R, &tx); |
b4489621 SP |
180 | #endif |
181 | #if defined(CONFIG_440) | |
d1c3b275 SR |
182 | mtdcr (MAL0_TXBADDR, 0x0); |
183 | mtdcr (MAL0_RXBADDR, 0x0); | |
b4489621 | 184 | #endif |
d1c3b275 | 185 | mtdcr (MAL0_RXCTP1R, &rx); |
b4489621 | 186 | /* set RX buffer size */ |
d1c3b275 | 187 | mtdcr (MAL0_RCBS1, PKTSIZE_ALIGN / 16); |
b4489621 SP |
188 | break; |
189 | case 0: | |
190 | default: | |
191 | /* setup MAL tx & rx channel pointers */ | |
192 | #if defined(CONFIG_440) | |
d1c3b275 SR |
193 | mtdcr (MAL0_TXBADDR, 0x0); |
194 | mtdcr (MAL0_RXBADDR, 0x0); | |
b4489621 | 195 | #endif |
d1c3b275 SR |
196 | mtdcr (MAL0_TXCTP0R, &tx); |
197 | mtdcr (MAL0_RXCTP0R, &rx); | |
b4489621 | 198 | /* set RX buffer size */ |
d1c3b275 | 199 | mtdcr (MAL0_RCBS0, PKTSIZE_ALIGN / 16); |
b4489621 SP |
200 | break; |
201 | } | |
202 | ||
203 | /* Enable MAL transmit and receive channels */ | |
204 | #if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) | |
d1c3b275 | 205 | mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> (devnum*2))); |
b4489621 | 206 | #else |
d1c3b275 | 207 | mtdcr (MAL0_TXCASR, (MAL_TXRX_CASR >> devnum)); |
b4489621 | 208 | #endif |
d1c3b275 | 209 | mtdcr (MAL0_RXCASR, (MAL_TXRX_CASR >> devnum)); |
b4489621 SP |
210 | |
211 | /* set internal loopback mode */ | |
6d0f6bcf | 212 | #ifdef CONFIG_SYS_POST_ETHER_EXT_LOOPBACK |
ddc922ff NG |
213 | out_be32 ((void*)(EMAC0_MR1 + hw_addr), EMAC_MR1_FDE | 0 | |
214 | EMAC_MR1_RFS_4K | EMAC_MR1_TX_FIFO_2K | | |
215 | EMAC_MR1_MF_100MBPS | EMAC_MR1_IST | | |
216 | in_be32 ((void*)(EMAC0_MR1 + hw_addr))); | |
c5a172a5 | 217 | #else |
ddc922ff NG |
218 | out_be32 ((void*)(EMAC0_MR1 + hw_addr), EMAC_MR1_FDE | EMAC_MR1_ILE | |
219 | EMAC_MR1_RFS_4K | EMAC_MR1_TX_FIFO_2K | | |
220 | EMAC_MR1_MF_100MBPS | EMAC_MR1_IST | | |
221 | in_be32 ((void*)(EMAC0_MR1 + hw_addr))); | |
c5a172a5 | 222 | #endif |
b4489621 SP |
223 | |
224 | /* set transmit enable & receive enable */ | |
ddc922ff | 225 | out_be32 ((void*)(EMAC0_MR0 + hw_addr), EMAC_MR0_TXE | EMAC_MR0_RXE); |
b4489621 SP |
226 | |
227 | /* enable broadcast address */ | |
ddc922ff | 228 | out_be32 ((void*)(EMAC0_RXM + hw_addr), EMAC_RMR_BAE); |
b4489621 SP |
229 | |
230 | /* set transmit request threshold register */ | |
ddc922ff | 231 | out_be32 ((void*)(EMAC0_TRTR + hw_addr), 0x18000000); /* 256 byte threshold */ |
b4489621 SP |
232 | |
233 | /* set receive low/high water mark register */ | |
234 | #if defined(CONFIG_440) | |
235 | /* 440s has a 64 byte burst length */ | |
ddc922ff | 236 | out_be32 ((void*)(EMAC0_RX_HI_LO_WMARK + hw_addr), 0x80009000); |
b4489621 SP |
237 | #else |
238 | /* 405s have a 16 byte burst length */ | |
ddc922ff | 239 | out_be32 ((void*)(EMAC0_RX_HI_LO_WMARK + hw_addr), 0x0f002000); |
b4489621 | 240 | #endif /* defined(CONFIG_440) */ |
ddc922ff | 241 | out_be32 ((void*)(EMAC0_TMR1 + hw_addr), 0xf8640000); |
b4489621 SP |
242 | |
243 | /* Set fifo limit entry in tx mode 0 */ | |
ddc922ff | 244 | out_be32 ((void*)(EMAC0_TMR0 + hw_addr), 0x00000003); |
b4489621 | 245 | /* Frame gap set */ |
ddc922ff | 246 | out_be32 ((void*)(EMAC0_I_FRAME_GAP_REG + hw_addr), 0x00000008); |
b4489621 SP |
247 | sync (); |
248 | } | |
249 | ||
250 | static void ether_post_halt (int devnum, int hw_addr) | |
251 | { | |
252 | int i = 0; | |
253 | #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) | |
254 | unsigned long mfr; | |
255 | #endif | |
256 | ||
257 | /* 1st reset MAL channel */ | |
258 | /* Note: writing a 0 to a channel has no effect */ | |
259 | #if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR) | |
d1c3b275 | 260 | mtdcr (MAL0_TXCARR, MAL_TXRX_CASR >> (devnum * 2)); |
b4489621 | 261 | #else |
d1c3b275 | 262 | mtdcr (MAL0_TXCARR, MAL_TXRX_CASR >> devnum); |
b4489621 | 263 | #endif |
d1c3b275 | 264 | mtdcr (MAL0_RXCARR, MAL_TXRX_CASR >> devnum); |
b4489621 SP |
265 | |
266 | /* wait for reset */ | |
d1c3b275 | 267 | while (mfdcr (MAL0_RXCASR) & (MAL_TXRX_CASR >> devnum)) { |
b4489621 SP |
268 | if (i++ >= 1000) |
269 | break; | |
270 | udelay (1000); | |
271 | } | |
272 | /* emac reset */ | |
ddc922ff | 273 | out_be32 ((void*)(EMAC0_MR0 + hw_addr), EMAC_MR0_SRST); |
b4489621 SP |
274 | |
275 | #if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX) | |
276 | /* remove clocks for EMAC internal loopback */ | |
d1c3b275 | 277 | mfsdr (SDR0_MFR, mfr); |
b4489621 | 278 | mfr &= ~SDR0_MFR_ETH_CLK_SEL_V(devnum); |
d1c3b275 | 279 | mtsdr (SDR0_MFR, mfr); |
b4489621 SP |
280 | #endif |
281 | } | |
282 | ||
283 | static void ether_post_send (int devnum, int hw_addr, void *packet, int length) | |
284 | { | |
285 | int i = 0; | |
286 | ||
287 | while (tx.ctrl & MAL_TX_CTRL_READY) { | |
288 | if (i++ > 100) { | |
289 | printf ("TX timeout\n"); | |
290 | return; | |
291 | } | |
292 | udelay (1000); | |
f71b2888 | 293 | invalidate_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); |
b4489621 SP |
294 | } |
295 | tx.ctrl = MAL_TX_CTRL_READY | MAL_TX_CTRL_WRAP | MAL_TX_CTRL_LAST | | |
296 | EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP; | |
297 | tx.data_len = length; | |
298 | memcpy (tx.data_ptr, packet, length); | |
f71b2888 SR |
299 | flush_dcache_range((u32)&tx, (u32)&tx + sizeof(mal_desc_t)); |
300 | flush_dcache_range((u32)tx.data_ptr, (u32)tx.data_ptr + length); | |
b4489621 SP |
301 | sync (); |
302 | ||
ddc922ff | 303 | out_be32 ((void*)(EMAC0_TMR0 + hw_addr), in_be32 ((void*)(EMAC0_TMR0 + hw_addr)) | EMAC_TMR0_GNP0); |
b4489621 SP |
304 | sync (); |
305 | } | |
306 | ||
307 | static int ether_post_recv (int devnum, int hw_addr, void *packet, int max_length) | |
308 | { | |
309 | int length; | |
310 | int i = 0; | |
311 | ||
312 | while (rx.ctrl & MAL_RX_CTRL_EMPTY) { | |
313 | if (i++ > 100) { | |
314 | printf ("RX timeout\n"); | |
315 | return 0; | |
316 | } | |
317 | udelay (1000); | |
f71b2888 | 318 | invalidate_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); |
b4489621 SP |
319 | } |
320 | length = rx.data_len - 4; | |
f71b2888 SR |
321 | if (length <= max_length) { |
322 | invalidate_dcache_range((u32)rx.data_ptr, (u32)rx.data_ptr + length); | |
b4489621 | 323 | memcpy(packet, rx.data_ptr, length); |
f71b2888 | 324 | } |
b4489621 SP |
325 | sync (); |
326 | ||
327 | rx.ctrl |= MAL_RX_CTRL_EMPTY; | |
f71b2888 | 328 | flush_dcache_range((u32)&rx, (u32)&rx + sizeof(mal_desc_t)); |
b4489621 SP |
329 | sync (); |
330 | ||
331 | return length; | |
332 | } | |
333 | ||
334 | /* | |
335 | * Test routines | |
336 | */ | |
337 | ||
338 | static void packet_fill (char *packet, int length) | |
339 | { | |
340 | char c = (char) length; | |
341 | int i; | |
342 | ||
343 | /* set up ethernet header */ | |
344 | memset (packet, 0xff, 14); | |
345 | ||
346 | for (i = 14; i < length; i++) { | |
347 | packet[i] = c++; | |
348 | } | |
349 | } | |
350 | ||
351 | static int packet_check (char *packet, int length) | |
352 | { | |
353 | char c = (char) length; | |
354 | int i; | |
355 | ||
356 | for (i = 14; i < length; i++) { | |
357 | if (packet[i] != c++) | |
358 | return -1; | |
359 | } | |
360 | ||
361 | return 0; | |
362 | } | |
363 | ||
364 | static int test_ctlr (int devnum, int hw_addr) | |
365 | { | |
366 | int res = -1; | |
367 | char packet_send[MAX_PACKET_LENGTH]; | |
368 | char packet_recv[MAX_PACKET_LENGTH]; | |
369 | int length; | |
370 | int i; | |
371 | int l; | |
372 | ||
373 | ether_post_init (devnum, hw_addr); | |
374 | ||
375 | for (i = 0; i < TEST_NUM; i++) { | |
376 | for (l = MIN_PACKET_LENGTH; l <= MAX_PACKET_LENGTH; l++) { | |
377 | packet_fill (packet_send, l); | |
378 | ||
379 | ether_post_send (devnum, hw_addr, packet_send, l); | |
380 | ||
381 | length = ether_post_recv (devnum, hw_addr, packet_recv, | |
382 | sizeof (packet_recv)); | |
383 | ||
384 | if (length != l || packet_check (packet_recv, length) < 0) { | |
385 | goto Done; | |
386 | } | |
387 | } | |
388 | } | |
389 | ||
390 | res = 0; | |
391 | ||
392 | Done: | |
393 | ||
394 | ether_post_halt (devnum, hw_addr); | |
395 | ||
396 | if (res != 0) { | |
397 | post_log ("EMAC%d test failed\n", devnum); | |
398 | } | |
399 | ||
400 | return res; | |
401 | } | |
402 | ||
403 | int ether_post_test (int flags) | |
404 | { | |
405 | int res = 0; | |
7d47cee2 | 406 | int i; |
b4489621 SP |
407 | |
408 | /* Allocate tx & rx packet buffers */ | |
6d0f6bcf JCPV |
409 | tx_buf = malloc (PKTSIZE_ALIGN + CONFIG_SYS_CACHELINE_SIZE); |
410 | rx_buf = malloc (PKTSIZE_ALIGN + CONFIG_SYS_CACHELINE_SIZE); | |
b4489621 SP |
411 | |
412 | if (!tx_buf || !rx_buf) { | |
413 | printf ("Failed to allocate packet buffers\n"); | |
414 | res = -1; | |
415 | goto out_free; | |
416 | } | |
417 | ||
7d47cee2 SR |
418 | for (i = 0; i < LAST_EMAC_NUM; i++) { |
419 | if (test_ctlr (i, i*0x100)) | |
420 | res = -1; | |
421 | } | |
b4489621 SP |
422 | |
423 | out_free: | |
424 | free (tx_buf); | |
425 | free (rx_buf); | |
426 | ||
427 | return res; | |
428 | } | |
429 | ||
6d0f6bcf | 430 | #endif /* CONFIG_POST & CONFIG_SYS_POST_ETHER */ |