/* * Copyright (C) 2010 by Ulrich Prinz (uprinz2@netscape.net) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /* * \verbatim * $Id: phy_drv.c 3143 2010-09-29 20:13:51Z Astralix $ * \endverbatim */ #include #include #include #include #include #include /* WARNING: Variadic macros are C99 and may fail with C89 compilers. */ #ifdef NUTDEBUG #include #define PHPRINTF(args,...) printf(args,##__VA_ARGS__); fflush(stdout); #else #define PHPRINTF(args,...) #endif /* * Basic Set Register Map: * This register map is valid for all manufactureres and chip. * Though some bits might not be supported. */ #define PHY_BMCR 0x00 /* Basic Mode Control Register */ #define PHY_BMSR 0x01 /* Basic Mode Status Register */ #define PHY_ID1 0x02 /* OUI High Register */ #define PHY_ID2 0x03 /* OUI Low Register */ #define PHY_ANAR 0x04 /* Auto-Negotiation Ability Register */ #define PHY_ANLP 0x05 /* Auto-Negotiation Link Partner Advertisement Register */ #define PHY_ANER 0x06 /* Auto-Negotiation Expansion Register */ #define PHY_ANTR 0x07 /* Auto-Negotiation Next Page TX Register */ /* * Basic Mode Control Register Options */ #define PHY_BMCR_RES 0x8000 /* 1: Reset PHY, flips to 0 if reset accomplished. */ #define PHY_BMCR_LOOP 0x4000 /* 1: Enable Loopback Mode. */ #define PHY_BMCR_SPEED 0x2000 /* 1: Manual Speed 100Mbit, 0: 10MBit). */ #define PHY_BMCR_ANEG 0x1000 /* 1: Enable Auto-Negotiation. */ #define PHY_BMCR_PDWN 0x0800 /* 1: Power Down Enabled. */ #define PHY_BMCR_ISO 0x0400 /* 1: Isolate PHY from MII interface, only MDIO is still available. */ #define PHY_BMCR_ANST 0x0200 /* 1: Restart Auto-Negotiation, flips to 0 if successfull. */ #define PHY_BMCR_DUPX 0x0100 /* 1: Enable Full Duplex Operation. */ #define PHYSET_COLTEST 0x0080 /* 1: Collision test enabled. */ /* * Basic Mode Status Register Options */ /* Chip capabilities */ /* (PR/O = fixed value, R/O read only */ #define PHY_BMSR_CT4 0x8000 /* PR/O: Chip supports 100BASE-T4 mode */ #define PHY_BMSR_C100FD 0x4000 /* PR/O: Chip supports 100BASE-TX Full Duplex mode */ #define PHY_BMSR_C100HD 0x2000 /* PR/O: Chip supports 100BASE-TX Half Duplex mode */ #define PHY_BMSR_C10FD 0x1000 /* PR/O: Chip supports 10BASE-TX Full Duplex mode */ #define PHY_BMSR_C10HD 0x0800 /* PR/O: Chip supports 10BASE-TX Half Duplex mode */ #define PHY_BMSR_CPRE 0x0040 /* PR/O: Chip supports MDIO preamble supression mode */ #define PHY_BMSR_CANEG 0x0008 /* PR/O: Chip supports Auto-Negotiation */ #define PHY_BMSR_CEXT 0x0001 /* PR/O: Chip supports extended MDIO registers */ #define PHY_BMSR_CMSK 0xF849 /* Mask for chip capabilities */ /* Chip status */ #define PHY_BMSR_ANEG 0x0020 /* R/O: 1: Auto-Negotiation complete */ #define PHY_BMSR_RFLT 0x0010 /* R/O: 1: Remote Fault (Partner cut us off) */ #define PHY_BMSR_LNK 0x0004 /* R/O: 1: Link established */ #define PHY_BMSR_JAB 0x0002 /* R/O: 1: Jabber condition detected */ #define PHY_BMSR_SMSK 0x0036 /* Mask for chip status */ #define phyw( reg, val) phydcb->mdiow( reg, val) #define phyr( reg) phydcb->mdior( reg) PHYDCB *phydcb = NULL; enum { PHY_BIT_DESCR_10M = 0, PHY_BIT_DESCR_100M, PHY_BIT_DESCR_1000M, PHY_BIT_DESCR_DUPLX, PHY_BIT_DESCR_POE, PHY_BIT_DESCR_MAX } phy_bit_descr_nr; typedef struct { uint8_t reg; /* register number */ uint16_t mask; /* bit mask to identify what we are looking */ } phy_bit_descr_t; typedef struct { uint32_t phy_oui; /* oui chip identifier */ phy_bit_descr_t phy_bit_descr[PHY_BIT_DESCR_MAX]; /* list of descriptors to identify where to get * [0] flag about 10Mbit/s speed * [1] flag about 100Mbit/s speed * [2] flag about 1000Mbit/s speed * [3] flag about full duplex * [4] flag about POE status */ } phy_status_descr_t; phy_status_descr_t phy_status_descr[] = { /* Davicom DM9000 derivates */ { DM9000, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, { DM9000A, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, { DM9000B, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, /* Davicom DM9161 derivates */ { DM9161, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, /* DM9161A is the same as DM9000A and DM9161B is the same as DM9000B { DM9161A, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, { DM9161B, { {17, 0x3000}, {17, 0xC000}, {0, 0}, {17, 0xA000}, {0, 0} } }, */ /* SMSC LAN8700 derivates */ { LAN8700, { {31, 0x0004}, {31, 0x0008}, {0, 0}, {31, 0x0010}, {0, 0} } }, { LAN8700r4, { {31, 0x0004}, {31, 0x0008}, {0, 0}, {31, 0x0010}, {0, 0} } }, { LAN8710, { {31, 0x0004}, {31, 0x0008}, {0, 0}, {31, 0x0010}, {0, 0} } }, { LAN8720A, { {31, 0x0004}, {31, 0x0008}, {0, 0}, {31, 0x0010}, {0, 0} } }, /* Micrel KS8721 */ { KS8721, { {31, 0x0004}, {31, 0x0008}, {0, 0}, {31, 0x0010}, {0, 0} } }, /* STM */ { ST802RT1, { {0,0} , {17, 0x0200}, {0, 0}, {17, 0x0100}, {0, 0} } }, /* NatSemi/TI DP83848 */ // Table approach does not work as 10BASET and 100BASET // status share the same bit. Special treatment in NutPhyCtl required. { DP83848, { {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} } }, }; /*! * \brief Read PHY's Basic Mode Status Register * * \param None. * * \return 0 on fail or status register of PHY. */ #define NutPhyGetStatus(void) phyr( PHY_BMSR)) /*! * \brief Control PHY Options * * This function controls the physical layer chip. * Give * * \param ctl is the PHY option to control or test. * \param par Pointer to value of 1 to set, 0 to reset an option * or the return of a value test. * \return 0 on success, -1 on failure. */ int NutPhyCtl( uint16_t ctl, uint32_t *par) { int rc = 0; uint16_t bmcr=0, bmsr=0; uint16_t p16 = (uint16_t)*par; uint8_t reg = (uint8_t)(*par>>16); PHPRINTF("NPCtl(0x%x, 0x%04x)\n", ctl, p16); /* Execute standard ioctl function */ bmcr = phyr( PHY_BMCR); PHPRINTF(" bmcr=0x%04x\n", bmcr); switch (ctl) { case PHY_CTL_RESET: if (p16) { int wait = 0; /* Set Reset bit in BMCR register */ phyw( PHY_BMCR, PHY_BMCR_RES); /* Wait till reset bit flips back to 0 */ while( (phyr( PHY_BMCR) & PHY_BMCR_RES) && wait<10) { NutDelay(100); wait++; } if(wait >= 10) { rc = -1; } } break; case PHY_CTL_LOOPBACK: if (p16) { bmcr |= PHY_BMCR_LOOP; } else { bmcr &= ~PHY_BMCR_LOOP; } phyw( PHY_BMCR, bmcr); break; case PHY_CTL_SPEED: if (p16 == 100) { bmcr |= PHY_BMCR_SPEED; phyw( PHY_BMCR, bmcr); } else if (p16==10) { bmcr &= ~PHY_BMCR_SPEED; phyw( PHY_BMCR, bmcr); } else { rc = -1; } break; case PHY_CTL_AUTONEG: if (p16) { bmcr |= PHY_BMCR_ANEG; } else { bmcr &= ~PHY_BMCR_ANEG; } phyw( PHY_BMCR, bmcr); break; case PHY_CTL_POWERDOWN: if (p16) { bmcr |= PHY_BMCR_PDWN; } else { bmcr &= ~PHY_BMCR_PDWN; } phyw( PHY_BMCR, bmcr); break; case PHY_CTL_ISOLATE: if (p16) { bmcr |= PHY_BMCR_ISO; } else { bmcr &= ~PHY_BMCR_ISO; } phyw( PHY_BMCR, bmcr); break; case PHY_CTL_DUPLEX: if (p16) { bmcr |= PHY_BMCR_DUPX; } else { bmcr &= ~PHY_BMCR_DUPX; } phyw( PHY_BMCR, bmcr); break; case PHY_CTL_AUTONEG_RE: if (p16) { bmcr |= PHY_BMCR_ANST; phyw (PHY_BMCR, bmcr); } break; case PHY_GET_LINK: *par = (uint32_t)(phyr(PHY_BMSR)&PHY_BMSR_LNK); break; case PHY_GET_STATUS: bmsr = phyr(PHY_BMSR); /* only return a value different to zero if link is true */ if (bmsr & PHY_BMSR_LNK) { int count, length; *par = PHY_STATUS_HAS_LINK | ((bmsr & PHY_BMSR_ANEG) ? PHY_STATUS_AUTONEG_OK : 0); /* KS8721 needs different interpretation of bits */ if (phydcb->oui == KS8721) { uint16_t tempreg; tempreg = phyr(0x1f); tempreg >>= 2; tempreg &= 0x7; switch (tempreg) { case 1: *par |= PHY_STATUS_10M; break; case 2: *par |= PHY_STATUS_100M; break; case 5: *par |= PHY_STATUS_10M | PHY_STATUS_FULLDUPLEX; break; case 6: *par |= PHY_STATUS_100M | PHY_STATUS_FULLDUPLEX; break; } } /* Same for the DP83848, table approach does not work as 10BASET and 100BASET status share the same bit */ else if (phydcb->oui == DP83848) { uint16_t tempreg; tempreg = phyr(0x10); tempreg &= 0x7; switch (tempreg) { case 1: *par |= PHY_STATUS_100M; break; case 3: *par |= PHY_STATUS_10M; break; case 5: *par |= PHY_STATUS_100M | PHY_STATUS_FULLDUPLEX; break; case 7: *par |= PHY_STATUS_10M | PHY_STATUS_FULLDUPLEX; break; } } else { length = sizeof(phy_status_descr) / sizeof(phy_status_descr[0]); for(count=0; countoui) { break; } } if(countoui) { break; } } if(countaddr = mda; phydcb->mdiow = mdiow; phydcb->mdior = mdior; /* Get chip's OUI data */ temp1 = phyr(PHY_ID1); temp2 = phyr(PHY_ID2); phydcb->oui = (((uint32_t)temp1<<16)|(uint32_t)temp2); /* Mask out revision bits */ phydcb->oui &= ~OUIMSK_REV; for(count=0; countoui) { break; } } if(count>=length) PHPRINTF("Unknown tranceiver "); PHPRINTF("PHY OUI=0x%08lx\n", phydcb->oui); return 0; }