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