]>
Commit | Line | Data |
---|---|---|
42d1f039 WD |
1 | /* |
2 | * tsec.c | |
97d80fc3 | 3 | * Freescale Three Speed Ethernet Controller driver |
42d1f039 WD |
4 | * |
5 | * This software may be used and distributed according to the | |
6 | * terms of the GNU Public License, Version 2, incorporated | |
7 | * herein by reference. | |
8 | * | |
97d80fc3 | 9 | * Copyright 2004 Freescale Semiconductor. |
42d1f039 | 10 | * (C) Copyright 2003, Motorola, Inc. |
42d1f039 WD |
11 | * author Andy Fleming |
12 | * | |
13 | */ | |
14 | ||
15 | #include <config.h> | |
16 | #include <mpc85xx.h> | |
17 | #include <common.h> | |
18 | #include <malloc.h> | |
19 | #include <net.h> | |
20 | #include <command.h> | |
21 | ||
22 | #if defined(CONFIG_TSEC_ENET) | |
23 | #include "tsec.h" | |
63ff004c | 24 | #include "miiphy.h" |
42d1f039 | 25 | |
d87080b7 WD |
26 | DECLARE_GLOBAL_DATA_PTR; |
27 | ||
63ff004c | 28 | #define TX_BUF_CNT 2 |
42d1f039 | 29 | |
42d1f039 WD |
30 | static uint rxIdx; /* index of the current RX buffer */ |
31 | static uint txIdx; /* index of the current TX buffer */ | |
32 | ||
33 | typedef volatile struct rtxbd { | |
34 | txbd8_t txbd[TX_BUF_CNT]; | |
35 | rxbd8_t rxbd[PKTBUFSRX]; | |
36 | } RTXBD; | |
37 | ||
97d80fc3 WD |
38 | struct tsec_info_struct { |
39 | unsigned int phyaddr; | |
d9b94f28 | 40 | u32 flags; |
97d80fc3 WD |
41 | unsigned int phyregidx; |
42 | }; | |
43 | ||
44 | ||
45 | /* The tsec_info structure contains 3 values which the | |
46 | * driver uses to determine how to operate a given ethernet | |
47 | * device. For now, the structure is initialized with the | |
48 | * knowledge that all current implementations have 2 TSEC | |
49 | * devices, and one FEC. The information needed is: | |
50 | * phyaddr - The address of the PHY which is attached to | |
9d46ea4a | 51 | * the given device. |
97d80fc3 | 52 | * |
d9b94f28 JL |
53 | * flags - This variable indicates whether the device |
54 | * supports gigabit speed ethernet, and whether it should be | |
55 | * in reduced mode. | |
97d80fc3 WD |
56 | * |
57 | * phyregidx - This variable specifies which ethernet device | |
9d46ea4a WD |
58 | * controls the MII Management registers which are connected |
59 | * to the PHY. For 8540/8560, only TSEC1 (index 0) has | |
60 | * access to the PHYs, so all of the entries have "0". | |
97d80fc3 WD |
61 | * |
62 | * The values specified in the table are taken from the board's | |
63 | * config file in include/configs/. When implementing a new | |
64 | * board with ethernet capability, it is necessary to define: | |
65 | * TSEC1_PHY_ADDR | |
66 | * TSEC1_PHYIDX | |
67 | * TSEC2_PHY_ADDR | |
68 | * TSEC2_PHYIDX | |
69 | * | |
70 | * and for 8560: | |
71 | * FEC_PHY_ADDR | |
72 | * FEC_PHYIDX | |
73 | */ | |
74 | static struct tsec_info_struct tsec_info[] = { | |
f046ccd1 | 75 | #if defined(CONFIG_MPC85XX_TSEC1) || defined(CONFIG_MPC83XX_TSEC1) |
d9b94f28 | 76 | {TSEC1_PHY_ADDR, TSEC_GIGABIT, TSEC1_PHYIDX}, |
9d46ea4a WD |
77 | #else |
78 | { 0, 0, 0}, | |
97d80fc3 | 79 | #endif |
f046ccd1 | 80 | #if defined(CONFIG_MPC85XX_TSEC2) || defined(CONFIG_MPC83XX_TSEC2) |
d9b94f28 | 81 | {TSEC2_PHY_ADDR, TSEC_GIGABIT, TSEC2_PHYIDX}, |
9d46ea4a WD |
82 | #else |
83 | { 0, 0, 0}, | |
97d80fc3 WD |
84 | #endif |
85 | #ifdef CONFIG_MPC85XX_FEC | |
86 | {FEC_PHY_ADDR, 0, FEC_PHYIDX}, | |
9d46ea4a | 87 | #else |
f046ccd1 | 88 | # if defined(CONFIG_MPC85XX_TSEC3) || defined(CONFIG_MPC83XX_TSEC3) |
d9b94f28 JL |
89 | {TSEC3_PHY_ADDR, TSEC_GIGABIT | TSEC_REDUCED, TSEC3_PHYIDX}, |
90 | # else | |
9d46ea4a | 91 | { 0, 0, 0}, |
d9b94f28 | 92 | # endif |
f046ccd1 | 93 | # if defined(CONFIG_MPC85XX_TSEC4) || defined(CONFIG_MPC83XX_TSEC4) |
d9b94f28 JL |
94 | {TSEC4_PHY_ADDR, TSEC_REDUCED, TSEC4_PHYIDX}, |
95 | # else | |
96 | { 0, 0, 0}, | |
97 | # endif | |
97d80fc3 WD |
98 | #endif |
99 | }; | |
100 | ||
d9b94f28 | 101 | #define MAXCONTROLLERS (4) |
97d80fc3 WD |
102 | |
103 | static int relocated = 0; | |
104 | ||
105 | static struct tsec_private *privlist[MAXCONTROLLERS]; | |
106 | ||
42d1f039 WD |
107 | #ifdef __GNUC__ |
108 | static RTXBD rtx __attribute__ ((aligned(8))); | |
109 | #else | |
110 | #error "rtx must be 64-bit aligned" | |
111 | #endif | |
112 | ||
113 | static int tsec_send(struct eth_device* dev, volatile void *packet, int length); | |
114 | static int tsec_recv(struct eth_device* dev); | |
115 | static int tsec_init(struct eth_device* dev, bd_t * bd); | |
116 | static void tsec_halt(struct eth_device* dev); | |
97d80fc3 WD |
117 | static void init_registers(volatile tsec_t *regs); |
118 | static void startup_tsec(struct eth_device *dev); | |
119 | static int init_phy(struct eth_device *dev); | |
120 | void write_phy_reg(struct tsec_private *priv, uint regnum, uint value); | |
121 | uint read_phy_reg(struct tsec_private *priv, uint regnum); | |
122 | struct phy_info * get_phy_info(struct eth_device *dev); | |
123 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd); | |
124 | static void adjust_link(struct eth_device *dev); | |
125 | static void relocate_cmds(void); | |
63ff004c MB |
126 | static int tsec_miiphy_write(char *devname, unsigned char addr, |
127 | unsigned char reg, unsigned short value); | |
128 | static int tsec_miiphy_read(char *devname, unsigned char addr, | |
129 | unsigned char reg, unsigned short *value); | |
97d80fc3 WD |
130 | |
131 | /* Initialize device structure. Returns success if PHY | |
132 | * initialization succeeded (i.e. if it recognizes the PHY) | |
133 | */ | |
d9b94f28 | 134 | int tsec_initialize(bd_t *bis, int index, char *devname) |
42d1f039 WD |
135 | { |
136 | struct eth_device* dev; | |
137 | int i; | |
97d80fc3 | 138 | struct tsec_private *priv; |
42d1f039 WD |
139 | |
140 | dev = (struct eth_device*) malloc(sizeof *dev); | |
141 | ||
97d80fc3 | 142 | if(NULL == dev) |
42d1f039 WD |
143 | return 0; |
144 | ||
145 | memset(dev, 0, sizeof *dev); | |
146 | ||
97d80fc3 WD |
147 | priv = (struct tsec_private *) malloc(sizeof(*priv)); |
148 | ||
149 | if(NULL == priv) | |
150 | return 0; | |
151 | ||
152 | privlist[index] = priv; | |
153 | priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index*TSEC_SIZE); | |
154 | priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR + | |
155 | tsec_info[index].phyregidx*TSEC_SIZE); | |
156 | ||
157 | priv->phyaddr = tsec_info[index].phyaddr; | |
d9b94f28 | 158 | priv->flags = tsec_info[index].flags; |
97d80fc3 | 159 | |
d9b94f28 | 160 | sprintf(dev->name, devname); |
42d1f039 | 161 | dev->iobase = 0; |
97d80fc3 | 162 | dev->priv = priv; |
42d1f039 WD |
163 | dev->init = tsec_init; |
164 | dev->halt = tsec_halt; | |
165 | dev->send = tsec_send; | |
166 | dev->recv = tsec_recv; | |
167 | ||
168 | /* Tell u-boot to get the addr from the env */ | |
169 | for(i=0;i<6;i++) | |
170 | dev->enetaddr[i] = 0; | |
171 | ||
172 | eth_register(dev); | |
173 | ||
7abf0c58 | 174 | |
97d80fc3 WD |
175 | /* Reset the MAC */ |
176 | priv->regs->maccfg1 |= MACCFG1_SOFT_RESET; | |
177 | priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET); | |
7abf0c58 | 178 | |
63ff004c MB |
179 | #if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) \ |
180 | && !defined(BITBANGMII) | |
181 | miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write); | |
182 | #endif | |
183 | ||
97d80fc3 WD |
184 | /* Try to initialize PHY here, and return */ |
185 | return init_phy(dev); | |
42d1f039 WD |
186 | } |
187 | ||
188 | ||
189 | /* Initializes data structures and registers for the controller, | |
9d46ea4a | 190 | * and brings the interface up. Returns the link status, meaning |
97d80fc3 WD |
191 | * that it returns success if the link is up, failure otherwise. |
192 | * This allows u-boot to find the first active controller. */ | |
42d1f039 WD |
193 | int tsec_init(struct eth_device* dev, bd_t * bd) |
194 | { | |
42d1f039 WD |
195 | uint tempval; |
196 | char tmpbuf[MAC_ADDR_LEN]; | |
197 | int i; | |
97d80fc3 WD |
198 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
199 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
200 | |
201 | /* Make sure the controller is stopped */ | |
202 | tsec_halt(dev); | |
203 | ||
97d80fc3 | 204 | /* Init MACCFG2. Defaults to GMII */ |
42d1f039 WD |
205 | regs->maccfg2 = MACCFG2_INIT_SETTINGS; |
206 | ||
207 | /* Init ECNTRL */ | |
208 | regs->ecntrl = ECNTRL_INIT_SETTINGS; | |
209 | ||
210 | /* Copy the station address into the address registers. | |
211 | * Backwards, because little endian MACS are dumb */ | |
212 | for(i=0;i<MAC_ADDR_LEN;i++) { | |
97d80fc3 | 213 | tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i]; |
42d1f039 | 214 | } |
77ddac94 | 215 | regs->macstnaddr1 = *((uint *)(tmpbuf)); |
42d1f039 WD |
216 | |
217 | tempval = *((uint *)(tmpbuf +4)); | |
218 | ||
77ddac94 | 219 | regs->macstnaddr2 = tempval; |
42d1f039 | 220 | |
42d1f039 WD |
221 | /* reset the indices to zero */ |
222 | rxIdx = 0; | |
223 | txIdx = 0; | |
224 | ||
225 | /* Clear out (for the most part) the other registers */ | |
226 | init_registers(regs); | |
227 | ||
228 | /* Ready the device for tx/rx */ | |
97d80fc3 | 229 | startup_tsec(dev); |
42d1f039 | 230 | |
97d80fc3 WD |
231 | /* If there's no link, fail */ |
232 | return priv->link; | |
42d1f039 WD |
233 | |
234 | } | |
235 | ||
236 | ||
97d80fc3 WD |
237 | /* Write value to the device's PHY through the registers |
238 | * specified in priv, modifying the register specified in regnum. | |
239 | * It will wait for the write to be done (or for a timeout to | |
240 | * expire) before exiting | |
241 | */ | |
242 | void write_phy_reg(struct tsec_private *priv, uint regnum, uint value) | |
243 | { | |
244 | volatile tsec_t *regbase = priv->phyregs; | |
245 | uint phyid = priv->phyaddr; | |
246 | int timeout=1000000; | |
247 | ||
248 | regbase->miimadd = (phyid << 8) | regnum; | |
249 | regbase->miimcon = value; | |
f046ccd1 | 250 | asm("sync"); |
97d80fc3 WD |
251 | |
252 | timeout=1000000; | |
253 | while((regbase->miimind & MIIMIND_BUSY) && timeout--); | |
254 | } | |
255 | ||
256 | ||
257 | /* Reads register regnum on the device's PHY through the | |
9d46ea4a | 258 | * registers specified in priv. It lowers and raises the read |
97d80fc3 WD |
259 | * command, and waits for the data to become valid (miimind |
260 | * notvalid bit cleared), and the bus to cease activity (miimind | |
261 | * busy bit cleared), and then returns the value | |
262 | */ | |
263 | uint read_phy_reg(struct tsec_private *priv, uint regnum) | |
42d1f039 WD |
264 | { |
265 | uint value; | |
97d80fc3 WD |
266 | volatile tsec_t *regbase = priv->phyregs; |
267 | uint phyid = priv->phyaddr; | |
42d1f039 | 268 | |
97d80fc3 WD |
269 | /* Put the address of the phy, and the register |
270 | * number into MIIMADD */ | |
271 | regbase->miimadd = (phyid << 8) | regnum; | |
42d1f039 WD |
272 | |
273 | /* Clear the command register, and wait */ | |
274 | regbase->miimcom = 0; | |
f046ccd1 | 275 | asm("sync"); |
42d1f039 WD |
276 | |
277 | /* Initiate a read command, and wait */ | |
278 | regbase->miimcom = MIIM_READ_COMMAND; | |
f046ccd1 | 279 | asm("sync"); |
42d1f039 WD |
280 | |
281 | /* Wait for the the indication that the read is done */ | |
282 | while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))); | |
283 | ||
284 | /* Grab the value read from the PHY */ | |
285 | value = regbase->miimstat; | |
286 | ||
287 | return value; | |
288 | } | |
289 | ||
97d80fc3 WD |
290 | |
291 | /* Discover which PHY is attached to the device, and configure it | |
292 | * properly. If the PHY is not recognized, then return 0 | |
293 | * (failure). Otherwise, return 1 | |
294 | */ | |
295 | static int init_phy(struct eth_device *dev) | |
42d1f039 | 296 | { |
97d80fc3 WD |
297 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
298 | struct phy_info *curphy; | |
42d1f039 WD |
299 | |
300 | /* Assign a Physical address to the TBI */ | |
3c2b3d45 | 301 | |
3dd7f0f0 WD |
302 | { |
303 | volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR); | |
304 | regs->tbipa = TBIPA_VALUE; | |
305 | regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE); | |
306 | regs->tbipa = TBIPA_VALUE; | |
f046ccd1 | 307 | asm("sync"); |
3dd7f0f0 WD |
308 | } |
309 | ||
310 | /* Reset MII (due to new addresses) */ | |
311 | priv->phyregs->miimcfg = MIIMCFG_RESET; | |
f046ccd1 | 312 | asm("sync"); |
3dd7f0f0 | 313 | priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE; |
f046ccd1 | 314 | asm("sync"); |
3dd7f0f0 | 315 | while(priv->phyregs->miimind & MIIMIND_BUSY); |
42d1f039 | 316 | |
97d80fc3 WD |
317 | if(0 == relocated) |
318 | relocate_cmds(); | |
42d1f039 | 319 | |
97d80fc3 WD |
320 | /* Get the cmd structure corresponding to the attached |
321 | * PHY */ | |
322 | curphy = get_phy_info(dev); | |
42d1f039 | 323 | |
97d80fc3 WD |
324 | if(NULL == curphy) { |
325 | printf("%s: No PHY found\n", dev->name); | |
42d1f039 | 326 | |
97d80fc3 WD |
327 | return 0; |
328 | } | |
42d1f039 | 329 | |
97d80fc3 | 330 | priv->phyinfo = curphy; |
42d1f039 | 331 | |
97d80fc3 | 332 | phy_run_commands(priv, priv->phyinfo->config); |
42d1f039 | 333 | |
97d80fc3 WD |
334 | return 1; |
335 | } | |
42d1f039 | 336 | |
42d1f039 | 337 | |
97d80fc3 WD |
338 | /* Returns which value to write to the control register. */ |
339 | /* For 10/100, the value is slightly different */ | |
340 | uint mii_cr_init(uint mii_reg, struct tsec_private *priv) | |
341 | { | |
d9b94f28 | 342 | if(priv->flags & TSEC_GIGABIT) |
97d80fc3 | 343 | return MIIM_CONTROL_INIT; |
42d1f039 | 344 | else |
97d80fc3 WD |
345 | return MIIM_CR_INIT; |
346 | } | |
42d1f039 | 347 | |
42d1f039 | 348 | |
97d80fc3 WD |
349 | /* Parse the status register for link, and then do |
350 | * auto-negotiation */ | |
351 | uint mii_parse_sr(uint mii_reg, struct tsec_private *priv) | |
352 | { | |
5810dc3a SR |
353 | /* |
354 | * Wait if PHY is capable of autonegotiation and autonegotiation is not complete | |
355 | */ | |
356 | mii_reg = read_phy_reg(priv, MIIM_STATUS); | |
357 | if ((mii_reg & PHY_BMSR_AUTN_ABLE) && !(mii_reg & PHY_BMSR_AUTN_COMP)) { | |
358 | int i = 0; | |
359 | ||
360 | puts ("Waiting for PHY auto negotiation to complete"); | |
361 | while (!((mii_reg & PHY_BMSR_AUTN_COMP) && (mii_reg & MIIM_STATUS_LINK))) { | |
362 | /* | |
363 | * Timeout reached ? | |
364 | */ | |
365 | if (i > PHY_AUTONEGOTIATE_TIMEOUT) { | |
366 | puts (" TIMEOUT !\n"); | |
367 | priv->link = 0; | |
368 | break; | |
369 | } | |
42d1f039 | 370 | |
5810dc3a SR |
371 | if ((i++ % 1000) == 0) { |
372 | putc ('.'); | |
373 | } | |
374 | udelay (1000); /* 1 ms */ | |
97d80fc3 | 375 | mii_reg = read_phy_reg(priv, MIIM_STATUS); |
5810dc3a SR |
376 | } |
377 | puts (" done\n"); | |
378 | priv->link = 1; | |
379 | udelay (500000); /* another 500 ms (results in faster booting) */ | |
380 | } else { | |
381 | priv->link = 1; | |
42d1f039 WD |
382 | } |
383 | ||
97d80fc3 WD |
384 | return 0; |
385 | } | |
42d1f039 | 386 | |
42d1f039 | 387 | |
97d80fc3 WD |
388 | /* Parse the 88E1011's status register for speed and duplex |
389 | * information */ | |
390 | uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private *priv) | |
391 | { | |
392 | uint speed; | |
393 | ||
5810dc3a SR |
394 | mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); |
395 | ||
396 | if (!((mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE) && | |
397 | (mii_reg & MIIM_88E1011_PHYSTAT_LINK))) { | |
398 | int i = 0; | |
399 | ||
400 | puts ("Waiting for PHY realtime link"); | |
401 | while (!((mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE) && | |
402 | (mii_reg & MIIM_88E1011_PHYSTAT_LINK))) { | |
403 | /* | |
404 | * Timeout reached ? | |
405 | */ | |
406 | if (i > PHY_AUTONEGOTIATE_TIMEOUT) { | |
407 | puts (" TIMEOUT !\n"); | |
408 | priv->link = 0; | |
409 | break; | |
410 | } | |
411 | ||
412 | if ((i++ % 1000) == 0) { | |
413 | putc ('.'); | |
414 | } | |
415 | udelay (1000); /* 1 ms */ | |
416 | mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); | |
417 | } | |
418 | puts (" done\n"); | |
419 | udelay (500000); /* another 500 ms (results in faster booting) */ | |
420 | } | |
421 | ||
97d80fc3 WD |
422 | if(mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) |
423 | priv->duplexity = 1; | |
424 | else | |
425 | priv->duplexity = 0; | |
426 | ||
427 | speed = (mii_reg &MIIM_88E1011_PHYSTAT_SPEED); | |
428 | ||
429 | switch(speed) { | |
430 | case MIIM_88E1011_PHYSTAT_GBIT: | |
431 | priv->speed = 1000; | |
432 | break; | |
433 | case MIIM_88E1011_PHYSTAT_100: | |
434 | priv->speed = 100; | |
435 | break; | |
436 | default: | |
437 | priv->speed = 10; | |
42d1f039 WD |
438 | } |
439 | ||
97d80fc3 WD |
440 | return 0; |
441 | } | |
42d1f039 | 442 | |
97d80fc3 WD |
443 | |
444 | /* Parse the cis8201's status register for speed and duplex | |
445 | * information */ | |
446 | uint mii_parse_cis8201(uint mii_reg, struct tsec_private *priv) | |
447 | { | |
448 | uint speed; | |
449 | ||
450 | if(mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX) | |
451 | priv->duplexity = 1; | |
452 | else | |
453 | priv->duplexity = 0; | |
454 | ||
455 | speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED; | |
456 | switch(speed) { | |
457 | case MIIM_CIS8201_AUXCONSTAT_GBIT: | |
458 | priv->speed = 1000; | |
459 | break; | |
460 | case MIIM_CIS8201_AUXCONSTAT_100: | |
461 | priv->speed = 100; | |
462 | break; | |
463 | default: | |
464 | priv->speed = 10; | |
465 | break; | |
42d1f039 WD |
466 | } |
467 | ||
97d80fc3 WD |
468 | return 0; |
469 | } | |
42d1f039 | 470 | |
97d80fc3 WD |
471 | |
472 | /* Parse the DM9161's status register for speed and duplex | |
473 | * information */ | |
474 | uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private *priv) | |
475 | { | |
476 | if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) | |
477 | priv->speed = 100; | |
478 | else | |
479 | priv->speed = 10; | |
480 | ||
481 | if(mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) | |
482 | priv->duplexity = 1; | |
483 | else | |
484 | priv->duplexity = 0; | |
485 | ||
486 | return 0; | |
487 | } | |
488 | ||
489 | ||
490 | /* Hack to write all 4 PHYs with the LED values */ | |
491 | uint mii_cis8204_fixled(uint mii_reg, struct tsec_private *priv) | |
492 | { | |
493 | uint phyid; | |
494 | volatile tsec_t *regbase = priv->phyregs; | |
495 | int timeout=1000000; | |
496 | ||
497 | for(phyid=0;phyid<4;phyid++) { | |
498 | regbase->miimadd = (phyid << 8) | mii_reg; | |
499 | regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT; | |
f046ccd1 | 500 | asm("sync"); |
97d80fc3 WD |
501 | |
502 | timeout=1000000; | |
503 | while((regbase->miimind & MIIMIND_BUSY) && timeout--); | |
42d1f039 | 504 | } |
42d1f039 | 505 | |
97d80fc3 | 506 | return MIIM_CIS8204_SLEDCON_INIT; |
42d1f039 WD |
507 | } |
508 | ||
d9b94f28 JL |
509 | uint mii_cis8204_setmode(uint mii_reg, struct tsec_private *priv) |
510 | { | |
511 | if (priv->flags & TSEC_REDUCED) | |
512 | return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII; | |
513 | else | |
514 | return MIIM_CIS8204_EPHYCON_INIT; | |
515 | } | |
42d1f039 | 516 | |
97d80fc3 WD |
517 | /* Initialized required registers to appropriate values, zeroing |
518 | * those we don't care about (unless zero is bad, in which case, | |
519 | * choose a more appropriate value) */ | |
520 | static void init_registers(volatile tsec_t *regs) | |
42d1f039 WD |
521 | { |
522 | /* Clear IEVENT */ | |
523 | regs->ievent = IEVENT_INIT_CLEAR; | |
524 | ||
525 | regs->imask = IMASK_INIT_CLEAR; | |
526 | ||
527 | regs->hash.iaddr0 = 0; | |
528 | regs->hash.iaddr1 = 0; | |
529 | regs->hash.iaddr2 = 0; | |
530 | regs->hash.iaddr3 = 0; | |
531 | regs->hash.iaddr4 = 0; | |
532 | regs->hash.iaddr5 = 0; | |
533 | regs->hash.iaddr6 = 0; | |
534 | regs->hash.iaddr7 = 0; | |
535 | ||
536 | regs->hash.gaddr0 = 0; | |
537 | regs->hash.gaddr1 = 0; | |
538 | regs->hash.gaddr2 = 0; | |
539 | regs->hash.gaddr3 = 0; | |
540 | regs->hash.gaddr4 = 0; | |
541 | regs->hash.gaddr5 = 0; | |
542 | regs->hash.gaddr6 = 0; | |
543 | regs->hash.gaddr7 = 0; | |
544 | ||
545 | regs->rctrl = 0x00000000; | |
546 | ||
547 | /* Init RMON mib registers */ | |
548 | memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t)); | |
549 | ||
550 | regs->rmon.cam1 = 0xffffffff; | |
551 | regs->rmon.cam2 = 0xffffffff; | |
552 | ||
553 | regs->mrblr = MRBLR_INIT_SETTINGS; | |
554 | ||
555 | regs->minflr = MINFLR_INIT_SETTINGS; | |
556 | ||
557 | regs->attr = ATTR_INIT_SETTINGS; | |
558 | regs->attreli = ATTRELI_INIT_SETTINGS; | |
559 | ||
560 | } | |
561 | ||
97d80fc3 WD |
562 | |
563 | /* Configure maccfg2 based on negotiated speed and duplex | |
564 | * reported by PHY handling code */ | |
565 | static void adjust_link(struct eth_device *dev) | |
566 | { | |
567 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
568 | volatile tsec_t *regs = priv->regs; | |
569 | ||
570 | if(priv->link) { | |
571 | if(priv->duplexity != 0) | |
572 | regs->maccfg2 |= MACCFG2_FULL_DUPLEX; | |
573 | else | |
574 | regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX); | |
575 | ||
576 | switch(priv->speed) { | |
577 | case 1000: | |
578 | regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | |
579 | | MACCFG2_GMII); | |
580 | break; | |
581 | case 100: | |
582 | case 10: | |
583 | regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF)) | |
584 | | MACCFG2_MII); | |
d9b94f28 | 585 | |
de1d0a69 JL |
586 | /* If We're in reduced mode, we need |
587 | * to say whether we're 10 or 100 MB. | |
588 | */ | |
589 | if ((priv->speed == 100) | |
590 | && (priv->flags & TSEC_REDUCED)) | |
d9b94f28 JL |
591 | regs->ecntrl |= ECNTRL_R100; |
592 | else | |
593 | regs->ecntrl &= ~(ECNTRL_R100); | |
97d80fc3 WD |
594 | break; |
595 | default: | |
596 | printf("%s: Speed was bad\n", dev->name); | |
597 | break; | |
598 | } | |
599 | ||
600 | printf("Speed: %d, %s duplex\n", priv->speed, | |
601 | (priv->duplexity) ? "full" : "half"); | |
602 | ||
603 | } else { | |
604 | printf("%s: No link.\n", dev->name); | |
605 | } | |
606 | } | |
607 | ||
608 | ||
609 | /* Set up the buffers and their descriptors, and bring up the | |
610 | * interface */ | |
611 | static void startup_tsec(struct eth_device *dev) | |
42d1f039 WD |
612 | { |
613 | int i; | |
97d80fc3 WD |
614 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
615 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
616 | |
617 | /* Point to the buffer descriptors */ | |
618 | regs->tbase = (unsigned int)(&rtx.txbd[txIdx]); | |
619 | regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]); | |
620 | ||
621 | /* Initialize the Rx Buffer descriptors */ | |
622 | for (i = 0; i < PKTBUFSRX; i++) { | |
623 | rtx.rxbd[i].status = RXBD_EMPTY; | |
624 | rtx.rxbd[i].length = 0; | |
625 | rtx.rxbd[i].bufPtr = (uint)NetRxPackets[i]; | |
626 | } | |
627 | rtx.rxbd[PKTBUFSRX -1].status |= RXBD_WRAP; | |
628 | ||
629 | /* Initialize the TX Buffer Descriptors */ | |
630 | for(i=0; i<TX_BUF_CNT; i++) { | |
631 | rtx.txbd[i].status = 0; | |
632 | rtx.txbd[i].length = 0; | |
633 | rtx.txbd[i].bufPtr = 0; | |
634 | } | |
635 | rtx.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP; | |
636 | ||
97d80fc3 WD |
637 | /* Start up the PHY */ |
638 | phy_run_commands(priv, priv->phyinfo->startup); | |
639 | adjust_link(dev); | |
640 | ||
42d1f039 WD |
641 | /* Enable Transmit and Receive */ |
642 | regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN); | |
643 | ||
644 | /* Tell the DMA it is clear to go */ | |
645 | regs->dmactrl |= DMACTRL_INIT_SETTINGS; | |
646 | regs->tstat = TSTAT_CLEAR_THALT; | |
647 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); | |
648 | } | |
649 | ||
9d46ea4a | 650 | /* This returns the status bits of the device. The return value |
42d1f039 | 651 | * is never checked, and this is what the 8260 driver did, so we |
9d46ea4a | 652 | * do the same. Presumably, this would be zero if there were no |
42d1f039 WD |
653 | * errors */ |
654 | static int tsec_send(struct eth_device* dev, volatile void *packet, int length) | |
655 | { | |
656 | int i; | |
657 | int result = 0; | |
97d80fc3 WD |
658 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
659 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
660 | |
661 | /* Find an empty buffer descriptor */ | |
662 | for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { | |
663 | if (i >= TOUT_LOOP) { | |
8b07a110 | 664 | debug ("%s: tsec: tx buffers full\n", dev->name); |
42d1f039 WD |
665 | return result; |
666 | } | |
667 | } | |
668 | ||
669 | rtx.txbd[txIdx].bufPtr = (uint)packet; | |
670 | rtx.txbd[txIdx].length = length; | |
671 | rtx.txbd[txIdx].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT); | |
672 | ||
673 | /* Tell the DMA to go */ | |
674 | regs->tstat = TSTAT_CLEAR_THALT; | |
675 | ||
676 | /* Wait for buffer to be transmitted */ | |
677 | for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) { | |
678 | if (i >= TOUT_LOOP) { | |
8b07a110 | 679 | debug ("%s: tsec: tx error\n", dev->name); |
42d1f039 WD |
680 | return result; |
681 | } | |
682 | } | |
683 | ||
684 | txIdx = (txIdx + 1) % TX_BUF_CNT; | |
685 | result = rtx.txbd[txIdx].status & TXBD_STATS; | |
686 | ||
687 | return result; | |
688 | } | |
689 | ||
690 | static int tsec_recv(struct eth_device* dev) | |
691 | { | |
692 | int length; | |
97d80fc3 WD |
693 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
694 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
695 | |
696 | while(!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) { | |
697 | ||
698 | length = rtx.rxbd[rxIdx].length; | |
699 | ||
700 | /* Send the packet up if there were no errors */ | |
701 | if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) { | |
702 | NetReceive(NetRxPackets[rxIdx], length - 4); | |
97d80fc3 WD |
703 | } else { |
704 | printf("Got error %x\n", | |
705 | (rtx.rxbd[rxIdx].status & RXBD_STATS)); | |
42d1f039 WD |
706 | } |
707 | ||
708 | rtx.rxbd[rxIdx].length = 0; | |
709 | ||
710 | /* Set the wrap bit if this is the last element in the list */ | |
711 | rtx.rxbd[rxIdx].status = RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); | |
712 | ||
713 | rxIdx = (rxIdx + 1) % PKTBUFSRX; | |
714 | } | |
715 | ||
716 | if(regs->ievent&IEVENT_BSY) { | |
717 | regs->ievent = IEVENT_BSY; | |
718 | regs->rstat = RSTAT_CLEAR_RHALT; | |
719 | } | |
720 | ||
721 | return -1; | |
722 | ||
723 | } | |
724 | ||
725 | ||
97d80fc3 | 726 | /* Stop the interface */ |
42d1f039 WD |
727 | static void tsec_halt(struct eth_device* dev) |
728 | { | |
97d80fc3 WD |
729 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
730 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
731 | |
732 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); | |
733 | regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS); | |
734 | ||
735 | while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))); | |
736 | ||
737 | regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN); | |
738 | ||
97d80fc3 WD |
739 | /* Shut down the PHY, as needed */ |
740 | phy_run_commands(priv, priv->phyinfo->shutdown); | |
741 | } | |
742 | ||
743 | ||
744 | struct phy_info phy_info_M88E1011S = { | |
745 | 0x01410c6, | |
746 | "Marvell 88E1011S", | |
747 | 4, | |
748 | (struct phy_cmd[]) { /* config */ | |
749 | /* Reset and configure the PHY */ | |
750 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
751 | {0x1d, 0x1f, NULL}, | |
752 | {0x1e, 0x200c, NULL}, | |
753 | {0x1d, 0x5, NULL}, | |
754 | {0x1e, 0x0, NULL}, | |
755 | {0x1e, 0x100, NULL}, | |
756 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
757 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
758 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
759 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
760 | {miim_end,} | |
761 | }, | |
762 | (struct phy_cmd[]) { /* startup */ | |
763 | /* Status is read once to clear old link state */ | |
764 | {MIIM_STATUS, miim_read, NULL}, | |
765 | /* Auto-negotiate */ | |
766 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
767 | /* Read the status */ | |
768 | {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, | |
769 | {miim_end,} | |
770 | }, | |
771 | (struct phy_cmd[]) { /* shutdown */ | |
772 | {miim_end,} | |
773 | }, | |
774 | }; | |
775 | ||
9d46ea4a WD |
776 | struct phy_info phy_info_M88E1111S = { |
777 | 0x01410cc, | |
778 | "Marvell 88E1111S", | |
779 | 4, | |
780 | (struct phy_cmd[]) { /* config */ | |
781 | /* Reset and configure the PHY */ | |
782 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
783 | {0x1d, 0x1f, NULL}, | |
784 | {0x1e, 0x200c, NULL}, | |
785 | {0x1d, 0x5, NULL}, | |
786 | {0x1e, 0x0, NULL}, | |
787 | {0x1e, 0x100, NULL}, | |
788 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
789 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
790 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
791 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
792 | {miim_end,} | |
793 | }, | |
794 | (struct phy_cmd[]) { /* startup */ | |
795 | /* Status is read once to clear old link state */ | |
796 | {MIIM_STATUS, miim_read, NULL}, | |
797 | /* Auto-negotiate */ | |
798 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
799 | /* Read the status */ | |
800 | {MIIM_88E1011_PHY_STATUS, miim_read, &mii_parse_88E1011_psr}, | |
801 | {miim_end,} | |
802 | }, | |
803 | (struct phy_cmd[]) { /* shutdown */ | |
804 | {miim_end,} | |
805 | }, | |
806 | }; | |
807 | ||
97d80fc3 WD |
808 | struct phy_info phy_info_cis8204 = { |
809 | 0x3f11, | |
810 | "Cicada Cis8204", | |
811 | 6, | |
812 | (struct phy_cmd[]) { /* config */ | |
813 | /* Override PHY config settings */ | |
814 | {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
815 | /* Configure some basic stuff */ | |
816 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
817 | {MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT, &mii_cis8204_fixled}, | |
d9b94f28 | 818 | {MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT, &mii_cis8204_setmode}, |
97d80fc3 WD |
819 | {miim_end,} |
820 | }, | |
821 | (struct phy_cmd[]) { /* startup */ | |
822 | /* Read the Status (2x to make sure link is right) */ | |
823 | {MIIM_STATUS, miim_read, NULL}, | |
824 | /* Auto-negotiate */ | |
825 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
826 | /* Read the status */ | |
827 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, | |
828 | {miim_end,} | |
829 | }, | |
830 | (struct phy_cmd[]) { /* shutdown */ | |
831 | {miim_end,} | |
832 | }, | |
833 | }; | |
834 | ||
835 | /* Cicada 8201 */ | |
836 | struct phy_info phy_info_cis8201 = { | |
837 | 0xfc41, | |
838 | "CIS8201", | |
839 | 4, | |
840 | (struct phy_cmd[]) { /* config */ | |
841 | /* Override PHY config settings */ | |
842 | {MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
843 | /* Set up the interface mode */ | |
844 | {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL}, | |
845 | /* Configure some basic stuff */ | |
846 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
847 | {miim_end,} | |
848 | }, | |
849 | (struct phy_cmd[]) { /* startup */ | |
850 | /* Read the Status (2x to make sure link is right) */ | |
851 | {MIIM_STATUS, miim_read, NULL}, | |
852 | /* Auto-negotiate */ | |
853 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
854 | /* Read the status */ | |
855 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, &mii_parse_cis8201}, | |
856 | {miim_end,} | |
857 | }, | |
858 | (struct phy_cmd[]) { /* shutdown */ | |
859 | {miim_end,} | |
860 | }, | |
861 | }; | |
862 | ||
863 | ||
864 | struct phy_info phy_info_dm9161 = { | |
865 | 0x0181b88, | |
866 | "Davicom DM9161E", | |
867 | 4, | |
868 | (struct phy_cmd[]) { /* config */ | |
869 | {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL}, | |
870 | /* Do not bypass the scrambler/descrambler */ | |
871 | {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL}, | |
872 | /* Clear 10BTCSR to default */ | |
873 | {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL}, | |
874 | /* Configure some basic stuff */ | |
875 | {MIIM_CONTROL, MIIM_CR_INIT, NULL}, | |
876 | /* Restart Auto Negotiation */ | |
877 | {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL}, | |
878 | {miim_end,} | |
879 | }, | |
880 | (struct phy_cmd[]) { /* startup */ | |
881 | /* Status is read once to clear old link state */ | |
882 | {MIIM_STATUS, miim_read, NULL}, | |
883 | /* Auto-negotiate */ | |
884 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
885 | /* Read the status */ | |
886 | {MIIM_DM9161_SCSR, miim_read, &mii_parse_dm9161_scsr}, | |
887 | {miim_end,} | |
888 | }, | |
889 | (struct phy_cmd[]) { /* shutdown */ | |
890 | {miim_end,} | |
891 | }, | |
892 | }; | |
893 | ||
3dd7f0f0 WD |
894 | uint mii_parse_lxt971_sr2(uint mii_reg, struct tsec_private *priv) |
895 | { | |
3c2b3d45 WD |
896 | unsigned int speed; |
897 | if (priv->link) { | |
898 | speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK; | |
899 | ||
900 | switch (speed) { | |
901 | case MIIM_LXT971_SR2_10HDX: | |
902 | priv->speed = 10; | |
903 | priv->duplexity = 0; | |
904 | break; | |
905 | case MIIM_LXT971_SR2_10FDX: | |
906 | priv->speed = 10; | |
907 | priv->duplexity = 1; | |
908 | break; | |
909 | case MIIM_LXT971_SR2_100HDX: | |
910 | priv->speed = 100; | |
911 | priv->duplexity = 0; | |
912 | default: | |
913 | priv->speed = 100; | |
914 | priv->duplexity = 1; | |
915 | break; | |
916 | } | |
917 | } else { | |
918 | priv->speed = 0; | |
919 | priv->duplexity = 0; | |
920 | } | |
921 | ||
922 | return 0; | |
3dd7f0f0 WD |
923 | } |
924 | ||
9d46ea4a WD |
925 | static struct phy_info phy_info_lxt971 = { |
926 | 0x0001378e, | |
927 | "LXT971", | |
928 | 4, | |
929 | (struct phy_cmd []) { /* config */ | |
3dd7f0f0 | 930 | { MIIM_CR, MIIM_CR_INIT, mii_cr_init }, /* autonegotiate */ |
9d46ea4a WD |
931 | { miim_end, } |
932 | }, | |
933 | (struct phy_cmd []) { /* startup - enable interrupts */ | |
934 | /* { 0x12, 0x00f2, NULL }, */ | |
9d46ea4a | 935 | { MIIM_STATUS, miim_read, NULL }, |
3dd7f0f0 WD |
936 | { MIIM_STATUS, miim_read, &mii_parse_sr }, |
937 | { MIIM_LXT971_SR2, miim_read, &mii_parse_lxt971_sr2 }, | |
9d46ea4a WD |
938 | { miim_end, } |
939 | }, | |
940 | (struct phy_cmd []) { /* shutdown - disable interrupts */ | |
941 | { miim_end, } | |
942 | }, | |
943 | }; | |
944 | ||
be5048f1 WD |
945 | /* Parse the DP83865's link and auto-neg status register for speed and duplex |
946 | * information */ | |
947 | uint mii_parse_dp83865_lanr(uint mii_reg, struct tsec_private *priv) | |
948 | { | |
949 | switch (mii_reg & MIIM_DP83865_SPD_MASK) { | |
950 | ||
951 | case MIIM_DP83865_SPD_1000: | |
952 | priv->speed = 1000; | |
953 | break; | |
954 | ||
955 | case MIIM_DP83865_SPD_100: | |
956 | priv->speed = 100; | |
957 | break; | |
958 | ||
959 | default: | |
960 | priv->speed = 10; | |
961 | break; | |
962 | ||
963 | } | |
964 | ||
965 | if (mii_reg & MIIM_DP83865_DPX_FULL) | |
966 | priv->duplexity = 1; | |
967 | else | |
968 | priv->duplexity = 0; | |
969 | ||
970 | return 0; | |
971 | } | |
972 | ||
973 | struct phy_info phy_info_dp83865 = { | |
974 | 0x20005c7, | |
975 | "NatSemi DP83865", | |
976 | 4, | |
977 | (struct phy_cmd[]) { /* config */ | |
978 | {MIIM_CONTROL, MIIM_DP83865_CR_INIT, NULL}, | |
979 | {miim_end,} | |
980 | }, | |
981 | (struct phy_cmd[]) { /* startup */ | |
982 | /* Status is read once to clear old link state */ | |
983 | {MIIM_STATUS, miim_read, NULL}, | |
984 | /* Auto-negotiate */ | |
985 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
986 | /* Read the link and auto-neg status */ | |
987 | {MIIM_DP83865_LANR, miim_read, &mii_parse_dp83865_lanr}, | |
988 | {miim_end,} | |
989 | }, | |
990 | (struct phy_cmd[]) { /* shutdown */ | |
991 | {miim_end,} | |
992 | }, | |
993 | }; | |
994 | ||
97d80fc3 WD |
995 | struct phy_info *phy_info[] = { |
996 | #if 0 | |
997 | &phy_info_cis8201, | |
998 | #endif | |
999 | &phy_info_cis8204, | |
1000 | &phy_info_M88E1011S, | |
9d46ea4a | 1001 | &phy_info_M88E1111S, |
97d80fc3 | 1002 | &phy_info_dm9161, |
9d46ea4a | 1003 | &phy_info_lxt971, |
be5048f1 | 1004 | &phy_info_dp83865, |
97d80fc3 WD |
1005 | NULL |
1006 | }; | |
1007 | ||
1008 | ||
1009 | /* Grab the identifier of the device's PHY, and search through | |
9d46ea4a | 1010 | * all of the known PHYs to see if one matches. If so, return |
97d80fc3 WD |
1011 | * it, if not, return NULL */ |
1012 | struct phy_info * get_phy_info(struct eth_device *dev) | |
1013 | { | |
1014 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
1015 | uint phy_reg, phy_ID; | |
1016 | int i; | |
1017 | struct phy_info *theInfo = NULL; | |
1018 | ||
1019 | /* Grab the bits from PHYIR1, and put them in the upper half */ | |
1020 | phy_reg = read_phy_reg(priv, MIIM_PHYIR1); | |
1021 | phy_ID = (phy_reg & 0xffff) << 16; | |
1022 | ||
1023 | /* Grab the bits from PHYIR2, and put them in the lower half */ | |
1024 | phy_reg = read_phy_reg(priv, MIIM_PHYIR2); | |
1025 | phy_ID |= (phy_reg & 0xffff); | |
1026 | ||
1027 | /* loop through all the known PHY types, and find one that */ | |
1028 | /* matches the ID we read from the PHY. */ | |
1029 | for(i=0; phy_info[i]; i++) { | |
1030 | if(phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) | |
1031 | theInfo = phy_info[i]; | |
1032 | } | |
1033 | ||
1034 | if(theInfo == NULL) | |
1035 | { | |
1036 | printf("%s: PHY id %x is not supported!\n", dev->name, phy_ID); | |
1037 | return NULL; | |
1038 | } else { | |
5810dc3a | 1039 | debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID); |
97d80fc3 WD |
1040 | } |
1041 | ||
1042 | return theInfo; | |
42d1f039 | 1043 | } |
7abf0c58 | 1044 | |
97d80fc3 WD |
1045 | |
1046 | /* Execute the given series of commands on the given device's | |
1047 | * PHY, running functions as necessary*/ | |
1048 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd) | |
1049 | { | |
1050 | int i; | |
1051 | uint result; | |
1052 | volatile tsec_t *phyregs = priv->phyregs; | |
1053 | ||
1054 | phyregs->miimcfg = MIIMCFG_RESET; | |
1055 | ||
1056 | phyregs->miimcfg = MIIMCFG_INIT_VALUE; | |
1057 | ||
1058 | while(phyregs->miimind & MIIMIND_BUSY); | |
1059 | ||
1060 | for(i=0;cmd->mii_reg != miim_end;i++) { | |
1061 | if(cmd->mii_data == miim_read) { | |
1062 | result = read_phy_reg(priv, cmd->mii_reg); | |
1063 | ||
1064 | if(cmd->funct != NULL) | |
1065 | (*(cmd->funct))(result, priv); | |
1066 | ||
1067 | } else { | |
1068 | if(cmd->funct != NULL) | |
1069 | result = (*(cmd->funct))(cmd->mii_reg, priv); | |
1070 | else | |
1071 | result = cmd->mii_data; | |
1072 | ||
1073 | write_phy_reg(priv, cmd->mii_reg, result); | |
1074 | ||
1075 | } | |
1076 | cmd++; | |
1077 | } | |
1078 | } | |
1079 | ||
1080 | ||
1081 | /* Relocate the function pointers in the phy cmd lists */ | |
1082 | static void relocate_cmds(void) | |
1083 | { | |
1084 | struct phy_cmd **cmdlistptr; | |
1085 | struct phy_cmd *cmd; | |
1086 | int i,j,k; | |
97d80fc3 WD |
1087 | |
1088 | for(i=0; phy_info[i]; i++) { | |
1089 | /* First thing's first: relocate the pointers to the | |
1090 | * PHY command structures (the structs were done) */ | |
1091 | phy_info[i] = (struct phy_info *) ((uint)phy_info[i] | |
1092 | + gd->reloc_off); | |
1093 | phy_info[i]->name += gd->reloc_off; | |
1094 | phy_info[i]->config = | |
1095 | (struct phy_cmd *)((uint)phy_info[i]->config | |
1096 | + gd->reloc_off); | |
1097 | phy_info[i]->startup = | |
1098 | (struct phy_cmd *)((uint)phy_info[i]->startup | |
1099 | + gd->reloc_off); | |
1100 | phy_info[i]->shutdown = | |
1101 | (struct phy_cmd *)((uint)phy_info[i]->shutdown | |
1102 | + gd->reloc_off); | |
1103 | ||
1104 | cmdlistptr = &phy_info[i]->config; | |
1105 | j=0; | |
1106 | for(;cmdlistptr <= &phy_info[i]->shutdown;cmdlistptr++) { | |
1107 | k=0; | |
1108 | for(cmd=*cmdlistptr;cmd->mii_reg != miim_end;cmd++) { | |
1109 | /* Only relocate non-NULL pointers */ | |
1110 | if(cmd->funct) | |
1111 | cmd->funct += gd->reloc_off; | |
1112 | ||
1113 | k++; | |
1114 | } | |
1115 | j++; | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | relocated = 1; | |
1120 | } | |
1121 | ||
1122 | ||
63ff004c MB |
1123 | #if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) \ |
1124 | && !defined(BITBANGMII) | |
97d80fc3 WD |
1125 | |
1126 | struct tsec_private * get_priv_for_phy(unsigned char phyaddr) | |
1127 | { | |
1128 | int i; | |
1129 | ||
1130 | for(i=0;i<MAXCONTROLLERS;i++) { | |
1131 | if(privlist[i]->phyaddr == phyaddr) | |
1132 | return privlist[i]; | |
1133 | } | |
1134 | ||
1135 | return NULL; | |
1136 | } | |
1137 | ||
7abf0c58 WD |
1138 | /* |
1139 | * Read a MII PHY register. | |
1140 | * | |
1141 | * Returns: | |
97d80fc3 | 1142 | * 0 on success |
7abf0c58 | 1143 | */ |
63ff004c MB |
1144 | static int tsec_miiphy_read(char *devname, unsigned char addr, |
1145 | unsigned char reg, unsigned short *value) | |
7abf0c58 | 1146 | { |
97d80fc3 WD |
1147 | unsigned short ret; |
1148 | struct tsec_private *priv = get_priv_for_phy(addr); | |
1149 | ||
1150 | if(NULL == priv) { | |
1151 | printf("Can't read PHY at address %d\n", addr); | |
1152 | return -1; | |
1153 | } | |
7abf0c58 | 1154 | |
97d80fc3 WD |
1155 | ret = (unsigned short)read_phy_reg(priv, reg); |
1156 | *value = ret; | |
7abf0c58 WD |
1157 | |
1158 | return 0; | |
1159 | } | |
1160 | ||
1161 | /* | |
1162 | * Write a MII PHY register. | |
1163 | * | |
1164 | * Returns: | |
97d80fc3 | 1165 | * 0 on success |
7abf0c58 | 1166 | */ |
63ff004c MB |
1167 | static int tsec_miiphy_write(char *devname, unsigned char addr, |
1168 | unsigned char reg, unsigned short value) | |
7abf0c58 | 1169 | { |
97d80fc3 WD |
1170 | struct tsec_private *priv = get_priv_for_phy(addr); |
1171 | ||
1172 | if(NULL == priv) { | |
1173 | printf("Can't write PHY at address %d\n", addr); | |
1174 | return -1; | |
1175 | } | |
7abf0c58 | 1176 | |
97d80fc3 | 1177 | write_phy_reg(priv, reg, value); |
7abf0c58 WD |
1178 | |
1179 | return 0; | |
1180 | } | |
97d80fc3 | 1181 | |
63ff004c MB |
1182 | #endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) |
1183 | && !defined(BITBANGMII) */ | |
97d80fc3 | 1184 | |
42d1f039 | 1185 | #endif /* CONFIG_TSEC_ENET */ |