]>
Commit | Line | Data |
---|---|---|
80885a9d WD |
1 | /*********************************************************************** |
2 | * | |
3 | * Copyright (C) 2004 by FS Forth-Systeme GmbH. | |
4 | * All rights reserved. | |
5 | * | |
6 | * $Id: ns9750_eth.c,v 1.2 2004/02/24 14:09:39 mpietrek Exp $ | |
7 | * @Author: Markus Pietrek | |
8 | * @Descr: Ethernet driver for the NS9750. Uses DMA Engine with polling | |
9 | * interrupt status. But interrupts are not enabled. | |
10 | * Only one tx buffer descriptor and the RXA buffer descriptor are used | |
11 | * Currently no transmit lockup handling is included. eth_send has a 5s | |
12 | * timeout for sending frames. No retransmits are performed when an | |
13 | * error occurs. | |
14 | * @References: [1] NS9750 Hardware Reference, December 2003 | |
15 | * [2] Intel LXT971 Datasheet #249414 Rev. 02 | |
16 | * [3] NS7520 Linux Ethernet Driver | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or | |
19 | * modify it under the terms of the GNU General Public License as | |
20 | * published by the Free Software Foundation; either version 2 of | |
21 | * the License, or (at your option) any later version. | |
22 | * | |
23 | * This program is distributed in the hope that it will be useful, | |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
26 | * GNU General Public License for more details. | |
27 | * | |
28 | * You should have received a copy of the GNU General Public License | |
29 | * along with this program; if not, write to the Free Software | |
30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
31 | * MA 02111-1307 USA | |
32 | * | |
33 | ***********************************************************************/ | |
34 | ||
35 | #include <common.h> | |
36 | #include <net.h> /* NetSendPacket */ | |
37 | ||
38 | #include "ns9750_eth.h" /* for Ethernet and PHY */ | |
39 | ||
fec61431 | 40 | /* some definition to make transition to linux easier */ |
80885a9d WD |
41 | |
42 | #define NS9750_DRIVER_NAME "eth" | |
43 | #define KERN_WARNING "Warning:" | |
44 | #define KERN_ERR "Error:" | |
45 | #define KERN_INFO "Info:" | |
46 | ||
47 | #if 0 | |
48 | # define DEBUG | |
49 | #endif | |
50 | ||
51 | #ifdef DEBUG | |
52 | # define printk printf | |
53 | ||
54 | # define DEBUG_INIT 0x0001 | |
55 | # define DEBUG_MINOR 0x0002 | |
56 | # define DEBUG_RX 0x0004 | |
57 | # define DEBUG_TX 0x0008 | |
58 | # define DEBUG_INT 0x0010 | |
59 | # define DEBUG_POLL 0x0020 | |
60 | # define DEBUG_LINK 0x0040 | |
61 | # define DEBUG_MII 0x0100 | |
62 | # define DEBUG_MII_LOW 0x0200 | |
63 | # define DEBUG_MEM 0x0400 | |
64 | # define DEBUG_ERROR 0x4000 | |
65 | # define DEBUG_ERROR_CRIT 0x8000 | |
66 | ||
67 | static int nDebugLvl = DEBUG_ERROR_CRIT; | |
68 | ||
69 | # define DEBUG_ARGS0( FLG, a0 ) if( ( nDebugLvl & (FLG) ) == (FLG) ) \ | |
70 | printf("%s: " a0, __FUNCTION__, 0, 0, 0, 0, 0, 0 ) | |
71 | # define DEBUG_ARGS1( FLG, a0, a1 ) if( ( nDebugLvl & (FLG) ) == (FLG)) \ | |
72 | printf("%s: " a0, __FUNCTION__, (int)(a1), 0, 0, 0, 0, 0 ) | |
73 | # define DEBUG_ARGS2( FLG, a0, a1, a2 ) if( (nDebugLvl & (FLG)) ==(FLG))\ | |
74 | printf("%s: " a0, __FUNCTION__, (int)(a1), (int)(a2), 0, 0,0,0 ) | |
75 | # define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) if((nDebugLvl &(FLG))==(FLG))\ | |
76 | printf("%s: "a0,__FUNCTION__,(int)(a1),(int)(a2),(int)(a3),0,0,0) | |
77 | # define DEBUG_FN( FLG ) if( (nDebugLvl & (FLG)) == (FLG) ) \ | |
78 | printf("\r%s:line %d\n", (int)__FUNCTION__, __LINE__, 0,0,0,0); | |
79 | # define ASSERT( expr, func ) if( !( expr ) ) { \ | |
80 | printf( "Assertion failed! %s:line %d %s\n", \ | |
81 | (int)__FUNCTION__,__LINE__,(int)(#expr),0,0,0); \ | |
82 | func } | |
83 | #else /* DEBUG */ | |
84 | # define printk(...) | |
85 | # define DEBUG_ARGS0( FLG, a0 ) | |
86 | # define DEBUG_ARGS1( FLG, a0, a1 ) | |
87 | # define DEBUG_ARGS2( FLG, a0, a1, a2 ) | |
88 | # define DEBUG_ARGS3( FLG, a0, a1, a2, a3 ) | |
89 | # define DEBUG_FN( n ) | |
90 | # define ASSERT(expr, func) | |
91 | #endif /* DEBUG */ | |
92 | ||
93 | #define NS9750_MII_NEG_DELAY (5*CFG_HZ) /* in s */ | |
94 | #define TX_TIMEOUT (5*CFG_HZ) /* in s */ | |
95 | ||
96 | /* @TODO move it to eeprom.h */ | |
97 | #define FS_EEPROM_AUTONEG_MASK 0x7 | |
98 | #define FS_EEPROM_AUTONEG_SPEED_MASK 0x1 | |
99 | #define FS_EEPROM_AUTONEG_SPEED_10 0x0 | |
100 | #define FS_EEPROM_AUTONEG_SPEED_100 0x1 | |
101 | #define FS_EEPROM_AUTONEG_DUPLEX_MASK 0x2 | |
102 | #define FS_EEPROM_AUTONEG_DUPLEX_HALF 0x0 | |
103 | #define FS_EEPROM_AUTONEG_DUPLEX_FULL 0x2 | |
104 | #define FS_EEPROM_AUTONEG_ENABLE_MASK 0x4 | |
105 | #define FS_EEPROM_AUTONEG_DISABLE 0x0 | |
106 | #define FS_EEPROM_AUTONEG_ENABLE 0x4 | |
107 | ||
108 | /* buffer descriptors taken from [1] p.306 */ | |
109 | typedef struct | |
110 | { | |
111 | unsigned int* punSrc; | |
112 | unsigned int unLen; /* 11 bits */ | |
113 | unsigned int* punDest; /* unused */ | |
114 | union { | |
115 | unsigned int unReg; | |
116 | struct { | |
117 | unsigned uStatus : 16; | |
118 | unsigned uRes : 12; | |
119 | unsigned uFull : 1; | |
120 | unsigned uEnable : 1; | |
121 | unsigned uInt : 1; | |
122 | unsigned uWrap : 1; | |
123 | } bits; | |
124 | } s; | |
125 | } rx_buffer_desc_t; | |
126 | ||
127 | typedef struct | |
128 | { | |
129 | unsigned int* punSrc; | |
130 | unsigned int unLen; /* 10 bits */ | |
131 | unsigned int* punDest; /* unused */ | |
132 | union { | |
133 | unsigned int unReg; /* only 32bit accesses may done to NS9750 | |
134 | * eth engine */ | |
135 | struct { | |
136 | unsigned uStatus : 16; | |
137 | unsigned uRes : 12; | |
138 | unsigned uFull : 1; | |
139 | unsigned uLast : 1; | |
140 | unsigned uInt : 1; | |
141 | unsigned uWrap : 1; | |
142 | } bits; | |
143 | } s; | |
144 | } tx_buffer_desc_t; | |
145 | ||
146 | static int ns9750_eth_reset( void ); | |
147 | ||
148 | static void ns9750_link_force( void ); | |
149 | static void ns9750_link_auto_negotiate( void ); | |
150 | static void ns9750_link_update_egcr( void ); | |
151 | static void ns9750_link_print_changed( void ); | |
152 | ||
153 | /* the PHY stuff */ | |
154 | ||
155 | static char ns9750_mii_identify_phy( void ); | |
156 | static unsigned short ns9750_mii_read( unsigned short uiRegister ); | |
157 | static void ns9750_mii_write( unsigned short uiRegister, unsigned short uiData ); | |
158 | static unsigned int ns9750_mii_get_clock_divisor( unsigned int unMaxMDIOClk ); | |
159 | static unsigned int ns9750_mii_poll_busy( void ); | |
160 | ||
161 | static unsigned int nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; | |
162 | static unsigned char ucLinkMode = FS_EEPROM_AUTONEG_ENABLE; | |
163 | static unsigned int uiLastLinkStatus; | |
164 | static PhyType phyDetected = PHY_NONE; | |
165 | ||
166 | /* we use only one tx buffer descriptor */ | |
167 | static tx_buffer_desc_t* pTxBufferDesc = | |
168 | (tx_buffer_desc_t*) get_eth_reg_addr( NS9750_ETH_TXBD ); | |
169 | ||
170 | /* we use only one rx buffer descriptor of the 4 */ | |
171 | static rx_buffer_desc_t aRxBufferDesc[ 4 ]; | |
172 | ||
173 | /*********************************************************************** | |
174 | * @Function: eth_init | |
175 | * @Return: -1 on failure otherwise 0 | |
176 | * @Descr: Initializes the ethernet engine and uses either FS Forth's default | |
177 | * MAC addr or the one in environment | |
178 | ***********************************************************************/ | |
179 | ||
180 | int eth_init (bd_t * pbis) | |
181 | { | |
182 | /* This default MAC Addr is reserved by FS Forth-Systeme for the case of | |
183 | EEPROM failures */ | |
184 | unsigned char aucMACAddr[6] = { 0x00, 0x04, 0xf3, 0x00, 0x06, 0x35 }; | |
185 | char *pcTmp = getenv ("ethaddr"); | |
186 | char *pcEnd; | |
187 | int i; | |
188 | ||
189 | DEBUG_FN (DEBUG_INIT); | |
190 | ||
191 | /* no need to check for hardware */ | |
192 | ||
193 | if (!ns9750_eth_reset ()) | |
194 | return -1; | |
195 | ||
196 | if (pcTmp != NULL) | |
197 | for (i = 0; i < 6; i++) { | |
198 | aucMACAddr[i] = | |
199 | pcTmp ? simple_strtoul (pcTmp, &pcEnd, | |
200 | 16) : 0; | |
201 | pcTmp = (*pcTmp) ? pcEnd + 1 : pcEnd; | |
202 | } | |
203 | ||
204 | /* configure ethernet address */ | |
205 | ||
206 | *get_eth_reg_addr (NS9750_ETH_SA1) = | |
207 | aucMACAddr[5] << 8 | aucMACAddr[4]; | |
208 | *get_eth_reg_addr (NS9750_ETH_SA2) = | |
209 | aucMACAddr[3] << 8 | aucMACAddr[2]; | |
210 | *get_eth_reg_addr (NS9750_ETH_SA3) = | |
211 | aucMACAddr[1] << 8 | aucMACAddr[0]; | |
212 | ||
213 | /* enable hardware */ | |
214 | ||
215 | *get_eth_reg_addr (NS9750_ETH_MAC1) = NS9750_ETH_MAC1_RXEN; | |
216 | ||
217 | /* the linux kernel may give packets < 60 bytes, for example arp */ | |
218 | *get_eth_reg_addr (NS9750_ETH_MAC2) = NS9750_ETH_MAC2_CRCEN | | |
219 | NS9750_ETH_MAC2_PADEN | NS9750_ETH_MAC2_HUGE; | |
220 | ||
221 | /* enable receive and transmit FIFO, use 10/100 Mbps MII */ | |
222 | *get_eth_reg_addr (NS9750_ETH_EGCR1) = | |
223 | NS9750_ETH_EGCR1_ETXWM | | |
224 | NS9750_ETH_EGCR1_ERX | | |
225 | NS9750_ETH_EGCR1_ERXDMA | | |
226 | NS9750_ETH_EGCR1_ETX | | |
227 | NS9750_ETH_EGCR1_ETXDMA | NS9750_ETH_EGCR1_ITXA; | |
228 | ||
229 | /* prepare DMA descriptors */ | |
230 | for (i = 0; i < 4; i++) { | |
231 | aRxBufferDesc[i].punSrc = 0; | |
232 | aRxBufferDesc[i].unLen = 0; | |
233 | aRxBufferDesc[i].s.bits.uWrap = 1; | |
234 | aRxBufferDesc[i].s.bits.uInt = 1; | |
235 | aRxBufferDesc[i].s.bits.uEnable = 0; | |
236 | aRxBufferDesc[i].s.bits.uFull = 0; | |
237 | } | |
238 | ||
239 | /* NetRxPackets[ 0 ] is initialized before eth_init is called and never | |
240 | changes. NetRxPackets is 32bit aligned */ | |
241 | aRxBufferDesc[0].punSrc = (unsigned int *) NetRxPackets[0]; | |
242 | aRxBufferDesc[0].s.bits.uEnable = 1; | |
243 | aRxBufferDesc[0].unLen = 1522; /* as stated in [1] p.307 */ | |
244 | ||
245 | *get_eth_reg_addr (NS9750_ETH_RXAPTR) = | |
246 | (unsigned int) &aRxBufferDesc[0]; | |
247 | ||
248 | /* [1] Tab. 221 states less than 5us */ | |
249 | *get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_ERXINIT; | |
250 | while (! | |
251 | (*get_eth_reg_addr (NS9750_ETH_EGSR) & NS9750_ETH_EGSR_RXINIT)) | |
252 | /* wait for finish */ | |
253 | udelay (1); | |
254 | ||
255 | /* @TODO do we need to clear RXINIT? */ | |
256 | *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_ERXINIT; | |
257 | ||
258 | *get_eth_reg_addr (NS9750_ETH_RXFREE) = 0x1; | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | /*********************************************************************** | |
264 | * @Function: eth_send | |
265 | * @Return: -1 on timeout otherwise 1 | |
266 | * @Descr: sends one frame by DMA | |
267 | ***********************************************************************/ | |
268 | ||
269 | int eth_send (volatile void *pPacket, int nLen) | |
270 | { | |
271 | ulong ulTimeout; | |
272 | ||
273 | DEBUG_FN (DEBUG_TX); | |
274 | ||
275 | /* clear old status values */ | |
276 | *get_eth_reg_addr (NS9750_ETH_EINTR) &= | |
277 | *get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_TX_MA; | |
278 | ||
279 | /* prepare Tx Descriptors */ | |
280 | ||
281 | pTxBufferDesc->punSrc = (unsigned int *) pPacket; /* pPacket is 32bit | |
282 | * aligned */ | |
283 | pTxBufferDesc->unLen = nLen; | |
284 | /* only 32bit accesses allowed. wrap, full, interrupt and enabled to 1 */ | |
285 | pTxBufferDesc->s.unReg = 0xf0000000; | |
286 | /* pTxBufferDesc is the first possible buffer descriptor */ | |
287 | *get_eth_reg_addr (NS9750_ETH_TXPTR) = 0x0; | |
288 | ||
289 | /* enable processor for next frame */ | |
290 | ||
291 | *get_eth_reg_addr (NS9750_ETH_EGCR2) &= ~NS9750_ETH_EGCR2_TCLER; | |
292 | *get_eth_reg_addr (NS9750_ETH_EGCR2) |= NS9750_ETH_EGCR2_TCLER; | |
293 | ||
294 | ulTimeout = get_timer (0); | |
295 | ||
296 | DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR, | |
297 | "Waiting for transmission to finish\n"); | |
298 | while (! | |
299 | (*get_eth_reg_addr (NS9750_ETH_EINTR) & | |
300 | (NS9750_ETH_EINTR_TXDONE | NS9750_ETH_EINTR_TXERR))) { | |
301 | /* do nothing, wait for completion */ | |
302 | if (get_timer (0) - ulTimeout > TX_TIMEOUT) { | |
303 | DEBUG_ARGS0 (DEBUG_TX, "Transmit Timed out\n"); | |
304 | return -1; | |
305 | } | |
306 | } | |
307 | DEBUG_ARGS0 (DEBUG_TX | DEBUG_MINOR, "transmitted...\n"); | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | /*********************************************************************** | |
313 | * @Function: eth_rx | |
314 | * @Return: size of last frame in bytes or 0 if no frame available | |
315 | * @Descr: gives one frame to U-Boot which has been copied by DMA engine already | |
316 | * to NetRxPackets[ 0 ]. | |
317 | ***********************************************************************/ | |
318 | ||
319 | int eth_rx (void) | |
320 | { | |
321 | int nLen = 0; | |
322 | unsigned int unStatus; | |
323 | ||
324 | unStatus = | |
325 | *get_eth_reg_addr (NS9750_ETH_EINTR) & NS9750_ETH_EINTR_RX_MA; | |
326 | ||
327 | if (!unStatus) | |
328 | /* no packet available, return immediately */ | |
329 | return 0; | |
330 | ||
331 | DEBUG_FN (DEBUG_RX); | |
332 | ||
333 | /* unLen always < max(nLen) and discard checksum */ | |
334 | nLen = (int) aRxBufferDesc[0].unLen - 4; | |
335 | ||
336 | /* acknowledge status register */ | |
337 | *get_eth_reg_addr (NS9750_ETH_EINTR) = unStatus; | |
338 | ||
339 | aRxBufferDesc[0].unLen = 1522; | |
340 | aRxBufferDesc[0].s.bits.uFull = 0; | |
341 | ||
342 | /* Buffer A descriptor available again */ | |
343 | *get_eth_reg_addr (NS9750_ETH_RXFREE) |= 0x1; | |
344 | ||
345 | /* NetReceive may call eth_send. Due to a possible bug of the NS9750 we | |
346 | * have to acknowledge the received frame before sending a new one */ | |
347 | if (unStatus & NS9750_ETH_EINTR_RXDONEA) | |
348 | NetReceive (NetRxPackets[0], nLen); | |
349 | ||
350 | return nLen; | |
351 | } | |
352 | ||
353 | /*********************************************************************** | |
354 | * @Function: eth_halt | |
355 | * @Return: n/a | |
356 | * @Descr: stops the ethernet engine | |
357 | ***********************************************************************/ | |
358 | ||
359 | void eth_halt (void) | |
360 | { | |
361 | DEBUG_FN (DEBUG_INIT); | |
362 | ||
363 | *get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_ETH_MAC1_RXEN; | |
364 | *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~(NS9750_ETH_EGCR1_ERX | | |
365 | NS9750_ETH_EGCR1_ERXDMA | | |
366 | NS9750_ETH_EGCR1_ETX | | |
367 | NS9750_ETH_EGCR1_ETXDMA); | |
368 | } | |
369 | ||
370 | /*********************************************************************** | |
371 | * @Function: ns9750_eth_reset | |
372 | * @Return: 0 on failure otherwise 1 | |
373 | * @Descr: resets the ethernet interface and the PHY, | |
374 | * performs auto negotiation or fixed modes | |
375 | ***********************************************************************/ | |
376 | ||
377 | static int ns9750_eth_reset (void) | |
378 | { | |
379 | DEBUG_FN (DEBUG_MINOR); | |
380 | ||
381 | /* Reset MAC */ | |
382 | *get_eth_reg_addr (NS9750_ETH_EGCR1) |= NS9750_ETH_EGCR1_MAC_HRST; | |
383 | udelay (5); /* according to [1], p.322 */ | |
384 | *get_eth_reg_addr (NS9750_ETH_EGCR1) &= ~NS9750_ETH_EGCR1_MAC_HRST; | |
385 | ||
386 | /* reset and initialize PHY */ | |
387 | ||
388 | *get_eth_reg_addr (NS9750_ETH_MAC1) &= ~NS9750_ETH_MAC1_SRST; | |
389 | ||
390 | /* we don't support hot plugging of PHY, therefore we don't reset | |
391 | phyDetected and nPhyMaxMdioClock here. The risk is if the setting is | |
392 | incorrect the first open | |
393 | may detect the PHY correctly but succeding will fail | |
394 | For reseting the PHY and identifying we have to use the standard | |
395 | MDIO CLOCK value 2.5 MHz only after hardware reset | |
396 | After having identified the PHY we will do faster */ | |
397 | ||
398 | *get_eth_reg_addr (NS9750_ETH_MCFG) = | |
399 | ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); | |
400 | ||
401 | /* reset PHY */ | |
fec61431 HV |
402 | ns9750_mii_write(PHY_BMCR, PHY_BMCR_RESET); |
403 | ns9750_mii_write(PHY_BMCR, 0); | |
80885a9d WD |
404 | |
405 | /* @TODO check time */ | |
406 | udelay (3000); /* [2] p.70 says at least 300us reset recovery time. But | |
407 | go sure, it didn't worked stable at higher timer | |
408 | frequencies under LxNETES-2.x */ | |
409 | ||
410 | /* MII clock has been setup to default, ns9750_mii_identify_phy should | |
411 | work for all */ | |
412 | ||
413 | if (!ns9750_mii_identify_phy ()) { | |
414 | printk (KERN_ERR NS9750_DRIVER_NAME | |
415 | ": Unsupported PHY, aborting\n"); | |
416 | return 0; | |
417 | } | |
418 | ||
419 | /* now take the highest MDIO clock possible after detection */ | |
420 | *get_eth_reg_addr (NS9750_ETH_MCFG) = | |
421 | ns9750_mii_get_clock_divisor (nPhyMaxMdioClock); | |
422 | ||
423 | ||
424 | /* PHY has been detected, so there can be no abort reason and we can | |
425 | finish initializing ethernet */ | |
426 | ||
427 | uiLastLinkStatus = 0xff; /* undefined */ | |
428 | ||
429 | if ((ucLinkMode & FS_EEPROM_AUTONEG_ENABLE_MASK) == | |
430 | FS_EEPROM_AUTONEG_DISABLE) | |
431 | /* use parameters defined */ | |
432 | ns9750_link_force (); | |
433 | else | |
434 | ns9750_link_auto_negotiate (); | |
435 | ||
436 | if (phyDetected == PHY_LXT971A) | |
437 | /* set LED2 to link mode */ | |
438 | ns9750_mii_write (PHY_LXT971_LED_CFG, | |
439 | PHY_LXT971_LED_CFG_LINK_ACT << | |
440 | PHY_LXT971_LED_CFG_SHIFT_LED2); | |
441 | ||
442 | return 1; | |
443 | } | |
444 | ||
445 | /*********************************************************************** | |
446 | * @Function: ns9750_link_force | |
447 | * @Return: void | |
448 | * @Descr: configures eth and MII to use the link mode defined in | |
449 | * ucLinkMode | |
450 | ***********************************************************************/ | |
451 | ||
452 | static void ns9750_link_force (void) | |
453 | { | |
454 | unsigned short uiControl; | |
455 | ||
456 | DEBUG_FN (DEBUG_LINK); | |
457 | ||
fec61431 HV |
458 | uiControl = ns9750_mii_read(PHY_BMCR); |
459 | uiControl &= ~(PHY_BMCR_SPEED_MASK | | |
460 | PHY_BMCR_AUTON | PHY_BMCR_DPLX); | |
80885a9d WD |
461 | |
462 | uiLastLinkStatus = 0; | |
463 | ||
464 | if ((ucLinkMode & FS_EEPROM_AUTONEG_SPEED_MASK) == | |
465 | FS_EEPROM_AUTONEG_SPEED_100) { | |
fec61431 | 466 | uiControl |= PHY_BMCR_100MB; |
80885a9d WD |
467 | uiLastLinkStatus |= PHY_LXT971_STAT2_100BTX; |
468 | } else | |
fec61431 | 469 | uiControl |= PHY_BMCR_10_MBPS; |
80885a9d WD |
470 | |
471 | if ((ucLinkMode & FS_EEPROM_AUTONEG_DUPLEX_MASK) == | |
472 | FS_EEPROM_AUTONEG_DUPLEX_FULL) { | |
fec61431 | 473 | uiControl |= PHY_BMCR_DPLX; |
80885a9d WD |
474 | uiLastLinkStatus |= PHY_LXT971_STAT2_DUPLEX_MODE; |
475 | } | |
476 | ||
fec61431 | 477 | ns9750_mii_write(PHY_BMCR, uiControl); |
80885a9d WD |
478 | |
479 | ns9750_link_print_changed (); | |
480 | ns9750_link_update_egcr (); | |
481 | } | |
482 | ||
483 | /*********************************************************************** | |
484 | * @Function: ns9750_link_auto_negotiate | |
485 | * @Return: void | |
486 | * @Descr: performs auto-negotation of link. | |
487 | ***********************************************************************/ | |
488 | ||
489 | static void ns9750_link_auto_negotiate (void) | |
490 | { | |
491 | unsigned long ulStartJiffies; | |
492 | unsigned short uiStatus; | |
493 | ||
494 | DEBUG_FN (DEBUG_LINK); | |
495 | ||
496 | /* run auto-negotation */ | |
497 | /* define what we are capable of */ | |
fec61431 HV |
498 | ns9750_mii_write(PHY_ANAR, |
499 | PHY_ANLPAR_TXFD | | |
500 | PHY_ANLPAR_TX | | |
501 | PHY_ANLPAR_10FD | | |
502 | PHY_ANLPAR_10 | | |
503 | PHY_ANLPAR_PSB_802_3); | |
80885a9d | 504 | /* start auto-negotiation */ |
fec61431 | 505 | ns9750_mii_write(PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); |
80885a9d WD |
506 | |
507 | /* wait for completion */ | |
508 | ||
509 | ulStartJiffies = get_ticks (); | |
510 | while (get_ticks () < ulStartJiffies + NS9750_MII_NEG_DELAY) { | |
fec61431 | 511 | uiStatus = ns9750_mii_read(PHY_BMSR); |
80885a9d | 512 | if ((uiStatus & |
fec61431 HV |
513 | (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) == |
514 | (PHY_BMSR_AUTN_COMP | PHY_BMSR_LS)) { | |
80885a9d WD |
515 | /* lucky we are, auto-negotiation succeeded */ |
516 | ns9750_link_print_changed (); | |
517 | ns9750_link_update_egcr (); | |
518 | return; | |
519 | } | |
520 | } | |
521 | ||
522 | DEBUG_ARGS0 (DEBUG_LINK, "auto-negotiation timed out\n"); | |
523 | /* ignore invalid link settings */ | |
524 | } | |
525 | ||
526 | /*********************************************************************** | |
527 | * @Function: ns9750_link_update_egcr | |
528 | * @Return: void | |
529 | * @Descr: updates the EGCR and MAC2 link status after mode change or | |
530 | * auto-negotation | |
531 | ***********************************************************************/ | |
532 | ||
533 | static void ns9750_link_update_egcr (void) | |
534 | { | |
535 | unsigned int unEGCR; | |
536 | unsigned int unMAC2; | |
537 | unsigned int unIPGT; | |
538 | ||
539 | DEBUG_FN (DEBUG_LINK); | |
540 | ||
541 | unEGCR = *get_eth_reg_addr (NS9750_ETH_EGCR1); | |
542 | unMAC2 = *get_eth_reg_addr (NS9750_ETH_MAC2); | |
543 | unIPGT = *get_eth_reg_addr (NS9750_ETH_IPGT) & ~NS9750_ETH_IPGT_MA; | |
544 | ||
545 | unMAC2 &= ~NS9750_ETH_MAC2_FULLD; | |
546 | if ((uiLastLinkStatus & PHY_LXT971_STAT2_DUPLEX_MODE) | |
547 | == PHY_LXT971_STAT2_DUPLEX_MODE) { | |
548 | unMAC2 |= NS9750_ETH_MAC2_FULLD; | |
549 | unIPGT |= 0x15; /* see [1] p. 339 */ | |
550 | } else | |
551 | unIPGT |= 0x12; /* see [1] p. 339 */ | |
552 | ||
553 | *get_eth_reg_addr (NS9750_ETH_MAC2) = unMAC2; | |
554 | *get_eth_reg_addr (NS9750_ETH_EGCR1) = unEGCR; | |
555 | *get_eth_reg_addr (NS9750_ETH_IPGT) = unIPGT; | |
556 | } | |
557 | ||
558 | /*********************************************************************** | |
559 | * @Function: ns9750_link_print_changed | |
560 | * @Return: void | |
561 | * @Descr: checks whether the link status has changed and if so prints | |
562 | * the new mode | |
563 | ***********************************************************************/ | |
564 | ||
565 | static void ns9750_link_print_changed (void) | |
566 | { | |
567 | unsigned short uiStatus; | |
568 | unsigned short uiControl; | |
569 | ||
570 | DEBUG_FN (DEBUG_LINK); | |
571 | ||
fec61431 | 572 | uiControl = ns9750_mii_read(PHY_BMCR); |
80885a9d | 573 | |
fec61431 HV |
574 | if ((uiControl & PHY_BMCR_AUTON) == PHY_BMCR_AUTON) { |
575 | /* PHY_BMSR_LS is only set on autonegotiation */ | |
576 | uiStatus = ns9750_mii_read(PHY_BMSR); | |
80885a9d | 577 | |
fec61431 | 578 | if (!(uiStatus & PHY_BMSR_LS)) { |
80885a9d WD |
579 | printk (KERN_WARNING NS9750_DRIVER_NAME |
580 | ": link down\n"); | |
581 | /* @TODO Linux: carrier_off */ | |
582 | } else { | |
583 | /* @TODO Linux: carrier_on */ | |
584 | if (phyDetected == PHY_LXT971A) { | |
585 | uiStatus = ns9750_mii_read (PHY_LXT971_STAT2); | |
586 | uiStatus &= (PHY_LXT971_STAT2_100BTX | | |
587 | PHY_LXT971_STAT2_DUPLEX_MODE | | |
588 | PHY_LXT971_STAT2_AUTO_NEG); | |
589 | ||
590 | /* mask out all uninteresting parts */ | |
591 | } | |
fec61431 | 592 | /* other PHYs must store their link information in |
80885a9d WD |
593 | uiStatus as PHY_LXT971 */ |
594 | } | |
595 | } else { | |
596 | /* mode has been forced, so uiStatus should be the same as the | |
597 | last link status, enforce printing */ | |
598 | uiStatus = uiLastLinkStatus; | |
599 | uiLastLinkStatus = 0xff; | |
600 | } | |
601 | ||
602 | if (uiStatus != uiLastLinkStatus) { | |
603 | /* save current link status */ | |
604 | uiLastLinkStatus = uiStatus; | |
605 | ||
606 | /* print new link status */ | |
607 | ||
608 | printk (KERN_INFO NS9750_DRIVER_NAME | |
609 | ": link mode %i Mbps %s duplex %s\n", | |
610 | (uiStatus & PHY_LXT971_STAT2_100BTX) ? 100 : 10, | |
611 | (uiStatus & PHY_LXT971_STAT2_DUPLEX_MODE) ? "full" : | |
612 | "half", | |
613 | (uiStatus & PHY_LXT971_STAT2_AUTO_NEG) ? "(auto)" : | |
614 | ""); | |
615 | } | |
616 | } | |
617 | ||
618 | /*********************************************************************** | |
619 | * the MII low level stuff | |
620 | ***********************************************************************/ | |
621 | ||
622 | /*********************************************************************** | |
623 | * @Function: ns9750_mii_identify_phy | |
624 | * @Return: 1 if supported PHY has been detected otherwise 0 | |
625 | * @Descr: checks for supported PHY and prints the IDs. | |
626 | ***********************************************************************/ | |
627 | ||
628 | static char ns9750_mii_identify_phy (void) | |
629 | { | |
630 | unsigned short uiID1; | |
631 | unsigned short uiID2; | |
632 | unsigned char *szName; | |
633 | char cRes = 0; | |
634 | ||
635 | DEBUG_FN (DEBUG_MII); | |
636 | ||
fec61431 | 637 | phyDetected = (PhyType) uiID1 = ns9750_mii_read(PHY_PHYIDR1); |
80885a9d WD |
638 | |
639 | switch (phyDetected) { | |
640 | case PHY_LXT971A: | |
641 | szName = "LXT971A"; | |
fec61431 | 642 | uiID2 = ns9750_mii_read(PHY_PHYIDR2); |
80885a9d WD |
643 | nPhyMaxMdioClock = PHY_LXT971_MDIO_MAX_CLK; |
644 | cRes = 1; | |
645 | break; | |
646 | case PHY_NONE: | |
647 | default: | |
648 | /* in case uiID1 == 0 && uiID2 == 0 we may have the wrong | |
649 | address or reset sets the wrong NS9750_ETH_MCFG_CLKS */ | |
650 | ||
651 | uiID2 = 0; | |
652 | szName = "unknown"; | |
653 | nPhyMaxMdioClock = PHY_MDIO_MAX_CLK; | |
654 | phyDetected = PHY_NONE; | |
655 | } | |
656 | ||
657 | printk (KERN_INFO NS9750_DRIVER_NAME | |
658 | ": PHY (0x%x, 0x%x) = %s detected\n", uiID1, uiID2, szName); | |
659 | ||
660 | return cRes; | |
661 | } | |
662 | ||
663 | /*********************************************************************** | |
664 | * @Function: ns9750_mii_read | |
665 | * @Return: the data read from PHY register uiRegister | |
666 | * @Descr: the data read may be invalid if timed out. If so, a message | |
667 | * is printed but the invalid data is returned. | |
668 | * The fixed device address is being used. | |
669 | ***********************************************************************/ | |
670 | ||
671 | static unsigned short ns9750_mii_read (unsigned short uiRegister) | |
672 | { | |
673 | DEBUG_FN (DEBUG_MII_LOW); | |
674 | ||
675 | /* write MII register to be read */ | |
676 | *get_eth_reg_addr (NS9750_ETH_MADR) = | |
677 | NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; | |
678 | ||
679 | *get_eth_reg_addr (NS9750_ETH_MCMD) = NS9750_ETH_MCMD_READ; | |
680 | ||
681 | if (!ns9750_mii_poll_busy ()) | |
682 | printk (KERN_WARNING NS9750_DRIVER_NAME | |
683 | ": MII still busy in read\n"); | |
684 | /* continue to read */ | |
685 | ||
686 | *get_eth_reg_addr (NS9750_ETH_MCMD) = 0; | |
687 | ||
688 | return (unsigned short) (*get_eth_reg_addr (NS9750_ETH_MRDD)); | |
689 | } | |
690 | ||
691 | ||
692 | /*********************************************************************** | |
693 | * @Function: ns9750_mii_write | |
694 | * @Return: nothing | |
695 | * @Descr: writes the data to the PHY register. In case of a timeout, | |
696 | * no special handling is performed but a message printed | |
697 | * The fixed device address is being used. | |
698 | ***********************************************************************/ | |
699 | ||
700 | static void ns9750_mii_write (unsigned short uiRegister, | |
701 | unsigned short uiData) | |
702 | { | |
703 | DEBUG_FN (DEBUG_MII_LOW); | |
704 | ||
705 | /* write MII register to be written */ | |
706 | *get_eth_reg_addr (NS9750_ETH_MADR) = | |
707 | NS9750_ETH_PHY_ADDRESS << 8 | uiRegister; | |
708 | ||
709 | *get_eth_reg_addr (NS9750_ETH_MWTD) = uiData; | |
710 | ||
711 | if (!ns9750_mii_poll_busy ()) { | |
712 | printf (KERN_WARNING NS9750_DRIVER_NAME | |
713 | ": MII still busy in write\n"); | |
714 | } | |
715 | } | |
716 | ||
717 | ||
718 | /*********************************************************************** | |
719 | * @Function: ns9750_mii_get_clock_divisor | |
720 | * @Return: the clock divisor that should be used in NS9750_ETH_MCFG_CLKS | |
721 | * @Descr: if no clock divisor can be calculated for the | |
722 | * current SYSCLK and the maximum MDIO Clock, a warning is printed | |
723 | * and the greatest divisor is taken | |
724 | ***********************************************************************/ | |
725 | ||
726 | static unsigned int ns9750_mii_get_clock_divisor (unsigned int unMaxMDIOClk) | |
727 | { | |
728 | struct { | |
729 | unsigned int unSysClkDivisor; | |
730 | unsigned int unClks; /* field for NS9750_ETH_MCFG_CLKS */ | |
731 | } PHYClockDivisors[] = { | |
732 | { | |
733 | 4, NS9750_ETH_MCFG_CLKS_4}, { | |
734 | 6, NS9750_ETH_MCFG_CLKS_6}, { | |
735 | 8, NS9750_ETH_MCFG_CLKS_8}, { | |
736 | 10, NS9750_ETH_MCFG_CLKS_10}, { | |
737 | 20, NS9750_ETH_MCFG_CLKS_20}, { | |
738 | 30, NS9750_ETH_MCFG_CLKS_30}, { | |
739 | 40, NS9750_ETH_MCFG_CLKS_40} | |
740 | }; | |
741 | ||
742 | int nIndexSysClkDiv; | |
743 | int nArraySize = | |
744 | sizeof (PHYClockDivisors) / sizeof (PHYClockDivisors[0]); | |
745 | unsigned int unClks = NS9750_ETH_MCFG_CLKS_40; /* defaults to | |
746 | greatest div */ | |
747 | ||
748 | DEBUG_FN (DEBUG_INIT); | |
749 | ||
750 | for (nIndexSysClkDiv = 0; nIndexSysClkDiv < nArraySize; | |
751 | nIndexSysClkDiv++) { | |
752 | /* find first sysclock divisor that isn't higher than 2.5 MHz | |
753 | clock */ | |
754 | if (AHB_CLK_FREQ / | |
755 | PHYClockDivisors[nIndexSysClkDiv].unSysClkDivisor <= | |
756 | unMaxMDIOClk) { | |
757 | unClks = PHYClockDivisors[nIndexSysClkDiv].unClks; | |
758 | break; | |
759 | } | |
760 | } | |
761 | ||
762 | DEBUG_ARGS2 (DEBUG_INIT, | |
763 | "Taking MDIO Clock bit mask 0x%0x for max clock %i\n", | |
764 | unClks, unMaxMDIOClk); | |
765 | ||
766 | /* return greatest divisor */ | |
767 | return unClks; | |
768 | } | |
769 | ||
770 | /*********************************************************************** | |
771 | * @Function: ns9750_mii_poll_busy | |
772 | * @Return: 0 if timed out otherwise the remaing timeout | |
773 | * @Descr: waits until the MII has completed a command or it times out | |
774 | * code may be interrupted by hard interrupts. | |
775 | * It is not checked what happens on multiple actions when | |
776 | * the first is still being busy and we timeout. | |
777 | ***********************************************************************/ | |
778 | ||
779 | static unsigned int ns9750_mii_poll_busy (void) | |
780 | { | |
781 | unsigned int unTimeout = 10000; | |
782 | ||
783 | DEBUG_FN (DEBUG_MII_LOW); | |
784 | ||
785 | while (((*get_eth_reg_addr (NS9750_ETH_MIND) & NS9750_ETH_MIND_BUSY) | |
786 | == NS9750_ETH_MIND_BUSY) && unTimeout) | |
787 | unTimeout--; | |
788 | ||
789 | return unTimeout; | |
790 | } |