123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- MODULE EnetPhy;
- (**
- AUTHOR: Alexey Morozov, HighDim GmbH, 2015
- PURPOSE: Ethernet networking stack, PHY management
- *)
- IMPORT
- S := SYSTEM, EnetTiming, EnetBase;
- TYPE
- Int8 = EnetBase.Int8;
- Int16 = EnetBase.Int16;
- Int = EnetBase.Int;
- UInt = EnetBase.UInt;
- CONST
- InvalidPhyAddr* = 0xFF; (** Invalid PHY address *)
- (**
- Generic MII registers.
- *)
- MII_BMCR* = 0x00; (** Basic mode control register *)
- MII_BMSR* = 0x01; (** Basic mode status register *)
- MII_PHYSID1* = 0x02; (** PHYS ID 1 *)
- MII_PHYSID2* = 0x03; (** PHYS ID 2 *)
- MII_ADVERTISE* = 0x04; (** Advertisement control reg *)
- MII_LPA* = 0x05; (** Link partner ability reg *)
- MII_EXPANSION* = 0x06; (** Expansion register *)
- MII_CTRL1000* = 0x09; (** 1000BASE-T control *)
- MII_STAT1000* = 0x0a; (** 1000BASE-T status *)
- MII_ESTATUS* = 0x0f; (** Extended Status *)
- MII_DCOUNTER* = 0x12; (** Disconnect counter *)
- MII_FCSCOUNTER* = 0x13; (** False carrier counter *)
- MII_NWAYTEST* = 0x14; (** N-way auto-neg test reg *)
- MII_RERRCOUNTER* = 0x15; (** Receive error counter *)
- MII_SREVISION* = 0x16; (** Silicon revision *)
- MII_RESV1* = 0x17; (** Reserved *)
- MII_LBRERROR* = 0x18; (** Lpback, rx, bypass error *)
- MII_PHYADDR* = 0x19; (** PHY address *)
- MII_RESV2* = 0x1a; (** Reserved *)
- MII_TPISTATUS* = 0x1b; (** TPI status for 10mbps *)
- MII_NCONFIG* = 0x1c; (** Network interface config *)
- (** Basic mode control register. *)
- BMCR_RESV* = 0x003f; (** Reserved *)
- BMCR_SPEED1000* = 0x0040; (** MSB of Speed (1000) *)
- BMCR_CTST* = 0x0080; (** Collision test *)
- BMCR_FULLDPLX* = 0x0100; (** Full duplex *)
- BMCR_ANRESTART* = 0x0200; (** Auto negotiation restart *)
- BMCR_ISOLATE* = 0x0400; (** Disconnect DP83840 from MII *)
- BMCR_PDOWN* = 0x0800; (** Powerdown the DP83840 *)
- BMCR_ANENABLE* = 0x1000; (** Enable auto negotiation *)
- BMCR_SPEED100* = 0x2000; (** Select 100Mbps *)
- BMCR_LOOPBACK* = 0x4000; (** TXD loopback bits *)
- BMCR_RESET* = 0x8000; (** Reset the DP83840 *)
- (** Basic mode status register. *)
- BMSR_ERCAP* = 0x0001; (** Ext-reg capability *)
- BMSR_JCD* = 0x0002; (** Jabber detected *)
- BMSR_LSTATUS* = 0x0004; (** Link status *)
- BMSR_ANEGCAPABLE* = 0x0008; (** Able to do auto-negotiation *)
- BMSR_RFAULT* = 0x0010; (** Remote fault detected *)
- BMSR_ANEGCOMPLETE* = 0x0020; (** Auto-negotiation complete *)
- BMSR_RESV* = 0x00c0; (** Reserved *)
- BMSR_ESTATEN* = 0x0100; (** Extended Status in R15 *)
- BMSR_100FULL2* = 0x0200; (** Can do 100BASE-T2 HDX *)
- BMSR_100HALF2* = 0x0400; (** Can do 100BASE-T2 FDX *)
- BMSR_10HALF* = 0x0800; (** Can do 10mbps, half-duplex *)
- BMSR_10FULL* = 0x1000; (** Can do 10mbps, full-duplex *)
- BMSR_100HALF* = 0x2000; (** Can do 100mbps, half-duplex *)
- BMSR_100FULL* = 0x4000; (** Can do 100mbps, full-duplex *)
- BMSR_100BASE4* = 0x8000; (** Can do 100mbps, 4k packets *)
- (** Advertisement control register. *)
- ADVERTISE_SLCT* = 0x001f; (** Selector bits *)
- ADVERTISE_CSMA* = 0x0001; (** Only selector supported *)
- ADVERTISE_10HALF* = 0x0020; (** Try for 10mbps half-duplex *)
- ADVERTISE_1000XFULL* = 0x0020; (** Try for 1000BASE-X full-duplex *)
- ADVERTISE_10FULL* = 0x0040; (** Try for 10mbps full-duplex *)
- ADVERTISE_1000XHALF* = 0x0040; (** Try for 1000BASE-X half-duplex *)
- ADVERTISE_100HALF* = 0x0080; (** Try for 100mbps half-duplex *)
- ADVERTISE_1000XPAUSE* = 0x0080; (** Try for 1000BASE-X pause *)
- ADVERTISE_100FULL* = 0x0100; (** Try for 100mbps full-duplex *)
- ADVERTISE_1000XPSE_ASYM* = 0x0100; (** Try for 1000BASE-X asym pause *)
- ADVERTISE_100BASE4* = 0x0200; (** Try for 100mbps 4k packets *)
- ADVERTISE_PAUSE_CAP* = 0x0400; (** Try for pause *)
- ADVERTISE_PAUSE_ASYM* = 0x0800; (** Try for asymetric pause *)
- ADVERTISE_RESV* = 0x1000; (** Reserved *)
- ADVERTISE_RFAULT* = 0x2000; (** Say we can detect faults *)
- ADVERTISE_LPACK* = 0x4000; (** Ack link partners response *)
- ADVERTISE_NPAGE* = 0x8000; (** Next page bit *)
- ADVERTISE_FULL* = ADVERTISE_100FULL + ADVERTISE_10FULL + ADVERTISE_CSMA;
- ADVERTISE_ALL* = ADVERTISE_10HALF + ADVERTISE_10FULL + ADVERTISE_100HALF + ADVERTISE_100FULL;
- (** Link partner ability register. *)
- LPA_SLCT* = 0x001f; (** Same as advertise selector *)
- LPA_10HALF* = 0x0020; (** Can do 10mbps half-duplex *)
- LPA_1000XFULL* = 0x0020; (** Can do 1000BASE-X full-duplex *)
- LPA_10FULL* = 0x0040; (** Can do 10mbps full-duplex *)
- LPA_1000XHALF* = 0x0040; (** Can do 1000BASE-X half-duplex *)
- LPA_100HALF* = 0x0080; (** Can do 100mbps half-duplex *)
- LPA_1000XPAUSE* = 0x0080; (** Can do 1000BASE-X pause *)
- LPA_100FULL* = 0x0100; (** Can do 100mbps full-duplex *)
- LPA_1000XPAUSE_ASYM* = 0x0100; (** Can do 1000BASE-X pause asym *)
- LPA_100BASE4* = 0x0200; (** Can do 100mbps 4k packets *)
- LPA_PAUSE_CAP* = 0x0400; (** Can pause *)
- LPA_PAUSE_ASYM* = 0x0800; (** Can pause asymetrically *)
- LPA_RESV* = 0x1000; (** Reserved *)
- LPA_RFAULT* = 0x2000; (** Link partner faulted *)
- LPA_LPACK* = 0x4000; (** Link partner acked us *)
- LPA_NPAGE* = 0x8000; (** Next page bit *)
- LPA_DUPLEX* = LPA_10FULL + LPA_100FULL;
- LPA_100* = LPA_100FULL + LPA_100HALF + LPA_100BASE4;
- (** Expansion register for auto-negotiation. *)
- EXPANSION_NWAY* = 0x0001; (** Can do N-way auto-nego *)
- EXPANSION_LCWP* = 0x0002; (** Got new RX page code word *)
- EXPANSION_ENABLENPAGE* = 0x0004; (** This enables npage words *)
- EXPANSION_NPCAPABLE* = 0x0008; (** Link partner supports npage *)
- EXPANSION_MFAULTS* = 0x0010; (** Multiple faults detected *)
- EXPANSION_RESV* = 0xffe0; (** Reserved *)
- ESTATUS_1000_TFULL* = 0x2000; (** Can do 1000BT Full *)
- ESTATUS_1000_THALF* = 0x1000; (** Can do 1000BT Half *)
- (** N-way test register. *)
- NWAYTEST_RESV1* = 0x00ff; (** Reserved *)
- NWAYTEST_LOOPBACK* = 0x0100; (** Enable loopback for N-way *)
- NWAYTEST_RESV2* = 0xfe00; (** Reserved *)
- (** 1000BASE-T Control register *)
- ADVERTISE_1000FULL* = 0x0200; (** Advertise 1000BASE-T full duplex *)
- ADVERTISE_1000HALF* = 0x0100; (** Advertise 1000BASE-T half duplex *)
- (** 1000BASE-T Status register *)
- LPA_1000LOCALRXOK* = 0x2000; (** Link partner local receiver status *)
- LPA_1000REMRXOK* = 0x1000; (** Link partner remote receiver status *)
- LPA_1000FULL* = 0x0800; (** Link partner 1000BASE-T full duplex *)
- LPA_1000HALF* = 0x0400; (** Link partner 1000BASE-T half duplex *)
-
- (**
- Detect PHY device
- phyAddr: retuned PHY address (EnetBase.InvalidPhyAddr in case if no PHY found)
- allowZeroPhyAddr: TRUE to allow the use of PHY address 0
- res: error code
- *)
- PROCEDURE DetectPhy*(dev: EnetBase.LinkDevice; VAR phyAddr: UInt; allowZeroPhyAddr: BOOLEAN; VAR res: Int): BOOLEAN;
- VAR
- minAddr: UInt;
- d1, d2: UInt;
- BEGIN
- (*
- PHY address 0 cannot be used when multiple PHY's a sitting on the same MDIO serial bus.
- This address is reserved for broadcasting and can work only for writing (e.g. a command to
- reset multiple PHY's at the same time). Reading from this address will lead to contention on the bus.
- *)
- IF allowZeroPhyAddr THEN minAddr := 0; ELSE minAddr := 1; END;
- phyAddr := 32;
- REPEAT
- DEC(phyAddr);
- IF ~dev.phyRead(dev,phyAddr,MII_PHYSID1,d1,res) OR ~dev.phyRead(dev,phyAddr,MII_PHYSID2,d2,res) THEN
- phyAddr := InvalidPhyAddr;
- RETURN FALSE;
- END;
- IF (d1 # 0xFFFF) & (d2 # 0xFFFF) THEN
- res := 0;
- RETURN TRUE;
- END;
- UNTIL phyAddr = minAddr;
- phyAddr := InvalidPhyAddr;
- res := EnetBase.ErrPhyNotDetected;
- RETURN FALSE;
- END DetectPhy;
- (**
- Reset a PHY device
- *)
- PROCEDURE ResetPhy*(dev: EnetBase.LinkDevice; phyAddr: UInt; timeoutInMs: EnetTiming.Time; VAR res: Int): BOOLEAN;
- VAR
- d: UInt;
- t: EnetTiming.Timer;
- BEGIN
- IF ~dev.phyRead(dev,phyAddr,MII_BMCR,d,res) THEN RETURN FALSE; END;
- d := S.MSK(d,-BMCR_RESET-1) + BMCR_RESET;
- IF ~dev.phyWrite(dev,phyAddr,MII_BMCR,d,res) THEN RETURN FALSE; END;
- EnetTiming.SetTimerMilli(t,timeoutInMs);
- EnetTiming.StartTimer(t);
- REPEAT
- IF ~dev.phyRead(dev,phyAddr,MII_BMCR,d,res) THEN RETURN FALSE; END;
- UNTIL (S.MSK(d,BMCR_RESET) = 0) OR EnetTiming.IsTimerExpired(t);
- IF S.MSK(d,BMCR_RESET) = 0 THEN
- res := 0;
- RETURN TRUE;
- ELSE
- res := EnetBase.ErrTimeoutExpired;
- RETURN FALSE;
- END;
- END ResetPhy;
- END EnetPhy.
|