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
6 * Copyright © 2003 Atheros Communications, Inc., All Rights Reserved.
10 * Manage the atheros ethernet PHY.
12 * All definitions in this file are operating system independent!
16 #include <linux/types.h>
20 //#include "ar7100_soc.h"
21 #include "athrs26_phy.h"
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);
29 /* PHY selections and access functions */
47 #define DRV_DEBUG_PHYERROR 0x00000001
48 #define DRV_DEBUG_PHYCHANGE 0x00000002
49 #define DRV_DEBUG_PHYSETUP 0x00000004
52 int athrPhyDebug
= DRV_DEBUG_PHYERROR
|DRV_DEBUG_PHYCHANGE
|DRV_DEBUG_PHYSETUP
;
54 #define DRV_LOG(FLG, X0, X1, X2, X3, X4, X5, X6) \
56 if (athrPhyDebug & (FLG)) { \
57 logMsg(X0, X1, X2, X3, X4, X5, X6); \
61 #define DRV_MSG(x,a,b,c,d,e,f) \
64 #define DRV_PRINT(FLG, X) \
66 if (athrPhyDebug & (FLG)) { \
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)
77 #define ATHR_LAN_PORT_VLAN 1
78 #define ATHR_WAN_PORT_VLAN 2
80 #define ENET_UNIT_LAN 0
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
92 * Track per-PHY port information.
95 BOOL isEnetPort
; /* normal enet port */
96 BOOL isPhyAlive
; /* last known state of link */
97 int ethUnit
; /* MAC associated with this phy port */
99 uint32_t phyAddr
; /* PHY registers associated with this phy port */
100 uint32_t VLANTableSetting
; /* Value to be written to VLAN table */
104 * Per-PHY information, indexed by PHY unit number.
106 static athrPhyInfo_t athrPhyInfo
[] = {
107 {TRUE
, /* phy port 0 -- LAN port 0 */
115 {TRUE
, /* phy port 1 -- LAN port 1 */
123 {TRUE
, /* phy port 2 -- LAN port 2 */
131 {TRUE
, /* phy port 3 -- LAN port 3 */
139 {TRUE
, /* phy port 4 -- WAN port or LAN port 4 */
144 ATHR_LAN_PORT_VLAN
/* Send to all ports */
147 {FALSE
, /* phy port 5 -- CPU port (no RJ45 connector) */
152 ATHR_LAN_PORT_VLAN
/* Send to all ports */
156 #ifdef CFG_ATHRHDR_EN
158 uint8_t data
[ATHRHDR_MAX_DATA
];
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;
175 //volatile uchar AthrHdrPkt[60];
178 #define ATHR_GLOBALREGBASE 0
180 //#define ATHR_PHY_MAX (sizeof(athrPhyInfo) / sizeof(athrPhyInfo[0]))
181 #define ATHR_PHY_MAX 5
183 /* Range of valid PHY IDs is [MIN..MAX] */
184 #define ATHR_ID_MIN 0
185 #define ATHR_ID_MAX (ATHR_PHY_MAX-1)
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)
196 #define ATHR_IS_ETHUNIT(phyUnit, ethUnit) \
197 (ATHR_IS_ENET_PORT(phyUnit) && \
198 ATHR_ETHUNIT(phyUnit) == (ethUnit))
200 #define ATHR_IS_WAN_PORT(phyUnit) (!(ATHR_ETHUNIT(phyUnit)==ENET_UNIT_LAN))
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
,
208 /******************************************************************************
210 * athrs26_phy_is_link_alive - test to see if the specified link is alive
213 * TRUE --> link is alive
214 * FALSE --> link is down
217 void athrs26_reg_init(void)
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);
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);
239 athrs26_reg_write(0x38, 0xc000050e);
241 #ifdef CFG_ATHRHDR_EN
242 athrs26_reg_write(0x104, 0x4804);
244 athrs26_reg_write(0x104, 0x4004);
247 athrs26_reg_write(0x60, 0xffffffff);
248 athrs26_reg_write(0x64, 0xaaaaaaaa);
249 athrs26_reg_write(0x68, 0x55555555);
250 athrs26_reg_write(0x6c, 0x0);
252 athrs26_reg_write(0x70, 0x41af);
256 athrs26_phy_is_link_alive(int phyUnit
)
258 uint16_t phyHwStatus
;
262 phyBase
= ATHR_PHYBASE(phyUnit
);
263 phyAddr
= ATHR_PHYADDR(phyUnit
);
265 phy_reg_read(phyBase
, phyAddr
, ATHR_PHY_SPEC_STATUS
, &phyHwStatus
);
267 if (phyHwStatus
& ATHR_STATUS_LINK_PASS
)
274 /******************************************************************************
276 * athrs26_phy_setup - reset and setup the PHY associated with
277 * the specified MAC unit number.
279 * Resets the associated PHY port.
282 * TRUE --> associated PHY is alive
283 * FALSE --> no LINKs on this ethernet unit
287 athrs26_phy_setup(int ethUnit
)
290 uint16_t phyHwStatus
;
293 uint32_t phyBase
= 0;
294 BOOL foundPhy
= FALSE
;
295 uint32_t phyAddr
= 0;
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
)) {
308 phyBase
= ATHR_PHYBASE(phyUnit
);
309 phyAddr
= ATHR_PHYADDR(phyUnit
);
311 phy_reg_write(phyBase
, phyAddr
, ATHR_AUTONEG_ADVERT
,
315 phy_reg_write(phyBase
, phyAddr
, ATHR_PHY_CONTROL
,
316 ATHR_CTRL_AUTONEGOTIATION_ENABLE
317 | ATHR_CTRL_SOFTWARE_RESET
);
322 return FALSE
; /* No PHY's configured for this ethUnit */
326 * After the phy is reset, it takes a little while before
327 * it can respond properly.
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.
336 for (phyUnit
=0; (phyUnit
< ATHR_PHY_MAX
) /*&& (timeout > 0) */; phyUnit
++) {
337 if (!ATHR_IS_ETHUNIT(phyUnit
, ethUnit
)) {
344 phy_reg_read(phyBase
, phyAddr
, ATHR_PHY_CONTROL
, &phyHwStatus
);
346 if (ATHR_RESET_DONE(phyHwStatus
)) {
347 DRV_PRINT(DRV_DEBUG_PHYSETUP
,
348 ("Port %d, Neg Success\n", phyUnit
));
352 DRV_PRINT(DRV_DEBUG_PHYSETUP
,
353 ("Port %d, Negogiation timeout\n", phyUnit
));
356 if (--timeout
== 0) {
357 DRV_PRINT(DRV_DEBUG_PHYSETUP
,
358 ("Port %d, Negogiation timeout\n", phyUnit
));
367 * All PHYs have had adequate time to autonegotiate.
368 * Now initialize software status.
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.
375 for (phyUnit
=0; phyUnit
< ATHR_PHY_MAX
; phyUnit
++) {
376 if (!ATHR_IS_ETHUNIT(phyUnit
, ethUnit
)) {
380 if (athrs26_phy_is_link_alive(phyUnit
)) {
382 ATHR_IS_PHY_ALIVE(phyUnit
) = TRUE
;
384 ATHR_IS_PHY_ALIVE(phyUnit
) = FALSE
;
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
));
393 /* if using header for register configuration, we have to */
394 /* configure s26 register after frame transmission is enabled */
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);
403 #ifndef CFG_ATHRHDR_EN
404 /* if using header for register configuration, we have to */
405 /* configure s26 register after frame transmission is enabled */
409 return (liveLinks
> 0);
412 /******************************************************************************
414 * athrs26_phy_is_fdx - Determines whether the phy ports associated with the
415 * specified device are FULL or HALF duplex.
422 athrs26_phy_is_fdx(int ethUnit
)
427 uint16_t phyHwStatus
;
430 if (ethUnit
== ENET_UNIT_LAN
)
433 for (phyUnit
=0; phyUnit
< ATHR_PHY_MAX
; phyUnit
++) {
434 if (!ATHR_IS_ETHUNIT(phyUnit
, ethUnit
)) {
438 if (athrs26_phy_is_link_alive(phyUnit
)) {
440 phyBase
= ATHR_PHYBASE(phyUnit
);
441 phyAddr
= ATHR_PHYADDR(phyUnit
);
444 phy_reg_read(phyBase
, phyAddr
, ATHR_PHY_SPEC_STATUS
, &phyHwStatus
);
446 } while((!(phyHwStatus
& ATHR_STATUS_RESOVLED
)) && --ii
);
448 if (phyHwStatus
& ATHER_STATUS_FULL_DEPLEX
)
457 /******************************************************************************
459 * athrs26_phy_speed - Determines the speed of phy ports associated with the
463 * AG7100_PHY_SPEED_10T, AG7100_PHY_SPEED_100TX;
464 * AG7100_PHY_SPEED_1000T;
468 athrs26_phy_speed(int ethUnit
)
471 uint16_t phyHwStatus
;
476 if (ethUnit
== ENET_UNIT_LAN
)
479 for (phyUnit
=0; phyUnit
< ATHR_PHY_MAX
; phyUnit
++) {
480 if (!ATHR_IS_ETHUNIT(phyUnit
, ethUnit
)) {
484 if (athrs26_phy_is_link_alive(phyUnit
)) {
486 phyBase
= ATHR_PHYBASE(phyUnit
);
487 phyAddr
= ATHR_PHYADDR(phyUnit
);
490 phy_reg_read(phyBase
, phyAddr
,
491 ATHR_PHY_SPEC_STATUS
, &phyHwStatus
);
493 }while((!(phyHwStatus
& ATHR_STATUS_RESOVLED
)) && --ii
);
495 phyHwStatus
= ((phyHwStatus
& ATHER_STATUS_LINK_MASK
) >>
496 ATHER_STATUS_LINK_SHIFT
);
498 switch(phyHwStatus
) {
506 DRV_PRINT(DRV_DEBUG_PHYERROR
, ("Unkown speed read!\n"));
514 /*****************************************************************************
516 * athr_phy_is_up -- checks for significant changes in PHY state.
518 * A "significant change" is:
519 * dropped link (e.g. ethernet cable unplugged) OR
520 * autonegotiation completed + link (e.g. ethernet cable plugged in)
522 * When a PHY is plugged in, phyLinkGained is called.
523 * When a PHY is unplugged, phyLinkLost is called.
527 athrs26_phy_is_up(int ethUnit
)
530 uint16_t phyHwStatus
;
531 athrPhyInfo_t
*lastStatus
;
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
)
543 for (phyUnit
=0; phyUnit
< ATHR_PHY_MAX
; phyUnit
++) {
544 if (!ATHR_IS_ETHUNIT(phyUnit
, ethUnit
)) {
548 phyBase
= ATHR_PHYBASE(phyUnit
);
549 phyAddr
= ATHR_PHYADDR(phyUnit
);
552 lastStatus
= &athrPhyInfo
[phyUnit
];
553 phy_reg_read(phyBase
, phyAddr
, ATHR_PHY_SPEC_STATUS
, &phyHwStatus
);
555 if (lastStatus
->isPhyAlive
) { /* last known link status was ALIVE */
556 /* See if we've lost link */
557 if (phyHwStatus
& ATHR_STATUS_LINK_PASS
) {
561 DRV_PRINT(DRV_DEBUG_PHYCHANGE
,("\nenet%d port%d down\n",
563 lastStatus
->isPhyAlive
= FALSE
;
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
))
571 /* Check for AutoNegotiation complete */
572 if (ATHR_AUTONEG_DONE(phyHwStatus
)) {
573 //printk("autoneg done\n");
576 DRV_PRINT(DRV_DEBUG_PHYCHANGE
,("\nenet%d port%d up\n",
578 lastStatus
->isPhyAlive
= TRUE
;
586 if (linkCount
== 0) {
588 /* We just lost the last link for this MAC */
589 phyLinkLost(ethUnit
);
592 if (gainedLinks
== linkCount
) {
593 /* We just gained our first link(s) for this MAC */
594 phyLinkGained(ethUnit
);
600 #ifdef CFG_ATHRHDR_EN
601 void athr_hdr_timeout(void){
603 NetState
= NETLOOP_FAIL
;
606 void athr_hdr_handler(uchar
*recv_pkt
, unsigned dest
, unsigned src
, unsigned len
){
607 header_receive_pkt(recv_pkt
);
608 NetState
= NETLOOP_SUCCESS
;
611 athrs26_header_config_reg (struct eth_device
*dev
, uint8_t wr_flag
,
612 uint16_t reg_addr
, uint16_t cmd_len
,
615 at_header_t at_header
;
619 AthrHdrPkt
= NetTxPacket
;
621 if(AthrHdrPkt
== NULL
) {
622 printf("Null packet\n");
625 memset(AthrHdrPkt
,0,60);
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;
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;
641 AthrHdrPkt
[1] = at_header
.type
;
642 AthrHdrPkt
[1] |= at_header
.priority
<< 4;
643 AthrHdrPkt
[1] |= at_header
.reserved0
<< 6;
648 cmd_len
= 4;//only support 32bits register r/w
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
;
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;
669 memcpy((AthrHdrPkt
+ 10), val
, cmd_len
);
673 printf("ERROR device not found\n");
676 header_xmit(dev
, AthrHdrPkt
,60);
679 void athr_hdr_func(void) {
681 NetSetTimeout (1 * CFG_HZ
,athr_hdr_timeout
);
682 NetSetHandler (athr_hdr_handler
);
685 athrs26_header_config_reg(lan_mac
, cmd
, cmd_read
.reg_addr
, cmd_read
.cmd_len
, cmd_read
.reg_data
);
687 athrs26_header_config_reg(lan_mac
, cmd
, cmd_write
.reg_addr
, cmd_write
.cmd_len
, cmd_write
.reg_data
);
690 athrs26_header_write_reg(uint16_t reg_addr
, uint16_t cmd_len
, uint8_t *reg_data
)
693 cmd_write
.reg_addr
= reg_addr
;
694 cmd_write
.cmd_len
= cmd_len
;
695 cmd_write
.reg_data
= reg_data
;
700 if (NetLoop(ATHRHDR
) >= 0) /* polls for read/write ack from PHY */
708 athrs26_header_read_reg(uint16_t reg_addr
, uint16_t cmd_len
, uint8_t *reg_data
)
712 cmd_read
.reg_addr
= reg_addr
;
713 cmd_read
.cmd_len
= cmd_len
;
714 cmd_read
.reg_data
= reg_data
;
719 if (NetLoop(ATHRHDR
) >= 0) /* polls for read/write ack from PHY */
723 if ((i
==0) || (seqcnt
!= cmd_resp
.seq
) || (cmd_len
!= cmd_resp
.len
)) {
726 memcpy (cmd_read
.reg_data
, cmd_resp
.data
, cmd_len
);
729 int header_receive_pkt(uchar
*recv_pkt
)
731 cmd_resp
.len
= recv_pkt
[4] >> 4;
732 if (cmd_resp
.len
> 10)
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;
740 if (cmd_resp
.seq
< seqcnt
)
742 memcpy (cmd_resp
.data
, (recv_pkt
+ 10), cmd_resp
.len
);
747 void athrs26_reg_dev(struct eth_device
*mac
)
755 athrs26_reg_read(uint16_t reg_addr)
757 #ifndef CFG_ATHRHDR_REG
758 uint16_t reg_word_addr = reg_addr / 2, phy_val;
764 phy_val = (reg_word_addr >> 8) & 0x1ff;
765 phy_reg_write (0, phy_addr, phy_reg, phy_val);
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);
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));
782 athrs26_reg_write(uint16_t reg_addr
, uint32_t reg_val
)
784 #ifndef CFG_ATHRHDR_REG
785 uint16_t reg_word_addr
= reg_addr
/ 2, phy_val
;
789 /* configure register high address */
792 phy_val
= (reg_word_addr
>> 8) & 0x1ff; /* bit16-8 of reg address*/
793 phy_reg_write (0, phy_addr
, phy_reg
, phy_val
);
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
);
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);
808 athrs26_header_write_reg (reg_addr
, 4, reg_data
);