| 1 | /* |
| 2 | * This file is subject to the terms and conditions of the GNU General Public |
| 3 | * License. See the file "COPYING" in the main directory of this archive |
| 4 | * for more details. |
| 5 | * |
| 6 | * Copyright © 2003 Atheros Communications, Inc., All Rights Reserved. |
| 7 | */ |
| 8 | |
| 9 | /* |
| 10 | * Manage the atheros ethernet PHY. |
| 11 | * |
| 12 | * All definitions in this file are operating system independent! |
| 13 | */ |
| 14 | |
| 15 | #include <config.h> |
| 16 | #include <linux/types.h> |
| 17 | #include <common.h> |
| 18 | #include <miiphy.h> |
| 19 | //#include "phy.h" |
| 20 | //#include "ar7100_soc.h" |
| 21 | #include "athrs26_phy.h" |
| 22 | |
| 23 | #define phy_reg_read(base, addr, reg, datap) \ |
| 24 | miiphy_read("lq_cpe_eth", addr, reg, datap); |
| 25 | #define phy_reg_write(base, addr, reg, data) \ |
| 26 | miiphy_write("lq_cpe_eth", addr, reg, data); |
| 27 | |
| 28 | |
| 29 | /* PHY selections and access functions */ |
| 30 | |
| 31 | typedef enum { |
| 32 | PHY_SRCPORT_INFO, |
| 33 | PHY_PORTINFO_SIZE, |
| 34 | } PHY_CAP_TYPE; |
| 35 | |
| 36 | typedef enum { |
| 37 | PHY_SRCPORT_NONE, |
| 38 | PHY_SRCPORT_VLANTAG, |
| 39 | PHY_SRCPORT_TRAILER, |
| 40 | } PHY_SRCPORT_TYPE; |
| 41 | |
| 42 | #ifdef DEBUG |
| 43 | #define DRV_DEBUG 1 |
| 44 | #endif |
| 45 | //#define DRV_DEBUG 1 |
| 46 | |
| 47 | #define DRV_DEBUG_PHYERROR 0x00000001 |
| 48 | #define DRV_DEBUG_PHYCHANGE 0x00000002 |
| 49 | #define DRV_DEBUG_PHYSETUP 0x00000004 |
| 50 | |
| 51 | #if DRV_DEBUG |
| 52 | int athrPhyDebug = DRV_DEBUG_PHYERROR|DRV_DEBUG_PHYCHANGE|DRV_DEBUG_PHYSETUP; |
| 53 | |
| 54 | #define DRV_LOG(FLG, X0, X1, X2, X3, X4, X5, X6) \ |
| 55 | { \ |
| 56 | if (athrPhyDebug & (FLG)) { \ |
| 57 | logMsg(X0, X1, X2, X3, X4, X5, X6); \ |
| 58 | } \ |
| 59 | } |
| 60 | |
| 61 | #define DRV_MSG(x,a,b,c,d,e,f) \ |
| 62 | logMsg(x,a,b,c,d,e,f) |
| 63 | |
| 64 | #define DRV_PRINT(FLG, X) \ |
| 65 | { \ |
| 66 | if (athrPhyDebug & (FLG)) { \ |
| 67 | printf X; \ |
| 68 | } \ |
| 69 | } |
| 70 | |
| 71 | #else /* !DRV_DEBUG */ |
| 72 | #define DRV_LOG(DBG_SW, X0, X1, X2, X3, X4, X5, X6) |
| 73 | #define DRV_MSG(x,a,b,c,d,e,f) |
| 74 | #define DRV_PRINT(DBG_SW,X) |
| 75 | #endif |
| 76 | |
| 77 | #define ATHR_LAN_PORT_VLAN 1 |
| 78 | #define ATHR_WAN_PORT_VLAN 2 |
| 79 | |
| 80 | #define ENET_UNIT_LAN 0 |
| 81 | |
| 82 | #define TRUE 1 |
| 83 | #define FALSE 0 |
| 84 | |
| 85 | #define ATHR_PHY0_ADDR 0x0 |
| 86 | #define ATHR_PHY1_ADDR 0x1 |
| 87 | #define ATHR_PHY2_ADDR 0x2 |
| 88 | #define ATHR_PHY3_ADDR 0x3 |
| 89 | #define ATHR_PHY4_ADDR 0x4 |
| 90 | |
| 91 | /* |
| 92 | * Track per-PHY port information. |
| 93 | */ |
| 94 | typedef struct { |
| 95 | BOOL isEnetPort; /* normal enet port */ |
| 96 | BOOL isPhyAlive; /* last known state of link */ |
| 97 | int ethUnit; /* MAC associated with this phy port */ |
| 98 | uint32_t phyBase; |
| 99 | uint32_t phyAddr; /* PHY registers associated with this phy port */ |
| 100 | uint32_t VLANTableSetting; /* Value to be written to VLAN table */ |
| 101 | } athrPhyInfo_t; |
| 102 | |
| 103 | /* |
| 104 | * Per-PHY information, indexed by PHY unit number. |
| 105 | */ |
| 106 | static athrPhyInfo_t athrPhyInfo[] = { |
| 107 | {TRUE, /* phy port 0 -- LAN port 0 */ |
| 108 | FALSE, |
| 109 | ENET_UNIT_LAN, |
| 110 | 0, |
| 111 | ATHR_PHY0_ADDR, |
| 112 | ATHR_LAN_PORT_VLAN |
| 113 | }, |
| 114 | |
| 115 | {TRUE, /* phy port 1 -- LAN port 1 */ |
| 116 | FALSE, |
| 117 | ENET_UNIT_LAN, |
| 118 | 0, |
| 119 | ATHR_PHY1_ADDR, |
| 120 | ATHR_LAN_PORT_VLAN |
| 121 | }, |
| 122 | |
| 123 | {TRUE, /* phy port 2 -- LAN port 2 */ |
| 124 | FALSE, |
| 125 | ENET_UNIT_LAN, |
| 126 | 0, |
| 127 | ATHR_PHY2_ADDR, |
| 128 | ATHR_LAN_PORT_VLAN |
| 129 | }, |
| 130 | |
| 131 | {TRUE, /* phy port 3 -- LAN port 3 */ |
| 132 | FALSE, |
| 133 | ENET_UNIT_LAN, |
| 134 | 0, |
| 135 | ATHR_PHY3_ADDR, |
| 136 | ATHR_LAN_PORT_VLAN |
| 137 | }, |
| 138 | |
| 139 | {TRUE, /* phy port 4 -- WAN port or LAN port 4 */ |
| 140 | FALSE, |
| 141 | 1, |
| 142 | 0, |
| 143 | ATHR_PHY4_ADDR, |
| 144 | ATHR_LAN_PORT_VLAN /* Send to all ports */ |
| 145 | }, |
| 146 | |
| 147 | {FALSE, /* phy port 5 -- CPU port (no RJ45 connector) */ |
| 148 | TRUE, |
| 149 | ENET_UNIT_LAN, |
| 150 | 0, |
| 151 | 0x00, |
| 152 | ATHR_LAN_PORT_VLAN /* Send to all ports */ |
| 153 | }, |
| 154 | }; |
| 155 | |
| 156 | #ifdef CFG_ATHRHDR_EN |
| 157 | typedef struct { |
| 158 | uint8_t data[ATHRHDR_MAX_DATA]; |
| 159 | uint8_t len; |
| 160 | uint32_t seq; |
| 161 | } cmd_resp_t; |
| 162 | |
| 163 | typedef struct { |
| 164 | uint16_t reg_addr; |
| 165 | uint16_t cmd_len; |
| 166 | uint8_t *reg_data; |
| 167 | }cmd_write_t; |
| 168 | |
| 169 | static cmd_write_t cmd_write,cmd_read; |
| 170 | static cmd_resp_t cmd_resp; |
| 171 | static struct eth_device *lan_mac; |
| 172 | //static atomic_t seqcnt = ATOMIC_INIT(0); |
| 173 | static int seqcnt = 0; |
| 174 | static int cmd = 1; |
| 175 | //volatile uchar AthrHdrPkt[60]; |
| 176 | #endif |
| 177 | |
| 178 | #define ATHR_GLOBALREGBASE 0 |
| 179 | |
| 180 | //#define ATHR_PHY_MAX (sizeof(athrPhyInfo) / sizeof(athrPhyInfo[0])) |
| 181 | #define ATHR_PHY_MAX 5 |
| 182 | |
| 183 | /* Range of valid PHY IDs is [MIN..MAX] */ |
| 184 | #define ATHR_ID_MIN 0 |
| 185 | #define ATHR_ID_MAX (ATHR_PHY_MAX-1) |
| 186 | |
| 187 | /* Convenience macros to access myPhyInfo */ |
| 188 | #define ATHR_IS_ENET_PORT(phyUnit) (athrPhyInfo[phyUnit].isEnetPort) |
| 189 | #define ATHR_IS_PHY_ALIVE(phyUnit) (athrPhyInfo[phyUnit].isPhyAlive) |
| 190 | #define ATHR_ETHUNIT(phyUnit) (athrPhyInfo[phyUnit].ethUnit) |
| 191 | #define ATHR_PHYBASE(phyUnit) (athrPhyInfo[phyUnit].phyBase) |
| 192 | #define ATHR_PHYADDR(phyUnit) (athrPhyInfo[phyUnit].phyAddr) |
| 193 | #define ATHR_VLAN_TABLE_SETTING(phyUnit) (athrPhyInfo[phyUnit].VLANTableSetting) |
| 194 | |
| 195 | |
| 196 | #define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \ |
| 197 | (ATHR_IS_ENET_PORT(phyUnit) && \ |
| 198 | ATHR_ETHUNIT(phyUnit) == (ethUnit)) |
| 199 | |
| 200 | #define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN)) |
| 201 | |
| 202 | /* Forward references */ |
| 203 | BOOL athrs26_phy_is_link_alive(int phyUnit); |
| 204 | //static uint32_t athrs26_reg_read(uint16_t reg_addr); |
| 205 | static void athrs26_reg_write(uint16_t reg_addr, |
| 206 | uint32_t reg_val); |
| 207 | |
| 208 | /****************************************************************************** |
| 209 | * |
| 210 | * athrs26_phy_is_link_alive - test to see if the specified link is alive |
| 211 | * |
| 212 | * RETURNS: |
| 213 | * TRUE --> link is alive |
| 214 | * FALSE --> link is down |
| 215 | */ |
| 216 | |
| 217 | void athrs26_reg_init(void) |
| 218 | { |
| 219 | |
| 220 | athrs26_reg_write(0x200, 0x200); |
| 221 | athrs26_reg_write(0x300, 0x200); |
| 222 | athrs26_reg_write(0x400, 0x200); |
| 223 | athrs26_reg_write(0x500, 0x200); |
| 224 | athrs26_reg_write(0x600, 0x7d); |
| 225 | |
| 226 | #ifdef S26_VER_1_0 |
| 227 | phy_reg_write(0, 0, 29, 41); |
| 228 | phy_reg_write(0, 0, 30, 0); |
| 229 | phy_reg_write(0, 1, 29, 41); |
| 230 | phy_reg_write(0, 1, 30, 0); |
| 231 | phy_reg_write(0, 2, 29, 41); |
| 232 | phy_reg_write(0, 2, 30, 0); |
| 233 | phy_reg_write(0, 3, 29, 41); |
| 234 | phy_reg_write(0, 3, 30, 0); |
| 235 | phy_reg_write(0, 4, 29, 41); |
| 236 | phy_reg_write(0, 4, 30, 0); |
| 237 | #endif |
| 238 | |
| 239 | athrs26_reg_write(0x38, 0xc000050e); |
| 240 | |
| 241 | #ifdef CFG_ATHRHDR_EN |
| 242 | athrs26_reg_write(0x104, 0x4804); |
| 243 | #else |
| 244 | athrs26_reg_write(0x104, 0x4004); |
| 245 | #endif |
| 246 | |
| 247 | athrs26_reg_write(0x60, 0xffffffff); |
| 248 | athrs26_reg_write(0x64, 0xaaaaaaaa); |
| 249 | athrs26_reg_write(0x68, 0x55555555); |
| 250 | athrs26_reg_write(0x6c, 0x0); |
| 251 | |
| 252 | athrs26_reg_write(0x70, 0x41af); |
| 253 | } |
| 254 | |
| 255 | BOOL |
| 256 | athrs26_phy_is_link_alive(int phyUnit) |
| 257 | { |
| 258 | uint16_t phyHwStatus; |
| 259 | uint32_t phyBase; |
| 260 | uint32_t phyAddr; |
| 261 | |
| 262 | phyBase = ATHR_PHYBASE(phyUnit); |
| 263 | phyAddr = ATHR_PHYADDR(phyUnit); |
| 264 | |
| 265 | phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS, &phyHwStatus); |
| 266 | |
| 267 | if (phyHwStatus & ATHR_STATUS_LINK_PASS) |
| 268 | return TRUE; |
| 269 | |
| 270 | return FALSE; |
| 271 | } |
| 272 | |
| 273 | |
| 274 | /****************************************************************************** |
| 275 | * |
| 276 | * athrs26_phy_setup - reset and setup the PHY associated with |
| 277 | * the specified MAC unit number. |
| 278 | * |
| 279 | * Resets the associated PHY port. |
| 280 | * |
| 281 | * RETURNS: |
| 282 | * TRUE --> associated PHY is alive |
| 283 | * FALSE --> no LINKs on this ethernet unit |
| 284 | */ |
| 285 | |
| 286 | BOOL |
| 287 | athrs26_phy_setup(int ethUnit) |
| 288 | { |
| 289 | int phyUnit; |
| 290 | uint16_t phyHwStatus; |
| 291 | uint16_t timeout; |
| 292 | int liveLinks = 0; |
| 293 | uint32_t phyBase = 0; |
| 294 | BOOL foundPhy = FALSE; |
| 295 | uint32_t phyAddr = 0; |
| 296 | uint32_t regVal; |
| 297 | |
| 298 | |
| 299 | /* See if there's any configuration data for this enet */ |
| 300 | /* start auto negogiation on each phy */ |
| 301 | for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { |
| 302 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 303 | continue; |
| 304 | } |
| 305 | |
| 306 | |
| 307 | foundPhy = TRUE; |
| 308 | phyBase = ATHR_PHYBASE(phyUnit); |
| 309 | phyAddr = ATHR_PHYADDR(phyUnit); |
| 310 | |
| 311 | phy_reg_write(phyBase, phyAddr, ATHR_AUTONEG_ADVERT, |
| 312 | ATHR_ADVERTISE_ALL); |
| 313 | |
| 314 | /* Reset PHYs*/ |
| 315 | phy_reg_write(phyBase, phyAddr, ATHR_PHY_CONTROL, |
| 316 | ATHR_CTRL_AUTONEGOTIATION_ENABLE |
| 317 | | ATHR_CTRL_SOFTWARE_RESET); |
| 318 | |
| 319 | } |
| 320 | |
| 321 | if (!foundPhy) { |
| 322 | return FALSE; /* No PHY's configured for this ethUnit */ |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | * After the phy is reset, it takes a little while before |
| 327 | * it can respond properly. |
| 328 | */ |
| 329 | sysMsDelay(1000); |
| 330 | |
| 331 | /* |
| 332 | * Wait up to .75 seconds for ALL associated PHYs to finish |
| 333 | * autonegotiation. The only way we get out of here sooner is |
| 334 | * if ALL PHYs are connected AND finish autonegotiation. |
| 335 | */ |
| 336 | for (phyUnit=0; (phyUnit < ATHR_PHY_MAX) /*&& (timeout > 0) */; phyUnit++) { |
| 337 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 338 | continue; |
| 339 | } |
| 340 | |
| 341 | timeout=20; |
| 342 | for (;;) { |
| 343 | phyHwStatus = 0; |
| 344 | phy_reg_read(phyBase, phyAddr, ATHR_PHY_CONTROL, &phyHwStatus); |
| 345 | |
| 346 | if (ATHR_RESET_DONE(phyHwStatus)) { |
| 347 | DRV_PRINT(DRV_DEBUG_PHYSETUP, |
| 348 | ("Port %d, Neg Success\n", phyUnit)); |
| 349 | break; |
| 350 | } |
| 351 | if (timeout == 0) { |
| 352 | DRV_PRINT(DRV_DEBUG_PHYSETUP, |
| 353 | ("Port %d, Negogiation timeout\n", phyUnit)); |
| 354 | break; |
| 355 | } |
| 356 | if (--timeout == 0) { |
| 357 | DRV_PRINT(DRV_DEBUG_PHYSETUP, |
| 358 | ("Port %d, Negogiation timeout\n", phyUnit)); |
| 359 | break; |
| 360 | } |
| 361 | |
| 362 | sysMsDelay(150); |
| 363 | } |
| 364 | } |
| 365 | |
| 366 | /* |
| 367 | * All PHYs have had adequate time to autonegotiate. |
| 368 | * Now initialize software status. |
| 369 | * |
| 370 | * It's possible that some ports may take a bit longer |
| 371 | * to autonegotiate; but we can't wait forever. They'll |
| 372 | * get noticed by mv_phyCheckStatusChange during regular |
| 373 | * polling activities. |
| 374 | */ |
| 375 | for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { |
| 376 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 377 | continue; |
| 378 | } |
| 379 | |
| 380 | if (athrs26_phy_is_link_alive(phyUnit)) { |
| 381 | liveLinks++; |
| 382 | ATHR_IS_PHY_ALIVE(phyUnit) = TRUE; |
| 383 | } else { |
| 384 | ATHR_IS_PHY_ALIVE(phyUnit) = FALSE; |
| 385 | } |
| 386 | |
| 387 | phy_reg_read(ATHR_PHYBASE(phyUnit), ATHR_PHYADDR(phyUnit), |
| 388 | ATHR_PHY_SPEC_STATUS, ®Val); |
| 389 | DRV_PRINT(DRV_DEBUG_PHYSETUP, |
| 390 | ("eth%d: Phy Specific Status=%4.4x\n", ethUnit, regVal)); |
| 391 | } |
| 392 | #if 0 |
| 393 | /* if using header for register configuration, we have to */ |
| 394 | /* configure s26 register after frame transmission is enabled */ |
| 395 | |
| 396 | athrs26_reg_write(0x200, 0x200); |
| 397 | athrs26_reg_write(0x300, 0x200); |
| 398 | athrs26_reg_write(0x400, 0x200); |
| 399 | athrs26_reg_write(0x500, 0x200); |
| 400 | athrs26_reg_write(0x600, 0x200); |
| 401 | athrs26_reg_write(0x38, 0x50e); |
| 402 | #endif |
| 403 | #ifndef CFG_ATHRHDR_EN |
| 404 | /* if using header for register configuration, we have to */ |
| 405 | /* configure s26 register after frame transmission is enabled */ |
| 406 | athrs26_reg_init(); |
| 407 | #endif |
| 408 | |
| 409 | return (liveLinks > 0); |
| 410 | } |
| 411 | |
| 412 | /****************************************************************************** |
| 413 | * |
| 414 | * athrs26_phy_is_fdx - Determines whether the phy ports associated with the |
| 415 | * specified device are FULL or HALF duplex. |
| 416 | * |
| 417 | * RETURNS: |
| 418 | * 1 --> FULL |
| 419 | * 0 --> HALF |
| 420 | */ |
| 421 | int |
| 422 | athrs26_phy_is_fdx(int ethUnit) |
| 423 | { |
| 424 | int phyUnit; |
| 425 | uint32_t phyBase; |
| 426 | uint32_t phyAddr; |
| 427 | uint16_t phyHwStatus; |
| 428 | int ii = 200; |
| 429 | |
| 430 | if (ethUnit == ENET_UNIT_LAN) |
| 431 | return TRUE; |
| 432 | |
| 433 | for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { |
| 434 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 435 | continue; |
| 436 | } |
| 437 | |
| 438 | if (athrs26_phy_is_link_alive(phyUnit)) { |
| 439 | |
| 440 | phyBase = ATHR_PHYBASE(phyUnit); |
| 441 | phyAddr = ATHR_PHYADDR(phyUnit); |
| 442 | |
| 443 | do { |
| 444 | phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS, &phyHwStatus); |
| 445 | sysMsDelay(10); |
| 446 | } while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); |
| 447 | |
| 448 | if (phyHwStatus & ATHER_STATUS_FULL_DEPLEX) |
| 449 | return TRUE; |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | return FALSE; |
| 454 | } |
| 455 | |
| 456 | |
| 457 | /****************************************************************************** |
| 458 | * |
| 459 | * athrs26_phy_speed - Determines the speed of phy ports associated with the |
| 460 | * specified device. |
| 461 | * |
| 462 | * RETURNS: |
| 463 | * AG7100_PHY_SPEED_10T, AG7100_PHY_SPEED_100TX; |
| 464 | * AG7100_PHY_SPEED_1000T; |
| 465 | */ |
| 466 | |
| 467 | BOOL |
| 468 | athrs26_phy_speed(int ethUnit) |
| 469 | { |
| 470 | int phyUnit; |
| 471 | uint16_t phyHwStatus; |
| 472 | uint32_t phyBase; |
| 473 | uint32_t phyAddr; |
| 474 | int ii = 200; |
| 475 | |
| 476 | if (ethUnit == ENET_UNIT_LAN) |
| 477 | return _100BASET; |
| 478 | |
| 479 | for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { |
| 480 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 481 | continue; |
| 482 | } |
| 483 | |
| 484 | if (athrs26_phy_is_link_alive(phyUnit)) { |
| 485 | |
| 486 | phyBase = ATHR_PHYBASE(phyUnit); |
| 487 | phyAddr = ATHR_PHYADDR(phyUnit); |
| 488 | |
| 489 | do { |
| 490 | phy_reg_read(phyBase, phyAddr, |
| 491 | ATHR_PHY_SPEC_STATUS, &phyHwStatus); |
| 492 | sysMsDelay(10); |
| 493 | }while((!(phyHwStatus & ATHR_STATUS_RESOVLED)) && --ii); |
| 494 | |
| 495 | phyHwStatus = ((phyHwStatus & ATHER_STATUS_LINK_MASK) >> |
| 496 | ATHER_STATUS_LINK_SHIFT); |
| 497 | |
| 498 | switch(phyHwStatus) { |
| 499 | case 0: |
| 500 | return _10BASET; |
| 501 | case 1: |
| 502 | return _100BASET; |
| 503 | case 2: |
| 504 | return _1000BASET; |
| 505 | default: |
| 506 | DRV_PRINT(DRV_DEBUG_PHYERROR, ("Unkown speed read!\n")); |
| 507 | } |
| 508 | } |
| 509 | } |
| 510 | |
| 511 | return _10BASET; |
| 512 | } |
| 513 | |
| 514 | /***************************************************************************** |
| 515 | * |
| 516 | * athr_phy_is_up -- checks for significant changes in PHY state. |
| 517 | * |
| 518 | * A "significant change" is: |
| 519 | * dropped link (e.g. ethernet cable unplugged) OR |
| 520 | * autonegotiation completed + link (e.g. ethernet cable plugged in) |
| 521 | * |
| 522 | * When a PHY is plugged in, phyLinkGained is called. |
| 523 | * When a PHY is unplugged, phyLinkLost is called. |
| 524 | */ |
| 525 | |
| 526 | int |
| 527 | athrs26_phy_is_up(int ethUnit) |
| 528 | { |
| 529 | int phyUnit; |
| 530 | uint16_t phyHwStatus; |
| 531 | athrPhyInfo_t *lastStatus; |
| 532 | int linkCount = 0; |
| 533 | int lostLinks = 0; |
| 534 | int gainedLinks = 0; |
| 535 | uint32_t phyBase; |
| 536 | uint32_t phyAddr; |
| 537 | #ifdef CFG_ATHRHDR_REG |
| 538 | /* if using header to config s26, the link of MAC0 should always be up */ |
| 539 | if (ethUnit == ENET_UNIT_LAN) |
| 540 | return 1; |
| 541 | #endif |
| 542 | |
| 543 | for (phyUnit=0; phyUnit < ATHR_PHY_MAX; phyUnit++) { |
| 544 | if (!ATHR_IS_ETHUNIT(phyUnit, ethUnit)) { |
| 545 | continue; |
| 546 | } |
| 547 | |
| 548 | phyBase = ATHR_PHYBASE(phyUnit); |
| 549 | phyAddr = ATHR_PHYADDR(phyUnit); |
| 550 | |
| 551 | |
| 552 | lastStatus = &athrPhyInfo[phyUnit]; |
| 553 | phy_reg_read(phyBase, phyAddr, ATHR_PHY_SPEC_STATUS, &phyHwStatus); |
| 554 | |
| 555 | if (lastStatus->isPhyAlive) { /* last known link status was ALIVE */ |
| 556 | /* See if we've lost link */ |
| 557 | if (phyHwStatus & ATHR_STATUS_LINK_PASS) { |
| 558 | linkCount++; |
| 559 | } else { |
| 560 | lostLinks++; |
| 561 | DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d down\n", |
| 562 | ethUnit, phyUnit)); |
| 563 | lastStatus->isPhyAlive = FALSE; |
| 564 | } |
| 565 | } else { /* last known link status was DEAD */ |
| 566 | /* Check for reset complete */ |
| 567 | phy_reg_read(phyBase, phyAddr, ATHR_PHY_STATUS, &phyHwStatus); |
| 568 | if (!ATHR_RESET_DONE(phyHwStatus)) |
| 569 | continue; |
| 570 | |
| 571 | /* Check for AutoNegotiation complete */ |
| 572 | if (ATHR_AUTONEG_DONE(phyHwStatus)) { |
| 573 | //printk("autoneg done\n"); |
| 574 | gainedLinks++; |
| 575 | linkCount++; |
| 576 | DRV_PRINT(DRV_DEBUG_PHYCHANGE,("\nenet%d port%d up\n", |
| 577 | ethUnit, phyUnit)); |
| 578 | lastStatus->isPhyAlive = TRUE; |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | return (linkCount); |
| 584 | |
| 585 | #if 0 |
| 586 | if (linkCount == 0) { |
| 587 | if (lostLinks) { |
| 588 | /* We just lost the last link for this MAC */ |
| 589 | phyLinkLost(ethUnit); |
| 590 | } |
| 591 | } else { |
| 592 | if (gainedLinks == linkCount) { |
| 593 | /* We just gained our first link(s) for this MAC */ |
| 594 | phyLinkGained(ethUnit); |
| 595 | } |
| 596 | } |
| 597 | #endif |
| 598 | } |
| 599 | |
| 600 | #ifdef CFG_ATHRHDR_EN |
| 601 | void athr_hdr_timeout(void){ |
| 602 | eth_halt(); |
| 603 | NetState = NETLOOP_FAIL; |
| 604 | } |
| 605 | |
| 606 | void athr_hdr_handler(uchar *recv_pkt, unsigned dest, unsigned src, unsigned len){ |
| 607 | header_receive_pkt(recv_pkt); |
| 608 | NetState = NETLOOP_SUCCESS; |
| 609 | } |
| 610 | static int |
| 611 | athrs26_header_config_reg (struct eth_device *dev, uint8_t wr_flag, |
| 612 | uint16_t reg_addr, uint16_t cmd_len, |
| 613 | uint8_t *val) |
| 614 | { |
| 615 | at_header_t at_header; |
| 616 | reg_cmd_t reg_cmd; |
| 617 | uchar *AthrHdrPkt; |
| 618 | |
| 619 | AthrHdrPkt = NetTxPacket; |
| 620 | |
| 621 | if(AthrHdrPkt == NULL) { |
| 622 | printf("Null packet\n"); |
| 623 | return; |
| 624 | } |
| 625 | memset(AthrHdrPkt,0,60); |
| 626 | |
| 627 | /*fill at_header*/ |
| 628 | at_header.reserved0 = 0x10; //default |
| 629 | at_header.priority = 0; |
| 630 | at_header.type = 0x5; |
| 631 | at_header.broadcast = 0; |
| 632 | at_header.from_cpu = 1; |
| 633 | at_header.reserved1 = 0x01; //default |
| 634 | at_header.port_num = 0; |
| 635 | |
| 636 | AthrHdrPkt[0] = at_header.port_num; |
| 637 | AthrHdrPkt[0] |= at_header.reserved1 << 4; |
| 638 | AthrHdrPkt[0] |= at_header.from_cpu << 6; |
| 639 | AthrHdrPkt[0] |= at_header.broadcast << 7; |
| 640 | |
| 641 | AthrHdrPkt[1] = at_header.type; |
| 642 | AthrHdrPkt[1] |= at_header.priority << 4; |
| 643 | AthrHdrPkt[1] |= at_header.reserved0 << 6; |
| 644 | |
| 645 | |
| 646 | /*fill reg cmd*/ |
| 647 | if(cmd_len > 4) |
| 648 | cmd_len = 4;//only support 32bits register r/w |
| 649 | |
| 650 | reg_cmd.reg_addr = reg_addr&0x3FFFC; |
| 651 | reg_cmd.cmd_len = cmd_len; |
| 652 | reg_cmd.cmd = wr_flag; |
| 653 | reg_cmd.reserved2 = 0x5; //default |
| 654 | reg_cmd.seq_num = seqcnt; |
| 655 | |
| 656 | AthrHdrPkt[2] = reg_cmd.reg_addr & 0xff; |
| 657 | AthrHdrPkt[3] = (reg_cmd.reg_addr & 0xff00) >> 8; |
| 658 | AthrHdrPkt[4] = (reg_cmd.reg_addr & 0x30000) >> 16; |
| 659 | AthrHdrPkt[4] |= reg_cmd.cmd_len << 4; |
| 660 | AthrHdrPkt[5] = reg_cmd.cmd << 4; |
| 661 | AthrHdrPkt[5] |= reg_cmd.reserved2 << 5; |
| 662 | AthrHdrPkt[6] = (reg_cmd.seq_num & 0x7f) << 1; |
| 663 | AthrHdrPkt[7] = (reg_cmd.seq_num & 0x7f80) >> 7; |
| 664 | AthrHdrPkt[8] = (reg_cmd.seq_num & 0x7f8000) >> 15; |
| 665 | AthrHdrPkt[9] = (reg_cmd.seq_num & 0x7f800000) >> 23; |
| 666 | |
| 667 | /*fill reg data*/ |
| 668 | if(!wr_flag)//write |
| 669 | memcpy((AthrHdrPkt + 10), val, cmd_len); |
| 670 | |
| 671 | /*start xmit*/ |
| 672 | if(dev == NULL) { |
| 673 | printf("ERROR device not found\n"); |
| 674 | return -1; |
| 675 | } |
| 676 | header_xmit(dev, AthrHdrPkt ,60); |
| 677 | return 0; |
| 678 | } |
| 679 | void athr_hdr_func(void) { |
| 680 | |
| 681 | NetSetTimeout (1 * CFG_HZ,athr_hdr_timeout ); |
| 682 | NetSetHandler (athr_hdr_handler); |
| 683 | |
| 684 | if(cmd) |
| 685 | athrs26_header_config_reg(lan_mac, cmd, cmd_read.reg_addr, cmd_read.cmd_len, cmd_read.reg_data); |
| 686 | else |
| 687 | athrs26_header_config_reg(lan_mac, cmd, cmd_write.reg_addr, cmd_write.cmd_len, cmd_write.reg_data); |
| 688 | } |
| 689 | static int |
| 690 | athrs26_header_write_reg(uint16_t reg_addr, uint16_t cmd_len, uint8_t *reg_data) |
| 691 | { |
| 692 | int i = 2; |
| 693 | cmd_write.reg_addr = reg_addr; |
| 694 | cmd_write.cmd_len = cmd_len; |
| 695 | cmd_write.reg_data = reg_data; |
| 696 | cmd = 0; |
| 697 | seqcnt++; |
| 698 | |
| 699 | do { |
| 700 | if (NetLoop(ATHRHDR) >= 0) /* polls for read/write ack from PHY */ |
| 701 | break; |
| 702 | } while (i--); |
| 703 | |
| 704 | return i; |
| 705 | } |
| 706 | |
| 707 | static int |
| 708 | athrs26_header_read_reg(uint16_t reg_addr, uint16_t cmd_len, uint8_t *reg_data) |
| 709 | { |
| 710 | int i = 2; |
| 711 | |
| 712 | cmd_read.reg_addr = reg_addr; |
| 713 | cmd_read.cmd_len = cmd_len; |
| 714 | cmd_read.reg_data = reg_data; |
| 715 | cmd = 1; |
| 716 | seqcnt++; |
| 717 | |
| 718 | do { |
| 719 | if (NetLoop(ATHRHDR) >= 0) /* polls for read/write ack from PHY */ |
| 720 | break; |
| 721 | } while (i--); |
| 722 | |
| 723 | if ((i==0) || (seqcnt != cmd_resp.seq) || (cmd_len != cmd_resp.len)) { |
| 724 | return -1; |
| 725 | } |
| 726 | memcpy (cmd_read.reg_data, cmd_resp.data, cmd_len); |
| 727 | return 0; |
| 728 | } |
| 729 | int header_receive_pkt(uchar *recv_pkt) |
| 730 | { |
| 731 | cmd_resp.len = recv_pkt[4] >> 4; |
| 732 | if (cmd_resp.len > 10) |
| 733 | goto out; |
| 734 | |
| 735 | cmd_resp.seq = recv_pkt[6] >> 1; |
| 736 | cmd_resp.seq |= recv_pkt[7] << 7; |
| 737 | cmd_resp.seq |= recv_pkt[8] << 15; |
| 738 | cmd_resp.seq |= recv_pkt[9] << 23; |
| 739 | |
| 740 | if (cmd_resp.seq < seqcnt) |
| 741 | goto out; |
| 742 | memcpy (cmd_resp.data, (recv_pkt + 10), cmd_resp.len); |
| 743 | out: |
| 744 | return 0; |
| 745 | } |
| 746 | |
| 747 | void athrs26_reg_dev(struct eth_device *mac) |
| 748 | { |
| 749 | lan_mac = mac; |
| 750 | } |
| 751 | |
| 752 | #endif |
| 753 | |
| 754 | /*static uint32_t |
| 755 | athrs26_reg_read(uint16_t reg_addr) |
| 756 | { |
| 757 | #ifndef CFG_ATHRHDR_REG |
| 758 | uint16_t reg_word_addr = reg_addr / 2, phy_val; |
| 759 | uint32_t phy_addr; |
| 760 | uint8_t phy_reg; |
| 761 | |
| 762 | phy_addr = 0x18; |
| 763 | phy_reg = 0x0; |
| 764 | phy_val = (reg_word_addr >> 8) & 0x1ff; |
| 765 | phy_reg_write (0, phy_addr, phy_reg, phy_val); |
| 766 | |
| 767 | phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); |
| 768 | phy_reg = reg_word_addr & 0x1f; |
| 769 | phy_reg_read(0, phy_addr, phy_reg, &phy_val); |
| 770 | |
| 771 | return phy_val; |
| 772 | #else |
| 773 | uint8_t reg_data[4]; |
| 774 | |
| 775 | memset (reg_data, 0, 4); |
| 776 | athrs26_header_read_reg(reg_addr, 4, reg_data); |
| 777 | return (reg_data[0] | (reg_data[1] << 8) | (reg_data[2] << 16) | (reg_data[3] << 24)); |
| 778 | #endif |
| 779 | } |
| 780 | */ |
| 781 | static void |
| 782 | athrs26_reg_write(uint16_t reg_addr, uint32_t reg_val) |
| 783 | { |
| 784 | #ifndef CFG_ATHRHDR_REG |
| 785 | uint16_t reg_word_addr = reg_addr / 2, phy_val; |
| 786 | uint32_t phy_addr; |
| 787 | uint8_t phy_reg; |
| 788 | |
| 789 | /* configure register high address */ |
| 790 | phy_addr = 0x18; |
| 791 | phy_reg = 0x0; |
| 792 | phy_val = (reg_word_addr >> 8) & 0x1ff; /* bit16-8 of reg address*/ |
| 793 | phy_reg_write (0, phy_addr, phy_reg, phy_val); |
| 794 | |
| 795 | /* read register with low address */ |
| 796 | phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ |
| 797 | phy_reg = reg_word_addr & 0x1f; /* bit 4-0 of reg address */ |
| 798 | phy_reg_write (0, phy_addr, phy_reg, reg_val); |
| 799 | #else |
| 800 | uint8_t reg_data[4]; |
| 801 | |
| 802 | memset (reg_data, 0, 4); |
| 803 | reg_data[0] = (uint8_t)(0x00ff & reg_val); |
| 804 | reg_data[1] = (uint8_t)((0xff00 & reg_val) >> 8); |
| 805 | reg_data[2] = (uint8_t)((0xff0000 & reg_val) >> 16); |
| 806 | reg_data[3] = (uint8_t)((0xff000000 & reg_val) >> 24); |
| 807 | |
| 808 | athrs26_header_write_reg (reg_addr, 4, reg_data); |
| 809 | #endif |
| 810 | |
| 811 | } |
| 812 | |
| 813 | |