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$");
   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_state,
  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_state) {
  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_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
  154                     PHY_LINK_DOWN;
  155         }
  156         if (!(bmcr & BMCR_ANENABLE)) {
  157                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  158                 if (bmcr & BMCR_SPEED1000)
  159                         sp = SPEED_1000;
  160                 else if (bmcr & BMCR_SPEED100)
  161                         sp = SPEED_100;
  162                 else
  163                         sp = SPEED_10;
  164         } else if (status & BMSR_ANEGCOMPLETE) {
  165                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
  166                 if (err)
  167                         return err;
  168 
  169                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
  170                 sp = G_ACSR_SPEED(status);
  171                 if (sp == 0)
  172                         sp = SPEED_10;
  173                 else if (sp == 1)
  174                         sp = SPEED_100;
  175                 else
  176                         sp = SPEED_1000;
  177 
  178                 if (fc && dplx == DUPLEX_FULL) {
  179                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
  180                         if (!err)
  181                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  182                         if (err)
  183                                 return err;
  184 
  185                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
  186                                 pause = PAUSE_RX | PAUSE_TX;
  187                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
  188                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
  189                                  (adv & ADVERTISE_PAUSE_ASYM))
  190                                 pause = PAUSE_TX;
  191                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
  192                                  (adv & ADVERTISE_PAUSE_CAP))
  193                                 pause = PAUSE_RX;
  194                 }
  195         }
  196         if (speed)
  197                 *speed = sp;
  198         if (duplex)
  199                 *duplex = dplx;
  200         if (fc)
  201                 *fc = pause;
  202         return 0;
  203 }
  204 
  205 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
  206                                          int *speed, int *duplex, int *fc)
  207 {
  208         unsigned int bmcr, status, lpa, adv;
  209         int err, sp = -1, dplx = -1, pause = 0;
  210 
  211         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
  212         if (!err)
  213                 err = mdio_read(cphy, 0, MII_BMSR, &status);
  214         if (err)
  215                 return err;
  216 
  217         if (link_state) {
  218                 /*
  219                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
  220                  * once more to get the current link state.
  221                  */
  222                 if (!(status & BMSR_LSTATUS))
  223                         err = mdio_read(cphy, 0, MII_BMSR, &status);
  224                 if (err)
  225                         return err;
  226                 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
  227                     PHY_LINK_DOWN;
  228         }
  229         if (!(bmcr & BMCR_ANENABLE)) {
  230                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
  231                 if (bmcr & BMCR_SPEED1000)
  232                         sp = SPEED_1000;
  233                 else if (bmcr & BMCR_SPEED100)
  234                         sp = SPEED_100;
  235                 else
  236                         sp = SPEED_10;
  237         } else if (status & BMSR_ANEGCOMPLETE) {
  238                 err = mdio_read(cphy, 0, MII_LPA, &lpa);
  239                 if (!err)
  240                         err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
  241                 if (err)
  242                         return err;
  243 
  244                 if (adv & lpa & ADVERTISE_1000XFULL) {
  245                         dplx = DUPLEX_FULL;
  246                         sp = SPEED_1000;
  247                 } else if (adv & lpa & ADVERTISE_1000XHALF) {
  248                         dplx = DUPLEX_HALF;
  249                         sp = SPEED_1000;
  250                 }
  251 
  252                 if (fc && dplx == DUPLEX_FULL) {
  253                         if (lpa & adv & ADVERTISE_1000XPAUSE)
  254                                 pause = PAUSE_RX | PAUSE_TX;
  255                         else if ((lpa & ADVERTISE_1000XPAUSE) &&
  256                                  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
  257                                 pause = PAUSE_TX;
  258                         else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
  259                                  (adv & ADVERTISE_1000XPAUSE))
  260                                 pause = PAUSE_RX;
  261                 }
  262         }
  263         if (speed)
  264                 *speed = sp;
  265         if (duplex)
  266                 *duplex = dplx;
  267         if (fc)
  268                 *fc = pause;
  269         return 0;
  270 }
  271 
  272 /*
  273  * Enable/disable auto MDI/MDI-X in forced link speed mode.
  274  */
  275 static int vsc8211_set_automdi(struct cphy *phy, int enable)
  276 {
  277         int err;
  278 
  279         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
  280             (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
  281             (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
  282             (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
  283             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
  284                 return err;
  285         return 0;
  286 }
  287 
  288 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
  289 {
  290         int err;
  291 
  292         err = t3_set_phy_speed_duplex(phy, speed, duplex);
  293         if (!err)
  294                 err = vsc8211_set_automdi(phy, 1);
  295         return err;
  296 }
  297 
  298 static int vsc8211_power_down(struct cphy *cphy, int enable)
  299 {
  300         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
  301                                    enable ? BMCR_PDOWN : 0);
  302 }
  303 
  304 static int vsc8211_intr_handler(struct cphy *cphy)
  305 {
  306         unsigned int cause;
  307         int err, cphy_cause = 0;
  308 
  309         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
  310         if (err)
  311                 return err;
  312 
  313         cause &= INTR_MASK;
  314         if (cause & CFG_CHG_INTR_MASK)
  315                 cphy_cause |= cphy_cause_link_change;
  316         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
  317                 cphy_cause |= cphy_cause_fifo_error;
  318         return cphy_cause;
  319 }
  320 
  321 #ifdef C99_NOT_SUPPORTED
  322 static struct cphy_ops vsc8211_ops = {
  323         vsc8211_reset,
  324         vsc8211_intr_enable,
  325         vsc8211_intr_disable,
  326         vsc8211_intr_clear,
  327         vsc8211_intr_handler,
  328         vsc8211_autoneg_enable,
  329         vsc8211_autoneg_restart,
  330         t3_phy_advertise,
  331         NULL,
  332         vsc8211_set_speed_duplex,
  333         vsc8211_get_link_status,
  334         vsc8211_power_down,
  335 };
  336 
  337 static struct cphy_ops vsc8211_fiber_ops = {
  338         vsc8211_reset,
  339         vsc8211_intr_enable,
  340         vsc8211_intr_disable,
  341         vsc8211_intr_clear,
  342         vsc8211_intr_handler,
  343         vsc8211_autoneg_enable,
  344         vsc8211_autoneg_restart,
  345         t3_phy_advertise_fiber,
  346         NULL,
  347         t3_set_phy_speed_duplex,
  348         vsc8211_get_link_status_fiber,
  349         vsc8211_power_down,
  350 };
  351 #else
  352 static struct cphy_ops vsc8211_ops = {
  353         .reset             = vsc8211_reset,
  354         .intr_enable       = vsc8211_intr_enable,
  355         .intr_disable      = vsc8211_intr_disable,
  356         .intr_clear        = vsc8211_intr_clear,
  357         .intr_handler      = vsc8211_intr_handler,
  358         .autoneg_enable    = vsc8211_autoneg_enable,
  359         .autoneg_restart   = vsc8211_autoneg_restart,
  360         .advertise         = t3_phy_advertise,
  361         .set_speed_duplex  = vsc8211_set_speed_duplex,
  362         .get_link_status   = vsc8211_get_link_status,
  363         .power_down        = vsc8211_power_down,
  364 };
  365 
  366 static struct cphy_ops vsc8211_fiber_ops = {
  367         .reset             = vsc8211_reset,
  368         .intr_enable       = vsc8211_intr_enable,
  369         .intr_disable      = vsc8211_intr_disable,
  370         .intr_clear        = vsc8211_intr_clear,
  371         .intr_handler      = vsc8211_intr_handler,
  372         .autoneg_enable    = vsc8211_autoneg_enable,
  373         .autoneg_restart   = vsc8211_autoneg_restart,
  374         .advertise         = t3_phy_advertise_fiber,
  375         .set_speed_duplex  = t3_set_phy_speed_duplex,
  376         .get_link_status   = vsc8211_get_link_status_fiber,
  377         .power_down        = vsc8211_power_down,
  378 };
  379 #endif
  380 
  381 #define VSC8211_PHY_CTRL 24
  382 
  383 #define S_VSC8211_TXFIFODEPTH    7
  384 #define M_VSC8211_TXFIFODEPTH    0x7
  385 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
  386 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
  387 
  388 #define S_VSC8211_RXFIFODEPTH    4
  389 #define M_VSC8211_RXFIFODEPTH    0x7
  390 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
  391 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
  392 
  393 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
  394 {
  395         /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
  396         unsigned int val = 4;
  397         unsigned int currentregval;
  398         unsigned int regval;
  399         int err;
  400 
  401         /* Retrieve the port info structure from adater_t */
  402         struct port_info *portinfo = adap2pinfo(adap, port);
  403 
  404         /* What phy is this */
  405         struct cphy *phy = &portinfo->phy;
  406 
  407         /* Read the current value of the PHY control Register */
  408         err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
  409 
  410         if (err)
  411                 return err;
  412 
  413         /* IEEE mode supports up to 1518 bytes */
  414         /* mtu does not contain the header + FCS (18 bytes) */
  415         if (mtu > 1500)
  416                 /* 
  417                  * If using a packet size > 1500  set TX FIFO Depth bits 
  418                  * 9:7 to 011 (Jumbo packet mode) 
  419                  */
  420                 val = 3;
  421 
  422         regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) | 
  423                 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
  424                 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
  425 
  426         return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
  427 }
  428 
  429 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
  430                         const struct mdio_ops *mdio_ops)
  431 {
  432         struct cphy *phy = &pinfo->phy;
  433         int err;
  434         unsigned int val;
  435 
  436         cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
  437                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
  438                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
  439                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
  440         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
  441 
  442         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
  443         if (err)
  444                 return err;
  445         if (val & VSC_CTRL_MEDIA_MODE_HI) {
  446                 /* copper interface, just need to configure the LEDs */
  447                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
  448         }
  449 
  450         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
  451                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
  452         phy->desc = "1000BASE-X";
  453         phy->ops = &vsc8211_fiber_ops;
  454 
  455         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
  456             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
  457             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
  458             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
  459                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
  460             (err = vsc8211_reset(phy, 0)) != 0)
  461                 return err;
  462 
  463         udelay(5); /* delay after reset before next SMI */
  464         return 0;
  465 }

Cache object: 107232d4dad546d11e985e9f7ab927dd


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