1451bcf1bSMarcin Wojtas /*- 2451bcf1bSMarcin Wojtas * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3451bcf1bSMarcin Wojtas * 4451bcf1bSMarcin Wojtas * Copyright (c) 2021 Alstom Group. 5451bcf1bSMarcin Wojtas * Copyright (c) 2021 Semihalf. 6451bcf1bSMarcin Wojtas * 7451bcf1bSMarcin Wojtas * Redistribution and use in source and binary forms, with or without 8451bcf1bSMarcin Wojtas * modification, are permitted provided that the following conditions 9451bcf1bSMarcin Wojtas * are met: 10451bcf1bSMarcin Wojtas * 1. Redistributions of source code must retain the above copyright 11451bcf1bSMarcin Wojtas * notice, this list of conditions and the following disclaimer. 12451bcf1bSMarcin Wojtas * 2. Redistributions in binary form must reproduce the above copyright 13451bcf1bSMarcin Wojtas * notice, this list of conditions and the following disclaimer in the 14451bcf1bSMarcin Wojtas * documentation and/or other materials provided with the distribution. 15451bcf1bSMarcin Wojtas * 16451bcf1bSMarcin Wojtas * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17451bcf1bSMarcin Wojtas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18451bcf1bSMarcin Wojtas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19451bcf1bSMarcin Wojtas * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20451bcf1bSMarcin Wojtas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21451bcf1bSMarcin Wojtas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22451bcf1bSMarcin Wojtas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23451bcf1bSMarcin Wojtas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24451bcf1bSMarcin Wojtas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25451bcf1bSMarcin Wojtas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26451bcf1bSMarcin Wojtas * SUCH DAMAGE. 27451bcf1bSMarcin Wojtas */ 28451bcf1bSMarcin Wojtas 29451bcf1bSMarcin Wojtas #include <sys/cdefs.h> 30451bcf1bSMarcin Wojtas __FBSDID("$FreeBSD$"); 31451bcf1bSMarcin Wojtas 32451bcf1bSMarcin Wojtas #include <sys/param.h> 33451bcf1bSMarcin Wojtas #include <sys/bus.h> 34451bcf1bSMarcin Wojtas #include <sys/kernel.h> 35451bcf1bSMarcin Wojtas #include <sys/module.h> 36451bcf1bSMarcin Wojtas #include <sys/socket.h> 37451bcf1bSMarcin Wojtas #include <sys/sockio.h> 38d88aecceSKornel Duleba #include <sys/sysctl.h> 39451bcf1bSMarcin Wojtas #include <sys/rman.h> 40451bcf1bSMarcin Wojtas 41451bcf1bSMarcin Wojtas #include <machine/bus.h> 42451bcf1bSMarcin Wojtas #include <machine/resource.h> 43451bcf1bSMarcin Wojtas 44451bcf1bSMarcin Wojtas #include <net/if.h> 45451bcf1bSMarcin Wojtas #include <net/if_media.h> 46451bcf1bSMarcin Wojtas #include <net/if_types.h> 47451bcf1bSMarcin Wojtas 4829cf6a79SKornel Duleba #include <dev/enetc/enetc_mdio.h> 4929cf6a79SKornel Duleba 50451bcf1bSMarcin Wojtas #include <dev/etherswitch/etherswitch.h> 51451bcf1bSMarcin Wojtas #include <dev/mii/mii.h> 52451bcf1bSMarcin Wojtas #include <dev/mii/miivar.h> 53451bcf1bSMarcin Wojtas #include <dev/pci/pcireg.h> 54451bcf1bSMarcin Wojtas #include <dev/pci/pcivar.h> 55451bcf1bSMarcin Wojtas 56451bcf1bSMarcin Wojtas #include <dev/ofw/ofw_bus.h> 57451bcf1bSMarcin Wojtas #include <dev/ofw/ofw_bus_subr.h> 58451bcf1bSMarcin Wojtas 59451bcf1bSMarcin Wojtas #include <dev/etherswitch/felix/felix_var.h> 60451bcf1bSMarcin Wojtas #include <dev/etherswitch/felix/felix_reg.h> 61451bcf1bSMarcin Wojtas 62451bcf1bSMarcin Wojtas #include "etherswitch_if.h" 63451bcf1bSMarcin Wojtas #include "miibus_if.h" 64451bcf1bSMarcin Wojtas 65451bcf1bSMarcin Wojtas MALLOC_DECLARE(M_FELIX); 66451bcf1bSMarcin Wojtas MALLOC_DEFINE(M_FELIX, "felix", "felix switch"); 67451bcf1bSMarcin Wojtas 68451bcf1bSMarcin Wojtas static device_probe_t felix_probe; 69451bcf1bSMarcin Wojtas static device_attach_t felix_attach; 70451bcf1bSMarcin Wojtas static device_detach_t felix_detach; 71451bcf1bSMarcin Wojtas 72451bcf1bSMarcin Wojtas static etherswitch_info_t* felix_getinfo(device_t); 73451bcf1bSMarcin Wojtas static int felix_getconf(device_t, etherswitch_conf_t *); 74451bcf1bSMarcin Wojtas static int felix_setconf(device_t, etherswitch_conf_t *); 75451bcf1bSMarcin Wojtas static void felix_lock(device_t); 76451bcf1bSMarcin Wojtas static void felix_unlock(device_t); 77451bcf1bSMarcin Wojtas static int felix_getport(device_t, etherswitch_port_t *); 78451bcf1bSMarcin Wojtas static int felix_setport(device_t, etherswitch_port_t *); 79451bcf1bSMarcin Wojtas static int felix_readreg_wrapper(device_t, int); 80451bcf1bSMarcin Wojtas static int felix_writereg_wrapper(device_t, int, int); 81451bcf1bSMarcin Wojtas static int felix_readphy(device_t, int, int); 82451bcf1bSMarcin Wojtas static int felix_writephy(device_t, int, int, int); 83451bcf1bSMarcin Wojtas static int felix_setvgroup(device_t, etherswitch_vlangroup_t *); 84451bcf1bSMarcin Wojtas static int felix_getvgroup(device_t, etherswitch_vlangroup_t *); 85451bcf1bSMarcin Wojtas 86451bcf1bSMarcin Wojtas static int felix_parse_port_fdt(felix_softc_t, phandle_t, int *); 87451bcf1bSMarcin Wojtas static int felix_setup(felix_softc_t); 88451bcf1bSMarcin Wojtas static void felix_setup_port(felix_softc_t, int); 89451bcf1bSMarcin Wojtas 90451bcf1bSMarcin Wojtas static void felix_tick(void *); 91451bcf1bSMarcin Wojtas static int felix_ifmedia_upd(struct ifnet *); 92451bcf1bSMarcin Wojtas static void felix_ifmedia_sts(struct ifnet *, struct ifmediareq *); 93451bcf1bSMarcin Wojtas 94451bcf1bSMarcin Wojtas static void felix_get_port_cfg(felix_softc_t, etherswitch_port_t *); 95451bcf1bSMarcin Wojtas static void felix_set_port_cfg(felix_softc_t, etherswitch_port_t *); 96451bcf1bSMarcin Wojtas 97451bcf1bSMarcin Wojtas static bool felix_is_phyport(felix_softc_t, int); 98451bcf1bSMarcin Wojtas static struct mii_data *felix_miiforport(felix_softc_t, unsigned int); 99451bcf1bSMarcin Wojtas 100451bcf1bSMarcin Wojtas static struct felix_pci_id felix_pci_ids[] = { 101451bcf1bSMarcin Wojtas {PCI_VENDOR_FREESCALE, FELIX_DEV_ID, FELIX_DEV_NAME}, 102451bcf1bSMarcin Wojtas {0, 0, NULL} 103451bcf1bSMarcin Wojtas }; 104451bcf1bSMarcin Wojtas 105451bcf1bSMarcin Wojtas static device_method_t felix_methods[] = { 106451bcf1bSMarcin Wojtas /* device interface */ 107451bcf1bSMarcin Wojtas DEVMETHOD(device_probe, felix_probe), 108451bcf1bSMarcin Wojtas DEVMETHOD(device_attach, felix_attach), 109451bcf1bSMarcin Wojtas DEVMETHOD(device_detach, felix_detach), 110451bcf1bSMarcin Wojtas 111451bcf1bSMarcin Wojtas /* bus interface */ 112451bcf1bSMarcin Wojtas DEVMETHOD(bus_add_child, device_add_child_ordered), 1130f84feb9SKornel Duleba DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 1140f84feb9SKornel Duleba DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 1150f84feb9SKornel Duleba DEVMETHOD(bus_release_resource, bus_generic_release_resource), 1160f84feb9SKornel Duleba DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 1170f84feb9SKornel Duleba DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 1180f84feb9SKornel Duleba DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), 1190f84feb9SKornel Duleba DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 120451bcf1bSMarcin Wojtas 121451bcf1bSMarcin Wojtas /* etherswitch interface */ 122451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_getinfo, felix_getinfo), 123451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_getconf, felix_getconf), 124451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_setconf, felix_setconf), 125451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_lock, felix_lock), 126451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_unlock, felix_unlock), 127451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_getport, felix_getport), 128451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_setport, felix_setport), 129451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_readreg, felix_readreg_wrapper), 130451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_writereg, felix_writereg_wrapper), 131451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_readphyreg, felix_readphy), 132451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_writephyreg, felix_writephy), 133451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_setvgroup, felix_setvgroup), 134451bcf1bSMarcin Wojtas DEVMETHOD(etherswitch_getvgroup, felix_getvgroup), 135451bcf1bSMarcin Wojtas 13629cf6a79SKornel Duleba /* miibus interface */ 13729cf6a79SKornel Duleba DEVMETHOD(miibus_readreg, felix_readphy), 13829cf6a79SKornel Duleba DEVMETHOD(miibus_writereg, felix_writephy), 13929cf6a79SKornel Duleba 140451bcf1bSMarcin Wojtas DEVMETHOD_END 141451bcf1bSMarcin Wojtas }; 142451bcf1bSMarcin Wojtas 143451bcf1bSMarcin Wojtas static devclass_t felix_devclass; 144451bcf1bSMarcin Wojtas DEFINE_CLASS_0(felix, felix_driver, felix_methods, 145451bcf1bSMarcin Wojtas sizeof(struct felix_softc)); 146451bcf1bSMarcin Wojtas 14729cf6a79SKornel Duleba DRIVER_MODULE_ORDERED(felix, pci, felix_driver, felix_devclass, 14829cf6a79SKornel Duleba NULL, NULL, SI_ORDER_ANY); 1493e38757dSJohn Baldwin DRIVER_MODULE(miibus, felix, miibus_fdt_driver, NULL, NULL); 150829a13faSJohn Baldwin DRIVER_MODULE(etherswitch, felix, etherswitch_driver, NULL, NULL); 151451bcf1bSMarcin Wojtas MODULE_VERSION(felix, 1); 152451bcf1bSMarcin Wojtas MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, felix, 153451bcf1bSMarcin Wojtas felix_pci_ids, nitems(felix_pci_ids) - 1); 154451bcf1bSMarcin Wojtas 155451bcf1bSMarcin Wojtas static int 156451bcf1bSMarcin Wojtas felix_probe(device_t dev) 157451bcf1bSMarcin Wojtas { 158451bcf1bSMarcin Wojtas struct felix_pci_id *id; 159451bcf1bSMarcin Wojtas felix_softc_t sc; 160451bcf1bSMarcin Wojtas 161451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 162451bcf1bSMarcin Wojtas sc->dev = dev; 163451bcf1bSMarcin Wojtas 164451bcf1bSMarcin Wojtas for (id = felix_pci_ids; id->vendor != 0; ++id) { 165451bcf1bSMarcin Wojtas if (pci_get_device(dev) != id->device || 166451bcf1bSMarcin Wojtas pci_get_vendor(dev) != id->vendor) 167451bcf1bSMarcin Wojtas continue; 168451bcf1bSMarcin Wojtas 169451bcf1bSMarcin Wojtas device_set_desc(dev, id->desc); 170451bcf1bSMarcin Wojtas 171451bcf1bSMarcin Wojtas return (BUS_PROBE_DEFAULT); 172451bcf1bSMarcin Wojtas } 173451bcf1bSMarcin Wojtas 174451bcf1bSMarcin Wojtas return (ENXIO); 175451bcf1bSMarcin Wojtas } 176451bcf1bSMarcin Wojtas 177451bcf1bSMarcin Wojtas static int 178451bcf1bSMarcin Wojtas felix_parse_port_fdt(felix_softc_t sc, phandle_t child, int *pport) 179451bcf1bSMarcin Wojtas { 180451bcf1bSMarcin Wojtas uint32_t port, status; 181451bcf1bSMarcin Wojtas phandle_t node; 182451bcf1bSMarcin Wojtas 183451bcf1bSMarcin Wojtas if (OF_getencprop(child, "reg", (void *)&port, sizeof(port)) < 0) { 184451bcf1bSMarcin Wojtas device_printf(sc->dev, "Port node doesn't have reg property\n"); 185451bcf1bSMarcin Wojtas return (ENXIO); 186451bcf1bSMarcin Wojtas } 187451bcf1bSMarcin Wojtas 188451bcf1bSMarcin Wojtas *pport = port; 189451bcf1bSMarcin Wojtas 190451bcf1bSMarcin Wojtas node = OF_getproplen(child, "ethernet"); 191451bcf1bSMarcin Wojtas if (node <= 0) 192451bcf1bSMarcin Wojtas sc->ports[port].cpu_port = false; 193451bcf1bSMarcin Wojtas else 194451bcf1bSMarcin Wojtas sc->ports[port].cpu_port = true; 195451bcf1bSMarcin Wojtas 196451bcf1bSMarcin Wojtas node = ofw_bus_find_child(child, "fixed-link"); 197451bcf1bSMarcin Wojtas if (node <= 0) { 198451bcf1bSMarcin Wojtas sc->ports[port].fixed_port = false; 199451bcf1bSMarcin Wojtas return (0); 200451bcf1bSMarcin Wojtas } 201451bcf1bSMarcin Wojtas 202451bcf1bSMarcin Wojtas sc->ports[port].fixed_port = true;; 203451bcf1bSMarcin Wojtas 204451bcf1bSMarcin Wojtas if (OF_getencprop(node, "speed", &status, sizeof(status)) <= 0) { 205451bcf1bSMarcin Wojtas device_printf(sc->dev, 206451bcf1bSMarcin Wojtas "Port has fixed-link node without link speed specified\n"); 207451bcf1bSMarcin Wojtas return (ENXIO); 208451bcf1bSMarcin Wojtas } 209451bcf1bSMarcin Wojtas 210451bcf1bSMarcin Wojtas switch (status) { 211451bcf1bSMarcin Wojtas case 2500: 212451bcf1bSMarcin Wojtas status = IFM_2500_T; 213451bcf1bSMarcin Wojtas break; 214451bcf1bSMarcin Wojtas case 1000: 215451bcf1bSMarcin Wojtas status = IFM_1000_T; 216451bcf1bSMarcin Wojtas break; 217451bcf1bSMarcin Wojtas case 100: 218451bcf1bSMarcin Wojtas status = IFM_100_T; 219451bcf1bSMarcin Wojtas break; 220451bcf1bSMarcin Wojtas case 10: 221451bcf1bSMarcin Wojtas status = IFM_10_T; 222451bcf1bSMarcin Wojtas break; 223451bcf1bSMarcin Wojtas default: 224451bcf1bSMarcin Wojtas device_printf(sc->dev, 225451bcf1bSMarcin Wojtas "Unsupported link speed value of %d\n", 226451bcf1bSMarcin Wojtas status); 227451bcf1bSMarcin Wojtas return (ENXIO); 228451bcf1bSMarcin Wojtas } 229451bcf1bSMarcin Wojtas 230451bcf1bSMarcin Wojtas if (OF_hasprop(node, "full-duplex")) 231451bcf1bSMarcin Wojtas status |= IFM_FDX; 232451bcf1bSMarcin Wojtas else 233451bcf1bSMarcin Wojtas status |= IFM_HDX; 234451bcf1bSMarcin Wojtas 235451bcf1bSMarcin Wojtas status |= IFM_ETHER; 236451bcf1bSMarcin Wojtas sc->ports[port].fixed_link_status = status; 237451bcf1bSMarcin Wojtas return (0); 238451bcf1bSMarcin Wojtas } 239451bcf1bSMarcin Wojtas 240451bcf1bSMarcin Wojtas static int 241451bcf1bSMarcin Wojtas felix_init_interface(felix_softc_t sc, int port) 242451bcf1bSMarcin Wojtas { 243451bcf1bSMarcin Wojtas char name[IFNAMSIZ]; 244451bcf1bSMarcin Wojtas 245451bcf1bSMarcin Wojtas snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); 246451bcf1bSMarcin Wojtas 247451bcf1bSMarcin Wojtas sc->ports[port].ifp = if_alloc(IFT_ETHER); 248451bcf1bSMarcin Wojtas if (sc->ports[port].ifp == NULL) 249451bcf1bSMarcin Wojtas return (ENOMEM); 250451bcf1bSMarcin Wojtas 251451bcf1bSMarcin Wojtas sc->ports[port].ifp->if_softc = sc; 252451bcf1bSMarcin Wojtas sc->ports[port].ifp->if_flags = IFF_UP | IFF_BROADCAST | IFF_MULTICAST | 253451bcf1bSMarcin Wojtas IFF_DRV_RUNNING | IFF_SIMPLEX; 254451bcf1bSMarcin Wojtas sc->ports[port].ifname = malloc(strlen(name) + 1, M_FELIX, M_NOWAIT); 255451bcf1bSMarcin Wojtas if (sc->ports[port].ifname == NULL) { 256451bcf1bSMarcin Wojtas if_free(sc->ports[port].ifp); 257451bcf1bSMarcin Wojtas return (ENOMEM); 258451bcf1bSMarcin Wojtas } 259451bcf1bSMarcin Wojtas 260451bcf1bSMarcin Wojtas memcpy(sc->ports[port].ifname, name, strlen(name) + 1); 261451bcf1bSMarcin Wojtas if_initname(sc->ports[port].ifp, sc->ports[port].ifname, port); 262451bcf1bSMarcin Wojtas return (0); 263451bcf1bSMarcin Wojtas } 264451bcf1bSMarcin Wojtas 265451bcf1bSMarcin Wojtas static void 266451bcf1bSMarcin Wojtas felix_setup_port(felix_softc_t sc, int port) 267451bcf1bSMarcin Wojtas { 268451bcf1bSMarcin Wojtas 269451bcf1bSMarcin Wojtas /* Link speed has to be always set to 1000 in the clock register. */ 270451bcf1bSMarcin Wojtas FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_CLK_CFG, 271451bcf1bSMarcin Wojtas FELIX_DEVGMII_CLK_CFG_SPEED_1000); 272451bcf1bSMarcin Wojtas FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_MAC_CFG, 273451bcf1bSMarcin Wojtas FELIX_DEVGMII_MAC_CFG_TX_ENA | FELIX_DEVGMII_MAC_CFG_RX_ENA); 274451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_QSYS_PORT_MODE(port), 275451bcf1bSMarcin Wojtas FELIX_QSYS_PORT_MODE_PORT_ENA); 276451bcf1bSMarcin Wojtas 277451bcf1bSMarcin Wojtas /* 278451bcf1bSMarcin Wojtas * Enable "VLANMTU". Each port has a configurable MTU. 279451bcf1bSMarcin Wojtas * Accept frames that are 8 and 4 bytes longer than it 280451bcf1bSMarcin Wojtas * for double and single tagged frames respectively. 281451bcf1bSMarcin Wojtas * Since etherswitch API doesn't provide an option to change 282451bcf1bSMarcin Wojtas * MTU don't touch it for now. 283451bcf1bSMarcin Wojtas */ 284451bcf1bSMarcin Wojtas FELIX_DEVGMII_PORT_WR4(sc, port, FELIX_DEVGMII_VLAN_CFG, 285451bcf1bSMarcin Wojtas FELIX_DEVGMII_VLAN_CFG_ENA | 286451bcf1bSMarcin Wojtas FELIX_DEVGMII_VLAN_CFG_LEN_ENA | 287451bcf1bSMarcin Wojtas FELIX_DEVGMII_VLAN_CFG_DOUBLE_ENA); 288451bcf1bSMarcin Wojtas } 289451bcf1bSMarcin Wojtas 290451bcf1bSMarcin Wojtas static int 291451bcf1bSMarcin Wojtas felix_setup(felix_softc_t sc) 292451bcf1bSMarcin Wojtas { 293451bcf1bSMarcin Wojtas int timeout, i; 294451bcf1bSMarcin Wojtas uint32_t reg; 295451bcf1bSMarcin Wojtas 296451bcf1bSMarcin Wojtas /* Trigger soft reset, bit is self-clearing, with 5s timeout. */ 297451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_DEVCPU_GCB_RST, FELIX_DEVCPU_GCB_RST_EN); 298451bcf1bSMarcin Wojtas timeout = FELIX_INIT_TIMEOUT; 299451bcf1bSMarcin Wojtas do { 300451bcf1bSMarcin Wojtas DELAY(1000); 301451bcf1bSMarcin Wojtas reg = FELIX_RD4(sc, FELIX_DEVCPU_GCB_RST); 302451bcf1bSMarcin Wojtas if ((reg & FELIX_DEVCPU_GCB_RST_EN) == 0) 303451bcf1bSMarcin Wojtas break; 304451bcf1bSMarcin Wojtas } while (timeout-- > 0); 305451bcf1bSMarcin Wojtas if (timeout == 0) { 306451bcf1bSMarcin Wojtas device_printf(sc->dev, 307451bcf1bSMarcin Wojtas "Timeout while waiting for switch to reset\n"); 308451bcf1bSMarcin Wojtas return (ETIMEDOUT); 309451bcf1bSMarcin Wojtas } 310451bcf1bSMarcin Wojtas 311451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_SYS_RAM_CTRL, FELIX_SYS_RAM_CTRL_INIT); 312451bcf1bSMarcin Wojtas timeout = FELIX_INIT_TIMEOUT; 313451bcf1bSMarcin Wojtas do { 314451bcf1bSMarcin Wojtas DELAY(1000); 315451bcf1bSMarcin Wojtas reg = FELIX_RD4(sc, FELIX_SYS_RAM_CTRL); 316451bcf1bSMarcin Wojtas if ((reg & FELIX_SYS_RAM_CTRL_INIT) == 0) 317451bcf1bSMarcin Wojtas break; 318451bcf1bSMarcin Wojtas } while (timeout-- > 0); 319451bcf1bSMarcin Wojtas if (timeout == 0) { 320451bcf1bSMarcin Wojtas device_printf(sc->dev, 321451bcf1bSMarcin Wojtas "Timeout while waiting for switch RAM init.\n"); 322451bcf1bSMarcin Wojtas return (ETIMEDOUT); 323451bcf1bSMarcin Wojtas } 324451bcf1bSMarcin Wojtas 325451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_SYS_CFG, FELIX_SYS_CFG_CORE_EN); 326451bcf1bSMarcin Wojtas 327451bcf1bSMarcin Wojtas for (i = 0; i < sc->info.es_nports; i++) 328451bcf1bSMarcin Wojtas felix_setup_port(sc, i); 329451bcf1bSMarcin Wojtas 330451bcf1bSMarcin Wojtas return (0); 331451bcf1bSMarcin Wojtas } 332451bcf1bSMarcin Wojtas 333451bcf1bSMarcin Wojtas static int 334d88aecceSKornel Duleba felix_timer_rate(SYSCTL_HANDLER_ARGS) 335d88aecceSKornel Duleba { 336d88aecceSKornel Duleba felix_softc_t sc; 337d88aecceSKornel Duleba int error, value, old; 338d88aecceSKornel Duleba 339d88aecceSKornel Duleba sc = arg1; 340d88aecceSKornel Duleba 341d88aecceSKornel Duleba old = value = sc->timer_ticks; 342d88aecceSKornel Duleba error = sysctl_handle_int(oidp, &value, 0, req); 343d88aecceSKornel Duleba if (error != 0 || req->newptr == NULL) 344d88aecceSKornel Duleba return (error); 345d88aecceSKornel Duleba 346d88aecceSKornel Duleba if (value < 0) 347d88aecceSKornel Duleba return (EINVAL); 348d88aecceSKornel Duleba 349d88aecceSKornel Duleba if (value == old) 350d88aecceSKornel Duleba return (0); 351d88aecceSKornel Duleba 352d88aecceSKornel Duleba FELIX_LOCK(sc); 353d88aecceSKornel Duleba sc->timer_ticks = value; 354d88aecceSKornel Duleba callout_reset(&sc->tick_callout, sc->timer_ticks, felix_tick, sc); 355d88aecceSKornel Duleba FELIX_UNLOCK(sc); 356d88aecceSKornel Duleba 357d88aecceSKornel Duleba return (0); 358d88aecceSKornel Duleba } 359d88aecceSKornel Duleba 360d88aecceSKornel Duleba static int 361451bcf1bSMarcin Wojtas felix_attach(device_t dev) 362451bcf1bSMarcin Wojtas { 363451bcf1bSMarcin Wojtas phandle_t child, ports, node; 364451bcf1bSMarcin Wojtas int error, port, rid; 365451bcf1bSMarcin Wojtas felix_softc_t sc; 366451bcf1bSMarcin Wojtas uint32_t phy_addr; 367451bcf1bSMarcin Wojtas ssize_t size; 368451bcf1bSMarcin Wojtas 369451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 370451bcf1bSMarcin Wojtas sc->info.es_nports = 0; 371451bcf1bSMarcin Wojtas sc->info.es_vlan_caps = ETHERSWITCH_VLAN_DOT1Q; 372451bcf1bSMarcin Wojtas strlcpy(sc->info.es_name, "Felix TSN Switch", sizeof(sc->info.es_name)); 373451bcf1bSMarcin Wojtas 37429cf6a79SKornel Duleba rid = PCIR_BAR(FELIX_BAR_MDIO); 37529cf6a79SKornel Duleba sc->mdio = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 37629cf6a79SKornel Duleba RF_ACTIVE); 37729cf6a79SKornel Duleba if (sc->mdio == NULL) { 37829cf6a79SKornel Duleba device_printf(dev, "Failed to allocate MDIO registers.\n"); 37929cf6a79SKornel Duleba return (ENXIO); 38029cf6a79SKornel Duleba } 38129cf6a79SKornel Duleba 382451bcf1bSMarcin Wojtas rid = PCIR_BAR(FELIX_BAR_REGS); 383451bcf1bSMarcin Wojtas sc->regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 384451bcf1bSMarcin Wojtas RF_ACTIVE); 385451bcf1bSMarcin Wojtas if (sc->regs == NULL) { 386451bcf1bSMarcin Wojtas device_printf(dev, "Failed to allocate registers BAR.\n"); 38729cf6a79SKornel Duleba error = ENXIO; 38829cf6a79SKornel Duleba goto out_fail; 389451bcf1bSMarcin Wojtas } 390451bcf1bSMarcin Wojtas 391451bcf1bSMarcin Wojtas mtx_init(&sc->mtx, "felix lock", NULL, MTX_DEF); 392451bcf1bSMarcin Wojtas callout_init_mtx(&sc->tick_callout, &sc->mtx, 0); 393451bcf1bSMarcin Wojtas 394451bcf1bSMarcin Wojtas node = ofw_bus_get_node(dev); 395451bcf1bSMarcin Wojtas if (node <= 0) { 396451bcf1bSMarcin Wojtas error = ENXIO; 397451bcf1bSMarcin Wojtas goto out_fail; 398451bcf1bSMarcin Wojtas } 399451bcf1bSMarcin Wojtas 400451bcf1bSMarcin Wojtas ports = ofw_bus_find_child(node, "ports"); 401451bcf1bSMarcin Wojtas if (ports == 0) { 402451bcf1bSMarcin Wojtas device_printf(dev, 403451bcf1bSMarcin Wojtas "Failed to find \"ports\" property in DTS.\n"); 404451bcf1bSMarcin Wojtas error = ENXIO; 405451bcf1bSMarcin Wojtas goto out_fail; 406451bcf1bSMarcin Wojtas } 407451bcf1bSMarcin Wojtas 408451bcf1bSMarcin Wojtas for (child = OF_child(ports); child != 0; child = OF_peer(child)) { 409451bcf1bSMarcin Wojtas /* Do not parse disabled ports. */ 410451bcf1bSMarcin Wojtas if (ofw_bus_node_status_okay(child) == 0) 411451bcf1bSMarcin Wojtas continue; 412451bcf1bSMarcin Wojtas 413451bcf1bSMarcin Wojtas error = felix_parse_port_fdt(sc, child, &port); 414451bcf1bSMarcin Wojtas if (error != 0) 415451bcf1bSMarcin Wojtas goto out_fail; 416451bcf1bSMarcin Wojtas 417451bcf1bSMarcin Wojtas error = felix_init_interface(sc, port); 418451bcf1bSMarcin Wojtas if (error != 0) { 419451bcf1bSMarcin Wojtas device_printf(sc->dev, 420451bcf1bSMarcin Wojtas "Failed to initialize interface.\n"); 421451bcf1bSMarcin Wojtas goto out_fail; 422451bcf1bSMarcin Wojtas } 423451bcf1bSMarcin Wojtas 424451bcf1bSMarcin Wojtas if (sc->ports[port].fixed_port) { 425451bcf1bSMarcin Wojtas sc->info.es_nports++; 426451bcf1bSMarcin Wojtas continue; 427451bcf1bSMarcin Wojtas } 428451bcf1bSMarcin Wojtas 429451bcf1bSMarcin Wojtas size = OF_getencprop(child, "phy-handle", &node, sizeof(node)); 430451bcf1bSMarcin Wojtas if (size <= 0) { 431451bcf1bSMarcin Wojtas device_printf(sc->dev, 432451bcf1bSMarcin Wojtas "Failed to acquire PHY handle from FDT.\n"); 433451bcf1bSMarcin Wojtas error = ENXIO; 434451bcf1bSMarcin Wojtas goto out_fail; 435451bcf1bSMarcin Wojtas } 436451bcf1bSMarcin Wojtas 437451bcf1bSMarcin Wojtas node = OF_node_from_xref(node); 438451bcf1bSMarcin Wojtas size = OF_getencprop(node, "reg", &phy_addr, sizeof(phy_addr)); 439451bcf1bSMarcin Wojtas if (size <= 0) { 440451bcf1bSMarcin Wojtas device_printf(sc->dev, 441451bcf1bSMarcin Wojtas "Failed to obtain PHY address.\n"); 442451bcf1bSMarcin Wojtas error = ENXIO; 443451bcf1bSMarcin Wojtas goto out_fail; 444451bcf1bSMarcin Wojtas } 445451bcf1bSMarcin Wojtas 446451bcf1bSMarcin Wojtas sc->ports[port].phyaddr = phy_addr; 447451bcf1bSMarcin Wojtas sc->ports[port].miibus = NULL; 44829cf6a79SKornel Duleba error = mii_attach(dev, &sc->ports[port].miibus, sc->ports[port].ifp, 449451bcf1bSMarcin Wojtas felix_ifmedia_upd, felix_ifmedia_sts, BMSR_DEFCAPMASK, 450451bcf1bSMarcin Wojtas phy_addr, MII_OFFSET_ANY, 0); 451451bcf1bSMarcin Wojtas if (error != 0) 452451bcf1bSMarcin Wojtas goto out_fail; 453451bcf1bSMarcin Wojtas 454451bcf1bSMarcin Wojtas sc->info.es_nports++; 455451bcf1bSMarcin Wojtas } 456451bcf1bSMarcin Wojtas 457451bcf1bSMarcin Wojtas error = felix_setup(sc); 458451bcf1bSMarcin Wojtas if (error != 0) 459451bcf1bSMarcin Wojtas goto out_fail; 460451bcf1bSMarcin Wojtas 461d88aecceSKornel Duleba sc->timer_ticks = hz; /* Default to 1s. */ 462d88aecceSKornel Duleba SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 463d88aecceSKornel Duleba SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 464d88aecceSKornel Duleba OID_AUTO, "timer_ticks", CTLTYPE_INT | CTLFLAG_RW, 465d88aecceSKornel Duleba sc, 0, felix_timer_rate, "I", 466d88aecceSKornel Duleba "Number of ticks between timer invocations"); 467d88aecceSKornel Duleba 468451bcf1bSMarcin Wojtas /* The tick routine has to be called with the lock held. */ 469451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 470451bcf1bSMarcin Wojtas felix_tick(sc); 471451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 472451bcf1bSMarcin Wojtas 473451bcf1bSMarcin Wojtas /* Allow etherswitch to attach as our child. */ 474451bcf1bSMarcin Wojtas bus_generic_probe(dev); 475451bcf1bSMarcin Wojtas bus_generic_attach(dev); 476451bcf1bSMarcin Wojtas 477451bcf1bSMarcin Wojtas return (0); 478451bcf1bSMarcin Wojtas 479451bcf1bSMarcin Wojtas out_fail: 480451bcf1bSMarcin Wojtas felix_detach(dev); 481451bcf1bSMarcin Wojtas return (error); 482451bcf1bSMarcin Wojtas } 483451bcf1bSMarcin Wojtas 484451bcf1bSMarcin Wojtas static int 485451bcf1bSMarcin Wojtas felix_detach(device_t dev) 486451bcf1bSMarcin Wojtas { 487451bcf1bSMarcin Wojtas felix_softc_t sc; 488451bcf1bSMarcin Wojtas int error; 489451bcf1bSMarcin Wojtas int i; 490451bcf1bSMarcin Wojtas 491451bcf1bSMarcin Wojtas error = 0; 492451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 493451bcf1bSMarcin Wojtas bus_generic_detach(dev); 494451bcf1bSMarcin Wojtas 495451bcf1bSMarcin Wojtas mtx_lock(&sc->mtx); 496451bcf1bSMarcin Wojtas callout_stop(&sc->tick_callout); 497451bcf1bSMarcin Wojtas mtx_unlock(&sc->mtx); 498451bcf1bSMarcin Wojtas mtx_destroy(&sc->mtx); 499451bcf1bSMarcin Wojtas 500451bcf1bSMarcin Wojtas /* 501451bcf1bSMarcin Wojtas * If we have been fully attached do a soft reset. 502451bcf1bSMarcin Wojtas * This way after when driver is unloaded switch is left in unmanaged mode. 503451bcf1bSMarcin Wojtas */ 504451bcf1bSMarcin Wojtas if (device_is_attached(dev)) 505451bcf1bSMarcin Wojtas felix_setup(sc); 506451bcf1bSMarcin Wojtas 507451bcf1bSMarcin Wojtas for (i = 0; i < sc->info.es_nports; i++) { 50829cf6a79SKornel Duleba if (sc->ports[i].miibus != NULL) 50929cf6a79SKornel Duleba device_delete_child(dev, sc->ports[i].miibus); 510451bcf1bSMarcin Wojtas if (sc->ports[i].ifp != NULL) 511451bcf1bSMarcin Wojtas if_free(sc->ports[i].ifp); 512451bcf1bSMarcin Wojtas if (sc->ports[i].ifname != NULL) 513451bcf1bSMarcin Wojtas free(sc->ports[i].ifname, M_FELIX); 514451bcf1bSMarcin Wojtas } 515451bcf1bSMarcin Wojtas 516451bcf1bSMarcin Wojtas if (sc->regs != NULL) 517451bcf1bSMarcin Wojtas error = bus_release_resource(sc->dev, SYS_RES_MEMORY, 518451bcf1bSMarcin Wojtas rman_get_rid(sc->regs), sc->regs); 519451bcf1bSMarcin Wojtas 52029cf6a79SKornel Duleba if (sc->mdio != NULL) 52129cf6a79SKornel Duleba error = bus_release_resource(sc->dev, SYS_RES_MEMORY, 52229cf6a79SKornel Duleba rman_get_rid(sc->mdio), sc->mdio); 52329cf6a79SKornel Duleba 524451bcf1bSMarcin Wojtas return (error); 525451bcf1bSMarcin Wojtas } 526451bcf1bSMarcin Wojtas 527451bcf1bSMarcin Wojtas static etherswitch_info_t* 528451bcf1bSMarcin Wojtas felix_getinfo(device_t dev) 529451bcf1bSMarcin Wojtas { 530451bcf1bSMarcin Wojtas felix_softc_t sc; 531451bcf1bSMarcin Wojtas 532451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 533451bcf1bSMarcin Wojtas return (&sc->info); 534451bcf1bSMarcin Wojtas } 535451bcf1bSMarcin Wojtas 536451bcf1bSMarcin Wojtas static int 537451bcf1bSMarcin Wojtas felix_getconf(device_t dev, etherswitch_conf_t *conf) 538451bcf1bSMarcin Wojtas { 539451bcf1bSMarcin Wojtas felix_softc_t sc; 540451bcf1bSMarcin Wojtas 541451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 542451bcf1bSMarcin Wojtas 543451bcf1bSMarcin Wojtas /* Return the VLAN mode. */ 544451bcf1bSMarcin Wojtas conf->cmd = ETHERSWITCH_CONF_VLAN_MODE; 545451bcf1bSMarcin Wojtas conf->vlan_mode = sc->vlan_mode; 546451bcf1bSMarcin Wojtas return (0); 547451bcf1bSMarcin Wojtas } 548451bcf1bSMarcin Wojtas 549451bcf1bSMarcin Wojtas static int 550451bcf1bSMarcin Wojtas felix_init_vlan(felix_softc_t sc) 551451bcf1bSMarcin Wojtas { 552451bcf1bSMarcin Wojtas int timeout = FELIX_INIT_TIMEOUT; 553451bcf1bSMarcin Wojtas uint32_t reg; 554451bcf1bSMarcin Wojtas int i; 555451bcf1bSMarcin Wojtas 556451bcf1bSMarcin Wojtas /* Flush VLAN table in hardware. */ 557451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_RESET); 558451bcf1bSMarcin Wojtas do { 559451bcf1bSMarcin Wojtas DELAY(1000); 560451bcf1bSMarcin Wojtas reg = FELIX_RD4(sc, FELIX_ANA_VT); 561451bcf1bSMarcin Wojtas if ((reg & FELIX_ANA_VT_STS) == FELIX_ANA_VT_IDLE) 562451bcf1bSMarcin Wojtas break; 563451bcf1bSMarcin Wojtas } while (timeout-- > 0); 564451bcf1bSMarcin Wojtas if (timeout == 0) { 565451bcf1bSMarcin Wojtas device_printf(sc->dev, 566451bcf1bSMarcin Wojtas "Timeout during VLAN table reset.\n"); 567451bcf1bSMarcin Wojtas return (ETIMEDOUT); 568451bcf1bSMarcin Wojtas } 569451bcf1bSMarcin Wojtas 570451bcf1bSMarcin Wojtas /* Flush VLAN table in sc. */ 571451bcf1bSMarcin Wojtas for (i = 0; i < sc->info.es_nvlangroups; i++) 572451bcf1bSMarcin Wojtas sc->vlans[i] = 0; 573451bcf1bSMarcin Wojtas 574451bcf1bSMarcin Wojtas /* 575451bcf1bSMarcin Wojtas * Make all ports VLAN aware. 576451bcf1bSMarcin Wojtas * Read VID from incoming frames and use it for port grouping 577451bcf1bSMarcin Wojtas * purposes. 578451bcf1bSMarcin Wojtas * Don't set this if pvid is set. 579451bcf1bSMarcin Wojtas */ 580451bcf1bSMarcin Wojtas for (i = 0; i < sc->info.es_nports; i++) { 581451bcf1bSMarcin Wojtas reg = FELIX_ANA_PORT_RD4(sc, i, FELIX_ANA_PORT_VLAN_CFG); 582451bcf1bSMarcin Wojtas if ((reg & FELIX_ANA_PORT_VLAN_CFG_VID_MASK) != 0) 583451bcf1bSMarcin Wojtas continue; 584451bcf1bSMarcin Wojtas 585451bcf1bSMarcin Wojtas reg |= FELIX_ANA_PORT_VLAN_CFG_VID_AWARE; 586451bcf1bSMarcin Wojtas FELIX_ANA_PORT_WR4(sc, i, FELIX_ANA_PORT_VLAN_CFG, reg); 587451bcf1bSMarcin Wojtas } 588451bcf1bSMarcin Wojtas return (0); 589451bcf1bSMarcin Wojtas } 590451bcf1bSMarcin Wojtas 591451bcf1bSMarcin Wojtas static int 592451bcf1bSMarcin Wojtas felix_setconf(device_t dev, etherswitch_conf_t *conf) 593451bcf1bSMarcin Wojtas { 594451bcf1bSMarcin Wojtas felix_softc_t sc; 595451bcf1bSMarcin Wojtas int error; 596451bcf1bSMarcin Wojtas 597451bcf1bSMarcin Wojtas error = 0; 598451bcf1bSMarcin Wojtas /* Set the VLAN mode. */ 599451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 600451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 601451bcf1bSMarcin Wojtas if (conf->cmd & ETHERSWITCH_CONF_VLAN_MODE) { 602451bcf1bSMarcin Wojtas switch (conf->vlan_mode) { 603451bcf1bSMarcin Wojtas case ETHERSWITCH_VLAN_DOT1Q: 604451bcf1bSMarcin Wojtas sc->vlan_mode = ETHERSWITCH_VLAN_DOT1Q; 605451bcf1bSMarcin Wojtas sc->info.es_nvlangroups = FELIX_NUM_VLANS; 606451bcf1bSMarcin Wojtas error = felix_init_vlan(sc); 607451bcf1bSMarcin Wojtas break; 608451bcf1bSMarcin Wojtas default: 609451bcf1bSMarcin Wojtas error = EINVAL; 610451bcf1bSMarcin Wojtas } 611451bcf1bSMarcin Wojtas } 612451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 613451bcf1bSMarcin Wojtas return (error); 614451bcf1bSMarcin Wojtas } 615451bcf1bSMarcin Wojtas 616451bcf1bSMarcin Wojtas static void 617451bcf1bSMarcin Wojtas felix_lock(device_t dev) 618451bcf1bSMarcin Wojtas { 619451bcf1bSMarcin Wojtas felix_softc_t sc; 620451bcf1bSMarcin Wojtas 621451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 622451bcf1bSMarcin Wojtas FELIX_LOCK_ASSERT(sc, MA_NOTOWNED); 623451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 624451bcf1bSMarcin Wojtas } 625451bcf1bSMarcin Wojtas 626451bcf1bSMarcin Wojtas static void 627451bcf1bSMarcin Wojtas felix_unlock(device_t dev) 628451bcf1bSMarcin Wojtas { 629451bcf1bSMarcin Wojtas felix_softc_t sc; 630451bcf1bSMarcin Wojtas 631451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 632451bcf1bSMarcin Wojtas FELIX_LOCK_ASSERT(sc, MA_OWNED); 633451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 634451bcf1bSMarcin Wojtas } 635451bcf1bSMarcin Wojtas 636451bcf1bSMarcin Wojtas static void 637451bcf1bSMarcin Wojtas felix_get_port_cfg(felix_softc_t sc, etherswitch_port_t *p) 638451bcf1bSMarcin Wojtas { 639451bcf1bSMarcin Wojtas uint32_t reg; 640451bcf1bSMarcin Wojtas 641451bcf1bSMarcin Wojtas p->es_flags = 0; 642451bcf1bSMarcin Wojtas 643451bcf1bSMarcin Wojtas reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG); 644451bcf1bSMarcin Wojtas if (reg & FELIX_ANA_PORT_DROP_CFG_TAGGED) 645451bcf1bSMarcin Wojtas p->es_flags |= ETHERSWITCH_PORT_DROPTAGGED; 646451bcf1bSMarcin Wojtas 647451bcf1bSMarcin Wojtas if (reg & FELIX_ANA_PORT_DROP_CFG_UNTAGGED) 648451bcf1bSMarcin Wojtas p->es_flags |= ETHERSWITCH_PORT_DROPUNTAGGED; 649451bcf1bSMarcin Wojtas 650451bcf1bSMarcin Wojtas reg = FELIX_DEVGMII_PORT_RD4(sc, p->es_port, FELIX_DEVGMII_VLAN_CFG); 651451bcf1bSMarcin Wojtas if (reg & FELIX_DEVGMII_VLAN_CFG_DOUBLE_ENA) 652451bcf1bSMarcin Wojtas p->es_flags |= ETHERSWITCH_PORT_DOUBLE_TAG; 653451bcf1bSMarcin Wojtas 654451bcf1bSMarcin Wojtas reg = FELIX_REW_PORT_RD4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG); 655451bcf1bSMarcin Wojtas if (reg & FELIX_REW_PORT_TAG_CFG_ALL) 656451bcf1bSMarcin Wojtas p->es_flags |= ETHERSWITCH_PORT_ADDTAG; 657451bcf1bSMarcin Wojtas 658451bcf1bSMarcin Wojtas reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG); 659451bcf1bSMarcin Wojtas if (reg & FELIX_ANA_PORT_VLAN_CFG_POP) 660451bcf1bSMarcin Wojtas p->es_flags |= ETHERSWITCH_PORT_STRIPTAGINGRESS; 661451bcf1bSMarcin Wojtas 662451bcf1bSMarcin Wojtas p->es_pvid = reg & FELIX_ANA_PORT_VLAN_CFG_VID_MASK; 663451bcf1bSMarcin Wojtas } 664451bcf1bSMarcin Wojtas 665451bcf1bSMarcin Wojtas static int 666451bcf1bSMarcin Wojtas felix_getport(device_t dev, etherswitch_port_t *p) 667451bcf1bSMarcin Wojtas { 668451bcf1bSMarcin Wojtas struct ifmediareq *ifmr; 669451bcf1bSMarcin Wojtas struct mii_data *mii; 670451bcf1bSMarcin Wojtas felix_softc_t sc; 671451bcf1bSMarcin Wojtas int error; 672451bcf1bSMarcin Wojtas 673451bcf1bSMarcin Wojtas error = 0; 674451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 675451bcf1bSMarcin Wojtas FELIX_LOCK_ASSERT(sc, MA_NOTOWNED); 676451bcf1bSMarcin Wojtas 677451bcf1bSMarcin Wojtas if (p->es_port >= sc->info.es_nports || p->es_port < 0) 678451bcf1bSMarcin Wojtas return (EINVAL); 679451bcf1bSMarcin Wojtas 680451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 681451bcf1bSMarcin Wojtas felix_get_port_cfg(sc, p); 682451bcf1bSMarcin Wojtas if (sc->ports[p->es_port].fixed_port) { 683451bcf1bSMarcin Wojtas ifmr = &p->es_ifmr; 684451bcf1bSMarcin Wojtas ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; 685451bcf1bSMarcin Wojtas ifmr->ifm_count = 0; 686451bcf1bSMarcin Wojtas ifmr->ifm_active = sc->ports[p->es_port].fixed_link_status; 687451bcf1bSMarcin Wojtas ifmr->ifm_current = ifmr->ifm_active; 688451bcf1bSMarcin Wojtas ifmr->ifm_mask = 0; 689451bcf1bSMarcin Wojtas } else { 690451bcf1bSMarcin Wojtas mii = felix_miiforport(sc, p->es_port); 691451bcf1bSMarcin Wojtas error = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, 692451bcf1bSMarcin Wojtas &mii->mii_media, SIOCGIFMEDIA); 693451bcf1bSMarcin Wojtas } 694451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 695451bcf1bSMarcin Wojtas return (error); 696451bcf1bSMarcin Wojtas } 697451bcf1bSMarcin Wojtas 698451bcf1bSMarcin Wojtas static void 699451bcf1bSMarcin Wojtas felix_set_port_cfg(felix_softc_t sc, etherswitch_port_t *p) 700451bcf1bSMarcin Wojtas { 701451bcf1bSMarcin Wojtas uint32_t reg; 702451bcf1bSMarcin Wojtas 703451bcf1bSMarcin Wojtas reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG); 704451bcf1bSMarcin Wojtas if (p->es_flags & ETHERSWITCH_PORT_DROPTAGGED) 705451bcf1bSMarcin Wojtas reg |= FELIX_ANA_PORT_DROP_CFG_TAGGED; 706451bcf1bSMarcin Wojtas else 707451bcf1bSMarcin Wojtas reg &= ~FELIX_ANA_PORT_DROP_CFG_TAGGED; 708451bcf1bSMarcin Wojtas 709451bcf1bSMarcin Wojtas if (p->es_flags & ETHERSWITCH_PORT_DROPUNTAGGED) 710451bcf1bSMarcin Wojtas reg |= FELIX_ANA_PORT_DROP_CFG_UNTAGGED; 711451bcf1bSMarcin Wojtas else 712451bcf1bSMarcin Wojtas reg &= ~FELIX_ANA_PORT_DROP_CFG_UNTAGGED; 713451bcf1bSMarcin Wojtas 714451bcf1bSMarcin Wojtas FELIX_ANA_PORT_WR4(sc, p->es_port, FELIX_ANA_PORT_DROP_CFG, reg); 715451bcf1bSMarcin Wojtas 716451bcf1bSMarcin Wojtas reg = FELIX_REW_PORT_RD4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG); 717451bcf1bSMarcin Wojtas if (p->es_flags & ETHERSWITCH_PORT_ADDTAG) 718451bcf1bSMarcin Wojtas reg |= FELIX_REW_PORT_TAG_CFG_ALL; 719451bcf1bSMarcin Wojtas else 720451bcf1bSMarcin Wojtas reg &= ~FELIX_REW_PORT_TAG_CFG_ALL; 721451bcf1bSMarcin Wojtas 722451bcf1bSMarcin Wojtas FELIX_REW_PORT_WR4(sc, p->es_port, FELIX_REW_PORT_TAG_CFG, reg); 723451bcf1bSMarcin Wojtas 724451bcf1bSMarcin Wojtas reg = FELIX_ANA_PORT_RD4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG); 725451bcf1bSMarcin Wojtas if (p->es_flags & ETHERSWITCH_PORT_STRIPTAGINGRESS) 726451bcf1bSMarcin Wojtas reg |= FELIX_ANA_PORT_VLAN_CFG_POP; 727451bcf1bSMarcin Wojtas else 728451bcf1bSMarcin Wojtas reg &= ~FELIX_ANA_PORT_VLAN_CFG_POP; 729451bcf1bSMarcin Wojtas 730451bcf1bSMarcin Wojtas reg &= ~FELIX_ANA_PORT_VLAN_CFG_VID_MASK; 731451bcf1bSMarcin Wojtas reg |= p->es_pvid & FELIX_ANA_PORT_VLAN_CFG_VID_MASK; 732451bcf1bSMarcin Wojtas /* 733451bcf1bSMarcin Wojtas * If port VID is set use it for VLAN classification, 734451bcf1bSMarcin Wojtas * instead of frame VID. 735451bcf1bSMarcin Wojtas * By default the frame tag takes precedence. 736451bcf1bSMarcin Wojtas * Force the switch to ignore it. 737451bcf1bSMarcin Wojtas */ 738451bcf1bSMarcin Wojtas if (p->es_pvid != 0) 739451bcf1bSMarcin Wojtas reg &= ~FELIX_ANA_PORT_VLAN_CFG_VID_AWARE; 740451bcf1bSMarcin Wojtas else 741451bcf1bSMarcin Wojtas reg |= FELIX_ANA_PORT_VLAN_CFG_VID_AWARE; 742451bcf1bSMarcin Wojtas 743451bcf1bSMarcin Wojtas FELIX_ANA_PORT_WR4(sc, p->es_port, FELIX_ANA_PORT_VLAN_CFG, reg); 744451bcf1bSMarcin Wojtas } 745451bcf1bSMarcin Wojtas 746451bcf1bSMarcin Wojtas static int 747451bcf1bSMarcin Wojtas felix_setport(device_t dev, etherswitch_port_t *p) 748451bcf1bSMarcin Wojtas { 749451bcf1bSMarcin Wojtas felix_softc_t sc; 750451bcf1bSMarcin Wojtas struct mii_data *mii; 751451bcf1bSMarcin Wojtas int error; 752451bcf1bSMarcin Wojtas 753451bcf1bSMarcin Wojtas error = 0; 754451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 755451bcf1bSMarcin Wojtas FELIX_LOCK_ASSERT(sc, MA_NOTOWNED); 756451bcf1bSMarcin Wojtas 757451bcf1bSMarcin Wojtas if (p->es_port >= sc->info.es_nports || p->es_port < 0) 758451bcf1bSMarcin Wojtas return (EINVAL); 759451bcf1bSMarcin Wojtas 760451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 761451bcf1bSMarcin Wojtas felix_set_port_cfg(sc, p); 762451bcf1bSMarcin Wojtas if (felix_is_phyport(sc, p->es_port)) { 763451bcf1bSMarcin Wojtas mii = felix_miiforport(sc, p->es_port); 764451bcf1bSMarcin Wojtas error = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, &mii->mii_media, 765451bcf1bSMarcin Wojtas SIOCSIFMEDIA); 766451bcf1bSMarcin Wojtas } 767451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 768451bcf1bSMarcin Wojtas 769451bcf1bSMarcin Wojtas return (error); 770451bcf1bSMarcin Wojtas } 771451bcf1bSMarcin Wojtas 772451bcf1bSMarcin Wojtas static int 773451bcf1bSMarcin Wojtas felix_readreg_wrapper(device_t dev, int addr_reg) 774451bcf1bSMarcin Wojtas { 775451bcf1bSMarcin Wojtas felix_softc_t sc; 776451bcf1bSMarcin Wojtas 777451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 778451bcf1bSMarcin Wojtas if (addr_reg > rman_get_size(sc->regs)) 779451bcf1bSMarcin Wojtas return (UINT32_MAX); /* Can't return errors here. */ 780451bcf1bSMarcin Wojtas 781451bcf1bSMarcin Wojtas return (FELIX_RD4(sc, addr_reg)); 782451bcf1bSMarcin Wojtas } 783451bcf1bSMarcin Wojtas 784451bcf1bSMarcin Wojtas static int 785451bcf1bSMarcin Wojtas felix_writereg_wrapper(device_t dev, int addr_reg, int val) 786451bcf1bSMarcin Wojtas { 787451bcf1bSMarcin Wojtas felix_softc_t sc; 788451bcf1bSMarcin Wojtas 789451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 790451bcf1bSMarcin Wojtas if (addr_reg > rman_get_size(sc->regs)) 791451bcf1bSMarcin Wojtas return (EINVAL); 792451bcf1bSMarcin Wojtas 793451bcf1bSMarcin Wojtas FELIX_WR4(sc, addr_reg, val); 794451bcf1bSMarcin Wojtas return (0); 795451bcf1bSMarcin Wojtas } 796451bcf1bSMarcin Wojtas 797451bcf1bSMarcin Wojtas static int 798451bcf1bSMarcin Wojtas felix_readphy(device_t dev, int phy, int reg) 799451bcf1bSMarcin Wojtas { 800451bcf1bSMarcin Wojtas felix_softc_t sc; 801451bcf1bSMarcin Wojtas 802451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 803451bcf1bSMarcin Wojtas 80429cf6a79SKornel Duleba return (enetc_mdio_read(sc->mdio, FELIX_MDIO_BASE, phy, reg)); 805451bcf1bSMarcin Wojtas } 806451bcf1bSMarcin Wojtas 807451bcf1bSMarcin Wojtas static int 808451bcf1bSMarcin Wojtas felix_writephy(device_t dev, int phy, int reg, int data) 809451bcf1bSMarcin Wojtas { 810451bcf1bSMarcin Wojtas felix_softc_t sc; 811451bcf1bSMarcin Wojtas 812451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 813451bcf1bSMarcin Wojtas 81429cf6a79SKornel Duleba return (enetc_mdio_write(sc->mdio, FELIX_MDIO_BASE, phy, reg, data)); 815451bcf1bSMarcin Wojtas } 816451bcf1bSMarcin Wojtas 817451bcf1bSMarcin Wojtas static int 818451bcf1bSMarcin Wojtas felix_set_dot1q_vlan(felix_softc_t sc, etherswitch_vlangroup_t *vg) 819451bcf1bSMarcin Wojtas { 820451bcf1bSMarcin Wojtas uint32_t reg; 821451bcf1bSMarcin Wojtas int i, vid; 822451bcf1bSMarcin Wojtas 823451bcf1bSMarcin Wojtas vid = vg->es_vid & ETHERSWITCH_VID_MASK; 824451bcf1bSMarcin Wojtas 825451bcf1bSMarcin Wojtas /* Tagged mode is not supported. */ 826451bcf1bSMarcin Wojtas if (vg->es_member_ports != vg->es_untagged_ports) 827451bcf1bSMarcin Wojtas return (EINVAL); 828451bcf1bSMarcin Wojtas 829451bcf1bSMarcin Wojtas /* 830451bcf1bSMarcin Wojtas * Hardware support 4096 groups, but we can't do group_id == vid. 831451bcf1bSMarcin Wojtas * Note that hw_group_id == vid. 832451bcf1bSMarcin Wojtas */ 833451bcf1bSMarcin Wojtas if (vid == 0) { 834451bcf1bSMarcin Wojtas /* Clear VLAN table entry using old VID. */ 835451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VTIDX, sc->vlans[vg->es_vlangroup]); 836451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_WRITE); 837451bcf1bSMarcin Wojtas sc->vlans[vg->es_vlangroup] = 0; 838451bcf1bSMarcin Wojtas return (0); 839451bcf1bSMarcin Wojtas } 840451bcf1bSMarcin Wojtas 841451bcf1bSMarcin Wojtas /* The VID is already used in a different group. */ 842451bcf1bSMarcin Wojtas for (i = 0; i < sc->info.es_nvlangroups; i++) 843451bcf1bSMarcin Wojtas if (i != vg->es_vlangroup && vid == sc->vlans[i]) 844451bcf1bSMarcin Wojtas return (EINVAL); 845451bcf1bSMarcin Wojtas 846451bcf1bSMarcin Wojtas /* This group already uses a different VID. */ 847451bcf1bSMarcin Wojtas if (sc->vlans[vg->es_vlangroup] != 0 && 848451bcf1bSMarcin Wojtas sc->vlans[vg->es_vlangroup] != vid) 849451bcf1bSMarcin Wojtas return (EINVAL); 850451bcf1bSMarcin Wojtas 851451bcf1bSMarcin Wojtas sc->vlans[vg->es_vlangroup] = vid; 852451bcf1bSMarcin Wojtas 853451bcf1bSMarcin Wojtas /* Assign members to the given group. */ 854451bcf1bSMarcin Wojtas reg = vg->es_member_ports & FELIX_ANA_VT_PORTMASK_MASK; 855451bcf1bSMarcin Wojtas reg <<= FELIX_ANA_VT_PORTMASK_SHIFT; 856451bcf1bSMarcin Wojtas reg |= FELIX_ANA_VT_WRITE; 857451bcf1bSMarcin Wojtas 858451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VTIDX, vid); 859451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VT, reg); 860451bcf1bSMarcin Wojtas 861451bcf1bSMarcin Wojtas /* 862451bcf1bSMarcin Wojtas * According to documentation read and write commands 863451bcf1bSMarcin Wojtas * are instant. 864451bcf1bSMarcin Wojtas * Add a small delay just to be safe. 865451bcf1bSMarcin Wojtas */ 866451bcf1bSMarcin Wojtas mb(); 867451bcf1bSMarcin Wojtas DELAY(100); 868451bcf1bSMarcin Wojtas reg = FELIX_RD4(sc, FELIX_ANA_VT); 869451bcf1bSMarcin Wojtas if ((reg & FELIX_ANA_VT_STS) != FELIX_ANA_VT_IDLE) 870451bcf1bSMarcin Wojtas return (ENXIO); 871451bcf1bSMarcin Wojtas 872451bcf1bSMarcin Wojtas return (0); 873451bcf1bSMarcin Wojtas } 874451bcf1bSMarcin Wojtas 875451bcf1bSMarcin Wojtas static int 876451bcf1bSMarcin Wojtas felix_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) 877451bcf1bSMarcin Wojtas { 878451bcf1bSMarcin Wojtas felix_softc_t sc; 879451bcf1bSMarcin Wojtas int error; 880451bcf1bSMarcin Wojtas 881451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 882451bcf1bSMarcin Wojtas 883451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 884451bcf1bSMarcin Wojtas if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) 885451bcf1bSMarcin Wojtas error = felix_set_dot1q_vlan(sc, vg); 886451bcf1bSMarcin Wojtas else 887451bcf1bSMarcin Wojtas error = EINVAL; 888451bcf1bSMarcin Wojtas 889451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 890451bcf1bSMarcin Wojtas return (error); 891451bcf1bSMarcin Wojtas } 892451bcf1bSMarcin Wojtas 893451bcf1bSMarcin Wojtas static int 894451bcf1bSMarcin Wojtas felix_get_dot1q_vlan(felix_softc_t sc, etherswitch_vlangroup_t *vg) 895451bcf1bSMarcin Wojtas { 896451bcf1bSMarcin Wojtas uint32_t reg; 897451bcf1bSMarcin Wojtas int vid; 898451bcf1bSMarcin Wojtas 899451bcf1bSMarcin Wojtas vid = sc->vlans[vg->es_vlangroup]; 900451bcf1bSMarcin Wojtas 901451bcf1bSMarcin Wojtas if (vid == 0) 902451bcf1bSMarcin Wojtas return (0); 903451bcf1bSMarcin Wojtas 904451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VTIDX, vid); 905451bcf1bSMarcin Wojtas FELIX_WR4(sc, FELIX_ANA_VT, FELIX_ANA_VT_READ); 906451bcf1bSMarcin Wojtas 907451bcf1bSMarcin Wojtas /* 908451bcf1bSMarcin Wojtas * According to documentation read and write commands 909451bcf1bSMarcin Wojtas * are instant. 910451bcf1bSMarcin Wojtas * Add a small delay just to be safe. 911451bcf1bSMarcin Wojtas */ 912451bcf1bSMarcin Wojtas mb(); 913451bcf1bSMarcin Wojtas DELAY(100); 914451bcf1bSMarcin Wojtas reg = FELIX_RD4(sc, FELIX_ANA_VT); 915451bcf1bSMarcin Wojtas if ((reg & FELIX_ANA_VT_STS) != FELIX_ANA_VT_IDLE) 916451bcf1bSMarcin Wojtas return (ENXIO); 917451bcf1bSMarcin Wojtas 918451bcf1bSMarcin Wojtas reg >>= FELIX_ANA_VT_PORTMASK_SHIFT; 919451bcf1bSMarcin Wojtas reg &= FELIX_ANA_VT_PORTMASK_MASK; 920451bcf1bSMarcin Wojtas 921451bcf1bSMarcin Wojtas vg->es_untagged_ports = vg->es_member_ports = reg; 922451bcf1bSMarcin Wojtas vg->es_fid = 0; 923451bcf1bSMarcin Wojtas vg->es_vid = vid | ETHERSWITCH_VID_VALID; 924451bcf1bSMarcin Wojtas return (0); 925451bcf1bSMarcin Wojtas } 926451bcf1bSMarcin Wojtas 927451bcf1bSMarcin Wojtas static int 928451bcf1bSMarcin Wojtas felix_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) 929451bcf1bSMarcin Wojtas { 930451bcf1bSMarcin Wojtas felix_softc_t sc; 931451bcf1bSMarcin Wojtas int error; 932451bcf1bSMarcin Wojtas 933451bcf1bSMarcin Wojtas sc = device_get_softc(dev); 934451bcf1bSMarcin Wojtas 935451bcf1bSMarcin Wojtas FELIX_LOCK(sc); 936451bcf1bSMarcin Wojtas if (sc->vlan_mode == ETHERSWITCH_VLAN_DOT1Q) 937451bcf1bSMarcin Wojtas error = felix_get_dot1q_vlan(sc, vg); 938451bcf1bSMarcin Wojtas else 939451bcf1bSMarcin Wojtas error = EINVAL; 940451bcf1bSMarcin Wojtas 941451bcf1bSMarcin Wojtas FELIX_UNLOCK(sc); 942451bcf1bSMarcin Wojtas return (error); 943451bcf1bSMarcin Wojtas } 944451bcf1bSMarcin Wojtas 945451bcf1bSMarcin Wojtas static void 946451bcf1bSMarcin Wojtas felix_tick(void *arg) 947451bcf1bSMarcin Wojtas { 948451bcf1bSMarcin Wojtas struct mii_data *mii; 949451bcf1bSMarcin Wojtas felix_softc_t sc; 950451bcf1bSMarcin Wojtas int port; 951451bcf1bSMarcin Wojtas 952451bcf1bSMarcin Wojtas sc = arg; 953451bcf1bSMarcin Wojtas 954451bcf1bSMarcin Wojtas FELIX_LOCK_ASSERT(sc, MA_OWNED); 955451bcf1bSMarcin Wojtas 956451bcf1bSMarcin Wojtas for (port = 0; port < sc->info.es_nports; port++) { 957451bcf1bSMarcin Wojtas if (!felix_is_phyport(sc, port)) 958451bcf1bSMarcin Wojtas continue; 959451bcf1bSMarcin Wojtas 960451bcf1bSMarcin Wojtas mii = felix_miiforport(sc, port); 961451bcf1bSMarcin Wojtas MPASS(mii != NULL); 962451bcf1bSMarcin Wojtas mii_tick(mii); 963451bcf1bSMarcin Wojtas } 964d88aecceSKornel Duleba if (sc->timer_ticks != 0) 965d88aecceSKornel Duleba callout_reset(&sc->tick_callout, sc->timer_ticks, felix_tick, sc); 966451bcf1bSMarcin Wojtas } 967451bcf1bSMarcin Wojtas 968451bcf1bSMarcin Wojtas static int 969451bcf1bSMarcin Wojtas felix_ifmedia_upd(struct ifnet *ifp) 970451bcf1bSMarcin Wojtas { 971451bcf1bSMarcin Wojtas struct mii_data *mii; 972451bcf1bSMarcin Wojtas felix_softc_t sc; 973451bcf1bSMarcin Wojtas 974451bcf1bSMarcin Wojtas sc = ifp->if_softc; 975451bcf1bSMarcin Wojtas mii = felix_miiforport(sc, ifp->if_dunit); 976451bcf1bSMarcin Wojtas if (mii == NULL) 977451bcf1bSMarcin Wojtas return (ENXIO); 978451bcf1bSMarcin Wojtas 979451bcf1bSMarcin Wojtas mii_mediachg(mii); 980451bcf1bSMarcin Wojtas return (0); 981451bcf1bSMarcin Wojtas } 982451bcf1bSMarcin Wojtas 983451bcf1bSMarcin Wojtas static void 984451bcf1bSMarcin Wojtas felix_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) 985451bcf1bSMarcin Wojtas { 986451bcf1bSMarcin Wojtas felix_softc_t sc; 987451bcf1bSMarcin Wojtas struct mii_data *mii; 988451bcf1bSMarcin Wojtas 989451bcf1bSMarcin Wojtas sc = ifp->if_softc; 990451bcf1bSMarcin Wojtas mii = felix_miiforport(sc, ifp->if_dunit); 991451bcf1bSMarcin Wojtas if (mii == NULL) 992451bcf1bSMarcin Wojtas return; 993451bcf1bSMarcin Wojtas 994451bcf1bSMarcin Wojtas mii_pollstat(mii); 995451bcf1bSMarcin Wojtas ifmr->ifm_active = mii->mii_media_active; 996451bcf1bSMarcin Wojtas ifmr->ifm_status = mii->mii_media_status; 997451bcf1bSMarcin Wojtas } 998451bcf1bSMarcin Wojtas 999451bcf1bSMarcin Wojtas static bool 1000451bcf1bSMarcin Wojtas felix_is_phyport(felix_softc_t sc, int port) 1001451bcf1bSMarcin Wojtas { 1002451bcf1bSMarcin Wojtas 1003451bcf1bSMarcin Wojtas return (!sc->ports[port].fixed_port); 1004451bcf1bSMarcin Wojtas } 1005451bcf1bSMarcin Wojtas 1006451bcf1bSMarcin Wojtas static struct mii_data* 1007451bcf1bSMarcin Wojtas felix_miiforport(felix_softc_t sc, unsigned int port) 1008451bcf1bSMarcin Wojtas { 1009451bcf1bSMarcin Wojtas 1010451bcf1bSMarcin Wojtas if (!felix_is_phyport(sc, port)) 1011451bcf1bSMarcin Wojtas return (NULL); 1012451bcf1bSMarcin Wojtas 1013451bcf1bSMarcin Wojtas return (device_get_softc(sc->ports[port].miibus)); 1014451bcf1bSMarcin Wojtas } 1015