The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/cxgb/common/cxgb_vsc8211.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /**************************************************************************
    2 
    3 Copyright (c) 2007, Chelsio Inc.
    4 All rights reserved.
    5 
    6 Redistribution and use in source and binary forms, with or without
    7 modification, are permitted provided that the following conditions are met:
    8 
    9  1. Redistributions of source code must retain the above copyright notice,
   10     this list of conditions and the following disclaimer.
   11 
   12  2. Neither the name of the Chelsio Corporation nor the names of its
   13     contributors may be used to endorse or promote products derived from
   14     this software without specific prior written permission.
   15 
   16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
   20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   26 POSSIBILITY OF SUCH DAMAGE.
   27 
   28 ***************************************************************************/
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD: releng/8.0/sys/dev/cxgb/common/cxgb_vsc8211.c 183292 2008-09-23 03:16:54Z kmacy $");
   32 
   33 #include <cxgb_include.h>
   34 
   35 #undef msleep
   36 #define msleep t3_os_sleep
   37 
   38 /* VSC8211 PHY specific registers. */
   39 enum {
   40         VSC8211_SIGDET_CTRL   = 19,
   41         VSC8211_EXT_CTRL      = 23,
   42         VSC8211_INTR_ENABLE   = 25,
   43         VSC8211_INTR_STATUS   = 26,
   44         VSC8211_LED_CTRL      = 27,
   45         VSC8211_AUX_CTRL_STAT = 28,
   46         VSC8211_EXT_PAGE_AXS  = 31,
   47 };
   48 
   49 enum {
   50         VSC_INTR_RX_ERR     = 1 << 0,
   51         VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
   52         VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
   53         VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
   54         VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
   55         VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
   56         VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
   57         VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
   58         VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
   59         VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
   60         VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
   61         VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
   62         VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
   63         VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
   64         VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
   65 };
   66 
   67 enum {
   68         VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
   69         VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
   70 };
   71 
   72 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
   73                            VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
   74                            VSC_INTR_NEG_DONE)
   75 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
   76                    VSC_INTR_ENABLE)
   77 
   78 /* PHY specific auxiliary control & status register fields */
   79 #define S_ACSR_ACTIPHY_TMR    0
   80 #define M_ACSR_ACTIPHY_TMR    0x3
   81 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
   82 
   83 #define S_ACSR_SPEED    3
   84 #define M_ACSR_SPEED    0x3
   85 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
   86 
   87 #define S_ACSR_DUPLEX 5
   88 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
   89 
   90 #define S_ACSR_ACTIPHY 6
   91 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
   92 
   93 /*
   94  * Reset the PHY.  This PHY completes reset immediately so we never wait.
   95  */
   96 static int vsc8211_reset(struct cphy *cphy, int wait)
   97 {
   98         return t3_phy_reset(cphy, 0, 0);
   99 }
  100 
  101 static int vsc8211_intr_enable(struct cphy *cphy)
  102 {
  103         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
  104 }
  105 
  106 static int vsc8211_intr_disable(struct cphy *cphy)
  107 {
  108         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
  109 }
  110 
  111 static int vsc8211_intr_clear(struct cphy *cphy)
  112 {
  113         u32 val;
  114 
  115         /* Clear PHY interrupts by reading the register. */
  116         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
  117 }
  118 
  119 static int vsc8211_autoneg_enable(struct cphy *cphy)
  120 {
  121         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
  122                                    BMCR_ANENABLE | BMCR_ANRESTART);
  123 }
  124 
  125 static int vsc8211_autoneg_restart(struct cphy *cphy)
  126 {
  127         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
  128                                    BMCR_ANRESTART);
  129 }
  130 
  131 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
  132                                      int *speed, int *duplex, int *fc)
  133 {
  134         unsigned int bmcr, status, lpa, adv;
  135         int err, sp = -1, dplx = -1, pause = 0;
  136 
  137         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
  138         if (!err)
  139                 err = mdio_read(cphy, 0, MII_BMSR, &status);
  140         if (err)
  141                 return err;
  142 
  143         if (link_ok) {
  144                 /*
  145                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
  146                  * once more to get the current link state.
  147                  */
  148                 if (!(status & BMSR_LSTATUS))
  149                         err = mdio_read(cphy, 0, MII_BMSR, &status);
  150                 if (err)
  151                         return err;
  152                 *link_ok = (status & BMSR_LSTATUS) != 0;
  153         }
  154         if (!(bmcr & BMCR_ANENABLE)) {
  155                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  156                 if (bmcr & BMCR_SPEED1000)
  157                         sp = SPEED_1000;
  158                 else if (bmcr & BMCR_SPEED100)
  159                         sp = SPEED_100;
  160                 else
  161                         sp = SPEED_10;
  162         } else if (status & BMSR_ANEGCOMPLETE) {
  163                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
  164                 if (err)
  165                         return err;
  166 
  167                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
  168                 sp = G_ACSR_SPEED(status);
  169                 if (sp == 0)
  170                         sp = SPEED_10;
  171                 else if (sp == 1)
  172                         sp = SPEED_100;
  173                 else
  174                         sp = SPEED_1000;
  175 
  176                 if (fc && dplx == DUPLEX_FULL) {
  177                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
  178                         if (!err)
  179                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  180                         if (err)
  181                                 return err;
  182 
  183                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
  184                                 pause = PAUSE_RX | PAUSE_TX;
  185                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
  186                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
  187                                  (adv & ADVERTISE_PAUSE_ASYM))
  188                                 pause = PAUSE_TX;
  189                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
  190                                  (adv & ADVERTISE_PAUSE_CAP))
  191                                 pause = PAUSE_RX;
  192                 }
  193         }
  194         if (speed)
  195                 *speed = sp;
  196         if (duplex)
  197                 *duplex = dplx;
  198         if (fc)
  199                 *fc = pause;
  200         return 0;
  201 }
  202 
  203 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
  204                                          int *speed, int *duplex, int *fc)
  205 {
  206         unsigned int bmcr, status, lpa, adv;
  207         int err, sp = -1, dplx = -1, pause = 0;
  208 
  209         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
  210         if (!err)
  211                 err = mdio_read(cphy, 0, MII_BMSR, &status);
  212         if (err)
  213                 return err;
  214 
  215         if (link_ok) {
  216                 /*
  217                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
  218                  * once more to get the current link state.
  219                  */
  220                 if (!(status & BMSR_LSTATUS))
  221                         err = mdio_read(cphy, 0, MII_BMSR, &status);
  222                 if (err)
  223                         return err;
  224                 *link_ok = (status & BMSR_LSTATUS) != 0;
  225         }
  226         if (!(bmcr & BMCR_ANENABLE)) {
  227                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  228                 if (bmcr & BMCR_SPEED1000)
  229                         sp = SPEED_1000;
  230                 else if (bmcr & BMCR_SPEED100)
  231                         sp = SPEED_100;
  232                 else
  233                         sp = SPEED_10;
  234         } else if (status & BMSR_ANEGCOMPLETE) {
  235                 err = mdio_read(cphy, 0, MII_LPA, &lpa);
  236                 if (!err)
  237                         err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  238                 if (err)
  239                         return err;
  240 
  241                 if (adv & lpa & ADVERTISE_1000XFULL) {
  242                         dplx = DUPLEX_FULL;
  243                         sp = SPEED_1000;
  244                 } else if (adv & lpa & ADVERTISE_1000XHALF) {
  245                         dplx = DUPLEX_HALF;
  246                         sp = SPEED_1000;
  247                 }
  248 
  249                 if (fc && dplx == DUPLEX_FULL) {
  250                         if (lpa & adv & ADVERTISE_1000XPAUSE)
  251                                 pause = PAUSE_RX | PAUSE_TX;
  252                         else if ((lpa & ADVERTISE_1000XPAUSE) &&
  253                                  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
  254                                 pause = PAUSE_TX;
  255                         else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
  256                                  (adv & ADVERTISE_1000XPAUSE))
  257                                 pause = PAUSE_RX;
  258                 }
  259         }
  260         if (speed)
  261                 *speed = sp;
  262         if (duplex)
  263                 *duplex = dplx;
  264         if (fc)
  265                 *fc = pause;
  266         return 0;
  267 }
  268 
  269 /*
  270  * Enable/disable auto MDI/MDI-X in forced link speed mode.
  271  */
  272 static int vsc8211_set_automdi(struct cphy *phy, int enable)
  273 {
  274         int err;
  275 
  276         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
  277             (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
  278             (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
  279             (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
  280             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
  281                 return err;
  282         return 0;
  283 }
  284 
  285 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
  286 {
  287         int err;
  288 
  289         err = t3_set_phy_speed_duplex(phy, speed, duplex);
  290         if (!err)
  291                 err = vsc8211_set_automdi(phy, 1);
  292         return err;
  293 }
  294 
  295 static int vsc8211_power_down(struct cphy *cphy, int enable)
  296 {
  297         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
  298                                    enable ? BMCR_PDOWN : 0);
  299 }
  300 
  301 static int vsc8211_intr_handler(struct cphy *cphy)
  302 {
  303         unsigned int cause;
  304         int err, cphy_cause = 0;
  305 
  306         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
  307         if (err)
  308                 return err;
  309 
  310         cause &= INTR_MASK;
  311         if (cause & CFG_CHG_INTR_MASK)
  312                 cphy_cause |= cphy_cause_link_change;
  313         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
  314                 cphy_cause |= cphy_cause_fifo_error;
  315         return cphy_cause;
  316 }
  317 
  318 #ifdef C99_NOT_SUPPORTED
  319 static struct cphy_ops vsc8211_ops = {
  320         vsc8211_reset,
  321         vsc8211_intr_enable,
  322         vsc8211_intr_disable,
  323         vsc8211_intr_clear,
  324         vsc8211_intr_handler,
  325         vsc8211_autoneg_enable,
  326         vsc8211_autoneg_restart,
  327         t3_phy_advertise,
  328         NULL,
  329         vsc8211_set_speed_duplex,
  330         vsc8211_get_link_status,
  331         vsc8211_power_down,
  332 };
  333 
  334 static struct cphy_ops vsc8211_fiber_ops = {
  335         vsc8211_reset,
  336         vsc8211_intr_enable,
  337         vsc8211_intr_disable,
  338         vsc8211_intr_clear,
  339         vsc8211_intr_handler,
  340         vsc8211_autoneg_enable,
  341         vsc8211_autoneg_restart,
  342         t3_phy_advertise_fiber,
  343         NULL,
  344         t3_set_phy_speed_duplex,
  345         vsc8211_get_link_status_fiber,
  346         vsc8211_power_down,
  347 };
  348 #else
  349 static struct cphy_ops vsc8211_ops = {
  350         .reset             = vsc8211_reset,
  351         .intr_enable       = vsc8211_intr_enable,
  352         .intr_disable      = vsc8211_intr_disable,
  353         .intr_clear        = vsc8211_intr_clear,
  354         .intr_handler      = vsc8211_intr_handler,
  355         .autoneg_enable    = vsc8211_autoneg_enable,
  356         .autoneg_restart   = vsc8211_autoneg_restart,
  357         .advertise         = t3_phy_advertise,
  358         .set_speed_duplex  = vsc8211_set_speed_duplex,
  359         .get_link_status   = vsc8211_get_link_status,
  360         .power_down        = vsc8211_power_down,
  361 };
  362 
  363 static struct cphy_ops vsc8211_fiber_ops = {
  364         .reset             = vsc8211_reset,
  365         .intr_enable       = vsc8211_intr_enable,
  366         .intr_disable      = vsc8211_intr_disable,
  367         .intr_clear        = vsc8211_intr_clear,
  368         .intr_handler      = vsc8211_intr_handler,
  369         .autoneg_enable    = vsc8211_autoneg_enable,
  370         .autoneg_restart   = vsc8211_autoneg_restart,
  371         .advertise         = t3_phy_advertise_fiber,
  372         .set_speed_duplex  = t3_set_phy_speed_duplex,
  373         .get_link_status   = vsc8211_get_link_status_fiber,
  374         .power_down        = vsc8211_power_down,
  375 };
  376 #endif
  377 
  378 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
  379                         const struct mdio_ops *mdio_ops)
  380 {
  381         int err;
  382         unsigned int val;
  383 
  384         cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops,
  385                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
  386                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
  387                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
  388         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
  389 
  390         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
  391         if (err)
  392                 return err;
  393         if (val & VSC_CTRL_MEDIA_MODE_HI) {
  394                 /* copper interface, just need to configure the LEDs */
  395                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
  396         }
  397 
  398         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
  399                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
  400         phy->desc = "1000BASE-X";
  401         phy->ops = &vsc8211_fiber_ops;
  402 
  403         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
  404             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
  405             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
  406             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
  407                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
  408             (err = vsc8211_reset(phy, 0)) != 0)
  409                 return err;
  410 
  411         udelay(5); /* delay after reset before next SMI */
  412         return 0;
  413 }

Cache object: c496c7eff85563dab40e679f7a08c82d


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.