| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476 |
- /*
- * 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 <cfg/os.h>
- #include <stdint.h>
- #include <string.h>
- #include <sys/heap.h>
- #include <sys/timer.h>
- #include <dev/phy.h>
- /* WARNING: Variadic macros are C99 and may fail with C89 compilers. */
- #ifdef NUTDEBUG
- #include <stdio.h>
- #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; count<length; count++) {
- if(phy_status_descr[count].phy_oui == phydcb->oui) {
- break;
- }
- }
- if(count<length) {
- uint16_t tempreg;
- /* entry in table found */
- PHPRINTF(" Reading status of known phy\n");
- tempreg = phyr(phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_10M].reg);
- if(tempreg & phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_10M].mask) {
- *par |= PHY_STATUS_10M;
- }
- tempreg = phyr(phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_100M].reg);
- if(tempreg & phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_100M].mask) {
- *par |= PHY_STATUS_100M;
- }
- tempreg = phyr(phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_1000M].reg);
- if(tempreg & phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_1000M].mask) {
- *par |= PHY_STATUS_1000M;
- }
- tempreg = phyr(phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_DUPLX].reg);
- if(tempreg & phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_DUPLX].mask) {
- *par |= PHY_STATUS_FULLDUPLEX;
- }
- }
- else {
- *par |= PHY_STATUS_CON_UNKNOWN;
- }
- }
- }
- else {
- *par = 0;
- }
- break;
- case PHY_GET_POE: {
- int count, length;
- *par = 0;
- length = sizeof(phy_status_descr) / sizeof(phy_status_descr[0]);
- for(count=0; count<length; count++) {
- if(phy_status_descr[count].phy_oui == phydcb->oui) {
- break;
- }
- }
- if(count<length) {
- uint16_t tempreg;
- /* entry in table found */
- PHPRINTF(" Reading POE status of known phy\n");
- tempreg = phyr(phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_POE].reg);
- if(tempreg & phy_status_descr[count].phy_bit_descr[PHY_BIT_DESCR_POE].mask) {
- *par = 1;
- }
- }
- else {
- rc = -1;
- }
- break;
- } /* endof case PHY_GET_POE */
- case PHY_GET_REGVAL:
- *par = (uint32_t)phyr(reg);
- break;
- case PHY_SET_REGVAL:
- phyw( reg, p16);
- break;
- default:
- rc = -1;
- break;
- }
- PHPRINTF(" bmcr=0x%04x, rc=%d, par=%x\n", bmcr, rc, (unsigned int) *par);
- return rc;
- }
- /*!
- * \brief Register and initialize PHY communication.
- *
- * This function registers a PHY for use by an EMAC.
- * For communication tests, the function reads out the OUI and
- * Model/Revision registers of the PHY.
- *
- * \param mda id the PHY's address on the MDIO interface bus of the EMAC.
- * \param mdiow Function provided by EMAC driver to write PHY regisers.
- * \param mdior Function provided by EMAC driver to read PHY regisers.
- *
- * \return 0 on success, -1 on communication failure.
- */
- int NutRegisterPhy( uint8_t mda, void(*mdiow)(uint8_t, uint16_t), uint16_t(*mdior)(uint8_t))
- {
- uint16_t temp1 = 0, temp2 = 0;
- uint_fast16_t count, length = sizeof(phy_status_descr) / sizeof(phy_status_descr[0]);
- PHPRINTF("NRP(%u, %p, %p)\n", mda, mdiow, mdior);
- if (phydcb != NULL)
- {
- /* Phy is just registered */
- return 0;
- }
- if ((mdiow == NULL)||(mdior == NULL)) {
- /* PHY Access functions are not given */
- return -1;
- }
- phydcb = NutHeapAlloc( sizeof(PHYDCB));
- if (phydcb == NULL) {
- /* Not enough memory to register PHY */
- return -1;
- }
- phydcb->addr = 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; count<length; count++) {
- if(phy_status_descr[count].phy_oui == phydcb->oui) {
- break;
- }
- }
- if(count>=length)
- PHPRINTF("Unknown tranceiver ");
- PHPRINTF("PHY OUI=0x%08lx\n", phydcb->oui);
- return 0;
- }
|