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_main.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_phy.h>
   66 #include <dev/etherswitch/ar40xx/ar40xx_debug.h>
   67 #include <dev/etherswitch/ar40xx/ar40xx_hw.h>
   68 #include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
   69 #include <dev/etherswitch/ar40xx/ar40xx_hw_port.h>
   70 #include <dev/etherswitch/ar40xx/ar40xx_hw_mib.h>
   71 #include <dev/etherswitch/ar40xx/ar40xx_hw_vtu.h>
   72 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
   73 
   74 #include "mdio_if.h"
   75 #include "miibus_if.h"
   76 #include "etherswitch_if.h"
   77 
   78 static struct ofw_compat_data compat_data[] = {
   79         { "qcom,ess-switch",            1 },
   80         { NULL,                         0 },
   81 };
   82 
   83 static int
   84 ar40xx_probe(device_t dev)
   85 {
   86 
   87         if (! ofw_bus_status_okay(dev))
   88                 return (ENXIO);
   89 
   90         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
   91                 return (ENXIO);
   92 
   93         device_set_desc(dev, "IPQ4018 ESS Switch fabric / PSGMII PHY");
   94         return (BUS_PROBE_DEFAULT);
   95 }
   96 
   97 static void
   98 ar40xx_tick(void *arg)
   99 {
  100         struct ar40xx_softc *sc = arg;
  101 
  102         (void) ar40xx_phy_tick(sc);
  103         callout_reset(&sc->sc_phy_callout, hz, ar40xx_tick, sc);
  104 }
  105 
  106 static void
  107 ar40xx_statchg(device_t dev)
  108 {
  109         struct ar40xx_softc *sc = device_get_softc(dev);
  110 
  111         AR40XX_DPRINTF(sc, AR40XX_DBG_PORT_STATUS, "%s\n", __func__);
  112 }
  113 
  114 static int
  115 ar40xx_readphy(device_t dev, int phy, int reg)
  116 {
  117         struct ar40xx_softc *sc = device_get_softc(dev);
  118 
  119         return MDIO_READREG(sc->sc_mdio_dev, phy, reg);
  120 }
  121 
  122 static int
  123 ar40xx_writephy(device_t dev, int phy, int reg, int val)
  124 {
  125         struct ar40xx_softc *sc = device_get_softc(dev);
  126 
  127         return MDIO_WRITEREG(sc->sc_mdio_dev, phy, reg, val);
  128 }
  129 
  130 /*
  131  * Do the initial switch configuration.
  132  */
  133 static int
  134 ar40xx_reset_switch(struct ar40xx_softc *sc)
  135 {
  136         int ret, i;
  137 
  138         AR40XX_DPRINTF(sc, AR40XX_DBG_HW_INIT, "%s: called\n", __func__);
  139 
  140         /* blank the VLAN config */
  141         memset(&sc->sc_vlan, 0, sizeof(sc->sc_vlan));
  142 
  143         /* initial vlan port mapping */
  144         for (i = 0; i < AR40XX_NUM_VTU_ENTRIES; i++)
  145                 sc->sc_vlan.vlan_id[i] = 0;
  146 
  147         /* init vlan config */
  148         ret = ar40xx_hw_vlan_init(sc);
  149 
  150         /* init monitor config */
  151         sc->sc_monitor.mirror_tx = false;
  152         sc->sc_monitor.mirror_rx = false;
  153         sc->sc_monitor.source_port = 0;
  154         sc->sc_monitor.monitor_port = 0;
  155 
  156         /* apply switch config */
  157         ret = ar40xx_hw_sw_hw_apply(sc);
  158 
  159         return (ret);
  160 }
  161 
  162 static int
  163 ar40xx_sysctl_dump_port_state(SYSCTL_HANDLER_ARGS)
  164 {
  165         struct ar40xx_softc *sc = arg1;
  166         int val = 0;
  167         int error;
  168         int i;
  169 
  170         (void) i; (void) sc;
  171 
  172         error = sysctl_handle_int(oidp, &val, 0, req);
  173         if (error || !req->newptr)
  174                 return (error);
  175 
  176         if (val < 0 || val > 5) {
  177                 return (EINVAL);
  178         }
  179 
  180         AR40XX_LOCK(sc);
  181 
  182         device_printf(sc->sc_dev, "port %d: PORT_STATUS=0x%08x\n", val,
  183             AR40XX_REG_READ(sc, AR40XX_REG_PORT_STATUS(val)));
  184         device_printf(sc->sc_dev, "port %d: PORT_HEADER=0x%08x\n", val,
  185             AR40XX_REG_READ(sc, AR40XX_REG_PORT_HEADER(val)));
  186         device_printf(sc->sc_dev, "port %d: PORT_VLAN0=0x%08x\n", val,
  187             AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN0(val)));
  188         device_printf(sc->sc_dev, "port %d: PORT_VLAN1=0x%08x\n", val,
  189             AR40XX_REG_READ(sc, AR40XX_REG_PORT_VLAN1(val)));
  190         device_printf(sc->sc_dev, "port %d: PORT_LOOKUP=0x%08x\n", val,
  191             AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(val)));
  192         device_printf(sc->sc_dev, "port %d: PORT_HOL_CTRL1=0x%08x\n", val,
  193             AR40XX_REG_READ(sc, AR40XX_REG_PORT_HOL_CTRL1(val)));
  194         device_printf(sc->sc_dev, "port %d: PORT_FLOWCTRL_THRESH=0x%08x\n",
  195             val, AR40XX_REG_READ(sc, AR40XX_REG_PORT_FLOWCTRL_THRESH(val)));
  196 
  197         AR40XX_UNLOCK(sc);
  198 
  199         return (0);
  200 }
  201 
  202 static int
  203 ar40xx_sysctl_dump_port_mibstats(SYSCTL_HANDLER_ARGS)
  204 {
  205         struct ar40xx_softc *sc = arg1;
  206         int val = 0;
  207         int error;
  208         int i;
  209 
  210         (void) i; (void) sc;
  211 
  212         error = sysctl_handle_int(oidp, &val, 0, req);
  213         if (error || !req->newptr)
  214                 return (error);
  215 
  216         if (val < 0 || val > 5) {
  217                 return (EINVAL);
  218         }
  219 
  220         AR40XX_LOCK(sc);
  221 
  222         /* Yes, this snapshots all ports */
  223         (void) ar40xx_hw_mib_capture(sc);
  224         (void) ar40xx_hw_mib_fetch(sc, val);
  225 
  226         AR40XX_UNLOCK(sc);
  227 
  228         return (0);
  229 }
  230 
  231 
  232 static int
  233 ar40xx_sysctl_attach(struct ar40xx_softc *sc)
  234 {
  235         struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
  236         struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
  237 
  238         SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  239             "debug", CTLFLAG_RW, &sc->sc_debug, 0,
  240             "debugging flags");
  241 
  242         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  243             "port_state", CTLTYPE_INT | CTLFLAG_RW, sc,
  244             0, ar40xx_sysctl_dump_port_state, "I", "");
  245 
  246         SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
  247             "port_mibstats", CTLTYPE_INT | CTLFLAG_RW, sc,
  248             0, ar40xx_sysctl_dump_port_mibstats, "I", "");
  249 
  250         return (0);
  251 }
  252 
  253 static int
  254 ar40xx_detach(device_t dev)
  255 {
  256         struct ar40xx_softc *sc = device_get_softc(dev);
  257         int i;
  258 
  259         device_printf(sc->sc_dev, "%s: called\n", __func__);
  260 
  261         callout_drain(&sc->sc_phy_callout);
  262 
  263         /* Free PHYs */
  264         for (i = 0; i < AR40XX_NUM_PHYS; i++) {
  265                 if (sc->sc_phys.miibus[i] != NULL)
  266                         device_delete_child(dev, sc->sc_phys.miibus[i]);
  267                 if (sc->sc_phys.ifp[i] != NULL)
  268                         if_free(sc->sc_phys.ifp[i]);
  269                 free(sc->sc_phys.ifname[i], M_DEVBUF);
  270         }
  271 
  272         bus_generic_detach(dev);
  273         mtx_destroy(&sc->sc_mtx);
  274 
  275         return (0);
  276 }
  277 
  278 static int
  279 ar40xx_attach(device_t dev)
  280 {
  281         struct ar40xx_softc *sc = device_get_softc(dev);
  282         phandle_t psgmii_p, root_p, mdio_p;
  283         int ret, i;
  284 
  285         sc->sc_dev = dev;
  286         mtx_init(&sc->sc_mtx, "ar40xx_switch", NULL, MTX_DEF);
  287 
  288         psgmii_p = OF_finddevice("/soc/ess-psgmii");
  289         if (psgmii_p == -1) {
  290                 device_printf(dev,
  291                     "%s: couldn't find /soc/ess-psgmii DT node\n",
  292                     __func__);
  293                 goto error;
  294         }
  295 
  296         /*
  297          * Get the ipq4019-mdio node here, to talk to our local PHYs
  298          * if needed
  299          */
  300         root_p = OF_finddevice("/soc");
  301         mdio_p = ofw_bus_find_compatible(root_p, "qcom,ipq4019-mdio");
  302         if (mdio_p == -1) {
  303                 device_printf(dev, "%s: couldn't find ipq4019-mdio DT node\n",
  304                     __func__);
  305                 goto error;
  306         }
  307         sc->sc_mdio_phandle = mdio_p;
  308         sc->sc_mdio_dev = OF_device_from_xref(OF_xref_from_node(mdio_p));
  309         if (sc->sc_mdio_dev == NULL) {
  310                 device_printf(dev,
  311                     "%s: couldn't get mdio device (mdio_p=%u)\n",
  312                     __func__, mdio_p);
  313                 goto error;
  314         }
  315 
  316         /* get psgmii base address from psgmii node */
  317         ret = OF_decode_addr(psgmii_p, 0, &sc->sc_psgmii_mem_tag,
  318             &sc->sc_psgmii_mem_handle,
  319             &sc->sc_psgmii_mem_size);
  320         if (ret != 0) {
  321                 device_printf(dev, "%s: couldn't map psgmii mem (%d)\n",
  322                     __func__, ret);
  323                 goto error;
  324         }
  325 
  326         /* get switch base address */
  327         sc->sc_ess_mem_rid = 0;
  328         sc->sc_ess_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
  329             &sc->sc_ess_mem_rid, RF_ACTIVE);
  330         if (sc->sc_ess_mem_res == NULL) {
  331                 device_printf(dev, "%s: failed to find memory resource\n",
  332                     __func__);
  333                 goto error;
  334         }
  335         sc->sc_ess_mem_size = (size_t) bus_get_resource_count(dev,
  336             SYS_RES_MEMORY, sc->sc_ess_mem_rid);
  337         if (sc->sc_ess_mem_size == 0) {
  338                 device_printf(dev, "%s: failed to get device memory size\n",
  339                     __func__);
  340                 goto error;
  341         }
  342 
  343         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_mac_mode",
  344             &sc->sc_config.switch_mac_mode,
  345             sizeof(sc->sc_config.switch_mac_mode));
  346         if (ret < 0) {
  347                 device_printf(dev, "%s: missing switch_mac_mode property\n",
  348                     __func__);
  349                 goto error;
  350         }
  351 
  352         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_cpu_bmp",
  353             &sc->sc_config.switch_cpu_bmp,
  354             sizeof(sc->sc_config.switch_cpu_bmp));
  355         if (ret < 0) {
  356                 device_printf(dev, "%s: missing switch_cpu_bmp property\n",
  357                     __func__);
  358                 goto error;
  359         }
  360 
  361         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_lan_bmp",
  362             &sc->sc_config.switch_lan_bmp,
  363             sizeof(sc->sc_config.switch_lan_bmp));
  364         if (ret < 0) {
  365                 device_printf(dev, "%s: missing switch_lan_bmp property\n",
  366                     __func__);
  367                 goto error;
  368         }
  369 
  370         ret = OF_getencprop(ofw_bus_get_node(dev), "switch_wan_bmp",
  371             &sc->sc_config.switch_wan_bmp,
  372             sizeof(sc->sc_config.switch_wan_bmp));
  373         if (ret < 0) {
  374                 device_printf(dev, "%s: missing switch_wan_bmp property\n",
  375                     __func__);
  376                 goto error;
  377         }
  378 
  379         ret = clk_get_by_ofw_name(dev, 0, "ess_clk", &sc->sc_ess_clk);
  380         if (ret != 0) {
  381                 device_printf(dev, "%s: failed to find ess_clk (%d)\n",
  382                     __func__, ret);
  383                 goto error;
  384         }
  385         ret = clk_enable(sc->sc_ess_clk);
  386         if (ret != 0) {
  387                 device_printf(dev, "%s: failed to enable clock (%d)\n",
  388                     __func__, ret);
  389                 goto error;
  390         }
  391 
  392         ret = hwreset_get_by_ofw_name(dev, 0, "ess_rst", &sc->sc_ess_rst);
  393         if (ret != 0) {
  394                 device_printf(dev, "%s: failed to find ess_rst (%d)\n",
  395                     __func__, ret);
  396                 goto error;
  397         }
  398 
  399         /*
  400          * Ok, at this point we have enough resources to do an initial
  401          * reset and configuration.
  402          */
  403 
  404         AR40XX_LOCK(sc);
  405 
  406         /* Initial PSGMII/RGMII port configuration */
  407         ret = ar40xx_hw_psgmii_init_config(sc);
  408         if (ret != 0) {
  409                 device_printf(sc->sc_dev,
  410                     "ERROR: failed to init PSGMII (%d)\n", ret);
  411                 goto error_locked;
  412         }
  413 
  414         /*
  415          * ESS reset - this resets both the ethernet switch
  416          * AND the ethernet block.
  417          */
  418         ret = ar40xx_hw_ess_reset(sc);
  419         if (ret != 0) {
  420                 device_printf(sc->sc_dev,
  421                     "ERROR: failed to reset ESS block (%d)\n", ret);
  422                 goto error_locked;
  423         }
  424 
  425         /*
  426          * Check the PHY IDs for each of the PHYs from 0..4;
  427          * this is useful to make sure that we can SEE the external
  428          * PHY(s).
  429          */
  430         if (bootverbose) {
  431                 ret = ar40xx_hw_phy_get_ids(sc);
  432                 if (ret != 0) {
  433                         device_printf(sc->sc_dev,
  434                             "ERROR: failed to check PHY IDs (%d)\n", ret);
  435                         goto error_locked;
  436                 }
  437         }
  438 
  439         /*
  440          * Do PSGMII PHY self-test; work-around issues.
  441          */
  442         ret = ar40xx_hw_psgmii_self_test(sc);
  443         if (ret != 0) {
  444                 device_printf(sc->sc_dev,
  445                     "ERROR: failed to do PSGMII self-test (%d)\n", ret);
  446                 goto error_locked;
  447         }
  448 
  449         /* Return port config to runtime state */
  450         ret = ar40xx_hw_psgmii_self_test_clean(sc);
  451         if (ret != 0) {
  452                 device_printf(sc->sc_dev,
  453                     "ERROR: failed to do PSGMII runtime config (%d)\n", ret);
  454                 goto error_locked;
  455         }
  456 
  457         /* mac_mode_init */
  458         ret = ar40xx_hw_psgmii_set_mac_mode(sc,
  459             sc->sc_config.switch_mac_mode);
  460 
  461         /* Initialise each hardware port */
  462         for (i = 0; i < AR40XX_NUM_PORTS; i++) {
  463                 ret = ar40xx_hw_port_init(sc, i);
  464         }
  465 
  466         /* initialise the global switch configuration */
  467         ret = ar40xx_hw_init_globals(sc);
  468 
  469         /* reset the switch vlan/port learning config */
  470         ret = ar40xx_reset_switch(sc);
  471 
  472         /* cpuport setup */
  473         ret = ar40xx_hw_port_cpuport_setup(sc);
  474 
  475         AR40XX_UNLOCK(sc);
  476 
  477 #if 0
  478         /* We may end up needing the QM workaround code here.. */
  479         device_printf(dev, "%s: TODO: QM error check\n", __func__);
  480 #endif
  481 
  482         /* Attach PHYs */
  483         ret = ar40xx_attach_phys(sc);
  484 
  485         ret = bus_generic_probe(dev);
  486         bus_enumerate_hinted_children(dev);
  487         ret = bus_generic_attach(dev);
  488 
  489         /* Start timer */
  490         callout_init_mtx(&sc->sc_phy_callout, &sc->sc_mtx, 0);
  491 
  492         /*
  493          * Setup the etherswitch info block.
  494          */
  495         strlcpy(sc->sc_info.es_name, device_get_desc(dev),
  496             sizeof(sc->sc_info.es_name));
  497         sc->sc_info.es_nports = AR40XX_NUM_PORTS;
  498         sc->sc_info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q;
  499         /* XXX TODO: double-tag / 802.1ad */
  500         sc->sc_info.es_nvlangroups = AR40XX_NUM_VTU_ENTRIES;
  501 
  502         /*
  503          * Fetch the initial port configuration.
  504          */
  505         AR40XX_LOCK(sc);
  506         ar40xx_tick(sc);
  507         AR40XX_UNLOCK(sc);
  508 
  509         ar40xx_sysctl_attach(sc);
  510 
  511         return (0);
  512 error_locked:
  513         AR40XX_UNLOCK(sc);
  514 error:
  515         ar40xx_detach(dev);
  516         return (ENXIO);
  517 }
  518 
  519 static void
  520 ar40xx_lock(device_t dev)
  521 {
  522         struct ar40xx_softc *sc = device_get_softc(dev);
  523 
  524         AR40XX_LOCK(sc);
  525 }
  526 
  527 static void
  528 ar40xx_unlock(device_t dev)
  529 {
  530         struct ar40xx_softc *sc = device_get_softc(dev);
  531 
  532         AR40XX_LOCK_ASSERT(sc);
  533         AR40XX_UNLOCK(sc);
  534 }
  535 
  536 static etherswitch_info_t *
  537 ar40xx_getinfo(device_t dev)
  538 {
  539         struct ar40xx_softc *sc = device_get_softc(dev);
  540 
  541         return (&sc->sc_info);
  542 }
  543 
  544 static int
  545 ar40xx_readreg(device_t dev, int addr)
  546 {
  547         struct ar40xx_softc *sc = device_get_softc(dev);
  548 
  549         if (addr >= sc->sc_ess_mem_size - 1)
  550                 return (-1);
  551 
  552         AR40XX_REG_BARRIER_READ(sc);
  553 
  554         return AR40XX_REG_READ(sc, addr);
  555 }
  556 
  557 static int
  558 ar40xx_writereg(device_t dev, int addr, int value)
  559 {
  560         struct ar40xx_softc *sc = device_get_softc(dev);
  561 
  562         if (addr >= sc->sc_ess_mem_size - 1)
  563                 return (-1);
  564 
  565         AR40XX_REG_WRITE(sc, addr, value);
  566         AR40XX_REG_BARRIER_WRITE(sc);
  567         return (0);
  568 }
  569 
  570 /*
  571  * Get the port configuration and status.
  572  */
  573 static int
  574 ar40xx_getport(device_t dev, etherswitch_port_t *p)
  575 {
  576         struct ar40xx_softc *sc = device_get_softc(dev);
  577         struct mii_data *mii = NULL;
  578         struct ifmediareq *ifmr;
  579         int err;
  580 
  581         if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
  582                 return (ENXIO);
  583 
  584         AR40XX_LOCK(sc);
  585         /* Fetch the current VLAN configuration for this port */
  586         /* PVID */
  587         ar40xx_hw_get_port_pvid(sc, p->es_port, &p->es_pvid);
  588 
  589         /*
  590          * The VLAN egress aren't appropriate to the ports;
  591          * instead it's part of the VLAN group config.
  592          */
  593 
  594         /* Get MII config */
  595         mii = ar40xx_phy_miiforport(sc, p->es_port);
  596 
  597         AR40XX_UNLOCK(sc);
  598 
  599         if (p->es_port == 0) {
  600                 /* CPU port */
  601                 p->es_flags |= ETHERSWITCH_PORT_CPU;
  602                 ifmr = &p->es_ifmr;
  603                 ifmr->ifm_count = 0;
  604                 ifmr->ifm_current = ifmr->ifm_active =
  605                      IFM_ETHER | IFM_1000_T | IFM_FDX;
  606                 ifmr->ifm_mask = 0;
  607                 ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID;
  608         } else if (mii != NULL) {
  609                 /* non-CPU port */
  610                 err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr,
  611                     &mii->mii_media, SIOCGIFMEDIA);
  612                 if (err)
  613                         return (err);
  614         } else {
  615                 return (ENXIO);
  616         }
  617 
  618         return (0);
  619 }
  620 
  621 /*
  622  * Set the port configuration and status.
  623  */
  624 static int
  625 ar40xx_setport(device_t dev, etherswitch_port_t *p)
  626 {
  627         struct ar40xx_softc *sc = device_get_softc(dev);
  628         struct ifmedia *ifm;
  629         struct mii_data *mii;
  630         if_t ifp;
  631         int ret;
  632 
  633         if (p->es_port < 0 || p->es_port > sc->sc_info.es_nports)
  634                 return (EINVAL);
  635 
  636         /* Port flags */
  637         AR40XX_LOCK(sc);
  638         ret = ar40xx_hw_set_port_pvid(sc, p->es_port, p->es_pvid);
  639         if (ret != 0) {
  640                 AR40XX_UNLOCK(sc);
  641                 return (ret);
  642         }
  643         /* XXX TODO: tag strip/unstrip, double-tag, etc */
  644         AR40XX_UNLOCK(sc);
  645 
  646         /* Don't change media config on CPU port */
  647         if (p->es_port == 0)
  648                 return (0);
  649 
  650         mii = ar40xx_phy_miiforport(sc, p->es_port);
  651         if (mii == NULL)
  652                 return (ENXIO);
  653 
  654         ifp = ar40xx_phy_ifpforport(sc, p->es_port);
  655 
  656         ifm = &mii->mii_media;
  657         return (ifmedia_ioctl(ifp, &p->es_ifr, ifm, SIOCSIFMEDIA));
  658 
  659         return (0);
  660 }
  661 
  662 /*
  663  * Get the current VLAN group (per-port, ISL, dot1q) configuration.
  664  *
  665  * For now the only supported operating mode is dot1q.
  666  */
  667 static int
  668 ar40xx_getvgroup(device_t dev, etherswitch_vlangroup_t *vg)
  669 {
  670         struct ar40xx_softc *sc = device_get_softc(dev);
  671         int vid, ret;
  672 
  673         if (vg->es_vlangroup > sc->sc_info.es_nvlangroups)
  674                 return (EINVAL);
  675 
  676         vg->es_untagged_ports = 0;
  677         vg->es_member_ports = 0;
  678         vg->es_fid = 0;
  679 
  680         AR40XX_LOCK(sc);
  681 
  682         /* Note: only supporting 802.1q VLAN config for now */
  683         if (sc->sc_vlan.vlan != 1) {
  684                 vg->es_member_ports = 0;
  685                 vg->es_untagged_ports = 0;
  686                 AR40XX_UNLOCK(sc);
  687                 return (-1);
  688         }
  689 
  690         /* Get vlangroup mapping to VLAN id */
  691         vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
  692         if ((vid & ETHERSWITCH_VID_VALID) == 0) {
  693                 /* Not an active vgroup; bail */
  694                 AR40XX_UNLOCK(sc);
  695                 return (0);
  696         }
  697         vg->es_vid = vid;
  698 
  699         ret = ar40xx_hw_vtu_get_vlan(sc, vid, &vg->es_member_ports,
  700             &vg->es_untagged_ports);
  701 
  702         AR40XX_UNLOCK(sc);
  703 
  704         if (ret == 0) {
  705                 vg->es_vid |= ETHERSWITCH_VID_VALID;
  706         }
  707 
  708         return (ret);
  709 }
  710 
  711 /*
  712  * Set the current VLAN group (per-port, ISL, dot1q) configuration.
  713  *
  714  * For now the only supported operating mode is dot1q.
  715  */
  716 static int
  717 ar40xx_setvgroup(device_t dev, etherswitch_vlangroup_t *vg)
  718 {
  719         struct ar40xx_softc *sc = device_get_softc(dev);
  720         int err, vid;
  721 
  722         /* For now we only support 802.1q mode */
  723         if (sc->sc_vlan.vlan == 0)
  724                 return (EINVAL);
  725 
  726         AR40XX_LOCK(sc);
  727         vid = sc->sc_vlan.vlan_id[vg->es_vlangroup];
  728         /*
  729          * If we have an 802.1q VID and it's different to the current one,
  730          * purge the current VTU entry.
  731          */
  732         if ((vid != 0) &&
  733             ((vid & ETHERSWITCH_VID_VALID) != 0) &&
  734             ((vid & ETHERSWITCH_VID_MASK) !=
  735              (vg->es_vid & ETHERSWITCH_VID_MASK))) {
  736                 AR40XX_DPRINTF(sc, AR40XX_DBG_VTU_OP,
  737                     "%s: purging VID %d first\n", __func__, vid);
  738                 err = ar40xx_hw_vtu_flush(sc);
  739                 if (err != 0) {
  740                         AR40XX_UNLOCK(sc);
  741                         return (err);
  742                 }
  743         }
  744 
  745         /* Update VLAN ID */
  746         vid = vg->es_vid & ETHERSWITCH_VID_MASK;
  747         sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid;
  748         if (vid == 0) {
  749                 /* Setting it to 0 disables the group */
  750                 AR40XX_UNLOCK(sc);
  751                 return (0);
  752         }
  753         /* Add valid bit for this entry */
  754         sc->sc_vlan.vlan_id[vg->es_vlangroup] = vid | ETHERSWITCH_VID_VALID;
  755 
  756         /* Update hardware */
  757         err = ar40xx_hw_vtu_load_vlan(sc, vid, vg->es_member_ports,
  758             vg->es_untagged_ports);
  759         if (err != 0) {
  760                 AR40XX_UNLOCK(sc);
  761                 return (err);
  762         }
  763 
  764         /* Update the config for the given entry */
  765         sc->sc_vlan.vlan_ports[vg->es_vlangroup] = vg->es_member_ports;
  766         sc->sc_vlan.vlan_untagged[vg->es_vlangroup] = vg->es_untagged_ports;
  767 
  768         AR40XX_UNLOCK(sc);
  769 
  770         return (0);
  771 }
  772 
  773 /*
  774  * Get the current configuration mode.
  775  */
  776 static int
  777 ar40xx_getconf(device_t dev, etherswitch_conf_t *conf)
  778 {
  779         struct ar40xx_softc *sc = device_get_softc(dev);
  780         int ret;
  781 
  782         AR40XX_LOCK(sc);
  783 
  784         /* Only support dot1q VLAN for now */
  785         conf->cmd = ETHERSWITCH_CONF_VLAN_MODE;
  786         conf->vlan_mode = ETHERSWITCH_VLAN_DOT1Q;
  787 
  788         /* Switch MAC address */
  789         ret = ar40xx_hw_read_switch_mac_address(sc, &conf->switch_macaddr);
  790         if (ret == 0)
  791                 conf->cmd |= ETHERSWITCH_CONF_SWITCH_MACADDR;
  792 
  793         AR40XX_UNLOCK(sc);
  794 
  795         return (0);
  796 }
  797 
  798 /*
  799  * Set the current configuration and do a switch reset.
  800  *
  801  * For now the only supported operating mode is dot1q, don't
  802  * allow it to be set to non-dot1q.
  803  */
  804 static int
  805 ar40xx_setconf(device_t dev, etherswitch_conf_t *conf)
  806 {
  807         struct ar40xx_softc *sc = device_get_softc(dev);
  808         int ret = 0;
  809 
  810         if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) {
  811                 /* Only support dot1q VLAN for now */
  812                 if (conf->vlan_mode != ETHERSWITCH_VLAN_DOT1Q)
  813                         return (EINVAL);
  814         }
  815 
  816         if (conf->cmd & ETHERSWITCH_CONF_SWITCH_MACADDR) {
  817                 AR40XX_LOCK(sc);
  818                 ret = ar40xx_hw_read_switch_mac_address(sc,
  819                     &conf->switch_macaddr);
  820                 AR40XX_UNLOCK(sc);
  821         }
  822 
  823         return (ret);
  824 }
  825 
  826 /*
  827  * Flush all ATU entries.
  828  */
  829 static int
  830 ar40xx_atu_flush_all(device_t dev)
  831 {
  832         struct ar40xx_softc *sc = device_get_softc(dev);
  833         int ret;
  834 
  835         AR40XX_LOCK(sc);
  836         ret = ar40xx_hw_atu_flush_all(sc);
  837         AR40XX_UNLOCK(sc);
  838         return (ret);
  839 }
  840 
  841 /*
  842  * Flush all ATU entries for the given port.
  843  */
  844 static int
  845 ar40xx_atu_flush_port(device_t dev, int port)
  846 {
  847         struct ar40xx_softc *sc = device_get_softc(dev);
  848         int ret;
  849 
  850         AR40XX_LOCK(sc);
  851         ret = ar40xx_hw_atu_flush_port(sc, port);
  852         AR40XX_UNLOCK(sc);
  853         return (ret);
  854 }
  855 
  856 /*
  857  * Load the ATU table into local storage so it can be iterated
  858  * over.
  859  */
  860 static int
  861 ar40xx_atu_fetch_table(device_t dev, etherswitch_atu_table_t *table)
  862 {
  863         struct ar40xx_softc *sc = device_get_softc(dev);
  864         int err, nitems;
  865 
  866         memset(&sc->atu.entries, 0, sizeof(sc->atu.entries));
  867 
  868         table->es_nitems = 0;
  869         nitems = 0;
  870 
  871         AR40XX_LOCK(sc);
  872         sc->atu.count = 0;
  873         err = ar40xx_hw_atu_fetch_entry(sc, NULL, 0);
  874         if (err != 0)
  875                 goto done;
  876 
  877         while (nitems < AR40XX_NUM_ATU_ENTRIES) {
  878                 err = ar40xx_hw_atu_fetch_entry(sc,
  879                     &sc->atu.entries[nitems], 1);
  880                 if (err != 0)
  881                         goto done;
  882                 sc->atu.entries[nitems].id = nitems;
  883                 nitems++;
  884         }
  885 done:
  886         sc->atu.count = nitems;
  887         table->es_nitems = nitems;
  888         AR40XX_UNLOCK(sc);
  889 
  890         return (0);
  891 }
  892 
  893 /*
  894  * Iterate over the ATU table entries that have been previously
  895  * fetched.
  896  */
  897 static int
  898 ar40xx_atu_fetch_table_entry(device_t dev, etherswitch_atu_entry_t *e)
  899 {
  900         struct ar40xx_softc *sc = device_get_softc(dev);
  901         int id, err = 0;
  902 
  903         id = e->id;
  904         AR40XX_LOCK(sc);
  905         if (id > sc->atu.count) {
  906                 err = ENOENT;
  907                 goto done;
  908         }
  909         memcpy(e, &sc->atu.entries[id], sizeof(*e));
  910 done:
  911         AR40XX_UNLOCK(sc);
  912         return (err);
  913 }
  914 
  915 static device_method_t ar40xx_methods[] = {
  916         /* Device interface */
  917         DEVMETHOD(device_probe,                 ar40xx_probe),
  918         DEVMETHOD(device_attach,                ar40xx_attach),
  919         DEVMETHOD(device_detach,                ar40xx_detach),
  920 
  921         /* bus interface */
  922         DEVMETHOD(bus_add_child,                device_add_child_ordered),
  923 
  924         /* MII interface */
  925         DEVMETHOD(miibus_readreg,               ar40xx_readphy),
  926         DEVMETHOD(miibus_writereg,              ar40xx_writephy),
  927         DEVMETHOD(miibus_statchg,               ar40xx_statchg),
  928 
  929         /* MDIO interface */
  930         DEVMETHOD(mdio_readreg,                 ar40xx_readphy),
  931         DEVMETHOD(mdio_writereg,                ar40xx_writephy),
  932 
  933         /* etherswitch interface */
  934         DEVMETHOD(etherswitch_lock,             ar40xx_lock),
  935         DEVMETHOD(etherswitch_unlock,           ar40xx_unlock),
  936         DEVMETHOD(etherswitch_getinfo,          ar40xx_getinfo),
  937         DEVMETHOD(etherswitch_readreg,          ar40xx_readreg),
  938         DEVMETHOD(etherswitch_writereg,         ar40xx_writereg),
  939         DEVMETHOD(etherswitch_readphyreg,       ar40xx_readphy),
  940         DEVMETHOD(etherswitch_writephyreg,      ar40xx_writephy),
  941         DEVMETHOD(etherswitch_getport,          ar40xx_getport),
  942         DEVMETHOD(etherswitch_setport,          ar40xx_setport),
  943         DEVMETHOD(etherswitch_getvgroup,        ar40xx_getvgroup),
  944         DEVMETHOD(etherswitch_setvgroup,        ar40xx_setvgroup),
  945         DEVMETHOD(etherswitch_getconf,          ar40xx_getconf),
  946         DEVMETHOD(etherswitch_setconf,          ar40xx_setconf),
  947         DEVMETHOD(etherswitch_flush_all,        ar40xx_atu_flush_all),
  948         DEVMETHOD(etherswitch_flush_port,       ar40xx_atu_flush_port),
  949         DEVMETHOD(etherswitch_fetch_table,      ar40xx_atu_fetch_table),
  950         DEVMETHOD(etherswitch_fetch_table_entry,
  951                                              ar40xx_atu_fetch_table_entry),
  952 
  953         DEVMETHOD_END
  954 };
  955 
  956 DEFINE_CLASS_0(ar40xx, ar40xx_driver, ar40xx_methods,
  957     sizeof(struct ar40xx_softc));
  958 
  959 DRIVER_MODULE(ar40xx, simplebus, ar40xx_driver, 0, 0);
  960 DRIVER_MODULE(ar40xx, ofwbus, ar40xx_driver, 0, 0);
  961 DRIVER_MODULE(miibus, ar40xx, miibus_driver, 0, 0);
  962 DRIVER_MODULE(mdio, ar40xx, mdio_driver, 0, 0);
  963 DRIVER_MODULE(etherswitch, ar40xx, etherswitch_driver, 0, 0);
  964 MODULE_DEPEND(ar40xx, mdio, 1, 1, 1);
  965 MODULE_DEPEND(ar40xx, miibus, 1, 1, 1);
  966 MODULE_DEPEND(ar40xx, etherswitch, 1, 1, 1);
  967 MODULE_VERSION(ar40xx, 1);

Cache object: 9646da7ad5a355feeebe157b59425091


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