1*a8d7fc4aSZbigniew Bodek /* 2*a8d7fc4aSZbigniew Bodek * Copyright (c) 2017 Stormshield. 3*a8d7fc4aSZbigniew Bodek * Copyright (c) 2017 Semihalf. 4*a8d7fc4aSZbigniew Bodek * All rights reserved. 5*a8d7fc4aSZbigniew Bodek * 6*a8d7fc4aSZbigniew Bodek * Redistribution and use in source and binary forms, with or without 7*a8d7fc4aSZbigniew Bodek * modification, are permitted provided that the following conditions 8*a8d7fc4aSZbigniew Bodek * are met: 9*a8d7fc4aSZbigniew Bodek * 1. Redistributions of source code must retain the above copyright 10*a8d7fc4aSZbigniew Bodek * notice, this list of conditions and the following disclaimer. 11*a8d7fc4aSZbigniew Bodek * 2. Redistributions in binary form must reproduce the above copyright 12*a8d7fc4aSZbigniew Bodek * notice, this list of conditions and the following disclaimer in the 13*a8d7fc4aSZbigniew Bodek * documentation and/or other materials provided with the distribution. 14*a8d7fc4aSZbigniew Bodek * 15*a8d7fc4aSZbigniew Bodek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16*a8d7fc4aSZbigniew Bodek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17*a8d7fc4aSZbigniew Bodek * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18*a8d7fc4aSZbigniew Bodek * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19*a8d7fc4aSZbigniew Bodek * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20*a8d7fc4aSZbigniew Bodek * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21*a8d7fc4aSZbigniew Bodek * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22*a8d7fc4aSZbigniew Bodek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23*a8d7fc4aSZbigniew Bodek * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24*a8d7fc4aSZbigniew Bodek * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25*a8d7fc4aSZbigniew Bodek * POSSIBILITY OF SUCH DAMAGE. 26*a8d7fc4aSZbigniew Bodek */ 27*a8d7fc4aSZbigniew Bodek 28*a8d7fc4aSZbigniew Bodek #include "opt_platform.h" 29*a8d7fc4aSZbigniew Bodek #include <sys/cdefs.h> 30*a8d7fc4aSZbigniew Bodek __FBSDID("$FreeBSD$"); 31*a8d7fc4aSZbigniew Bodek 32*a8d7fc4aSZbigniew Bodek #include <sys/param.h> 33*a8d7fc4aSZbigniew Bodek #include <sys/systm.h> 34*a8d7fc4aSZbigniew Bodek #include <sys/kernel.h> 35*a8d7fc4aSZbigniew Bodek #include <sys/module.h> 36*a8d7fc4aSZbigniew Bodek #include <sys/bus.h> 37*a8d7fc4aSZbigniew Bodek #include <sys/rman.h> 38*a8d7fc4aSZbigniew Bodek #include <sys/socket.h> 39*a8d7fc4aSZbigniew Bodek #include <sys/taskqueue.h> 40*a8d7fc4aSZbigniew Bodek 41*a8d7fc4aSZbigniew Bodek #include <net/ethernet.h> 42*a8d7fc4aSZbigniew Bodek #include <net/if.h> 43*a8d7fc4aSZbigniew Bodek #include <net/if_media.h> 44*a8d7fc4aSZbigniew Bodek 45*a8d7fc4aSZbigniew Bodek #include <netinet/in.h> 46*a8d7fc4aSZbigniew Bodek #include <netinet/ip.h> 47*a8d7fc4aSZbigniew Bodek #include <netinet/tcp_lro.h> 48*a8d7fc4aSZbigniew Bodek 49*a8d7fc4aSZbigniew Bodek #include <machine/bus.h> 50*a8d7fc4aSZbigniew Bodek #include <machine/resource.h> 51*a8d7fc4aSZbigniew Bodek 52*a8d7fc4aSZbigniew Bodek #include <dev/ofw/ofw_bus.h> 53*a8d7fc4aSZbigniew Bodek #include <dev/ofw/ofw_bus_subr.h> 54*a8d7fc4aSZbigniew Bodek 55*a8d7fc4aSZbigniew Bodek #include <dev/mii/mii.h> 56*a8d7fc4aSZbigniew Bodek #include <dev/mii/miivar.h> 57*a8d7fc4aSZbigniew Bodek 58*a8d7fc4aSZbigniew Bodek #include "if_mvnetareg.h" 59*a8d7fc4aSZbigniew Bodek #include "if_mvnetavar.h" 60*a8d7fc4aSZbigniew Bodek 61*a8d7fc4aSZbigniew Bodek #define PHY_MODE_MAXLEN 10 62*a8d7fc4aSZbigniew Bodek #define INBAND_STATUS_MAXLEN 16 63*a8d7fc4aSZbigniew Bodek 64*a8d7fc4aSZbigniew Bodek static int mvneta_fdt_probe(device_t); 65*a8d7fc4aSZbigniew Bodek static int mvneta_fdt_attach(device_t); 66*a8d7fc4aSZbigniew Bodek 67*a8d7fc4aSZbigniew Bodek static device_method_t mvneta_fdt_methods[] = { 68*a8d7fc4aSZbigniew Bodek /* Device interface */ 69*a8d7fc4aSZbigniew Bodek DEVMETHOD(device_probe, mvneta_fdt_probe), 70*a8d7fc4aSZbigniew Bodek DEVMETHOD(device_attach, mvneta_fdt_attach), 71*a8d7fc4aSZbigniew Bodek 72*a8d7fc4aSZbigniew Bodek /* End */ 73*a8d7fc4aSZbigniew Bodek DEVMETHOD_END 74*a8d7fc4aSZbigniew Bodek }; 75*a8d7fc4aSZbigniew Bodek 76*a8d7fc4aSZbigniew Bodek DEFINE_CLASS_1(mvneta, mvneta_fdt_driver, mvneta_fdt_methods, 77*a8d7fc4aSZbigniew Bodek sizeof(struct mvneta_softc), mvneta_driver); 78*a8d7fc4aSZbigniew Bodek 79*a8d7fc4aSZbigniew Bodek static devclass_t mvneta_fdt_devclass; 80*a8d7fc4aSZbigniew Bodek 81*a8d7fc4aSZbigniew Bodek DRIVER_MODULE(mvneta, ofwbus, mvneta_fdt_driver, mvneta_fdt_devclass, 0, 0); 82*a8d7fc4aSZbigniew Bodek DRIVER_MODULE(mvneta, simplebus, mvneta_fdt_driver, mvneta_fdt_devclass, 0, 0); 83*a8d7fc4aSZbigniew Bodek 84*a8d7fc4aSZbigniew Bodek static int mvneta_fdt_phy_acquire(device_t); 85*a8d7fc4aSZbigniew Bodek 86*a8d7fc4aSZbigniew Bodek static int 87*a8d7fc4aSZbigniew Bodek mvneta_fdt_probe(device_t dev) 88*a8d7fc4aSZbigniew Bodek { 89*a8d7fc4aSZbigniew Bodek 90*a8d7fc4aSZbigniew Bodek if (!ofw_bus_status_okay(dev)) 91*a8d7fc4aSZbigniew Bodek return (ENXIO); 92*a8d7fc4aSZbigniew Bodek 93*a8d7fc4aSZbigniew Bodek if (!ofw_bus_is_compatible(dev, "marvell,armada-370-neta")) 94*a8d7fc4aSZbigniew Bodek return (ENXIO); 95*a8d7fc4aSZbigniew Bodek 96*a8d7fc4aSZbigniew Bodek device_set_desc(dev, "NETA controller"); 97*a8d7fc4aSZbigniew Bodek return (BUS_PROBE_DEFAULT); 98*a8d7fc4aSZbigniew Bodek } 99*a8d7fc4aSZbigniew Bodek 100*a8d7fc4aSZbigniew Bodek static int 101*a8d7fc4aSZbigniew Bodek mvneta_fdt_attach(device_t dev) 102*a8d7fc4aSZbigniew Bodek { 103*a8d7fc4aSZbigniew Bodek int err; 104*a8d7fc4aSZbigniew Bodek 105*a8d7fc4aSZbigniew Bodek /* Try to fetch PHY information from FDT */ 106*a8d7fc4aSZbigniew Bodek err = mvneta_fdt_phy_acquire(dev); 107*a8d7fc4aSZbigniew Bodek if (err != 0) 108*a8d7fc4aSZbigniew Bodek return (err); 109*a8d7fc4aSZbigniew Bodek 110*a8d7fc4aSZbigniew Bodek return (mvneta_attach(dev)); 111*a8d7fc4aSZbigniew Bodek } 112*a8d7fc4aSZbigniew Bodek 113*a8d7fc4aSZbigniew Bodek static int 114*a8d7fc4aSZbigniew Bodek mvneta_fdt_phy_acquire(device_t dev) 115*a8d7fc4aSZbigniew Bodek { 116*a8d7fc4aSZbigniew Bodek struct mvneta_softc *sc; 117*a8d7fc4aSZbigniew Bodek phandle_t node, child, phy_handle; 118*a8d7fc4aSZbigniew Bodek char phymode[PHY_MODE_MAXLEN]; 119*a8d7fc4aSZbigniew Bodek char managed[INBAND_STATUS_MAXLEN]; 120*a8d7fc4aSZbigniew Bodek char *name; 121*a8d7fc4aSZbigniew Bodek 122*a8d7fc4aSZbigniew Bodek sc = device_get_softc(dev); 123*a8d7fc4aSZbigniew Bodek node = ofw_bus_get_node(dev); 124*a8d7fc4aSZbigniew Bodek 125*a8d7fc4aSZbigniew Bodek /* PHY mode is crucial */ 126*a8d7fc4aSZbigniew Bodek if (OF_getprop(node, "phy-mode", phymode, sizeof(phymode)) <= 0) { 127*a8d7fc4aSZbigniew Bodek device_printf(dev, "Failed to acquire PHY mode from FDT.\n"); 128*a8d7fc4aSZbigniew Bodek return (ENXIO); 129*a8d7fc4aSZbigniew Bodek } 130*a8d7fc4aSZbigniew Bodek 131*a8d7fc4aSZbigniew Bodek if (strncmp(phymode, "rgmii-id", 8) == 0) 132*a8d7fc4aSZbigniew Bodek sc->phy_mode = MVNETA_PHY_RGMII_ID; 133*a8d7fc4aSZbigniew Bodek else if (strncmp(phymode, "rgmii", 5) == 0) 134*a8d7fc4aSZbigniew Bodek sc->phy_mode = MVNETA_PHY_RGMII; 135*a8d7fc4aSZbigniew Bodek else if (strncmp(phymode, "sgmii", 5) == 0) 136*a8d7fc4aSZbigniew Bodek sc->phy_mode = MVNETA_PHY_SGMII; 137*a8d7fc4aSZbigniew Bodek else if (strncmp(phymode, "qsgmii", 6) == 0) 138*a8d7fc4aSZbigniew Bodek sc->phy_mode = MVNETA_PHY_QSGMII; 139*a8d7fc4aSZbigniew Bodek else 140*a8d7fc4aSZbigniew Bodek sc->phy_mode = MVNETA_PHY_SGMII; 141*a8d7fc4aSZbigniew Bodek 142*a8d7fc4aSZbigniew Bodek /* Check if in-band link status will be used */ 143*a8d7fc4aSZbigniew Bodek if (OF_getprop(node, "managed", managed, sizeof(managed)) > 0) { 144*a8d7fc4aSZbigniew Bodek if (strncmp(managed, "in-band-status", 14) == 0) { 145*a8d7fc4aSZbigniew Bodek sc->use_inband_status = TRUE; 146*a8d7fc4aSZbigniew Bodek device_printf(dev, "Use in-band link status.\n"); 147*a8d7fc4aSZbigniew Bodek return (0); 148*a8d7fc4aSZbigniew Bodek } 149*a8d7fc4aSZbigniew Bodek } 150*a8d7fc4aSZbigniew Bodek 151*a8d7fc4aSZbigniew Bodek if (OF_getencprop(node, "phy", (void *)&phy_handle, 152*a8d7fc4aSZbigniew Bodek sizeof(phy_handle)) <= 0) { 153*a8d7fc4aSZbigniew Bodek /* Test for fixed-link (present i.e. in 388-gp) */ 154*a8d7fc4aSZbigniew Bodek for (child = OF_child(node); child != 0; child = OF_peer(child)) { 155*a8d7fc4aSZbigniew Bodek if (OF_getprop_alloc(child, 156*a8d7fc4aSZbigniew Bodek "name", 1, (void **)&name) <= 0) { 157*a8d7fc4aSZbigniew Bodek continue; 158*a8d7fc4aSZbigniew Bodek } 159*a8d7fc4aSZbigniew Bodek if (strncmp(name, "fixed-link", 10) == 0) { 160*a8d7fc4aSZbigniew Bodek free(name, M_OFWPROP); 161*a8d7fc4aSZbigniew Bodek if (OF_getencprop(child, "speed", 162*a8d7fc4aSZbigniew Bodek &sc->phy_speed, sizeof(sc->phy_speed)) <= 0) { 163*a8d7fc4aSZbigniew Bodek if (bootverbose) { 164*a8d7fc4aSZbigniew Bodek device_printf(dev, 165*a8d7fc4aSZbigniew Bodek "No PHY information.\n"); 166*a8d7fc4aSZbigniew Bodek } 167*a8d7fc4aSZbigniew Bodek return (ENXIO); 168*a8d7fc4aSZbigniew Bodek } 169*a8d7fc4aSZbigniew Bodek if (OF_hasprop(child, "full-duplex")) 170*a8d7fc4aSZbigniew Bodek sc->phy_fdx = TRUE; 171*a8d7fc4aSZbigniew Bodek else 172*a8d7fc4aSZbigniew Bodek sc->phy_fdx = FALSE; 173*a8d7fc4aSZbigniew Bodek 174*a8d7fc4aSZbigniew Bodek /* Keep this flag just for the record */ 175*a8d7fc4aSZbigniew Bodek sc->phy_addr = MII_PHY_ANY; 176*a8d7fc4aSZbigniew Bodek 177*a8d7fc4aSZbigniew Bodek return (0); 178*a8d7fc4aSZbigniew Bodek } 179*a8d7fc4aSZbigniew Bodek free(name, M_OFWPROP); 180*a8d7fc4aSZbigniew Bodek } 181*a8d7fc4aSZbigniew Bodek if (bootverbose) { 182*a8d7fc4aSZbigniew Bodek device_printf(dev, 183*a8d7fc4aSZbigniew Bodek "Could not find PHY information in FDT.\n"); 184*a8d7fc4aSZbigniew Bodek } 185*a8d7fc4aSZbigniew Bodek return (ENXIO); 186*a8d7fc4aSZbigniew Bodek } else { 187*a8d7fc4aSZbigniew Bodek phy_handle = OF_instance_to_package(phy_handle); 188*a8d7fc4aSZbigniew Bodek if (OF_getencprop(phy_handle, "reg", &sc->phy_addr, 189*a8d7fc4aSZbigniew Bodek sizeof(sc->phy_addr)) <= 0) { 190*a8d7fc4aSZbigniew Bodek device_printf(dev, 191*a8d7fc4aSZbigniew Bodek "Could not find PHY address in FDT.\n"); 192*a8d7fc4aSZbigniew Bodek return (ENXIO); 193*a8d7fc4aSZbigniew Bodek } 194*a8d7fc4aSZbigniew Bodek } 195*a8d7fc4aSZbigniew Bodek 196*a8d7fc4aSZbigniew Bodek return (0); 197*a8d7fc4aSZbigniew Bodek } 198*a8d7fc4aSZbigniew Bodek 199*a8d7fc4aSZbigniew Bodek int 200*a8d7fc4aSZbigniew Bodek mvneta_fdt_mac_address(struct mvneta_softc *sc, uint8_t *addr) 201*a8d7fc4aSZbigniew Bodek { 202*a8d7fc4aSZbigniew Bodek phandle_t node; 203*a8d7fc4aSZbigniew Bodek uint8_t lmac[ETHER_ADDR_LEN]; 204*a8d7fc4aSZbigniew Bodek uint8_t zeromac[] = {[0 ... (ETHER_ADDR_LEN - 1)] = 0}; 205*a8d7fc4aSZbigniew Bodek int len; 206*a8d7fc4aSZbigniew Bodek 207*a8d7fc4aSZbigniew Bodek /* 208*a8d7fc4aSZbigniew Bodek * Retrieve hw address from the device tree. 209*a8d7fc4aSZbigniew Bodek */ 210*a8d7fc4aSZbigniew Bodek node = ofw_bus_get_node(sc->dev); 211*a8d7fc4aSZbigniew Bodek if (node == 0) 212*a8d7fc4aSZbigniew Bodek return (ENXIO); 213*a8d7fc4aSZbigniew Bodek 214*a8d7fc4aSZbigniew Bodek len = OF_getprop(node, "local-mac-address", (void *)lmac, sizeof(lmac)); 215*a8d7fc4aSZbigniew Bodek if (len != ETHER_ADDR_LEN) 216*a8d7fc4aSZbigniew Bodek return (ENOENT); 217*a8d7fc4aSZbigniew Bodek 218*a8d7fc4aSZbigniew Bodek if (memcmp(lmac, zeromac, ETHER_ADDR_LEN) == 0) { 219*a8d7fc4aSZbigniew Bodek /* Invalid MAC address (all zeros) */ 220*a8d7fc4aSZbigniew Bodek return (EINVAL); 221*a8d7fc4aSZbigniew Bodek } 222*a8d7fc4aSZbigniew Bodek memcpy(addr, lmac, ETHER_ADDR_LEN); 223*a8d7fc4aSZbigniew Bodek 224*a8d7fc4aSZbigniew Bodek return (0); 225*a8d7fc4aSZbigniew Bodek } 226