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/etherswitch/ar40xx/ar40xx_hw_psgmii.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  */
   27 
   28 #include <sys/param.h>
   29 #include <sys/bus.h>
   30 #include <sys/errno.h>
   31 #include <sys/kernel.h>
   32 #include <sys/malloc.h>
   33 #include <sys/module.h>
   34 #include <sys/socket.h>
   35 #include <sys/sockio.h>
   36 #include <sys/sysctl.h>
   37 #include <sys/systm.h>
   38 
   39 #include <net/if.h>
   40 #include <net/if_var.h>
   41 #include <net/if_arp.h>
   42 #include <net/ethernet.h>
   43 #include <net/if_dl.h>
   44 #include <net/if_media.h>
   45 #include <net/if_types.h>
   46 
   47 #include <machine/bus.h>
   48 #include <dev/iicbus/iic.h>
   49 #include <dev/iicbus/iiconf.h>
   50 #include <dev/iicbus/iicbus.h>
   51 #include <dev/mii/mii.h>
   52 #include <dev/mii/miivar.h>
   53 #include <dev/mdio/mdio.h>
   54 #include <dev/extres/clk/clk.h>
   55 #include <dev/extres/hwreset/hwreset.h>
   56 
   57 #include <dev/fdt/fdt_common.h>
   58 #include <dev/ofw/ofw_bus.h>
   59 #include <dev/ofw/ofw_bus_subr.h>
   60 
   61 #include <dev/etherswitch/etherswitch.h>
   62 
   63 #include <dev/etherswitch/ar40xx/ar40xx_var.h>
   64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h>
   65 #include <dev/etherswitch/ar40xx/ar40xx_hw.h>
   66 #include <dev/etherswitch/ar40xx/ar40xx_phy.h>
   67 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
   68 #include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
   69 #include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
   70 
   71 #include "mdio_if.h"
   72 #include "miibus_if.h"
   73 #include "etherswitch_if.h"
   74 
   75 /*
   76  * Routines that control the ess-psgmii block - the interconnect
   77  * between the ess-switch and the external multi-port PHY
   78  * (eg Maple.)
   79  */
   80 
   81 static void
   82 ar40xx_hw_psgmii_reg_write(struct ar40xx_softc *sc, uint32_t reg,
   83     uint32_t val)
   84 {
   85         bus_space_write_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
   86             reg, val);
   87         bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
   88             0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_WRITE);
   89 }
   90 
   91 static int
   92 ar40xx_hw_psgmii_reg_read(struct ar40xx_softc *sc, uint32_t reg)
   93 {
   94         int ret;
   95 
   96         bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
   97             0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_READ);
   98         ret = bus_space_read_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
   99             reg);
  100 
  101         return (ret);
  102 }
  103 
  104 int
  105 ar40xx_hw_psgmii_set_mac_mode(struct ar40xx_softc *sc, uint32_t mac_mode)
  106 {
  107         if (mac_mode == PORT_WRAPPER_PSGMII) {
  108                 ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMII_MODE_CONTROL,
  109                     0x2200);
  110                 ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMIIPHY_TX_CONTROL,
  111                     0x8380);
  112         } else {
  113                 device_printf(sc->sc_dev, "WARNING: unknown MAC_MODE=%u\n",
  114                     mac_mode);
  115         }
  116 
  117         return (0);
  118 }
  119 
  120 int
  121 ar40xx_hw_psgmii_single_phy_testing(struct ar40xx_softc *sc, int phy)
  122 {
  123         int j;
  124         uint32_t tx_ok, tx_error;
  125         uint32_t rx_ok, rx_error;
  126         uint32_t tx_ok_high16;
  127         uint32_t rx_ok_high16;
  128         uint32_t tx_all_ok, rx_all_ok;
  129 
  130         MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x9000);
  131         MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x4140);
  132 
  133         for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) {
  134                 uint16_t status;
  135 
  136         status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11);
  137                 if (status & AR40XX_PHY_SPEC_STATUS_LINK)
  138                         break;
  139                         /*
  140                          * the polling interval to check if the PHY link up
  141                          * or not
  142                          * maxwait_timer: 750 ms +/-10 ms
  143                          * minwait_timer : 1 us +/- 0.1us
  144                          * time resides in minwait_timer ~ maxwait_timer
  145                          * see IEEE 802.3 section 40.4.5.2
  146                          */
  147                 DELAY(8 * 1000);
  148         }
  149 
  150         /* enable check */
  151         ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0000);
  152         ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0003);
  153 
  154         /* start traffic */
  155         ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8020, 0xa000);
  156         /*
  157          *wait for all traffic end
  158          * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms
  159          */
  160         DELAY(60 * 1000);
  161 
  162         /* check counter */
  163         tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e);
  164         tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d);
  165         tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f);
  166         rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b);
  167         rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a);
  168         rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c);
  169         tx_all_ok = tx_ok + (tx_ok_high16 << 16);
  170         rx_all_ok = rx_ok + (rx_ok_high16 << 16);
  171 
  172         if (tx_all_ok == 0x1000 && tx_error == 0) {
  173                 /* success */
  174                 sc->sc_psgmii.phy_t_status &= ~(1U << phy);
  175         } else {
  176                 device_printf(sc->sc_dev, "TX_OK=%d, tx_error=%d RX_OK=%d"
  177                     " rx_error=%d\n",
  178                     tx_all_ok, tx_error, rx_all_ok, rx_error);
  179                 device_printf(sc->sc_dev,
  180                     "PHY %d single test PSGMII issue happen!\n", phy);
  181                 sc->sc_psgmii.phy_t_status |= BIT(phy);
  182         }
  183 
  184         MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x1840);
  185         return (0);
  186 }
  187 
  188 int
  189 ar40xx_hw_psgmii_all_phy_testing(struct ar40xx_softc *sc)
  190 {
  191         int phy, j;
  192 
  193         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9000);
  194         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x4140);
  195 
  196         for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) {
  197                 for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
  198                         uint16_t status;
  199 
  200                         status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11);
  201                         if (!(status & (1U << 10)))
  202                                 break;
  203                 }
  204 
  205                 if (phy >= (AR40XX_NUM_PORTS - 1))
  206                         break;
  207                 /* The polling interval to check if the PHY link up or not */
  208                 DELAY(8*1000);
  209         }
  210 
  211         /* enable check */
  212         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0000);
  213         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0003);
  214 
  215         /* start traffic */
  216         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0xa000);
  217         /*
  218          * wait for all traffic end
  219          * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms
  220          */
  221         DELAY(60*1000); /* was 50ms */
  222 
  223         for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
  224                 uint32_t tx_ok, tx_error;
  225                 uint32_t rx_ok, rx_error;
  226                 uint32_t tx_ok_high16;
  227                 uint32_t rx_ok_high16;
  228                 uint32_t tx_all_ok, rx_all_ok;
  229 
  230                 /* check counter */
  231                 tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e);
  232                 tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d);
  233                 tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f);
  234                 rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b);
  235                 rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a);
  236                 rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c);
  237 
  238                 tx_all_ok = tx_ok + (tx_ok_high16<<16);
  239                 rx_all_ok = rx_ok + (rx_ok_high16<<16);
  240                 if (tx_all_ok == 0x1000 && tx_error == 0) {
  241                         /* success */
  242                         sc->sc_psgmii.phy_t_status &= ~(1U << (phy + 8));
  243                 } else {
  244                         device_printf(sc->sc_dev,
  245                             "PHY%d test see issue! (tx_all_ok=%u,"
  246                             " rx_all_ok=%u, tx_error=%u, rx_error=%u)\n",
  247                             phy, tx_all_ok, rx_all_ok, tx_error, rx_error);
  248                         sc->sc_psgmii.phy_t_status |= (1U << (phy + 8));
  249                 }
  250         }
  251 
  252         device_printf(sc->sc_dev, "PHY all test 0x%x\n",
  253             sc->sc_psgmii.phy_t_status);
  254         return (0);
  255 }
  256 
  257 /*
  258  * Reset PSGMII in the Malibu PHY.
  259  */
  260 int
  261 ar40xx_hw_malibu_psgmii_ess_reset(struct ar40xx_softc *sc)
  262 {
  263         device_printf(sc->sc_dev, "%s: called\n", __func__);
  264         uint32_t i;
  265 
  266         /* reset phy psgmii */
  267         /* fix phy psgmii RX 20bit */
  268         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b);
  269         /* reset phy psgmii */
  270         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x001b);
  271         /* release reset phy psgmii */
  272         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b);
  273 
  274         for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
  275                 uint32_t status;
  276 
  277                 status = ar40xx_hw_phy_mmd_read(sc, 5, 1, 0x28);
  278                 if (status & (1U << 0))
  279                         break;
  280                 /*
  281                  * Polling interval to check PSGMII PLL in malibu is ready
  282                  * the worst time is 8.67ms
  283                  * for 25MHz reference clock
  284                  * [512+(128+2048)*49]*80ns+100us
  285                  */
  286                 DELAY(2000);
  287         }
  288         /* XXX TODO ;see if it timed out? */
  289 
  290         /*check malibu psgmii calibration done end..*/
  291 
  292         /*freeze phy psgmii RX CDR*/
  293         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x2230);
  294 
  295         ar40xx_hw_ess_reset(sc);
  296 
  297         /*check psgmii calibration done start*/
  298         for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
  299                 uint32_t status;
  300 
  301                 status = ar40xx_hw_psgmii_reg_read(sc, 0xa0);
  302                 if (status & (1U << 0))
  303                         break;
  304                 /* Polling interval to check PSGMII PLL in ESS is ready */
  305                 DELAY(2000);
  306         }
  307         /* XXX TODO ;see if it timed out? */
  308 
  309         /* check dakota psgmii calibration done end..*/
  310 
  311         /* release phy psgmii RX CDR */
  312         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x3230);
  313         /* release phy psgmii RX 20bit */
  314         MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005f);
  315 
  316         return (0);
  317 }
  318 
  319 int
  320 ar40xx_hw_psgmii_self_test(struct ar40xx_softc *sc)
  321 {
  322         uint32_t i, phy, reg;
  323 
  324         device_printf(sc->sc_dev, "%s: called\n", __func__);
  325 
  326         ar40xx_hw_malibu_psgmii_ess_reset(sc);
  327 
  328         /* switch to access MII reg for copper */
  329         MDIO_WRITEREG(sc->sc_mdio_dev, 4, 0x1f, 0x8500);
  330         for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
  331                 /*enable phy mdio broadcast write*/
  332                 ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x801f);
  333         }
  334 
  335         /* force no link by power down */
  336         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x1840);
  337 
  338         /* packet number*/
  339         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x1000);
  340         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8062, 0x05e0);
  341 
  342         /* fix mdi status */
  343         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6800);
  344         for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
  345                 sc->sc_psgmii.phy_t_status = 0;
  346 
  347                 for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
  348                         /* Enable port loopback for testing */
  349                         AR40XX_REG_BARRIER_READ(sc);
  350                         reg = AR40XX_REG_READ(sc,
  351                             AR40XX_REG_PORT_LOOKUP(phy + 1));
  352                         reg |= AR40XX_PORT_LOOKUP_LOOPBACK;
  353                         AR40XX_REG_WRITE(sc,
  354                             AR40XX_REG_PORT_LOOKUP(phy + 1), reg);
  355                         AR40XX_REG_BARRIER_WRITE(sc);
  356                 }
  357 
  358                 for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++)
  359                         ar40xx_hw_psgmii_single_phy_testing(sc, phy);
  360 
  361                 ar40xx_hw_psgmii_all_phy_testing(sc);
  362 
  363                 if (sc->sc_psgmii.phy_t_status)
  364                         ar40xx_hw_malibu_psgmii_ess_reset(sc);
  365                 else
  366                         break;
  367         }
  368 
  369         if (i >= AR40XX_PSGMII_CALB_NUM)
  370                 device_printf(sc->sc_dev, "PSGMII cannot recover\n");
  371         else
  372                 device_printf(sc->sc_dev,
  373                     "PSGMII recovered after %d times reset\n", i);
  374 
  375         /* configuration recover */
  376         /* packet number */
  377         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x0);
  378         /* disable check */
  379         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0);
  380         /* disable traffic */
  381         ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0x0);
  382 
  383         return (0);
  384 }
  385 
  386 int
  387 ar40xx_hw_psgmii_self_test_clean(struct ar40xx_softc *sc)
  388 {
  389         uint32_t reg;
  390         int phy;
  391 
  392         device_printf(sc->sc_dev, "%s: called\n", __func__);
  393 
  394         /* disable phy internal loopback */
  395         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6860);
  396         MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9040);
  397 
  398         for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
  399                 /* disable mac loop back */
  400                 reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(phy + 1));
  401                 reg &= ~AR40XX_PORT_LOOKUP_LOOPBACK;
  402                 AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(phy + 1), reg);
  403                 AR40XX_REG_BARRIER_WRITE(sc);
  404 
  405                 /* disable phy mdio broadcast write */
  406                 ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x001f);
  407         }
  408 
  409         /* clear fdb entry */
  410         ar40xx_hw_atu_flush_all(sc);
  411 
  412         return (0);
  413 }
  414 
  415 int
  416 ar40xx_hw_psgmii_init_config(struct ar40xx_softc *sc)
  417 {
  418         uint32_t reg;
  419 
  420         /*
  421          * This is based on what I found in uboot - it configures
  422          * the initial ESS interconnect to either be PSGMII
  423          * or RGMII.
  424          */
  425 
  426         /* For now, just assume PSGMII and fix it in post. */
  427         /* PSGMIIPHY_PLL_VCO_RELATED_CTRL */
  428         reg = ar40xx_hw_psgmii_reg_read(sc, 0x78c);
  429         device_printf(sc->sc_dev,
  430             "%s: PSGMIIPHY_PLL_VCO_RELATED_CTRL=0x%08x\n", __func__, reg);
  431         /* PSGMIIPHY_VCO_CALIBRATION_CTRL */
  432         reg = ar40xx_hw_psgmii_reg_read(sc, 0x09c);
  433         device_printf(sc->sc_dev,
  434             "%s: PSGMIIPHY_VCO_CALIBRATION_CTRL=0x%08x\n", __func__, reg);
  435 
  436         return (0);
  437 }

Cache object: 5bdc40d7875898a6b583edc8e991952a


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