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.1/sys/dev/cxgb/common/cxgb_vsc8211.c 199206 2009-11-12 00:20:31Z np $");
   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_PHY_CTRL      = 24,
   43         VSC8211_INTR_ENABLE   = 25,
   44         VSC8211_INTR_STATUS   = 26,
   45         VSC8211_LED_CTRL      = 27,
   46         VSC8211_AUX_CTRL_STAT = 28,
   47         VSC8211_EXT_PAGE_AXS  = 31,
   48 };
   49 
   50 enum {
   51         VSC_INTR_RX_ERR     = 1 << 0,
   52         VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
   53         VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
   54         VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
   55         VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
   56         VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
   57         VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
   58         VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
   59         VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
   60         VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
   61         VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
   62         VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
   63         VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
   64         VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
   65         VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
   66 };
   67 
   68 enum {
   69         VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
   70         VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
   71 };
   72 
   73 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
   74                            VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
   75                            VSC_INTR_NEG_DONE)
   76 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
   77                    VSC_INTR_ENABLE)
   78 
   79 /* PHY specific auxiliary control & status register fields */
   80 #define S_ACSR_ACTIPHY_TMR    0
   81 #define M_ACSR_ACTIPHY_TMR    0x3
   82 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
   83 
   84 #define S_ACSR_SPEED    3
   85 #define M_ACSR_SPEED    0x3
   86 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
   87 
   88 #define S_ACSR_DUPLEX 5
   89 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
   90 
   91 #define S_ACSR_ACTIPHY 6
   92 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
   93 
   94 /*
   95  * Reset the PHY.  This PHY completes reset immediately so we never wait.
   96  */
   97 static int vsc8211_reset(struct cphy *cphy, int wait)
   98 {
   99         return t3_phy_reset(cphy, 0, 0);
  100 }
  101 
  102 static int vsc8211_intr_enable(struct cphy *cphy)
  103 {
  104         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
  105 }
  106 
  107 static int vsc8211_intr_disable(struct cphy *cphy)
  108 {
  109         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
  110 }
  111 
  112 static int vsc8211_intr_clear(struct cphy *cphy)
  113 {
  114         u32 val;
  115 
  116         /* Clear PHY interrupts by reading the register. */
  117         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
  118 }
  119 
  120 static int vsc8211_autoneg_enable(struct cphy *cphy)
  121 {
  122         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
  123                                    BMCR_ANENABLE | BMCR_ANRESTART);
  124 }
  125 
  126 static int vsc8211_autoneg_restart(struct cphy *cphy)
  127 {
  128         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
  129                                    BMCR_ANRESTART);
  130 }
  131 
  132 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
  133                                      int *speed, int *duplex, int *fc)
  134 {
  135         unsigned int bmcr, status, lpa, adv;
  136         int err, sp = -1, dplx = -1, pause = 0;
  137 
  138         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
  139         if (!err)
  140                 err = mdio_read(cphy, 0, MII_BMSR, &status);
  141         if (err)
  142                 return err;
  143 
  144         if (link_ok) {
  145                 /*
  146                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
  147                  * once more to get the current link state.
  148                  */
  149                 if (!(status & BMSR_LSTATUS))
  150                         err = mdio_read(cphy, 0, MII_BMSR, &status);
  151                 if (err)
  152                         return err;
  153                 *link_ok = (status & BMSR_LSTATUS) != 0;
  154         }
  155         if (!(bmcr & BMCR_ANENABLE)) {
  156                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  157                 if (bmcr & BMCR_SPEED1000)
  158                         sp = SPEED_1000;
  159                 else if (bmcr & BMCR_SPEED100)
  160                         sp = SPEED_100;
  161                 else
  162                         sp = SPEED_10;
  163         } else if (status & BMSR_ANEGCOMPLETE) {
  164                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
  165                 if (err)
  166                         return err;
  167 
  168                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
  169                 sp = G_ACSR_SPEED(status);
  170                 if (sp == 0)
  171                         sp = SPEED_10;
  172                 else if (sp == 1)
  173                         sp = SPEED_100;
  174                 else
  175                         sp = SPEED_1000;
  176 
  177                 if (fc && dplx == DUPLEX_FULL) {
  178                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
  179                         if (!err)
  180                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  181                         if (err)
  182                                 return err;
  183 
  184                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
  185                                 pause = PAUSE_RX | PAUSE_TX;
  186                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
  187                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
  188                                  (adv & ADVERTISE_PAUSE_ASYM))
  189                                 pause = PAUSE_TX;
  190                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
  191                                  (adv & ADVERTISE_PAUSE_CAP))
  192                                 pause = PAUSE_RX;
  193                 }
  194         }
  195         if (speed)
  196                 *speed = sp;
  197         if (duplex)
  198                 *duplex = dplx;
  199         if (fc)
  200                 *fc = pause;
  201         return 0;
  202 }
  203 
  204 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
  205                                          int *speed, int *duplex, int *fc)
  206 {
  207         unsigned int bmcr, status, lpa, adv;
  208         int err, sp = -1, dplx = -1, pause = 0;
  209 
  210         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
  211         if (!err)
  212                 err = mdio_read(cphy, 0, MII_BMSR, &status);
  213         if (err)
  214                 return err;
  215 
  216         if (link_ok) {
  217                 /*
  218                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
  219                  * once more to get the current link state.
  220                  */
  221                 if (!(status & BMSR_LSTATUS))
  222                         err = mdio_read(cphy, 0, MII_BMSR, &status);
  223                 if (err)
  224                         return err;
  225                 *link_ok = (status & BMSR_LSTATUS) != 0;
  226         }
  227         if (!(bmcr & BMCR_ANENABLE)) {
  228                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  229                 if (bmcr & BMCR_SPEED1000)
  230                         sp = SPEED_1000;
  231                 else if (bmcr & BMCR_SPEED100)
  232                         sp = SPEED_100;
  233                 else
  234                         sp = SPEED_10;
  235         } else if (status & BMSR_ANEGCOMPLETE) {
  236                 err = mdio_read(cphy, 0, MII_LPA, &lpa);
  237                 if (!err)
  238                         err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  239                 if (err)
  240                         return err;
  241 
  242                 if (adv & lpa & ADVERTISE_1000XFULL) {
  243                         dplx = DUPLEX_FULL;
  244                         sp = SPEED_1000;
  245                 } else if (adv & lpa & ADVERTISE_1000XHALF) {
  246                         dplx = DUPLEX_HALF;
  247                         sp = SPEED_1000;
  248                 }
  249 
  250                 if (fc && dplx == DUPLEX_FULL) {
  251                         if (lpa & adv & ADVERTISE_1000XPAUSE)
  252                                 pause = PAUSE_RX | PAUSE_TX;
  253                         else if ((lpa & ADVERTISE_1000XPAUSE) &&
  254                                  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
  255                                 pause = PAUSE_TX;
  256                         else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
  257                                  (adv & ADVERTISE_1000XPAUSE))
  258                                 pause = PAUSE_RX;
  259                 }
  260         }
  261         if (speed)
  262                 *speed = sp;
  263         if (duplex)
  264                 *duplex = dplx;
  265         if (fc)
  266                 *fc = pause;
  267         return 0;
  268 }
  269 
  270 /*
  271  * Enable/disable auto MDI/MDI-X in forced link speed mode.
  272  */
  273 static int vsc8211_set_automdi(struct cphy *phy, int enable)
  274 {
  275         int err;
  276 
  277         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
  278             (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
  279             (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
  280             (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
  281             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
  282                 return err;
  283         return 0;
  284 }
  285 
  286 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
  287 {
  288         int err;
  289 
  290         err = t3_set_phy_speed_duplex(phy, speed, duplex);
  291         if (!err)
  292                 err = vsc8211_set_automdi(phy, 1);
  293         return err;
  294 }
  295 
  296 static int vsc8211_power_down(struct cphy *cphy, int enable)
  297 {
  298         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
  299                                    enable ? BMCR_PDOWN : 0);
  300 }
  301 
  302 static int vsc8211_intr_handler(struct cphy *cphy)
  303 {
  304         unsigned int cause;
  305         int err, cphy_cause = 0;
  306 
  307         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
  308         if (err)
  309                 return err;
  310 
  311         cause &= INTR_MASK;
  312         if (cause & CFG_CHG_INTR_MASK)
  313                 cphy_cause |= cphy_cause_link_change;
  314         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
  315                 cphy_cause |= cphy_cause_fifo_error;
  316         return cphy_cause;
  317 }
  318 
  319 #ifdef C99_NOT_SUPPORTED
  320 static struct cphy_ops vsc8211_ops = {
  321         vsc8211_reset,
  322         vsc8211_intr_enable,
  323         vsc8211_intr_disable,
  324         vsc8211_intr_clear,
  325         vsc8211_intr_handler,
  326         vsc8211_autoneg_enable,
  327         vsc8211_autoneg_restart,
  328         t3_phy_advertise,
  329         NULL,
  330         vsc8211_set_speed_duplex,
  331         vsc8211_get_link_status,
  332         vsc8211_power_down,
  333 };
  334 
  335 static struct cphy_ops vsc8211_fiber_ops = {
  336         vsc8211_reset,
  337         vsc8211_intr_enable,
  338         vsc8211_intr_disable,
  339         vsc8211_intr_clear,
  340         vsc8211_intr_handler,
  341         vsc8211_autoneg_enable,
  342         vsc8211_autoneg_restart,
  343         t3_phy_advertise_fiber,
  344         NULL,
  345         t3_set_phy_speed_duplex,
  346         vsc8211_get_link_status_fiber,
  347         vsc8211_power_down,
  348 };
  349 #else
  350 static struct cphy_ops vsc8211_ops = {
  351         .reset             = vsc8211_reset,
  352         .intr_enable       = vsc8211_intr_enable,
  353         .intr_disable      = vsc8211_intr_disable,
  354         .intr_clear        = vsc8211_intr_clear,
  355         .intr_handler      = vsc8211_intr_handler,
  356         .autoneg_enable    = vsc8211_autoneg_enable,
  357         .autoneg_restart   = vsc8211_autoneg_restart,
  358         .advertise         = t3_phy_advertise,
  359         .set_speed_duplex  = vsc8211_set_speed_duplex,
  360         .get_link_status   = vsc8211_get_link_status,
  361         .power_down        = vsc8211_power_down,
  362 };
  363 
  364 static struct cphy_ops vsc8211_fiber_ops = {
  365         .reset             = vsc8211_reset,
  366         .intr_enable       = vsc8211_intr_enable,
  367         .intr_disable      = vsc8211_intr_disable,
  368         .intr_clear        = vsc8211_intr_clear,
  369         .intr_handler      = vsc8211_intr_handler,
  370         .autoneg_enable    = vsc8211_autoneg_enable,
  371         .autoneg_restart   = vsc8211_autoneg_restart,
  372         .advertise         = t3_phy_advertise_fiber,
  373         .set_speed_duplex  = t3_set_phy_speed_duplex,
  374         .get_link_status   = vsc8211_get_link_status_fiber,
  375         .power_down        = vsc8211_power_down,
  376 };
  377 #endif
  378 
  379 #define VSC8211_PHY_CTRL 24
  380 
  381 #define S_VSC8211_TXFIFODEPTH    7
  382 #define M_VSC8211_TXFIFODEPTH    0x7
  383 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
  384 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
  385 
  386 #define S_VSC8211_RXFIFODEPTH    4
  387 #define M_VSC8211_RXFIFODEPTH    0x7
  388 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
  389 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
  390 
  391 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
  392 {
  393         /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
  394         unsigned int val = 4;
  395         unsigned int currentregval;
  396         unsigned int regval;
  397         int err;
  398 
  399         /* Retrieve the port info structure from adater_t */
  400         struct port_info *portinfo = adap2pinfo(adap, port);
  401 
  402         /* What phy is this */
  403         struct cphy *phy = &portinfo->phy;
  404 
  405         /* Read the current value of the PHY control Register */
  406         err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
  407 
  408         if (err)
  409                 return err;
  410 
  411         /* IEEE mode supports up to 1518 bytes */
  412         /* mtu does not contain the header + FCS (18 bytes) */
  413         if (mtu > 1500)
  414                 /* 
  415                  * If using a packet size > 1500  set TX FIFO Depth bits 
  416                  * 9:7 to 011 (Jumbo packet mode) 
  417                  */
  418                 val = 3;
  419 
  420         regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) | 
  421                 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
  422                 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
  423 
  424         return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
  425 }
  426 
  427 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
  428                         const struct mdio_ops *mdio_ops)
  429 {
  430         struct cphy *phy = &pinfo->phy;
  431         int err;
  432         unsigned int val;
  433 
  434         cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
  435                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
  436                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
  437                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
  438         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
  439 
  440         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
  441         if (err)
  442                 return err;
  443         if (val & VSC_CTRL_MEDIA_MODE_HI) {
  444                 /* copper interface, just need to configure the LEDs */
  445                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
  446         }
  447 
  448         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
  449                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
  450         phy->desc = "1000BASE-X";
  451         phy->ops = &vsc8211_fiber_ops;
  452 
  453         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
  454             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
  455             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
  456             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
  457                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
  458             (err = vsc8211_reset(phy, 0)) != 0)
  459                 return err;
  460 
  461         udelay(5); /* delay after reset before next SMI */
  462         return 0;
  463 }

Cache object: 21edb0d190af1e94df73dc4fe449ba91


[ 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.