]>
Commit | Line | Data |
---|---|---|
42d1f039 | 1 | /* |
97d80fc3 | 2 | * Freescale Three Speed Ethernet Controller driver |
42d1f039 WD |
3 | * |
4 | * This software may be used and distributed according to the | |
5 | * terms of the GNU Public License, Version 2, incorporated | |
6 | * herein by reference. | |
7 | * | |
81f481ca | 8 | * Copyright 2004, 2007 Freescale Semiconductor, Inc. |
42d1f039 | 9 | * (C) Copyright 2003, Motorola, Inc. |
42d1f039 WD |
10 | * author Andy Fleming |
11 | * | |
12 | */ | |
13 | ||
14 | #include <config.h> | |
42d1f039 WD |
15 | #include <common.h> |
16 | #include <malloc.h> | |
17 | #include <net.h> | |
18 | #include <command.h> | |
dd3d1f56 | 19 | #include <tsec.h> |
42d1f039 | 20 | |
63ff004c | 21 | #include "miiphy.h" |
42d1f039 | 22 | |
d87080b7 WD |
23 | DECLARE_GLOBAL_DATA_PTR; |
24 | ||
63ff004c | 25 | #define TX_BUF_CNT 2 |
42d1f039 | 26 | |
89875e96 JL |
27 | static uint rxIdx; /* index of the current RX buffer */ |
28 | static uint txIdx; /* index of the current TX buffer */ | |
42d1f039 WD |
29 | |
30 | typedef volatile struct rtxbd { | |
31 | txbd8_t txbd[TX_BUF_CNT]; | |
32 | rxbd8_t rxbd[PKTBUFSRX]; | |
89875e96 | 33 | } RTXBD; |
42d1f039 | 34 | |
75b9d4ae | 35 | #define MAXCONTROLLERS (8) |
97d80fc3 WD |
36 | |
37 | static int relocated = 0; | |
38 | ||
39 | static struct tsec_private *privlist[MAXCONTROLLERS]; | |
75b9d4ae | 40 | static int num_tsecs = 0; |
97d80fc3 | 41 | |
42d1f039 WD |
42 | #ifdef __GNUC__ |
43 | static RTXBD rtx __attribute__ ((aligned(8))); | |
44 | #else | |
45 | #error "rtx must be 64-bit aligned" | |
46 | #endif | |
47 | ||
89875e96 JL |
48 | static int tsec_send(struct eth_device *dev, |
49 | volatile void *packet, int length); | |
50 | static int tsec_recv(struct eth_device *dev); | |
51 | static int tsec_init(struct eth_device *dev, bd_t * bd); | |
52 | static void tsec_halt(struct eth_device *dev); | |
53 | static void init_registers(volatile tsec_t * regs); | |
97d80fc3 WD |
54 | static void startup_tsec(struct eth_device *dev); |
55 | static int init_phy(struct eth_device *dev); | |
56 | void write_phy_reg(struct tsec_private *priv, uint regnum, uint value); | |
57 | uint read_phy_reg(struct tsec_private *priv, uint regnum); | |
89875e96 | 58 | struct phy_info *get_phy_info(struct eth_device *dev); |
97d80fc3 WD |
59 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd); |
60 | static void adjust_link(struct eth_device *dev); | |
61 | static void relocate_cmds(void); | |
409ecdc0 WD |
62 | #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ |
63 | && !defined(BITBANGMII) | |
63ff004c | 64 | static int tsec_miiphy_write(char *devname, unsigned char addr, |
89875e96 | 65 | unsigned char reg, unsigned short value); |
63ff004c | 66 | static int tsec_miiphy_read(char *devname, unsigned char addr, |
89875e96 | 67 | unsigned char reg, unsigned short *value); |
409ecdc0 | 68 | #endif |
53a5c424 DU |
69 | #ifdef CONFIG_MCAST_TFTP |
70 | static int tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set); | |
71 | #endif | |
97d80fc3 | 72 | |
75b9d4ae AF |
73 | /* Default initializations for TSEC controllers. */ |
74 | ||
75 | static struct tsec_info_struct tsec_info[] = { | |
76 | #ifdef CONFIG_TSEC1 | |
77 | STD_TSEC_INFO(1), /* TSEC1 */ | |
78 | #endif | |
79 | #ifdef CONFIG_TSEC2 | |
80 | STD_TSEC_INFO(2), /* TSEC2 */ | |
81 | #endif | |
82 | #ifdef CONFIG_MPC85XX_FEC | |
83 | { | |
84 | .regs = (tsec_t *)(TSEC_BASE_ADDR + 0x2000), | |
85 | .miiregs = (tsec_t *)(TSEC_BASE_ADDR), | |
86 | .devname = CONFIG_MPC85XX_FEC_NAME, | |
87 | .phyaddr = FEC_PHY_ADDR, | |
88 | .flags = FEC_FLAGS | |
89 | }, /* FEC */ | |
90 | #endif | |
91 | #ifdef CONFIG_TSEC3 | |
92 | STD_TSEC_INFO(3), /* TSEC3 */ | |
93 | #endif | |
94 | #ifdef CONFIG_TSEC4 | |
95 | STD_TSEC_INFO(4), /* TSEC4 */ | |
96 | #endif | |
97 | }; | |
98 | ||
99 | int tsec_eth_init(bd_t *bis, struct tsec_info_struct *tsecs, int num) | |
100 | { | |
101 | int i; | |
102 | ||
103 | for (i = 0; i < num; i++) | |
104 | tsec_initialize(bis, &tsecs[i]); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | int tsec_standard_init(bd_t *bis) | |
110 | { | |
111 | return tsec_eth_init(bis, tsec_info, ARRAY_SIZE(tsec_info)); | |
112 | } | |
113 | ||
97d80fc3 WD |
114 | /* Initialize device structure. Returns success if PHY |
115 | * initialization succeeded (i.e. if it recognizes the PHY) | |
116 | */ | |
75b9d4ae | 117 | int tsec_initialize(bd_t * bis, struct tsec_info_struct *tsec_info) |
42d1f039 | 118 | { |
89875e96 | 119 | struct eth_device *dev; |
42d1f039 | 120 | int i; |
97d80fc3 | 121 | struct tsec_private *priv; |
42d1f039 | 122 | |
89875e96 | 123 | dev = (struct eth_device *)malloc(sizeof *dev); |
42d1f039 | 124 | |
89875e96 | 125 | if (NULL == dev) |
42d1f039 WD |
126 | return 0; |
127 | ||
128 | memset(dev, 0, sizeof *dev); | |
129 | ||
89875e96 | 130 | priv = (struct tsec_private *)malloc(sizeof(*priv)); |
97d80fc3 | 131 | |
89875e96 | 132 | if (NULL == priv) |
97d80fc3 WD |
133 | return 0; |
134 | ||
75b9d4ae AF |
135 | privlist[num_tsecs++] = priv; |
136 | priv->regs = tsec_info->regs; | |
137 | priv->phyregs = tsec_info->miiregs; | |
97d80fc3 | 138 | |
75b9d4ae AF |
139 | priv->phyaddr = tsec_info->phyaddr; |
140 | priv->flags = tsec_info->flags; | |
97d80fc3 | 141 | |
75b9d4ae | 142 | sprintf(dev->name, tsec_info->devname); |
42d1f039 | 143 | dev->iobase = 0; |
89875e96 JL |
144 | dev->priv = priv; |
145 | dev->init = tsec_init; | |
146 | dev->halt = tsec_halt; | |
147 | dev->send = tsec_send; | |
148 | dev->recv = tsec_recv; | |
53a5c424 DU |
149 | #ifdef CONFIG_MCAST_TFTP |
150 | dev->mcast = tsec_mcast_addr; | |
151 | #endif | |
42d1f039 WD |
152 | |
153 | /* Tell u-boot to get the addr from the env */ | |
89875e96 | 154 | for (i = 0; i < 6; i++) |
42d1f039 WD |
155 | dev->enetaddr[i] = 0; |
156 | ||
157 | eth_register(dev); | |
158 | ||
97d80fc3 WD |
159 | /* Reset the MAC */ |
160 | priv->regs->maccfg1 |= MACCFG1_SOFT_RESET; | |
161 | priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET); | |
7abf0c58 | 162 | |
cb51c0bf | 163 | #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ |
63ff004c MB |
164 | && !defined(BITBANGMII) |
165 | miiphy_register(dev->name, tsec_miiphy_read, tsec_miiphy_write); | |
166 | #endif | |
167 | ||
97d80fc3 WD |
168 | /* Try to initialize PHY here, and return */ |
169 | return init_phy(dev); | |
42d1f039 WD |
170 | } |
171 | ||
42d1f039 | 172 | /* Initializes data structures and registers for the controller, |
9d46ea4a | 173 | * and brings the interface up. Returns the link status, meaning |
97d80fc3 | 174 | * that it returns success if the link is up, failure otherwise. |
89875e96 JL |
175 | * This allows u-boot to find the first active controller. |
176 | */ | |
177 | int tsec_init(struct eth_device *dev, bd_t * bd) | |
42d1f039 | 178 | { |
42d1f039 WD |
179 | uint tempval; |
180 | char tmpbuf[MAC_ADDR_LEN]; | |
181 | int i; | |
97d80fc3 WD |
182 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
183 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
184 | |
185 | /* Make sure the controller is stopped */ | |
186 | tsec_halt(dev); | |
187 | ||
97d80fc3 | 188 | /* Init MACCFG2. Defaults to GMII */ |
42d1f039 WD |
189 | regs->maccfg2 = MACCFG2_INIT_SETTINGS; |
190 | ||
191 | /* Init ECNTRL */ | |
192 | regs->ecntrl = ECNTRL_INIT_SETTINGS; | |
193 | ||
194 | /* Copy the station address into the address registers. | |
195 | * Backwards, because little endian MACS are dumb */ | |
89875e96 | 196 | for (i = 0; i < MAC_ADDR_LEN; i++) { |
97d80fc3 | 197 | tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i]; |
42d1f039 | 198 | } |
89875e96 | 199 | regs->macstnaddr1 = *((uint *) (tmpbuf)); |
42d1f039 | 200 | |
89875e96 | 201 | tempval = *((uint *) (tmpbuf + 4)); |
42d1f039 | 202 | |
77ddac94 | 203 | regs->macstnaddr2 = tempval; |
42d1f039 | 204 | |
42d1f039 WD |
205 | /* reset the indices to zero */ |
206 | rxIdx = 0; | |
207 | txIdx = 0; | |
208 | ||
209 | /* Clear out (for the most part) the other registers */ | |
210 | init_registers(regs); | |
211 | ||
212 | /* Ready the device for tx/rx */ | |
97d80fc3 | 213 | startup_tsec(dev); |
42d1f039 | 214 | |
97d80fc3 | 215 | /* If there's no link, fail */ |
422b1a01 | 216 | return (priv->link ? 0 : -1); |
42d1f039 WD |
217 | } |
218 | ||
2abe361c AF |
219 | /* Writes the given phy's reg with value, using the specified MDIO regs */ |
220 | static void tsec_local_mdio_write(volatile tsec_t *phyregs, uint addr, | |
221 | uint reg, uint value) | |
97d80fc3 | 222 | { |
89875e96 | 223 | int timeout = 1000000; |
97d80fc3 | 224 | |
2abe361c AF |
225 | phyregs->miimadd = (addr << 8) | reg; |
226 | phyregs->miimcon = value; | |
f046ccd1 | 227 | asm("sync"); |
97d80fc3 | 228 | |
89875e96 | 229 | timeout = 1000000; |
2abe361c | 230 | while ((phyregs->miimind & MIIMIND_BUSY) && timeout--) ; |
97d80fc3 WD |
231 | } |
232 | ||
2abe361c AF |
233 | |
234 | /* Provide the default behavior of writing the PHY of this ethernet device */ | |
235 | #define write_phy_reg(priv, regnum, value) tsec_local_mdio_write(priv->phyregs,priv->phyaddr,regnum,value) | |
55fe7c57 | 236 | |
97d80fc3 | 237 | /* Reads register regnum on the device's PHY through the |
2abe361c | 238 | * specified registers. It lowers and raises the read |
97d80fc3 WD |
239 | * command, and waits for the data to become valid (miimind |
240 | * notvalid bit cleared), and the bus to cease activity (miimind | |
241 | * busy bit cleared), and then returns the value | |
242 | */ | |
2abe361c | 243 | uint tsec_local_mdio_read(volatile tsec_t *phyregs, uint phyid, uint regnum) |
42d1f039 WD |
244 | { |
245 | uint value; | |
246 | ||
97d80fc3 WD |
247 | /* Put the address of the phy, and the register |
248 | * number into MIIMADD */ | |
2abe361c | 249 | phyregs->miimadd = (phyid << 8) | regnum; |
42d1f039 WD |
250 | |
251 | /* Clear the command register, and wait */ | |
2abe361c | 252 | phyregs->miimcom = 0; |
f046ccd1 | 253 | asm("sync"); |
42d1f039 WD |
254 | |
255 | /* Initiate a read command, and wait */ | |
2abe361c | 256 | phyregs->miimcom = MIIM_READ_COMMAND; |
f046ccd1 | 257 | asm("sync"); |
42d1f039 WD |
258 | |
259 | /* Wait for the the indication that the read is done */ | |
2abe361c | 260 | while ((phyregs->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY))) ; |
42d1f039 WD |
261 | |
262 | /* Grab the value read from the PHY */ | |
2abe361c | 263 | value = phyregs->miimstat; |
42d1f039 WD |
264 | |
265 | return value; | |
266 | } | |
267 | ||
55fe7c57 | 268 | /* #define to provide old read_phy_reg functionality without duplicating code */ |
2abe361c AF |
269 | #define read_phy_reg(priv,regnum) tsec_local_mdio_read(priv->phyregs,priv->phyaddr,regnum) |
270 | ||
271 | #define TBIANA_SETTINGS ( \ | |
272 | TBIANA_ASYMMETRIC_PAUSE \ | |
273 | | TBIANA_SYMMETRIC_PAUSE \ | |
274 | | TBIANA_FULL_DUPLEX \ | |
275 | ) | |
276 | ||
277 | #define TBICR_SETTINGS ( \ | |
278 | TBICR_PHY_RESET \ | |
279 | | TBICR_ANEG_ENABLE \ | |
280 | | TBICR_FULL_DUPLEX \ | |
281 | | TBICR_SPEED1_SET \ | |
282 | ) | |
283 | /* Configure the TBI for SGMII operation */ | |
284 | static void tsec_configure_serdes(struct tsec_private *priv) | |
285 | { | |
ce47eb40 PT |
286 | /* Access TBI PHY registers at given TSEC register offset as opposed to the |
287 | * register offset used for external PHY accesses */ | |
288 | tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_ANA, | |
2abe361c | 289 | TBIANA_SETTINGS); |
ce47eb40 | 290 | tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_TBICON, |
2abe361c | 291 | TBICON_CLK_SELECT); |
ce47eb40 | 292 | tsec_local_mdio_write(priv->regs, priv->regs->tbipa, TBI_CR, |
2abe361c AF |
293 | TBICR_SETTINGS); |
294 | } | |
55fe7c57 | 295 | |
97d80fc3 WD |
296 | /* Discover which PHY is attached to the device, and configure it |
297 | * properly. If the PHY is not recognized, then return 0 | |
298 | * (failure). Otherwise, return 1 | |
299 | */ | |
300 | static int init_phy(struct eth_device *dev) | |
42d1f039 | 301 | { |
97d80fc3 WD |
302 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
303 | struct phy_info *curphy; | |
2abe361c AF |
304 | volatile tsec_t *phyregs = priv->phyregs; |
305 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
306 | |
307 | /* Assign a Physical address to the TBI */ | |
6d0f6bcf JCPV |
308 | regs->tbipa = CONFIG_SYS_TBIPA_VALUE; |
309 | phyregs->tbipa = CONFIG_SYS_TBIPA_VALUE; | |
89875e96 | 310 | asm("sync"); |
3dd7f0f0 WD |
311 | |
312 | /* Reset MII (due to new addresses) */ | |
313 | priv->phyregs->miimcfg = MIIMCFG_RESET; | |
f046ccd1 | 314 | asm("sync"); |
3dd7f0f0 | 315 | priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE; |
f046ccd1 | 316 | asm("sync"); |
89875e96 | 317 | while (priv->phyregs->miimind & MIIMIND_BUSY) ; |
42d1f039 | 318 | |
89875e96 | 319 | if (0 == relocated) |
97d80fc3 | 320 | relocate_cmds(); |
42d1f039 | 321 | |
97d80fc3 WD |
322 | /* Get the cmd structure corresponding to the attached |
323 | * PHY */ | |
324 | curphy = get_phy_info(dev); | |
42d1f039 | 325 | |
4653f91c BW |
326 | if (curphy == NULL) { |
327 | priv->phyinfo = NULL; | |
97d80fc3 | 328 | printf("%s: No PHY found\n", dev->name); |
42d1f039 | 329 | |
97d80fc3 WD |
330 | return 0; |
331 | } | |
42d1f039 | 332 | |
2abe361c AF |
333 | if (regs->ecntrl & ECNTRL_SGMII_MODE) |
334 | tsec_configure_serdes(priv); | |
335 | ||
97d80fc3 | 336 | priv->phyinfo = curphy; |
42d1f039 | 337 | |
97d80fc3 | 338 | phy_run_commands(priv, priv->phyinfo->config); |
42d1f039 | 339 | |
97d80fc3 WD |
340 | return 1; |
341 | } | |
42d1f039 | 342 | |
89875e96 JL |
343 | /* |
344 | * Returns which value to write to the control register. | |
345 | * For 10/100, the value is slightly different | |
346 | */ | |
347 | uint mii_cr_init(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 | 348 | { |
89875e96 | 349 | if (priv->flags & TSEC_GIGABIT) |
97d80fc3 | 350 | return MIIM_CONTROL_INIT; |
42d1f039 | 351 | else |
97d80fc3 WD |
352 | return MIIM_CR_INIT; |
353 | } | |
42d1f039 | 354 | |
97d80fc3 | 355 | /* Parse the status register for link, and then do |
89875e96 JL |
356 | * auto-negotiation |
357 | */ | |
358 | uint mii_parse_sr(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 | 359 | { |
5810dc3a | 360 | /* |
7613afda AF |
361 | * Wait if the link is up, and autonegotiation is in progress |
362 | * (ie - we're capable and it's not done) | |
5810dc3a SR |
363 | */ |
364 | mii_reg = read_phy_reg(priv, MIIM_STATUS); | |
7613afda | 365 | if ((mii_reg & MIIM_STATUS_LINK) && (mii_reg & PHY_BMSR_AUTN_ABLE) |
89875e96 | 366 | && !(mii_reg & PHY_BMSR_AUTN_COMP)) { |
5810dc3a SR |
367 | int i = 0; |
368 | ||
89875e96 | 369 | puts("Waiting for PHY auto negotiation to complete"); |
7613afda | 370 | while (!(mii_reg & PHY_BMSR_AUTN_COMP)) { |
5810dc3a SR |
371 | /* |
372 | * Timeout reached ? | |
373 | */ | |
374 | if (i > PHY_AUTONEGOTIATE_TIMEOUT) { | |
89875e96 | 375 | puts(" TIMEOUT !\n"); |
5810dc3a | 376 | priv->link = 0; |
fcfb9a57 | 377 | return 0; |
5810dc3a | 378 | } |
42d1f039 | 379 | |
5810dc3a | 380 | if ((i++ % 1000) == 0) { |
89875e96 | 381 | putc('.'); |
5810dc3a | 382 | } |
89875e96 | 383 | udelay(1000); /* 1 ms */ |
97d80fc3 | 384 | mii_reg = read_phy_reg(priv, MIIM_STATUS); |
5810dc3a | 385 | } |
89875e96 | 386 | puts(" done\n"); |
5810dc3a | 387 | priv->link = 1; |
89875e96 | 388 | udelay(500000); /* another 500 ms (results in faster booting) */ |
5810dc3a | 389 | } else { |
7613afda AF |
390 | if (mii_reg & MIIM_STATUS_LINK) |
391 | priv->link = 1; | |
392 | else | |
393 | priv->link = 0; | |
42d1f039 WD |
394 | } |
395 | ||
97d80fc3 WD |
396 | return 0; |
397 | } | |
42d1f039 | 398 | |
af1c2b84 DU |
399 | /* Generic function which updates the speed and duplex. If |
400 | * autonegotiation is enabled, it uses the AND of the link | |
401 | * partner's advertised capabilities and our advertised | |
402 | * capabilities. If autonegotiation is disabled, we use the | |
403 | * appropriate bits in the control register. | |
404 | * | |
405 | * Stolen from Linux's mii.c and phy_device.c | |
406 | */ | |
407 | uint mii_parse_link(uint mii_reg, struct tsec_private *priv) | |
408 | { | |
409 | /* We're using autonegotiation */ | |
410 | if (mii_reg & PHY_BMSR_AUTN_ABLE) { | |
411 | uint lpa = 0; | |
412 | uint gblpa = 0; | |
413 | ||
414 | /* Check for gigabit capability */ | |
415 | if (mii_reg & PHY_BMSR_EXT) { | |
416 | /* We want a list of states supported by | |
417 | * both PHYs in the link | |
418 | */ | |
419 | gblpa = read_phy_reg(priv, PHY_1000BTSR); | |
420 | gblpa &= read_phy_reg(priv, PHY_1000BTCR) << 2; | |
421 | } | |
422 | ||
423 | /* Set the baseline so we only have to set them | |
424 | * if they're different | |
425 | */ | |
426 | priv->speed = 10; | |
427 | priv->duplexity = 0; | |
428 | ||
429 | /* Check the gigabit fields */ | |
430 | if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) { | |
431 | priv->speed = 1000; | |
432 | ||
433 | if (gblpa & PHY_1000BTSR_1000FD) | |
434 | priv->duplexity = 1; | |
435 | ||
436 | /* We're done! */ | |
437 | return 0; | |
438 | } | |
439 | ||
440 | lpa = read_phy_reg(priv, PHY_ANAR); | |
441 | lpa &= read_phy_reg(priv, PHY_ANLPAR); | |
442 | ||
443 | if (lpa & (PHY_ANLPAR_TXFD | PHY_ANLPAR_TX)) { | |
444 | priv->speed = 100; | |
445 | ||
446 | if (lpa & PHY_ANLPAR_TXFD) | |
447 | priv->duplexity = 1; | |
448 | ||
449 | } else if (lpa & PHY_ANLPAR_10FD) | |
450 | priv->duplexity = 1; | |
451 | } else { | |
452 | uint bmcr = read_phy_reg(priv, PHY_BMCR); | |
453 | ||
454 | priv->speed = 10; | |
455 | priv->duplexity = 0; | |
456 | ||
457 | if (bmcr & PHY_BMCR_DPLX) | |
458 | priv->duplexity = 1; | |
459 | ||
460 | if (bmcr & PHY_BMCR_1000_MBPS) | |
461 | priv->speed = 1000; | |
462 | else if (bmcr & PHY_BMCR_100_MBPS) | |
463 | priv->speed = 100; | |
464 | } | |
465 | ||
466 | return 0; | |
467 | } | |
468 | ||
91e25769 PG |
469 | /* |
470 | * Parse the BCM54xx status register for speed and duplex information. | |
471 | * The linux sungem_phy has this information, but in a table format. | |
472 | */ | |
473 | uint mii_parse_BCM54xx_sr(uint mii_reg, struct tsec_private *priv) | |
474 | { | |
475 | ||
476 | switch((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >> MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT){ | |
477 | ||
478 | case 1: | |
479 | printf("Enet starting in 10BT/HD\n"); | |
480 | priv->duplexity = 0; | |
481 | priv->speed = 10; | |
482 | break; | |
483 | ||
484 | case 2: | |
485 | printf("Enet starting in 10BT/FD\n"); | |
486 | priv->duplexity = 1; | |
487 | priv->speed = 10; | |
488 | break; | |
489 | ||
490 | case 3: | |
491 | printf("Enet starting in 100BT/HD\n"); | |
492 | priv->duplexity = 0; | |
493 | priv->speed = 100; | |
494 | break; | |
495 | ||
496 | case 5: | |
497 | printf("Enet starting in 100BT/FD\n"); | |
498 | priv->duplexity = 1; | |
499 | priv->speed = 100; | |
500 | break; | |
501 | ||
502 | case 6: | |
503 | printf("Enet starting in 1000BT/HD\n"); | |
504 | priv->duplexity = 0; | |
505 | priv->speed = 1000; | |
506 | break; | |
507 | ||
508 | case 7: | |
509 | printf("Enet starting in 1000BT/FD\n"); | |
510 | priv->duplexity = 1; | |
511 | priv->speed = 1000; | |
512 | break; | |
513 | ||
514 | default: | |
515 | printf("Auto-neg error, defaulting to 10BT/HD\n"); | |
516 | priv->duplexity = 0; | |
517 | priv->speed = 10; | |
518 | break; | |
519 | } | |
520 | ||
521 | return 0; | |
522 | ||
523 | } | |
97d80fc3 | 524 | /* Parse the 88E1011's status register for speed and duplex |
89875e96 JL |
525 | * information |
526 | */ | |
527 | uint mii_parse_88E1011_psr(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 WD |
528 | { |
529 | uint speed; | |
530 | ||
5810dc3a SR |
531 | mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); |
532 | ||
7613afda AF |
533 | if ((mii_reg & MIIM_88E1011_PHYSTAT_LINK) && |
534 | !(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { | |
5810dc3a SR |
535 | int i = 0; |
536 | ||
89875e96 | 537 | puts("Waiting for PHY realtime link"); |
7613afda AF |
538 | while (!(mii_reg & MIIM_88E1011_PHYSTAT_SPDDONE)) { |
539 | /* Timeout reached ? */ | |
5810dc3a | 540 | if (i > PHY_AUTONEGOTIATE_TIMEOUT) { |
89875e96 | 541 | puts(" TIMEOUT !\n"); |
5810dc3a SR |
542 | priv->link = 0; |
543 | break; | |
544 | } | |
545 | ||
546 | if ((i++ % 1000) == 0) { | |
89875e96 | 547 | putc('.'); |
5810dc3a | 548 | } |
89875e96 | 549 | udelay(1000); /* 1 ms */ |
5810dc3a SR |
550 | mii_reg = read_phy_reg(priv, MIIM_88E1011_PHY_STATUS); |
551 | } | |
89875e96 JL |
552 | puts(" done\n"); |
553 | udelay(500000); /* another 500 ms (results in faster booting) */ | |
7613afda AF |
554 | } else { |
555 | if (mii_reg & MIIM_88E1011_PHYSTAT_LINK) | |
556 | priv->link = 1; | |
557 | else | |
558 | priv->link = 0; | |
5810dc3a SR |
559 | } |
560 | ||
89875e96 | 561 | if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) |
97d80fc3 WD |
562 | priv->duplexity = 1; |
563 | else | |
564 | priv->duplexity = 0; | |
565 | ||
89875e96 | 566 | speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED); |
97d80fc3 | 567 | |
89875e96 JL |
568 | switch (speed) { |
569 | case MIIM_88E1011_PHYSTAT_GBIT: | |
570 | priv->speed = 1000; | |
571 | break; | |
572 | case MIIM_88E1011_PHYSTAT_100: | |
573 | priv->speed = 100; | |
574 | break; | |
575 | default: | |
576 | priv->speed = 10; | |
42d1f039 WD |
577 | } |
578 | ||
97d80fc3 WD |
579 | return 0; |
580 | } | |
42d1f039 | 581 | |
18ee320f DL |
582 | /* Parse the RTL8211B's status register for speed and duplex |
583 | * information | |
584 | */ | |
585 | uint mii_parse_RTL8211B_sr(uint mii_reg, struct tsec_private * priv) | |
586 | { | |
587 | uint speed; | |
588 | ||
589 | mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS); | |
c7604783 | 590 | if (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) { |
18ee320f DL |
591 | int i = 0; |
592 | ||
c7604783 AV |
593 | /* in case of timeout ->link is cleared */ |
594 | priv->link = 1; | |
18ee320f DL |
595 | puts("Waiting for PHY realtime link"); |
596 | while (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) { | |
597 | /* Timeout reached ? */ | |
598 | if (i > PHY_AUTONEGOTIATE_TIMEOUT) { | |
599 | puts(" TIMEOUT !\n"); | |
600 | priv->link = 0; | |
601 | break; | |
602 | } | |
603 | ||
604 | if ((i++ % 1000) == 0) { | |
605 | putc('.'); | |
606 | } | |
607 | udelay(1000); /* 1 ms */ | |
608 | mii_reg = read_phy_reg(priv, MIIM_RTL8211B_PHY_STATUS); | |
609 | } | |
610 | puts(" done\n"); | |
611 | udelay(500000); /* another 500 ms (results in faster booting) */ | |
612 | } else { | |
613 | if (mii_reg & MIIM_RTL8211B_PHYSTAT_LINK) | |
614 | priv->link = 1; | |
615 | else | |
616 | priv->link = 0; | |
617 | } | |
618 | ||
619 | if (mii_reg & MIIM_RTL8211B_PHYSTAT_DUPLEX) | |
620 | priv->duplexity = 1; | |
621 | else | |
622 | priv->duplexity = 0; | |
623 | ||
624 | speed = (mii_reg & MIIM_RTL8211B_PHYSTAT_SPEED); | |
625 | ||
626 | switch (speed) { | |
627 | case MIIM_RTL8211B_PHYSTAT_GBIT: | |
628 | priv->speed = 1000; | |
629 | break; | |
630 | case MIIM_RTL8211B_PHYSTAT_100: | |
631 | priv->speed = 100; | |
632 | break; | |
633 | default: | |
634 | priv->speed = 10; | |
635 | } | |
636 | ||
637 | return 0; | |
638 | } | |
639 | ||
97d80fc3 | 640 | /* Parse the cis8201's status register for speed and duplex |
89875e96 JL |
641 | * information |
642 | */ | |
643 | uint mii_parse_cis8201(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 WD |
644 | { |
645 | uint speed; | |
646 | ||
89875e96 | 647 | if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX) |
97d80fc3 WD |
648 | priv->duplexity = 1; |
649 | else | |
650 | priv->duplexity = 0; | |
651 | ||
652 | speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED; | |
89875e96 JL |
653 | switch (speed) { |
654 | case MIIM_CIS8201_AUXCONSTAT_GBIT: | |
655 | priv->speed = 1000; | |
656 | break; | |
657 | case MIIM_CIS8201_AUXCONSTAT_100: | |
658 | priv->speed = 100; | |
659 | break; | |
660 | default: | |
661 | priv->speed = 10; | |
662 | break; | |
42d1f039 WD |
663 | } |
664 | ||
97d80fc3 WD |
665 | return 0; |
666 | } | |
89875e96 | 667 | |
debb7354 | 668 | /* Parse the vsc8244's status register for speed and duplex |
89875e96 JL |
669 | * information |
670 | */ | |
671 | uint mii_parse_vsc8244(uint mii_reg, struct tsec_private * priv) | |
debb7354 | 672 | { |
89875e96 | 673 | uint speed; |
42d1f039 | 674 | |
89875e96 JL |
675 | if (mii_reg & MIIM_VSC8244_AUXCONSTAT_DUPLEX) |
676 | priv->duplexity = 1; | |
677 | else | |
678 | priv->duplexity = 0; | |
679 | ||
680 | speed = mii_reg & MIIM_VSC8244_AUXCONSTAT_SPEED; | |
681 | switch (speed) { | |
682 | case MIIM_VSC8244_AUXCONSTAT_GBIT: | |
683 | priv->speed = 1000; | |
684 | break; | |
685 | case MIIM_VSC8244_AUXCONSTAT_100: | |
686 | priv->speed = 100; | |
687 | break; | |
688 | default: | |
689 | priv->speed = 10; | |
690 | break; | |
691 | } | |
692 | ||
693 | return 0; | |
694 | } | |
97d80fc3 WD |
695 | |
696 | /* Parse the DM9161's status register for speed and duplex | |
89875e96 JL |
697 | * information |
698 | */ | |
699 | uint mii_parse_dm9161_scsr(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 | 700 | { |
89875e96 | 701 | if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) |
97d80fc3 WD |
702 | priv->speed = 100; |
703 | else | |
704 | priv->speed = 10; | |
705 | ||
89875e96 | 706 | if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) |
97d80fc3 WD |
707 | priv->duplexity = 1; |
708 | else | |
709 | priv->duplexity = 0; | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
89875e96 JL |
714 | /* |
715 | * Hack to write all 4 PHYs with the LED values | |
716 | */ | |
717 | uint mii_cis8204_fixled(uint mii_reg, struct tsec_private * priv) | |
97d80fc3 WD |
718 | { |
719 | uint phyid; | |
720 | volatile tsec_t *regbase = priv->phyregs; | |
89875e96 | 721 | int timeout = 1000000; |
97d80fc3 | 722 | |
89875e96 | 723 | for (phyid = 0; phyid < 4; phyid++) { |
97d80fc3 WD |
724 | regbase->miimadd = (phyid << 8) | mii_reg; |
725 | regbase->miimcon = MIIM_CIS8204_SLEDCON_INIT; | |
f046ccd1 | 726 | asm("sync"); |
97d80fc3 | 727 | |
89875e96 JL |
728 | timeout = 1000000; |
729 | while ((regbase->miimind & MIIMIND_BUSY) && timeout--) ; | |
42d1f039 | 730 | } |
42d1f039 | 731 | |
97d80fc3 | 732 | return MIIM_CIS8204_SLEDCON_INIT; |
42d1f039 WD |
733 | } |
734 | ||
89875e96 | 735 | uint mii_cis8204_setmode(uint mii_reg, struct tsec_private * priv) |
d9b94f28 JL |
736 | { |
737 | if (priv->flags & TSEC_REDUCED) | |
738 | return MIIM_CIS8204_EPHYCON_INIT | MIIM_CIS8204_EPHYCON_RGMII; | |
739 | else | |
740 | return MIIM_CIS8204_EPHYCON_INIT; | |
741 | } | |
42d1f039 | 742 | |
19580e66 DL |
743 | uint mii_m88e1111s_setmode(uint mii_reg, struct tsec_private *priv) |
744 | { | |
745 | uint mii_data = read_phy_reg(priv, mii_reg); | |
746 | ||
747 | if (priv->flags & TSEC_REDUCED) | |
748 | mii_data = (mii_data & 0xfff0) | 0x000b; | |
749 | return mii_data; | |
750 | } | |
751 | ||
97d80fc3 WD |
752 | /* Initialized required registers to appropriate values, zeroing |
753 | * those we don't care about (unless zero is bad, in which case, | |
89875e96 JL |
754 | * choose a more appropriate value) |
755 | */ | |
756 | static void init_registers(volatile tsec_t * regs) | |
42d1f039 WD |
757 | { |
758 | /* Clear IEVENT */ | |
759 | regs->ievent = IEVENT_INIT_CLEAR; | |
760 | ||
761 | regs->imask = IMASK_INIT_CLEAR; | |
762 | ||
763 | regs->hash.iaddr0 = 0; | |
764 | regs->hash.iaddr1 = 0; | |
765 | regs->hash.iaddr2 = 0; | |
766 | regs->hash.iaddr3 = 0; | |
767 | regs->hash.iaddr4 = 0; | |
768 | regs->hash.iaddr5 = 0; | |
769 | regs->hash.iaddr6 = 0; | |
770 | regs->hash.iaddr7 = 0; | |
771 | ||
772 | regs->hash.gaddr0 = 0; | |
773 | regs->hash.gaddr1 = 0; | |
774 | regs->hash.gaddr2 = 0; | |
775 | regs->hash.gaddr3 = 0; | |
776 | regs->hash.gaddr4 = 0; | |
777 | regs->hash.gaddr5 = 0; | |
778 | regs->hash.gaddr6 = 0; | |
779 | regs->hash.gaddr7 = 0; | |
780 | ||
781 | regs->rctrl = 0x00000000; | |
782 | ||
783 | /* Init RMON mib registers */ | |
784 | memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t)); | |
785 | ||
786 | regs->rmon.cam1 = 0xffffffff; | |
787 | regs->rmon.cam2 = 0xffffffff; | |
788 | ||
789 | regs->mrblr = MRBLR_INIT_SETTINGS; | |
790 | ||
791 | regs->minflr = MINFLR_INIT_SETTINGS; | |
792 | ||
793 | regs->attr = ATTR_INIT_SETTINGS; | |
794 | regs->attreli = ATTRELI_INIT_SETTINGS; | |
795 | ||
796 | } | |
797 | ||
97d80fc3 | 798 | /* Configure maccfg2 based on negotiated speed and duplex |
89875e96 JL |
799 | * reported by PHY handling code |
800 | */ | |
97d80fc3 WD |
801 | static void adjust_link(struct eth_device *dev) |
802 | { | |
803 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
804 | volatile tsec_t *regs = priv->regs; | |
805 | ||
89875e96 JL |
806 | if (priv->link) { |
807 | if (priv->duplexity != 0) | |
97d80fc3 WD |
808 | regs->maccfg2 |= MACCFG2_FULL_DUPLEX; |
809 | else | |
810 | regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX); | |
811 | ||
89875e96 JL |
812 | switch (priv->speed) { |
813 | case 1000: | |
814 | regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF)) | |
815 | | MACCFG2_GMII); | |
816 | break; | |
817 | case 100: | |
818 | case 10: | |
819 | regs->maccfg2 = ((regs->maccfg2 & ~(MACCFG2_IF)) | |
820 | | MACCFG2_MII); | |
821 | ||
f484dc79 NS |
822 | /* Set R100 bit in all modes although |
823 | * it is only used in RGMII mode | |
89875e96 | 824 | */ |
f484dc79 | 825 | if (priv->speed == 100) |
89875e96 JL |
826 | regs->ecntrl |= ECNTRL_R100; |
827 | else | |
828 | regs->ecntrl &= ~(ECNTRL_R100); | |
829 | break; | |
830 | default: | |
831 | printf("%s: Speed was bad\n", dev->name); | |
832 | break; | |
97d80fc3 WD |
833 | } |
834 | ||
835 | printf("Speed: %d, %s duplex\n", priv->speed, | |
89875e96 | 836 | (priv->duplexity) ? "full" : "half"); |
97d80fc3 WD |
837 | |
838 | } else { | |
839 | printf("%s: No link.\n", dev->name); | |
840 | } | |
841 | } | |
842 | ||
97d80fc3 | 843 | /* Set up the buffers and their descriptors, and bring up the |
89875e96 JL |
844 | * interface |
845 | */ | |
97d80fc3 | 846 | static void startup_tsec(struct eth_device *dev) |
42d1f039 WD |
847 | { |
848 | int i; | |
97d80fc3 WD |
849 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
850 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
851 | |
852 | /* Point to the buffer descriptors */ | |
853 | regs->tbase = (unsigned int)(&rtx.txbd[txIdx]); | |
854 | regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]); | |
855 | ||
856 | /* Initialize the Rx Buffer descriptors */ | |
857 | for (i = 0; i < PKTBUFSRX; i++) { | |
858 | rtx.rxbd[i].status = RXBD_EMPTY; | |
859 | rtx.rxbd[i].length = 0; | |
89875e96 | 860 | rtx.rxbd[i].bufPtr = (uint) NetRxPackets[i]; |
42d1f039 | 861 | } |
89875e96 | 862 | rtx.rxbd[PKTBUFSRX - 1].status |= RXBD_WRAP; |
42d1f039 WD |
863 | |
864 | /* Initialize the TX Buffer Descriptors */ | |
89875e96 | 865 | for (i = 0; i < TX_BUF_CNT; i++) { |
42d1f039 WD |
866 | rtx.txbd[i].status = 0; |
867 | rtx.txbd[i].length = 0; | |
868 | rtx.txbd[i].bufPtr = 0; | |
869 | } | |
89875e96 | 870 | rtx.txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP; |
42d1f039 | 871 | |
97d80fc3 | 872 | /* Start up the PHY */ |
4653f91c BW |
873 | if(priv->phyinfo) |
874 | phy_run_commands(priv, priv->phyinfo->startup); | |
af1c2b84 | 875 | |
97d80fc3 WD |
876 | adjust_link(dev); |
877 | ||
42d1f039 WD |
878 | /* Enable Transmit and Receive */ |
879 | regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN); | |
880 | ||
881 | /* Tell the DMA it is clear to go */ | |
882 | regs->dmactrl |= DMACTRL_INIT_SETTINGS; | |
883 | regs->tstat = TSTAT_CLEAR_THALT; | |
5c7ea64b | 884 | regs->rstat = RSTAT_CLEAR_RHALT; |
42d1f039 WD |
885 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); |
886 | } | |
887 | ||
9d46ea4a | 888 | /* This returns the status bits of the device. The return value |
42d1f039 | 889 | * is never checked, and this is what the 8260 driver did, so we |
9d46ea4a | 890 | * do the same. Presumably, this would be zero if there were no |
89875e96 JL |
891 | * errors |
892 | */ | |
893 | static int tsec_send(struct eth_device *dev, volatile void *packet, int length) | |
42d1f039 WD |
894 | { |
895 | int i; | |
896 | int result = 0; | |
97d80fc3 WD |
897 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
898 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
899 | |
900 | /* Find an empty buffer descriptor */ | |
89875e96 | 901 | for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) { |
42d1f039 | 902 | if (i >= TOUT_LOOP) { |
89875e96 | 903 | debug("%s: tsec: tx buffers full\n", dev->name); |
42d1f039 WD |
904 | return result; |
905 | } | |
906 | } | |
907 | ||
89875e96 | 908 | rtx.txbd[txIdx].bufPtr = (uint) packet; |
42d1f039 | 909 | rtx.txbd[txIdx].length = length; |
89875e96 JL |
910 | rtx.txbd[txIdx].status |= |
911 | (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT); | |
42d1f039 WD |
912 | |
913 | /* Tell the DMA to go */ | |
914 | regs->tstat = TSTAT_CLEAR_THALT; | |
915 | ||
916 | /* Wait for buffer to be transmitted */ | |
89875e96 | 917 | for (i = 0; rtx.txbd[txIdx].status & TXBD_READY; i++) { |
42d1f039 | 918 | if (i >= TOUT_LOOP) { |
89875e96 | 919 | debug("%s: tsec: tx error\n", dev->name); |
42d1f039 WD |
920 | return result; |
921 | } | |
922 | } | |
923 | ||
924 | txIdx = (txIdx + 1) % TX_BUF_CNT; | |
925 | result = rtx.txbd[txIdx].status & TXBD_STATS; | |
926 | ||
927 | return result; | |
928 | } | |
929 | ||
89875e96 | 930 | static int tsec_recv(struct eth_device *dev) |
42d1f039 WD |
931 | { |
932 | int length; | |
97d80fc3 WD |
933 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
934 | volatile tsec_t *regs = priv->regs; | |
42d1f039 | 935 | |
89875e96 | 936 | while (!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) { |
42d1f039 WD |
937 | |
938 | length = rtx.rxbd[rxIdx].length; | |
939 | ||
940 | /* Send the packet up if there were no errors */ | |
941 | if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) { | |
942 | NetReceive(NetRxPackets[rxIdx], length - 4); | |
97d80fc3 WD |
943 | } else { |
944 | printf("Got error %x\n", | |
89875e96 | 945 | (rtx.rxbd[rxIdx].status & RXBD_STATS)); |
42d1f039 WD |
946 | } |
947 | ||
948 | rtx.rxbd[rxIdx].length = 0; | |
949 | ||
950 | /* Set the wrap bit if this is the last element in the list */ | |
89875e96 JL |
951 | rtx.rxbd[rxIdx].status = |
952 | RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0); | |
42d1f039 WD |
953 | |
954 | rxIdx = (rxIdx + 1) % PKTBUFSRX; | |
955 | } | |
956 | ||
89875e96 | 957 | if (regs->ievent & IEVENT_BSY) { |
42d1f039 WD |
958 | regs->ievent = IEVENT_BSY; |
959 | regs->rstat = RSTAT_CLEAR_RHALT; | |
960 | } | |
961 | ||
962 | return -1; | |
963 | ||
964 | } | |
965 | ||
97d80fc3 | 966 | /* Stop the interface */ |
89875e96 | 967 | static void tsec_halt(struct eth_device *dev) |
42d1f039 | 968 | { |
97d80fc3 WD |
969 | struct tsec_private *priv = (struct tsec_private *)dev->priv; |
970 | volatile tsec_t *regs = priv->regs; | |
42d1f039 WD |
971 | |
972 | regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS); | |
973 | regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS); | |
974 | ||
89875e96 | 975 | while (!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC))) ; |
42d1f039 WD |
976 | |
977 | regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN); | |
978 | ||
97d80fc3 | 979 | /* Shut down the PHY, as needed */ |
4653f91c BW |
980 | if(priv->phyinfo) |
981 | phy_run_commands(priv, priv->phyinfo->shutdown); | |
97d80fc3 WD |
982 | } |
983 | ||
c7e717eb | 984 | struct phy_info phy_info_M88E1149S = { |
5728be38 WD |
985 | 0x1410ca, |
986 | "Marvell 88E1149S", | |
987 | 4, | |
988 | (struct phy_cmd[]){ /* config */ | |
989 | /* Reset and configure the PHY */ | |
990 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
991 | {0x1d, 0x1f, NULL}, | |
992 | {0x1e, 0x200c, NULL}, | |
993 | {0x1d, 0x5, NULL}, | |
994 | {0x1e, 0x0, NULL}, | |
995 | {0x1e, 0x100, NULL}, | |
996 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
997 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
998 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
999 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1000 | {miim_end,} | |
1001 | }, | |
1002 | (struct phy_cmd[]){ /* startup */ | |
1003 | /* Status is read once to clear old link state */ | |
1004 | {MIIM_STATUS, miim_read, NULL}, | |
1005 | /* Auto-negotiate */ | |
1006 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1007 | /* Read the status */ | |
1008 | {MIIM_88E1011_PHY_STATUS, miim_read, | |
1009 | &mii_parse_88E1011_psr}, | |
1010 | {miim_end,} | |
1011 | }, | |
1012 | (struct phy_cmd[]){ /* shutdown */ | |
1013 | {miim_end,} | |
1014 | }, | |
c7e717eb AF |
1015 | }; |
1016 | ||
91e25769 PG |
1017 | /* The 5411 id is 0x206070, the 5421 is 0x2060e0 */ |
1018 | struct phy_info phy_info_BCM5461S = { | |
1019 | 0x02060c1, /* 5461 ID */ | |
1020 | "Broadcom BCM5461S", | |
1021 | 0, /* not clear to me what minor revisions we can shift away */ | |
1022 | (struct phy_cmd[]) { /* config */ | |
1023 | /* Reset and configure the PHY */ | |
1024 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1025 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1026 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1027 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1028 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1029 | {miim_end,} | |
1030 | }, | |
1031 | (struct phy_cmd[]) { /* startup */ | |
1032 | /* Status is read once to clear old link state */ | |
1033 | {MIIM_STATUS, miim_read, NULL}, | |
1034 | /* Auto-negotiate */ | |
1035 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1036 | /* Read the status */ | |
1037 | {MIIM_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr}, | |
1038 | {miim_end,} | |
1039 | }, | |
1040 | (struct phy_cmd[]) { /* shutdown */ | |
1041 | {miim_end,} | |
1042 | }, | |
1043 | }; | |
1044 | ||
c3243cf7 JH |
1045 | struct phy_info phy_info_BCM5464S = { |
1046 | 0x02060b1, /* 5464 ID */ | |
1047 | "Broadcom BCM5464S", | |
1048 | 0, /* not clear to me what minor revisions we can shift away */ | |
1049 | (struct phy_cmd[]) { /* config */ | |
1050 | /* Reset and configure the PHY */ | |
1051 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1052 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1053 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1054 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1055 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1056 | {miim_end,} | |
1057 | }, | |
1058 | (struct phy_cmd[]) { /* startup */ | |
1059 | /* Status is read once to clear old link state */ | |
1060 | {MIIM_STATUS, miim_read, NULL}, | |
1061 | /* Auto-negotiate */ | |
1062 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1063 | /* Read the status */ | |
1064 | {MIIM_BCM54xx_AUXSTATUS, miim_read, &mii_parse_BCM54xx_sr}, | |
1065 | {miim_end,} | |
1066 | }, | |
1067 | (struct phy_cmd[]) { /* shutdown */ | |
1068 | {miim_end,} | |
1069 | }, | |
1070 | }; | |
1071 | ||
97d80fc3 WD |
1072 | struct phy_info phy_info_M88E1011S = { |
1073 | 0x01410c6, | |
1074 | "Marvell 88E1011S", | |
1075 | 4, | |
89875e96 JL |
1076 | (struct phy_cmd[]){ /* config */ |
1077 | /* Reset and configure the PHY */ | |
1078 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1079 | {0x1d, 0x1f, NULL}, | |
1080 | {0x1e, 0x200c, NULL}, | |
1081 | {0x1d, 0x5, NULL}, | |
1082 | {0x1e, 0x0, NULL}, | |
1083 | {0x1e, 0x100, NULL}, | |
1084 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1085 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1086 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1087 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1088 | {miim_end,} | |
1089 | }, | |
1090 | (struct phy_cmd[]){ /* startup */ | |
1091 | /* Status is read once to clear old link state */ | |
1092 | {MIIM_STATUS, miim_read, NULL}, | |
1093 | /* Auto-negotiate */ | |
1094 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1095 | /* Read the status */ | |
1096 | {MIIM_88E1011_PHY_STATUS, miim_read, | |
1097 | &mii_parse_88E1011_psr}, | |
1098 | {miim_end,} | |
1099 | }, | |
1100 | (struct phy_cmd[]){ /* shutdown */ | |
1101 | {miim_end,} | |
1102 | }, | |
97d80fc3 WD |
1103 | }; |
1104 | ||
9d46ea4a WD |
1105 | struct phy_info phy_info_M88E1111S = { |
1106 | 0x01410cc, | |
1107 | "Marvell 88E1111S", | |
1108 | 4, | |
89875e96 JL |
1109 | (struct phy_cmd[]){ /* config */ |
1110 | /* Reset and configure the PHY */ | |
1111 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
19580e66 | 1112 | {0x1b, 0x848f, &mii_m88e1111s_setmode}, |
f484dc79 | 1113 | {0x14, 0x0cd2, NULL}, /* Delay RGMII TX and RX */ |
89875e96 JL |
1114 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, |
1115 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1116 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1117 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1118 | {miim_end,} | |
1119 | }, | |
1120 | (struct phy_cmd[]){ /* startup */ | |
1121 | /* Status is read once to clear old link state */ | |
1122 | {MIIM_STATUS, miim_read, NULL}, | |
1123 | /* Auto-negotiate */ | |
1124 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1125 | /* Read the status */ | |
1126 | {MIIM_88E1011_PHY_STATUS, miim_read, | |
1127 | &mii_parse_88E1011_psr}, | |
1128 | {miim_end,} | |
1129 | }, | |
1130 | (struct phy_cmd[]){ /* shutdown */ | |
1131 | {miim_end,} | |
1132 | }, | |
9d46ea4a WD |
1133 | }; |
1134 | ||
290ef643 RM |
1135 | struct phy_info phy_info_M88E1118 = { |
1136 | 0x01410e1, | |
1137 | "Marvell 88E1118", | |
1138 | 4, | |
1139 | (struct phy_cmd[]){ /* config */ | |
1140 | /* Reset and configure the PHY */ | |
1141 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1142 | {0x16, 0x0002, NULL}, /* Change Page Number */ | |
1143 | {0x15, 0x1070, NULL}, /* Delay RGMII TX and RX */ | |
1144 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1145 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1146 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1147 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1148 | {miim_end,} | |
1149 | }, | |
1150 | (struct phy_cmd[]){ /* startup */ | |
1151 | {0x16, 0x0000, NULL}, /* Change Page Number */ | |
1152 | /* Status is read once to clear old link state */ | |
1153 | {MIIM_STATUS, miim_read, NULL}, | |
1154 | /* Auto-negotiate */ | |
1155 | /* Read the status */ | |
1156 | {MIIM_88E1011_PHY_STATUS, miim_read, | |
1157 | &mii_parse_88E1011_psr}, | |
1158 | {miim_end,} | |
1159 | }, | |
1160 | (struct phy_cmd[]){ /* shutdown */ | |
1161 | {miim_end,} | |
1162 | }, | |
1163 | }; | |
1164 | ||
d23dc394 SP |
1165 | /* |
1166 | * Since to access LED register we need do switch the page, we | |
1167 | * do LED configuring in the miim_read-like function as follows | |
1168 | */ | |
1169 | uint mii_88E1121_set_led (uint mii_reg, struct tsec_private *priv) | |
1170 | { | |
1171 | uint pg; | |
1172 | ||
1173 | /* Switch the page to access the led register */ | |
1174 | pg = read_phy_reg(priv, MIIM_88E1121_PHY_PAGE); | |
1175 | write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, MIIM_88E1121_PHY_LED_PAGE); | |
1176 | ||
1177 | /* Configure leds */ | |
1178 | write_phy_reg(priv, MIIM_88E1121_PHY_LED_CTRL, | |
1179 | MIIM_88E1121_PHY_LED_DEF); | |
1180 | ||
1181 | /* Restore the page pointer */ | |
1182 | write_phy_reg(priv, MIIM_88E1121_PHY_PAGE, pg); | |
1183 | return 0; | |
1184 | } | |
1185 | ||
1186 | struct phy_info phy_info_M88E1121R = { | |
1187 | 0x01410cb, | |
1188 | "Marvell 88E1121R", | |
1189 | 4, | |
1190 | (struct phy_cmd[]){ /* config */ | |
1191 | /* Reset and configure the PHY */ | |
1192 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1193 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1194 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1195 | /* Configure leds */ | |
1196 | {MIIM_88E1121_PHY_LED_CTRL, miim_read, | |
1197 | &mii_88E1121_set_led}, | |
1198 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1199 | {miim_end,} | |
1200 | }, | |
1201 | (struct phy_cmd[]){ /* startup */ | |
1202 | /* Status is read once to clear old link state */ | |
1203 | {MIIM_STATUS, miim_read, NULL}, | |
1204 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1205 | {MIIM_STATUS, miim_read, &mii_parse_link}, | |
1206 | {miim_end,} | |
1207 | }, | |
1208 | (struct phy_cmd[]){ /* shutdown */ | |
1209 | {miim_end,} | |
1210 | }, | |
1211 | }; | |
1212 | ||
09f3e09e AF |
1213 | static unsigned int m88e1145_setmode(uint mii_reg, struct tsec_private *priv) |
1214 | { | |
09f3e09e AF |
1215 | uint mii_data = read_phy_reg(priv, mii_reg); |
1216 | ||
09f3e09e AF |
1217 | /* Setting MIIM_88E1145_PHY_EXT_CR */ |
1218 | if (priv->flags & TSEC_REDUCED) | |
1219 | return mii_data | | |
89875e96 | 1220 | MIIM_M88E1145_RGMII_RX_DELAY | MIIM_M88E1145_RGMII_TX_DELAY; |
09f3e09e AF |
1221 | else |
1222 | return mii_data; | |
1223 | } | |
1224 | ||
1225 | static struct phy_info phy_info_M88E1145 = { | |
1226 | 0x01410cd, | |
1227 | "Marvell 88E1145", | |
1228 | 4, | |
89875e96 | 1229 | (struct phy_cmd[]){ /* config */ |
7507d56c AF |
1230 | /* Reset the PHY */ |
1231 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1232 | ||
89875e96 JL |
1233 | /* Errata E0, E1 */ |
1234 | {29, 0x001b, NULL}, | |
1235 | {30, 0x418f, NULL}, | |
1236 | {29, 0x0016, NULL}, | |
1237 | {30, 0xa2da, NULL}, | |
1238 | ||
7507d56c | 1239 | /* Configure the PHY */ |
89875e96 JL |
1240 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, |
1241 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1242 | {MIIM_88E1011_PHY_SCR, MIIM_88E1011_PHY_MDI_X_AUTO, | |
1243 | NULL}, | |
1244 | {MIIM_88E1145_PHY_EXT_CR, 0, &m88e1145_setmode}, | |
1245 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1246 | {MIIM_CONTROL, MIIM_CONTROL_INIT, NULL}, | |
1247 | {miim_end,} | |
1248 | }, | |
1249 | (struct phy_cmd[]){ /* startup */ | |
1250 | /* Status is read once to clear old link state */ | |
1251 | {MIIM_STATUS, miim_read, NULL}, | |
1252 | /* Auto-negotiate */ | |
1253 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1254 | {MIIM_88E1111_PHY_LED_CONTROL, | |
1255 | MIIM_88E1111_PHY_LED_DIRECT, NULL}, | |
1256 | /* Read the Status */ | |
1257 | {MIIM_88E1011_PHY_STATUS, miim_read, | |
1258 | &mii_parse_88E1011_psr}, | |
1259 | {miim_end,} | |
1260 | }, | |
1261 | (struct phy_cmd[]){ /* shutdown */ | |
1262 | {miim_end,} | |
1263 | }, | |
09f3e09e AF |
1264 | }; |
1265 | ||
97d80fc3 WD |
1266 | struct phy_info phy_info_cis8204 = { |
1267 | 0x3f11, | |
1268 | "Cicada Cis8204", | |
1269 | 6, | |
89875e96 JL |
1270 | (struct phy_cmd[]){ /* config */ |
1271 | /* Override PHY config settings */ | |
1272 | {MIIM_CIS8201_AUX_CONSTAT, | |
1273 | MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
1274 | /* Configure some basic stuff */ | |
1275 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1276 | {MIIM_CIS8204_SLED_CON, MIIM_CIS8204_SLEDCON_INIT, | |
1277 | &mii_cis8204_fixled}, | |
1278 | {MIIM_CIS8204_EPHY_CON, MIIM_CIS8204_EPHYCON_INIT, | |
1279 | &mii_cis8204_setmode}, | |
1280 | {miim_end,} | |
1281 | }, | |
1282 | (struct phy_cmd[]){ /* startup */ | |
1283 | /* Read the Status (2x to make sure link is right) */ | |
1284 | {MIIM_STATUS, miim_read, NULL}, | |
1285 | /* Auto-negotiate */ | |
1286 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1287 | /* Read the status */ | |
1288 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, | |
1289 | &mii_parse_cis8201}, | |
1290 | {miim_end,} | |
1291 | }, | |
1292 | (struct phy_cmd[]){ /* shutdown */ | |
1293 | {miim_end,} | |
1294 | }, | |
97d80fc3 WD |
1295 | }; |
1296 | ||
1297 | /* Cicada 8201 */ | |
1298 | struct phy_info phy_info_cis8201 = { | |
1299 | 0xfc41, | |
1300 | "CIS8201", | |
1301 | 4, | |
89875e96 JL |
1302 | (struct phy_cmd[]){ /* config */ |
1303 | /* Override PHY config settings */ | |
1304 | {MIIM_CIS8201_AUX_CONSTAT, | |
1305 | MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, | |
1306 | /* Set up the interface mode */ | |
1307 | {MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, | |
1308 | NULL}, | |
1309 | /* Configure some basic stuff */ | |
1310 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1311 | {miim_end,} | |
1312 | }, | |
1313 | (struct phy_cmd[]){ /* startup */ | |
1314 | /* Read the Status (2x to make sure link is right) */ | |
1315 | {MIIM_STATUS, miim_read, NULL}, | |
1316 | /* Auto-negotiate */ | |
1317 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1318 | /* Read the status */ | |
1319 | {MIIM_CIS8201_AUX_CONSTAT, miim_read, | |
1320 | &mii_parse_cis8201}, | |
1321 | {miim_end,} | |
1322 | }, | |
1323 | (struct phy_cmd[]){ /* shutdown */ | |
1324 | {miim_end,} | |
1325 | }, | |
97d80fc3 | 1326 | }; |
debb7354 | 1327 | struct phy_info phy_info_VSC8244 = { |
89875e96 JL |
1328 | 0x3f1b, |
1329 | "Vitesse VSC8244", | |
1330 | 6, | |
1331 | (struct phy_cmd[]){ /* config */ | |
1332 | /* Override PHY config settings */ | |
1333 | /* Configure some basic stuff */ | |
1334 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1335 | {miim_end,} | |
1336 | }, | |
1337 | (struct phy_cmd[]){ /* startup */ | |
1338 | /* Read the Status (2x to make sure link is right) */ | |
1339 | {MIIM_STATUS, miim_read, NULL}, | |
1340 | /* Auto-negotiate */ | |
1341 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1342 | /* Read the status */ | |
1343 | {MIIM_VSC8244_AUX_CONSTAT, miim_read, | |
1344 | &mii_parse_vsc8244}, | |
1345 | {miim_end,} | |
1346 | }, | |
1347 | (struct phy_cmd[]){ /* shutdown */ | |
1348 | {miim_end,} | |
1349 | }, | |
debb7354 | 1350 | }; |
97d80fc3 | 1351 | |
2d934ea5 TK |
1352 | struct phy_info phy_info_VSC8601 = { |
1353 | 0x00007042, | |
1354 | "Vitesse VSC8601", | |
1355 | 4, | |
1356 | (struct phy_cmd[]){ /* config */ | |
1357 | /* Override PHY config settings */ | |
1358 | /* Configure some basic stuff */ | |
1359 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
6d0f6bcf | 1360 | #ifdef CONFIG_SYS_VSC8601_SKEWFIX |
2d934ea5 | 1361 | {MIIM_VSC8601_EPHY_CON,MIIM_VSC8601_EPHY_CON_INIT_SKEW,NULL}, |
6d0f6bcf | 1362 | #if defined(CONFIG_SYS_VSC8601_SKEW_TX) && defined(CONFIG_SYS_VSC8601_SKEW_RX) |
9acde129 | 1363 | {MIIM_EXT_PAGE_ACCESS,1,NULL}, |
6d0f6bcf | 1364 | #define VSC8101_SKEW (CONFIG_SYS_VSC8601_SKEW_TX<<14)|(CONFIG_SYS_VSC8601_SKEW_RX<<12) |
9acde129 AS |
1365 | {MIIM_VSC8601_SKEW_CTRL,VSC8101_SKEW,NULL}, |
1366 | {MIIM_EXT_PAGE_ACCESS,0,NULL}, | |
1367 | #endif | |
2d934ea5 | 1368 | #endif |
c9d6b692 AS |
1369 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, |
1370 | {MIIM_CONTROL, MIIM_CONTROL_RESTART, &mii_cr_init}, | |
2d934ea5 TK |
1371 | {miim_end,} |
1372 | }, | |
1373 | (struct phy_cmd[]){ /* startup */ | |
1374 | /* Read the Status (2x to make sure link is right) */ | |
1375 | {MIIM_STATUS, miim_read, NULL}, | |
1376 | /* Auto-negotiate */ | |
1377 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1378 | /* Read the status */ | |
1379 | {MIIM_VSC8244_AUX_CONSTAT, miim_read, | |
1380 | &mii_parse_vsc8244}, | |
1381 | {miim_end,} | |
1382 | }, | |
1383 | (struct phy_cmd[]){ /* shutdown */ | |
1384 | {miim_end,} | |
1385 | }, | |
1386 | }; | |
1387 | ||
1388 | ||
97d80fc3 WD |
1389 | struct phy_info phy_info_dm9161 = { |
1390 | 0x0181b88, | |
1391 | "Davicom DM9161E", | |
1392 | 4, | |
89875e96 JL |
1393 | (struct phy_cmd[]){ /* config */ |
1394 | {MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL}, | |
1395 | /* Do not bypass the scrambler/descrambler */ | |
1396 | {MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL}, | |
1397 | /* Clear 10BTCSR to default */ | |
1398 | {MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, | |
1399 | NULL}, | |
1400 | /* Configure some basic stuff */ | |
1401 | {MIIM_CONTROL, MIIM_CR_INIT, NULL}, | |
1402 | /* Restart Auto Negotiation */ | |
1403 | {MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL}, | |
1404 | {miim_end,} | |
1405 | }, | |
1406 | (struct phy_cmd[]){ /* startup */ | |
1407 | /* Status is read once to clear old link state */ | |
1408 | {MIIM_STATUS, miim_read, NULL}, | |
1409 | /* Auto-negotiate */ | |
1410 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1411 | /* Read the status */ | |
1412 | {MIIM_DM9161_SCSR, miim_read, | |
1413 | &mii_parse_dm9161_scsr}, | |
1414 | {miim_end,} | |
1415 | }, | |
1416 | (struct phy_cmd[]){ /* shutdown */ | |
1417 | {miim_end,} | |
1418 | }, | |
97d80fc3 | 1419 | }; |
af1c2b84 DU |
1420 | /* a generic flavor. */ |
1421 | struct phy_info phy_info_generic = { | |
1422 | 0, | |
1423 | "Unknown/Generic PHY", | |
1424 | 32, | |
1425 | (struct phy_cmd[]) { /* config */ | |
1426 | {PHY_BMCR, PHY_BMCR_RESET, NULL}, | |
1427 | {PHY_BMCR, PHY_BMCR_AUTON|PHY_BMCR_RST_NEG, NULL}, | |
1428 | {miim_end,} | |
1429 | }, | |
1430 | (struct phy_cmd[]) { /* startup */ | |
1431 | {PHY_BMSR, miim_read, NULL}, | |
1432 | {PHY_BMSR, miim_read, &mii_parse_sr}, | |
1433 | {PHY_BMSR, miim_read, &mii_parse_link}, | |
1434 | {miim_end,} | |
1435 | }, | |
1436 | (struct phy_cmd[]) { /* shutdown */ | |
1437 | {miim_end,} | |
1438 | } | |
1439 | }; | |
1440 | ||
97d80fc3 | 1441 | |
3dd7f0f0 WD |
1442 | uint mii_parse_lxt971_sr2(uint mii_reg, struct tsec_private *priv) |
1443 | { | |
3c2b3d45 WD |
1444 | unsigned int speed; |
1445 | if (priv->link) { | |
1446 | speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK; | |
1447 | ||
1448 | switch (speed) { | |
1449 | case MIIM_LXT971_SR2_10HDX: | |
1450 | priv->speed = 10; | |
1451 | priv->duplexity = 0; | |
1452 | break; | |
1453 | case MIIM_LXT971_SR2_10FDX: | |
1454 | priv->speed = 10; | |
1455 | priv->duplexity = 1; | |
1456 | break; | |
1457 | case MIIM_LXT971_SR2_100HDX: | |
1458 | priv->speed = 100; | |
1459 | priv->duplexity = 0; | |
cd2d1602 | 1460 | break; |
3c2b3d45 WD |
1461 | default: |
1462 | priv->speed = 100; | |
1463 | priv->duplexity = 1; | |
3c2b3d45 WD |
1464 | } |
1465 | } else { | |
1466 | priv->speed = 0; | |
1467 | priv->duplexity = 0; | |
1468 | } | |
1469 | ||
1470 | return 0; | |
3dd7f0f0 WD |
1471 | } |
1472 | ||
9d46ea4a WD |
1473 | static struct phy_info phy_info_lxt971 = { |
1474 | 0x0001378e, | |
1475 | "LXT971", | |
1476 | 4, | |
89875e96 JL |
1477 | (struct phy_cmd[]){ /* config */ |
1478 | {MIIM_CR, MIIM_CR_INIT, mii_cr_init}, /* autonegotiate */ | |
1479 | {miim_end,} | |
1480 | }, | |
1481 | (struct phy_cmd[]){ /* startup - enable interrupts */ | |
1482 | /* { 0x12, 0x00f2, NULL }, */ | |
1483 | {MIIM_STATUS, miim_read, NULL}, | |
1484 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1485 | {MIIM_LXT971_SR2, miim_read, &mii_parse_lxt971_sr2}, | |
1486 | {miim_end,} | |
1487 | }, | |
1488 | (struct phy_cmd[]){ /* shutdown - disable interrupts */ | |
1489 | {miim_end,} | |
1490 | }, | |
9d46ea4a WD |
1491 | }; |
1492 | ||
be5048f1 | 1493 | /* Parse the DP83865's link and auto-neg status register for speed and duplex |
89875e96 JL |
1494 | * information |
1495 | */ | |
be5048f1 WD |
1496 | uint mii_parse_dp83865_lanr(uint mii_reg, struct tsec_private *priv) |
1497 | { | |
1498 | switch (mii_reg & MIIM_DP83865_SPD_MASK) { | |
1499 | ||
1500 | case MIIM_DP83865_SPD_1000: | |
1501 | priv->speed = 1000; | |
1502 | break; | |
1503 | ||
1504 | case MIIM_DP83865_SPD_100: | |
1505 | priv->speed = 100; | |
1506 | break; | |
1507 | ||
1508 | default: | |
1509 | priv->speed = 10; | |
1510 | break; | |
1511 | ||
1512 | } | |
1513 | ||
1514 | if (mii_reg & MIIM_DP83865_DPX_FULL) | |
1515 | priv->duplexity = 1; | |
1516 | else | |
1517 | priv->duplexity = 0; | |
1518 | ||
1519 | return 0; | |
1520 | } | |
1521 | ||
1522 | struct phy_info phy_info_dp83865 = { | |
1523 | 0x20005c7, | |
1524 | "NatSemi DP83865", | |
1525 | 4, | |
89875e96 JL |
1526 | (struct phy_cmd[]){ /* config */ |
1527 | {MIIM_CONTROL, MIIM_DP83865_CR_INIT, NULL}, | |
1528 | {miim_end,} | |
1529 | }, | |
1530 | (struct phy_cmd[]){ /* startup */ | |
1531 | /* Status is read once to clear old link state */ | |
1532 | {MIIM_STATUS, miim_read, NULL}, | |
1533 | /* Auto-negotiate */ | |
1534 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1535 | /* Read the link and auto-neg status */ | |
1536 | {MIIM_DP83865_LANR, miim_read, | |
1537 | &mii_parse_dp83865_lanr}, | |
1538 | {miim_end,} | |
1539 | }, | |
1540 | (struct phy_cmd[]){ /* shutdown */ | |
1541 | {miim_end,} | |
1542 | }, | |
be5048f1 WD |
1543 | }; |
1544 | ||
18ee320f DL |
1545 | struct phy_info phy_info_rtl8211b = { |
1546 | 0x001cc91, | |
1547 | "RealTek RTL8211B", | |
1548 | 4, | |
1549 | (struct phy_cmd[]){ /* config */ | |
1550 | /* Reset and configure the PHY */ | |
1551 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1552 | {MIIM_GBIT_CONTROL, MIIM_GBIT_CONTROL_INIT, NULL}, | |
1553 | {MIIM_ANAR, MIIM_ANAR_INIT, NULL}, | |
1554 | {MIIM_CONTROL, MIIM_CONTROL_RESET, NULL}, | |
1555 | {MIIM_CONTROL, MIIM_CONTROL_INIT, &mii_cr_init}, | |
1556 | {miim_end,} | |
1557 | }, | |
1558 | (struct phy_cmd[]){ /* startup */ | |
1559 | /* Status is read once to clear old link state */ | |
1560 | {MIIM_STATUS, miim_read, NULL}, | |
1561 | /* Auto-negotiate */ | |
1562 | {MIIM_STATUS, miim_read, &mii_parse_sr}, | |
1563 | /* Read the status */ | |
1564 | {MIIM_RTL8211B_PHY_STATUS, miim_read, &mii_parse_RTL8211B_sr}, | |
1565 | {miim_end,} | |
1566 | }, | |
1567 | (struct phy_cmd[]){ /* shutdown */ | |
1568 | {miim_end,} | |
1569 | }, | |
1570 | }; | |
1571 | ||
97d80fc3 | 1572 | struct phy_info *phy_info[] = { |
97d80fc3 | 1573 | &phy_info_cis8204, |
2ad6b513 | 1574 | &phy_info_cis8201, |
91e25769 | 1575 | &phy_info_BCM5461S, |
c3243cf7 | 1576 | &phy_info_BCM5464S, |
97d80fc3 | 1577 | &phy_info_M88E1011S, |
9d46ea4a | 1578 | &phy_info_M88E1111S, |
290ef643 | 1579 | &phy_info_M88E1118, |
d23dc394 | 1580 | &phy_info_M88E1121R, |
09f3e09e | 1581 | &phy_info_M88E1145, |
5728be38 | 1582 | &phy_info_M88E1149S, |
97d80fc3 | 1583 | &phy_info_dm9161, |
9d46ea4a | 1584 | &phy_info_lxt971, |
debb7354 | 1585 | &phy_info_VSC8244, |
2d934ea5 | 1586 | &phy_info_VSC8601, |
be5048f1 | 1587 | &phy_info_dp83865, |
18ee320f | 1588 | &phy_info_rtl8211b, |
af1c2b84 | 1589 | &phy_info_generic, |
97d80fc3 WD |
1590 | NULL |
1591 | }; | |
1592 | ||
97d80fc3 | 1593 | /* Grab the identifier of the device's PHY, and search through |
9d46ea4a | 1594 | * all of the known PHYs to see if one matches. If so, return |
89875e96 JL |
1595 | * it, if not, return NULL |
1596 | */ | |
1597 | struct phy_info *get_phy_info(struct eth_device *dev) | |
97d80fc3 WD |
1598 | { |
1599 | struct tsec_private *priv = (struct tsec_private *)dev->priv; | |
1600 | uint phy_reg, phy_ID; | |
1601 | int i; | |
1602 | struct phy_info *theInfo = NULL; | |
1603 | ||
1604 | /* Grab the bits from PHYIR1, and put them in the upper half */ | |
1605 | phy_reg = read_phy_reg(priv, MIIM_PHYIR1); | |
1606 | phy_ID = (phy_reg & 0xffff) << 16; | |
1607 | ||
1608 | /* Grab the bits from PHYIR2, and put them in the lower half */ | |
1609 | phy_reg = read_phy_reg(priv, MIIM_PHYIR2); | |
1610 | phy_ID |= (phy_reg & 0xffff); | |
1611 | ||
1612 | /* loop through all the known PHY types, and find one that */ | |
1613 | /* matches the ID we read from the PHY. */ | |
89875e96 | 1614 | for (i = 0; phy_info[i]; i++) { |
2a3cee43 | 1615 | if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) { |
97d80fc3 | 1616 | theInfo = phy_info[i]; |
2a3cee43 AF |
1617 | break; |
1618 | } | |
97d80fc3 WD |
1619 | } |
1620 | ||
89875e96 | 1621 | if (theInfo == NULL) { |
97d80fc3 WD |
1622 | printf("%s: PHY id %x is not supported!\n", dev->name, phy_ID); |
1623 | return NULL; | |
1624 | } else { | |
5810dc3a | 1625 | debug("%s: PHY is %s (%x)\n", dev->name, theInfo->name, phy_ID); |
97d80fc3 WD |
1626 | } |
1627 | ||
1628 | return theInfo; | |
42d1f039 | 1629 | } |
7abf0c58 | 1630 | |
97d80fc3 | 1631 | /* Execute the given series of commands on the given device's |
89875e96 JL |
1632 | * PHY, running functions as necessary |
1633 | */ | |
97d80fc3 WD |
1634 | void phy_run_commands(struct tsec_private *priv, struct phy_cmd *cmd) |
1635 | { | |
1636 | int i; | |
1637 | uint result; | |
1638 | volatile tsec_t *phyregs = priv->phyregs; | |
1639 | ||
1640 | phyregs->miimcfg = MIIMCFG_RESET; | |
1641 | ||
1642 | phyregs->miimcfg = MIIMCFG_INIT_VALUE; | |
1643 | ||
89875e96 | 1644 | while (phyregs->miimind & MIIMIND_BUSY) ; |
97d80fc3 | 1645 | |
89875e96 JL |
1646 | for (i = 0; cmd->mii_reg != miim_end; i++) { |
1647 | if (cmd->mii_data == miim_read) { | |
97d80fc3 WD |
1648 | result = read_phy_reg(priv, cmd->mii_reg); |
1649 | ||
89875e96 JL |
1650 | if (cmd->funct != NULL) |
1651 | (*(cmd->funct)) (result, priv); | |
97d80fc3 WD |
1652 | |
1653 | } else { | |
89875e96 JL |
1654 | if (cmd->funct != NULL) |
1655 | result = (*(cmd->funct)) (cmd->mii_reg, priv); | |
97d80fc3 WD |
1656 | else |
1657 | result = cmd->mii_data; | |
1658 | ||
1659 | write_phy_reg(priv, cmd->mii_reg, result); | |
1660 | ||
1661 | } | |
1662 | cmd++; | |
1663 | } | |
1664 | } | |
1665 | ||
97d80fc3 WD |
1666 | /* Relocate the function pointers in the phy cmd lists */ |
1667 | static void relocate_cmds(void) | |
1668 | { | |
1669 | struct phy_cmd **cmdlistptr; | |
1670 | struct phy_cmd *cmd; | |
89875e96 | 1671 | int i, j, k; |
97d80fc3 | 1672 | |
89875e96 | 1673 | for (i = 0; phy_info[i]; i++) { |
97d80fc3 WD |
1674 | /* First thing's first: relocate the pointers to the |
1675 | * PHY command structures (the structs were done) */ | |
89875e96 JL |
1676 | phy_info[i] = (struct phy_info *)((uint) phy_info[i] |
1677 | + gd->reloc_off); | |
97d80fc3 WD |
1678 | phy_info[i]->name += gd->reloc_off; |
1679 | phy_info[i]->config = | |
89875e96 JL |
1680 | (struct phy_cmd *)((uint) phy_info[i]->config |
1681 | + gd->reloc_off); | |
97d80fc3 | 1682 | phy_info[i]->startup = |
89875e96 JL |
1683 | (struct phy_cmd *)((uint) phy_info[i]->startup |
1684 | + gd->reloc_off); | |
97d80fc3 | 1685 | phy_info[i]->shutdown = |
89875e96 JL |
1686 | (struct phy_cmd *)((uint) phy_info[i]->shutdown |
1687 | + gd->reloc_off); | |
97d80fc3 WD |
1688 | |
1689 | cmdlistptr = &phy_info[i]->config; | |
89875e96 JL |
1690 | j = 0; |
1691 | for (; cmdlistptr <= &phy_info[i]->shutdown; cmdlistptr++) { | |
1692 | k = 0; | |
1693 | for (cmd = *cmdlistptr; | |
1694 | cmd->mii_reg != miim_end; | |
1695 | cmd++) { | |
97d80fc3 | 1696 | /* Only relocate non-NULL pointers */ |
89875e96 | 1697 | if (cmd->funct) |
97d80fc3 WD |
1698 | cmd->funct += gd->reloc_off; |
1699 | ||
1700 | k++; | |
1701 | } | |
1702 | j++; | |
1703 | } | |
1704 | } | |
1705 | ||
1706 | relocated = 1; | |
1707 | } | |
1708 | ||
cb51c0bf | 1709 | #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) \ |
63ff004c | 1710 | && !defined(BITBANGMII) |
97d80fc3 | 1711 | |
7abf0c58 WD |
1712 | /* |
1713 | * Read a MII PHY register. | |
1714 | * | |
1715 | * Returns: | |
97d80fc3 | 1716 | * 0 on success |
7abf0c58 | 1717 | */ |
63ff004c | 1718 | static int tsec_miiphy_read(char *devname, unsigned char addr, |
89875e96 | 1719 | unsigned char reg, unsigned short *value) |
7abf0c58 | 1720 | { |
97d80fc3 | 1721 | unsigned short ret; |
55fe7c57 | 1722 | struct tsec_private *priv = privlist[0]; |
97d80fc3 | 1723 | |
89875e96 | 1724 | if (NULL == priv) { |
97d80fc3 WD |
1725 | printf("Can't read PHY at address %d\n", addr); |
1726 | return -1; | |
1727 | } | |
7abf0c58 | 1728 | |
2abe361c | 1729 | ret = (unsigned short)tsec_local_mdio_read(priv->phyregs, addr, reg); |
97d80fc3 | 1730 | *value = ret; |
7abf0c58 WD |
1731 | |
1732 | return 0; | |
1733 | } | |
1734 | ||
1735 | /* | |
1736 | * Write a MII PHY register. | |
1737 | * | |
1738 | * Returns: | |
97d80fc3 | 1739 | * 0 on success |
7abf0c58 | 1740 | */ |
63ff004c | 1741 | static int tsec_miiphy_write(char *devname, unsigned char addr, |
89875e96 | 1742 | unsigned char reg, unsigned short value) |
7abf0c58 | 1743 | { |
55fe7c57 | 1744 | struct tsec_private *priv = privlist[0]; |
97d80fc3 | 1745 | |
89875e96 | 1746 | if (NULL == priv) { |
97d80fc3 WD |
1747 | printf("Can't write PHY at address %d\n", addr); |
1748 | return -1; | |
1749 | } | |
7abf0c58 | 1750 | |
2abe361c | 1751 | tsec_local_mdio_write(priv->phyregs, addr, reg, value); |
7abf0c58 WD |
1752 | |
1753 | return 0; | |
1754 | } | |
97d80fc3 | 1755 | |
cb51c0bf | 1756 | #endif |
97d80fc3 | 1757 | |
53a5c424 DU |
1758 | #ifdef CONFIG_MCAST_TFTP |
1759 | ||
1760 | /* CREDITS: linux gianfar driver, slightly adjusted... thanx. */ | |
1761 | ||
1762 | /* Set the appropriate hash bit for the given addr */ | |
1763 | ||
1764 | /* The algorithm works like so: | |
1765 | * 1) Take the Destination Address (ie the multicast address), and | |
1766 | * do a CRC on it (little endian), and reverse the bits of the | |
1767 | * result. | |
1768 | * 2) Use the 8 most significant bits as a hash into a 256-entry | |
1769 | * table. The table is controlled through 8 32-bit registers: | |
1770 | * gaddr0-7. gaddr0's MSB is entry 0, and gaddr7's LSB is | |
1771 | * gaddr7. This means that the 3 most significant bits in the | |
1772 | * hash index which gaddr register to use, and the 5 other bits | |
1773 | * indicate which bit (assuming an IBM numbering scheme, which | |
1774 | * for PowerPC (tm) is usually the case) in the tregister holds | |
1775 | * the entry. */ | |
1776 | static int | |
1777 | tsec_mcast_addr (struct eth_device *dev, u8 mcast_mac, u8 set) | |
1778 | { | |
1779 | struct tsec_private *priv = privlist[1]; | |
1780 | volatile tsec_t *regs = priv->regs; | |
1781 | volatile u32 *reg_array, value; | |
1782 | u8 result, whichbit, whichreg; | |
1783 | ||
1784 | result = (u8)((ether_crc(MAC_ADDR_LEN,mcast_mac) >> 24) & 0xff); | |
1785 | whichbit = result & 0x1f; /* the 5 LSB = which bit to set */ | |
1786 | whichreg = result >> 5; /* the 3 MSB = which reg to set it in */ | |
1787 | value = (1 << (31-whichbit)); | |
1788 | ||
1789 | reg_array = &(regs->hash.gaddr0); | |
1790 | ||
1791 | if (set) { | |
1792 | reg_array[whichreg] |= value; | |
1793 | } else { | |
1794 | reg_array[whichreg] &= ~value; | |
1795 | } | |
1796 | return 0; | |
1797 | } | |
1798 | #endif /* Multicast TFTP ? */ |