1*511f6c1aSRoger Quadros // SPDX-License-Identifier: GPL-2.0 2*511f6c1aSRoger Quadros 3*511f6c1aSRoger Quadros /* Texas Instruments ICSSM Ethernet Driver 4*511f6c1aSRoger Quadros * 5*511f6c1aSRoger Quadros * Copyright (C) 2018-2022 Texas Instruments Incorporated - https://www.ti.com/ 6*511f6c1aSRoger Quadros * 7*511f6c1aSRoger Quadros */ 8*511f6c1aSRoger Quadros 9*511f6c1aSRoger Quadros #include <linux/etherdevice.h> 10*511f6c1aSRoger Quadros #include <linux/genalloc.h> 11*511f6c1aSRoger Quadros #include <linux/if_bridge.h> 12*511f6c1aSRoger Quadros #include <linux/if_hsr.h> 13*511f6c1aSRoger Quadros #include <linux/if_vlan.h> 14*511f6c1aSRoger Quadros #include <linux/interrupt.h> 15*511f6c1aSRoger Quadros #include <linux/kernel.h> 16*511f6c1aSRoger Quadros #include <linux/mfd/syscon.h> 17*511f6c1aSRoger Quadros #include <linux/module.h> 18*511f6c1aSRoger Quadros #include <linux/net_tstamp.h> 19*511f6c1aSRoger Quadros #include <linux/of.h> 20*511f6c1aSRoger Quadros #include <linux/of_irq.h> 21*511f6c1aSRoger Quadros #include <linux/of_mdio.h> 22*511f6c1aSRoger Quadros #include <linux/of_net.h> 23*511f6c1aSRoger Quadros #include <linux/platform_device.h> 24*511f6c1aSRoger Quadros #include <linux/phy.h> 25*511f6c1aSRoger Quadros #include <linux/remoteproc/pruss.h> 26*511f6c1aSRoger Quadros #include <linux/ptp_classify.h> 27*511f6c1aSRoger Quadros #include <linux/regmap.h> 28*511f6c1aSRoger Quadros #include <linux/remoteproc.h> 29*511f6c1aSRoger Quadros #include <net/pkt_cls.h> 30*511f6c1aSRoger Quadros 31*511f6c1aSRoger Quadros #include "icssm_prueth.h" 32*511f6c1aSRoger Quadros 33*511f6c1aSRoger Quadros /* called back by PHY layer if there is change in link state of hw port*/ 34*511f6c1aSRoger Quadros static void icssm_emac_adjust_link(struct net_device *ndev) 35*511f6c1aSRoger Quadros { 36*511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 37*511f6c1aSRoger Quadros struct phy_device *phydev = emac->phydev; 38*511f6c1aSRoger Quadros bool new_state = false; 39*511f6c1aSRoger Quadros unsigned long flags; 40*511f6c1aSRoger Quadros 41*511f6c1aSRoger Quadros spin_lock_irqsave(&emac->lock, flags); 42*511f6c1aSRoger Quadros 43*511f6c1aSRoger Quadros if (phydev->link) { 44*511f6c1aSRoger Quadros /* check the mode of operation */ 45*511f6c1aSRoger Quadros if (phydev->duplex != emac->duplex) { 46*511f6c1aSRoger Quadros new_state = true; 47*511f6c1aSRoger Quadros emac->duplex = phydev->duplex; 48*511f6c1aSRoger Quadros } 49*511f6c1aSRoger Quadros if (phydev->speed != emac->speed) { 50*511f6c1aSRoger Quadros new_state = true; 51*511f6c1aSRoger Quadros emac->speed = phydev->speed; 52*511f6c1aSRoger Quadros } 53*511f6c1aSRoger Quadros if (!emac->link) { 54*511f6c1aSRoger Quadros new_state = true; 55*511f6c1aSRoger Quadros emac->link = 1; 56*511f6c1aSRoger Quadros } 57*511f6c1aSRoger Quadros } else if (emac->link) { 58*511f6c1aSRoger Quadros new_state = true; 59*511f6c1aSRoger Quadros emac->link = 0; 60*511f6c1aSRoger Quadros } 61*511f6c1aSRoger Quadros 62*511f6c1aSRoger Quadros if (new_state) 63*511f6c1aSRoger Quadros phy_print_status(phydev); 64*511f6c1aSRoger Quadros 65*511f6c1aSRoger Quadros if (emac->link) { 66*511f6c1aSRoger Quadros /* reactivate the transmit queue if it is stopped */ 67*511f6c1aSRoger Quadros if (netif_running(ndev) && netif_queue_stopped(ndev)) 68*511f6c1aSRoger Quadros netif_wake_queue(ndev); 69*511f6c1aSRoger Quadros } else { 70*511f6c1aSRoger Quadros if (!netif_queue_stopped(ndev)) 71*511f6c1aSRoger Quadros netif_stop_queue(ndev); 72*511f6c1aSRoger Quadros } 73*511f6c1aSRoger Quadros 74*511f6c1aSRoger Quadros spin_unlock_irqrestore(&emac->lock, flags); 75*511f6c1aSRoger Quadros } 76*511f6c1aSRoger Quadros 77*511f6c1aSRoger Quadros static int icssm_emac_set_boot_pru(struct prueth_emac *emac, 78*511f6c1aSRoger Quadros struct net_device *ndev) 79*511f6c1aSRoger Quadros { 80*511f6c1aSRoger Quadros const struct prueth_firmware *pru_firmwares; 81*511f6c1aSRoger Quadros struct prueth *prueth = emac->prueth; 82*511f6c1aSRoger Quadros const char *fw_name; 83*511f6c1aSRoger Quadros int ret; 84*511f6c1aSRoger Quadros 85*511f6c1aSRoger Quadros pru_firmwares = &prueth->fw_data->fw_pru[emac->port_id - 1]; 86*511f6c1aSRoger Quadros fw_name = pru_firmwares->fw_name[prueth->eth_type]; 87*511f6c1aSRoger Quadros if (!fw_name) { 88*511f6c1aSRoger Quadros netdev_err(ndev, "eth_type %d not supported\n", 89*511f6c1aSRoger Quadros prueth->eth_type); 90*511f6c1aSRoger Quadros return -ENODEV; 91*511f6c1aSRoger Quadros } 92*511f6c1aSRoger Quadros 93*511f6c1aSRoger Quadros ret = rproc_set_firmware(emac->pru, fw_name); 94*511f6c1aSRoger Quadros if (ret) { 95*511f6c1aSRoger Quadros netdev_err(ndev, "failed to set %s firmware: %d\n", 96*511f6c1aSRoger Quadros fw_name, ret); 97*511f6c1aSRoger Quadros return ret; 98*511f6c1aSRoger Quadros } 99*511f6c1aSRoger Quadros 100*511f6c1aSRoger Quadros ret = rproc_boot(emac->pru); 101*511f6c1aSRoger Quadros if (ret) { 102*511f6c1aSRoger Quadros netdev_err(ndev, "failed to boot %s firmware: %d\n", 103*511f6c1aSRoger Quadros fw_name, ret); 104*511f6c1aSRoger Quadros return ret; 105*511f6c1aSRoger Quadros } 106*511f6c1aSRoger Quadros 107*511f6c1aSRoger Quadros return ret; 108*511f6c1aSRoger Quadros } 109*511f6c1aSRoger Quadros 110*511f6c1aSRoger Quadros /** 111*511f6c1aSRoger Quadros * icssm_emac_ndo_open - EMAC device open 112*511f6c1aSRoger Quadros * @ndev: network adapter device 113*511f6c1aSRoger Quadros * 114*511f6c1aSRoger Quadros * Called when system wants to start the interface. 115*511f6c1aSRoger Quadros * 116*511f6c1aSRoger Quadros * Return: 0 for a successful open, or appropriate error code 117*511f6c1aSRoger Quadros */ 118*511f6c1aSRoger Quadros static int icssm_emac_ndo_open(struct net_device *ndev) 119*511f6c1aSRoger Quadros { 120*511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 121*511f6c1aSRoger Quadros int ret; 122*511f6c1aSRoger Quadros 123*511f6c1aSRoger Quadros ret = icssm_emac_set_boot_pru(emac, ndev); 124*511f6c1aSRoger Quadros if (ret) 125*511f6c1aSRoger Quadros return ret; 126*511f6c1aSRoger Quadros 127*511f6c1aSRoger Quadros /* start PHY */ 128*511f6c1aSRoger Quadros phy_start(emac->phydev); 129*511f6c1aSRoger Quadros 130*511f6c1aSRoger Quadros return 0; 131*511f6c1aSRoger Quadros } 132*511f6c1aSRoger Quadros 133*511f6c1aSRoger Quadros /** 134*511f6c1aSRoger Quadros * icssm_emac_ndo_stop - EMAC device stop 135*511f6c1aSRoger Quadros * @ndev: network adapter device 136*511f6c1aSRoger Quadros * 137*511f6c1aSRoger Quadros * Called when system wants to stop or down the interface. 138*511f6c1aSRoger Quadros * 139*511f6c1aSRoger Quadros * Return: Always 0 (Success) 140*511f6c1aSRoger Quadros */ 141*511f6c1aSRoger Quadros static int icssm_emac_ndo_stop(struct net_device *ndev) 142*511f6c1aSRoger Quadros { 143*511f6c1aSRoger Quadros struct prueth_emac *emac = netdev_priv(ndev); 144*511f6c1aSRoger Quadros 145*511f6c1aSRoger Quadros /* stop PHY */ 146*511f6c1aSRoger Quadros phy_stop(emac->phydev); 147*511f6c1aSRoger Quadros 148*511f6c1aSRoger Quadros rproc_shutdown(emac->pru); 149*511f6c1aSRoger Quadros 150*511f6c1aSRoger Quadros return 0; 151*511f6c1aSRoger Quadros } 152*511f6c1aSRoger Quadros 153*511f6c1aSRoger Quadros static const struct net_device_ops emac_netdev_ops = { 154*511f6c1aSRoger Quadros .ndo_open = icssm_emac_ndo_open, 155*511f6c1aSRoger Quadros .ndo_stop = icssm_emac_ndo_stop, 156*511f6c1aSRoger Quadros }; 157*511f6c1aSRoger Quadros 158*511f6c1aSRoger Quadros /* get emac_port corresponding to eth_node name */ 159*511f6c1aSRoger Quadros static int icssm_prueth_node_port(struct device_node *eth_node) 160*511f6c1aSRoger Quadros { 161*511f6c1aSRoger Quadros u32 port_id; 162*511f6c1aSRoger Quadros int ret; 163*511f6c1aSRoger Quadros 164*511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 165*511f6c1aSRoger Quadros if (ret) 166*511f6c1aSRoger Quadros return ret; 167*511f6c1aSRoger Quadros 168*511f6c1aSRoger Quadros if (port_id == 0) 169*511f6c1aSRoger Quadros return PRUETH_PORT_MII0; 170*511f6c1aSRoger Quadros else if (port_id == 1) 171*511f6c1aSRoger Quadros return PRUETH_PORT_MII1; 172*511f6c1aSRoger Quadros else 173*511f6c1aSRoger Quadros return PRUETH_PORT_INVALID; 174*511f6c1aSRoger Quadros } 175*511f6c1aSRoger Quadros 176*511f6c1aSRoger Quadros /* get MAC instance corresponding to eth_node name */ 177*511f6c1aSRoger Quadros static int icssm_prueth_node_mac(struct device_node *eth_node) 178*511f6c1aSRoger Quadros { 179*511f6c1aSRoger Quadros u32 port_id; 180*511f6c1aSRoger Quadros int ret; 181*511f6c1aSRoger Quadros 182*511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", &port_id); 183*511f6c1aSRoger Quadros if (ret) 184*511f6c1aSRoger Quadros return ret; 185*511f6c1aSRoger Quadros 186*511f6c1aSRoger Quadros if (port_id == 0) 187*511f6c1aSRoger Quadros return PRUETH_MAC0; 188*511f6c1aSRoger Quadros else if (port_id == 1) 189*511f6c1aSRoger Quadros return PRUETH_MAC1; 190*511f6c1aSRoger Quadros else 191*511f6c1aSRoger Quadros return PRUETH_MAC_INVALID; 192*511f6c1aSRoger Quadros } 193*511f6c1aSRoger Quadros 194*511f6c1aSRoger Quadros static int icssm_prueth_netdev_init(struct prueth *prueth, 195*511f6c1aSRoger Quadros struct device_node *eth_node) 196*511f6c1aSRoger Quadros { 197*511f6c1aSRoger Quadros struct prueth_emac *emac; 198*511f6c1aSRoger Quadros struct net_device *ndev; 199*511f6c1aSRoger Quadros enum prueth_port port; 200*511f6c1aSRoger Quadros enum prueth_mac mac; 201*511f6c1aSRoger Quadros int ret; 202*511f6c1aSRoger Quadros 203*511f6c1aSRoger Quadros port = icssm_prueth_node_port(eth_node); 204*511f6c1aSRoger Quadros if (port == PRUETH_PORT_INVALID) 205*511f6c1aSRoger Quadros return -EINVAL; 206*511f6c1aSRoger Quadros 207*511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 208*511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 209*511f6c1aSRoger Quadros return -EINVAL; 210*511f6c1aSRoger Quadros 211*511f6c1aSRoger Quadros ndev = devm_alloc_etherdev(prueth->dev, sizeof(*emac)); 212*511f6c1aSRoger Quadros if (!ndev) 213*511f6c1aSRoger Quadros return -ENOMEM; 214*511f6c1aSRoger Quadros 215*511f6c1aSRoger Quadros SET_NETDEV_DEV(ndev, prueth->dev); 216*511f6c1aSRoger Quadros emac = netdev_priv(ndev); 217*511f6c1aSRoger Quadros prueth->emac[mac] = emac; 218*511f6c1aSRoger Quadros emac->prueth = prueth; 219*511f6c1aSRoger Quadros emac->ndev = ndev; 220*511f6c1aSRoger Quadros emac->port_id = port; 221*511f6c1aSRoger Quadros 222*511f6c1aSRoger Quadros /* by default eth_type is EMAC */ 223*511f6c1aSRoger Quadros switch (port) { 224*511f6c1aSRoger Quadros case PRUETH_PORT_MII0: 225*511f6c1aSRoger Quadros emac->pru = prueth->pru0; 226*511f6c1aSRoger Quadros break; 227*511f6c1aSRoger Quadros case PRUETH_PORT_MII1: 228*511f6c1aSRoger Quadros emac->pru = prueth->pru1; 229*511f6c1aSRoger Quadros break; 230*511f6c1aSRoger Quadros default: 231*511f6c1aSRoger Quadros return -EINVAL; 232*511f6c1aSRoger Quadros } 233*511f6c1aSRoger Quadros /* get mac address from DT and set private and netdev addr */ 234*511f6c1aSRoger Quadros ret = of_get_ethdev_address(eth_node, ndev); 235*511f6c1aSRoger Quadros if (!is_valid_ether_addr(ndev->dev_addr)) { 236*511f6c1aSRoger Quadros eth_hw_addr_random(ndev); 237*511f6c1aSRoger Quadros dev_warn(prueth->dev, "port %d: using random MAC addr: %pM\n", 238*511f6c1aSRoger Quadros port, ndev->dev_addr); 239*511f6c1aSRoger Quadros } 240*511f6c1aSRoger Quadros ether_addr_copy(emac->mac_addr, ndev->dev_addr); 241*511f6c1aSRoger Quadros 242*511f6c1aSRoger Quadros /* connect PHY */ 243*511f6c1aSRoger Quadros emac->phydev = of_phy_get_and_connect(ndev, eth_node, 244*511f6c1aSRoger Quadros icssm_emac_adjust_link); 245*511f6c1aSRoger Quadros if (!emac->phydev) { 246*511f6c1aSRoger Quadros dev_dbg(prueth->dev, "PHY connection failed\n"); 247*511f6c1aSRoger Quadros ret = -ENODEV; 248*511f6c1aSRoger Quadros goto free; 249*511f6c1aSRoger Quadros } 250*511f6c1aSRoger Quadros 251*511f6c1aSRoger Quadros /* remove unsupported modes */ 252*511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Full_BIT); 253*511f6c1aSRoger Quadros 254*511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_10baseT_Half_BIT); 255*511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_100baseT_Half_BIT); 256*511f6c1aSRoger Quadros 257*511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Pause_BIT); 258*511f6c1aSRoger Quadros phy_remove_link_mode(emac->phydev, ETHTOOL_LINK_MODE_Asym_Pause_BIT); 259*511f6c1aSRoger Quadros 260*511f6c1aSRoger Quadros ndev->dev.of_node = eth_node; 261*511f6c1aSRoger Quadros ndev->netdev_ops = &emac_netdev_ops; 262*511f6c1aSRoger Quadros 263*511f6c1aSRoger Quadros return 0; 264*511f6c1aSRoger Quadros free: 265*511f6c1aSRoger Quadros emac->ndev = NULL; 266*511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 267*511f6c1aSRoger Quadros 268*511f6c1aSRoger Quadros return ret; 269*511f6c1aSRoger Quadros } 270*511f6c1aSRoger Quadros 271*511f6c1aSRoger Quadros static void icssm_prueth_netdev_exit(struct prueth *prueth, 272*511f6c1aSRoger Quadros struct device_node *eth_node) 273*511f6c1aSRoger Quadros { 274*511f6c1aSRoger Quadros struct prueth_emac *emac; 275*511f6c1aSRoger Quadros enum prueth_mac mac; 276*511f6c1aSRoger Quadros 277*511f6c1aSRoger Quadros mac = icssm_prueth_node_mac(eth_node); 278*511f6c1aSRoger Quadros if (mac == PRUETH_MAC_INVALID) 279*511f6c1aSRoger Quadros return; 280*511f6c1aSRoger Quadros 281*511f6c1aSRoger Quadros emac = prueth->emac[mac]; 282*511f6c1aSRoger Quadros if (!emac) 283*511f6c1aSRoger Quadros return; 284*511f6c1aSRoger Quadros 285*511f6c1aSRoger Quadros phy_disconnect(emac->phydev); 286*511f6c1aSRoger Quadros 287*511f6c1aSRoger Quadros prueth->emac[mac] = NULL; 288*511f6c1aSRoger Quadros } 289*511f6c1aSRoger Quadros 290*511f6c1aSRoger Quadros static int icssm_prueth_probe(struct platform_device *pdev) 291*511f6c1aSRoger Quadros { 292*511f6c1aSRoger Quadros struct device_node *eth0_node = NULL, *eth1_node = NULL; 293*511f6c1aSRoger Quadros struct device_node *eth_node, *eth_ports_node; 294*511f6c1aSRoger Quadros enum pruss_pru_id pruss_id0, pruss_id1; 295*511f6c1aSRoger Quadros struct device *dev = &pdev->dev; 296*511f6c1aSRoger Quadros struct device_node *np; 297*511f6c1aSRoger Quadros struct prueth *prueth; 298*511f6c1aSRoger Quadros int i, ret; 299*511f6c1aSRoger Quadros 300*511f6c1aSRoger Quadros np = dev->of_node; 301*511f6c1aSRoger Quadros if (!np) 302*511f6c1aSRoger Quadros return -ENODEV; /* we don't support non DT */ 303*511f6c1aSRoger Quadros 304*511f6c1aSRoger Quadros prueth = devm_kzalloc(dev, sizeof(*prueth), GFP_KERNEL); 305*511f6c1aSRoger Quadros if (!prueth) 306*511f6c1aSRoger Quadros return -ENOMEM; 307*511f6c1aSRoger Quadros 308*511f6c1aSRoger Quadros platform_set_drvdata(pdev, prueth); 309*511f6c1aSRoger Quadros prueth->dev = dev; 310*511f6c1aSRoger Quadros prueth->fw_data = device_get_match_data(dev); 311*511f6c1aSRoger Quadros 312*511f6c1aSRoger Quadros eth_ports_node = of_get_child_by_name(np, "ethernet-ports"); 313*511f6c1aSRoger Quadros if (!eth_ports_node) 314*511f6c1aSRoger Quadros return -ENOENT; 315*511f6c1aSRoger Quadros 316*511f6c1aSRoger Quadros for_each_child_of_node(eth_ports_node, eth_node) { 317*511f6c1aSRoger Quadros u32 reg; 318*511f6c1aSRoger Quadros 319*511f6c1aSRoger Quadros if (strcmp(eth_node->name, "ethernet-port")) 320*511f6c1aSRoger Quadros continue; 321*511f6c1aSRoger Quadros ret = of_property_read_u32(eth_node, "reg", ®); 322*511f6c1aSRoger Quadros if (ret < 0) { 323*511f6c1aSRoger Quadros dev_err(dev, "%pOF error reading port_id %d\n", 324*511f6c1aSRoger Quadros eth_node, ret); 325*511f6c1aSRoger Quadros of_node_put(eth_node); 326*511f6c1aSRoger Quadros return ret; 327*511f6c1aSRoger Quadros } 328*511f6c1aSRoger Quadros 329*511f6c1aSRoger Quadros of_node_get(eth_node); 330*511f6c1aSRoger Quadros 331*511f6c1aSRoger Quadros if (reg == 0 && !eth0_node) { 332*511f6c1aSRoger Quadros eth0_node = eth_node; 333*511f6c1aSRoger Quadros if (!of_device_is_available(eth0_node)) { 334*511f6c1aSRoger Quadros of_node_put(eth0_node); 335*511f6c1aSRoger Quadros eth0_node = NULL; 336*511f6c1aSRoger Quadros } 337*511f6c1aSRoger Quadros } else if (reg == 1 && !eth1_node) { 338*511f6c1aSRoger Quadros eth1_node = eth_node; 339*511f6c1aSRoger Quadros if (!of_device_is_available(eth1_node)) { 340*511f6c1aSRoger Quadros of_node_put(eth1_node); 341*511f6c1aSRoger Quadros eth1_node = NULL; 342*511f6c1aSRoger Quadros } 343*511f6c1aSRoger Quadros } else { 344*511f6c1aSRoger Quadros if (reg == 0 || reg == 1) 345*511f6c1aSRoger Quadros dev_err(dev, "duplicate port reg value: %d\n", 346*511f6c1aSRoger Quadros reg); 347*511f6c1aSRoger Quadros else 348*511f6c1aSRoger Quadros dev_err(dev, "invalid port reg value: %d\n", 349*511f6c1aSRoger Quadros reg); 350*511f6c1aSRoger Quadros 351*511f6c1aSRoger Quadros of_node_put(eth_node); 352*511f6c1aSRoger Quadros } 353*511f6c1aSRoger Quadros } 354*511f6c1aSRoger Quadros 355*511f6c1aSRoger Quadros of_node_put(eth_ports_node); 356*511f6c1aSRoger Quadros 357*511f6c1aSRoger Quadros /* At least one node must be present and available else we fail */ 358*511f6c1aSRoger Quadros if (!eth0_node && !eth1_node) { 359*511f6c1aSRoger Quadros dev_err(dev, "neither port0 nor port1 node available\n"); 360*511f6c1aSRoger Quadros return -ENODEV; 361*511f6c1aSRoger Quadros } 362*511f6c1aSRoger Quadros 363*511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC0] = eth0_node; 364*511f6c1aSRoger Quadros prueth->eth_node[PRUETH_MAC1] = eth1_node; 365*511f6c1aSRoger Quadros 366*511f6c1aSRoger Quadros if (eth0_node) { 367*511f6c1aSRoger Quadros prueth->pru0 = pru_rproc_get(np, 0, &pruss_id0); 368*511f6c1aSRoger Quadros if (IS_ERR(prueth->pru0)) { 369*511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru0); 370*511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU0"); 371*511f6c1aSRoger Quadros goto put_pru; 372*511f6c1aSRoger Quadros } 373*511f6c1aSRoger Quadros } 374*511f6c1aSRoger Quadros 375*511f6c1aSRoger Quadros if (eth1_node) { 376*511f6c1aSRoger Quadros prueth->pru1 = pru_rproc_get(np, 1, &pruss_id1); 377*511f6c1aSRoger Quadros if (IS_ERR(prueth->pru1)) { 378*511f6c1aSRoger Quadros ret = PTR_ERR(prueth->pru1); 379*511f6c1aSRoger Quadros dev_err_probe(dev, ret, "unable to get PRU1"); 380*511f6c1aSRoger Quadros goto put_pru; 381*511f6c1aSRoger Quadros } 382*511f6c1aSRoger Quadros } 383*511f6c1aSRoger Quadros 384*511f6c1aSRoger Quadros /* setup netdev interfaces */ 385*511f6c1aSRoger Quadros if (eth0_node) { 386*511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth0_node); 387*511f6c1aSRoger Quadros if (ret) { 388*511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 389*511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 390*511f6c1aSRoger Quadros eth0_node->name, ret); 391*511f6c1aSRoger Quadros } 392*511f6c1aSRoger Quadros goto put_pru; 393*511f6c1aSRoger Quadros } 394*511f6c1aSRoger Quadros } 395*511f6c1aSRoger Quadros 396*511f6c1aSRoger Quadros if (eth1_node) { 397*511f6c1aSRoger Quadros ret = icssm_prueth_netdev_init(prueth, eth1_node); 398*511f6c1aSRoger Quadros if (ret) { 399*511f6c1aSRoger Quadros if (ret != -EPROBE_DEFER) { 400*511f6c1aSRoger Quadros dev_err(dev, "netdev init %s failed: %d\n", 401*511f6c1aSRoger Quadros eth1_node->name, ret); 402*511f6c1aSRoger Quadros } 403*511f6c1aSRoger Quadros goto netdev_exit; 404*511f6c1aSRoger Quadros } 405*511f6c1aSRoger Quadros } 406*511f6c1aSRoger Quadros 407*511f6c1aSRoger Quadros /* register the network devices */ 408*511f6c1aSRoger Quadros if (eth0_node) { 409*511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC0]->ndev); 410*511f6c1aSRoger Quadros if (ret) { 411*511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII0"); 412*511f6c1aSRoger Quadros goto netdev_exit; 413*511f6c1aSRoger Quadros } 414*511f6c1aSRoger Quadros 415*511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC0] = 416*511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC0]->ndev; 417*511f6c1aSRoger Quadros } 418*511f6c1aSRoger Quadros 419*511f6c1aSRoger Quadros if (eth1_node) { 420*511f6c1aSRoger Quadros ret = register_netdev(prueth->emac[PRUETH_MAC1]->ndev); 421*511f6c1aSRoger Quadros if (ret) { 422*511f6c1aSRoger Quadros dev_err(dev, "can't register netdev for port MII1"); 423*511f6c1aSRoger Quadros goto netdev_unregister; 424*511f6c1aSRoger Quadros } 425*511f6c1aSRoger Quadros 426*511f6c1aSRoger Quadros prueth->registered_netdevs[PRUETH_MAC1] = 427*511f6c1aSRoger Quadros prueth->emac[PRUETH_MAC1]->ndev; 428*511f6c1aSRoger Quadros } 429*511f6c1aSRoger Quadros 430*511f6c1aSRoger Quadros if (eth1_node) 431*511f6c1aSRoger Quadros of_node_put(eth1_node); 432*511f6c1aSRoger Quadros if (eth0_node) 433*511f6c1aSRoger Quadros of_node_put(eth0_node); 434*511f6c1aSRoger Quadros return 0; 435*511f6c1aSRoger Quadros 436*511f6c1aSRoger Quadros netdev_unregister: 437*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 438*511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 439*511f6c1aSRoger Quadros continue; 440*511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 441*511f6c1aSRoger Quadros } 442*511f6c1aSRoger Quadros 443*511f6c1aSRoger Quadros netdev_exit: 444*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 445*511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 446*511f6c1aSRoger Quadros if (!eth_node) 447*511f6c1aSRoger Quadros continue; 448*511f6c1aSRoger Quadros 449*511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 450*511f6c1aSRoger Quadros } 451*511f6c1aSRoger Quadros 452*511f6c1aSRoger Quadros put_pru: 453*511f6c1aSRoger Quadros if (eth1_node) { 454*511f6c1aSRoger Quadros if (prueth->pru1) 455*511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 456*511f6c1aSRoger Quadros of_node_put(eth1_node); 457*511f6c1aSRoger Quadros } 458*511f6c1aSRoger Quadros 459*511f6c1aSRoger Quadros if (eth0_node) { 460*511f6c1aSRoger Quadros if (prueth->pru0) 461*511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 462*511f6c1aSRoger Quadros of_node_put(eth0_node); 463*511f6c1aSRoger Quadros } 464*511f6c1aSRoger Quadros 465*511f6c1aSRoger Quadros return ret; 466*511f6c1aSRoger Quadros } 467*511f6c1aSRoger Quadros 468*511f6c1aSRoger Quadros static void icssm_prueth_remove(struct platform_device *pdev) 469*511f6c1aSRoger Quadros { 470*511f6c1aSRoger Quadros struct prueth *prueth = platform_get_drvdata(pdev); 471*511f6c1aSRoger Quadros struct device_node *eth_node; 472*511f6c1aSRoger Quadros int i; 473*511f6c1aSRoger Quadros 474*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 475*511f6c1aSRoger Quadros if (!prueth->registered_netdevs[i]) 476*511f6c1aSRoger Quadros continue; 477*511f6c1aSRoger Quadros unregister_netdev(prueth->registered_netdevs[i]); 478*511f6c1aSRoger Quadros } 479*511f6c1aSRoger Quadros 480*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 481*511f6c1aSRoger Quadros eth_node = prueth->eth_node[i]; 482*511f6c1aSRoger Quadros if (!eth_node) 483*511f6c1aSRoger Quadros continue; 484*511f6c1aSRoger Quadros 485*511f6c1aSRoger Quadros icssm_prueth_netdev_exit(prueth, eth_node); 486*511f6c1aSRoger Quadros of_node_put(eth_node); 487*511f6c1aSRoger Quadros } 488*511f6c1aSRoger Quadros 489*511f6c1aSRoger Quadros pruss_put(prueth->pruss); 490*511f6c1aSRoger Quadros 491*511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC0]) 492*511f6c1aSRoger Quadros pru_rproc_put(prueth->pru0); 493*511f6c1aSRoger Quadros if (prueth->eth_node[PRUETH_MAC1]) 494*511f6c1aSRoger Quadros pru_rproc_put(prueth->pru1); 495*511f6c1aSRoger Quadros } 496*511f6c1aSRoger Quadros 497*511f6c1aSRoger Quadros #ifdef CONFIG_PM_SLEEP 498*511f6c1aSRoger Quadros static int icssm_prueth_suspend(struct device *dev) 499*511f6c1aSRoger Quadros { 500*511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 501*511f6c1aSRoger Quadros struct net_device *ndev; 502*511f6c1aSRoger Quadros int i, ret; 503*511f6c1aSRoger Quadros 504*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 505*511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 506*511f6c1aSRoger Quadros 507*511f6c1aSRoger Quadros if (!ndev) 508*511f6c1aSRoger Quadros continue; 509*511f6c1aSRoger Quadros 510*511f6c1aSRoger Quadros if (netif_running(ndev)) { 511*511f6c1aSRoger Quadros netif_device_detach(ndev); 512*511f6c1aSRoger Quadros ret = icssm_emac_ndo_stop(ndev); 513*511f6c1aSRoger Quadros if (ret < 0) { 514*511f6c1aSRoger Quadros netdev_err(ndev, "failed to stop: %d", ret); 515*511f6c1aSRoger Quadros return ret; 516*511f6c1aSRoger Quadros } 517*511f6c1aSRoger Quadros } 518*511f6c1aSRoger Quadros } 519*511f6c1aSRoger Quadros 520*511f6c1aSRoger Quadros return 0; 521*511f6c1aSRoger Quadros } 522*511f6c1aSRoger Quadros 523*511f6c1aSRoger Quadros static int icssm_prueth_resume(struct device *dev) 524*511f6c1aSRoger Quadros { 525*511f6c1aSRoger Quadros struct prueth *prueth = dev_get_drvdata(dev); 526*511f6c1aSRoger Quadros struct net_device *ndev; 527*511f6c1aSRoger Quadros int i, ret; 528*511f6c1aSRoger Quadros 529*511f6c1aSRoger Quadros for (i = 0; i < PRUETH_NUM_MACS; i++) { 530*511f6c1aSRoger Quadros ndev = prueth->registered_netdevs[i]; 531*511f6c1aSRoger Quadros 532*511f6c1aSRoger Quadros if (!ndev) 533*511f6c1aSRoger Quadros continue; 534*511f6c1aSRoger Quadros 535*511f6c1aSRoger Quadros if (netif_running(ndev)) { 536*511f6c1aSRoger Quadros ret = icssm_emac_ndo_open(ndev); 537*511f6c1aSRoger Quadros if (ret < 0) { 538*511f6c1aSRoger Quadros netdev_err(ndev, "failed to start: %d", ret); 539*511f6c1aSRoger Quadros return ret; 540*511f6c1aSRoger Quadros } 541*511f6c1aSRoger Quadros netif_device_attach(ndev); 542*511f6c1aSRoger Quadros } 543*511f6c1aSRoger Quadros } 544*511f6c1aSRoger Quadros 545*511f6c1aSRoger Quadros return 0; 546*511f6c1aSRoger Quadros } 547*511f6c1aSRoger Quadros 548*511f6c1aSRoger Quadros #endif /* CONFIG_PM_SLEEP */ 549*511f6c1aSRoger Quadros 550*511f6c1aSRoger Quadros static const struct dev_pm_ops prueth_dev_pm_ops = { 551*511f6c1aSRoger Quadros SET_SYSTEM_SLEEP_PM_OPS(icssm_prueth_suspend, icssm_prueth_resume) 552*511f6c1aSRoger Quadros }; 553*511f6c1aSRoger Quadros 554*511f6c1aSRoger Quadros /* AM335x SoC-specific firmware data */ 555*511f6c1aSRoger Quadros static struct prueth_private_data am335x_prueth_pdata = { 556*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 557*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 558*511f6c1aSRoger Quadros "ti-pruss/am335x-pru0-prueth-fw.elf", 559*511f6c1aSRoger Quadros }, 560*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 561*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 562*511f6c1aSRoger Quadros "ti-pruss/am335x-pru1-prueth-fw.elf", 563*511f6c1aSRoger Quadros }, 564*511f6c1aSRoger Quadros }; 565*511f6c1aSRoger Quadros 566*511f6c1aSRoger Quadros /* AM437x SoC-specific firmware data */ 567*511f6c1aSRoger Quadros static struct prueth_private_data am437x_prueth_pdata = { 568*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 569*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 570*511f6c1aSRoger Quadros "ti-pruss/am437x-pru0-prueth-fw.elf", 571*511f6c1aSRoger Quadros }, 572*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 573*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 574*511f6c1aSRoger Quadros "ti-pruss/am437x-pru1-prueth-fw.elf", 575*511f6c1aSRoger Quadros }, 576*511f6c1aSRoger Quadros }; 577*511f6c1aSRoger Quadros 578*511f6c1aSRoger Quadros /* AM57xx SoC-specific firmware data */ 579*511f6c1aSRoger Quadros static struct prueth_private_data am57xx_prueth_pdata = { 580*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU0] = { 581*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 582*511f6c1aSRoger Quadros "ti-pruss/am57xx-pru0-prueth-fw.elf", 583*511f6c1aSRoger Quadros }, 584*511f6c1aSRoger Quadros .fw_pru[PRUSS_PRU1] = { 585*511f6c1aSRoger Quadros .fw_name[PRUSS_ETHTYPE_EMAC] = 586*511f6c1aSRoger Quadros "ti-pruss/am57xx-pru1-prueth-fw.elf", 587*511f6c1aSRoger Quadros }, 588*511f6c1aSRoger Quadros }; 589*511f6c1aSRoger Quadros 590*511f6c1aSRoger Quadros static const struct of_device_id prueth_dt_match[] = { 591*511f6c1aSRoger Quadros { .compatible = "ti,am57-prueth", .data = &am57xx_prueth_pdata, }, 592*511f6c1aSRoger Quadros { .compatible = "ti,am4376-prueth", .data = &am437x_prueth_pdata, }, 593*511f6c1aSRoger Quadros { .compatible = "ti,am3359-prueth", .data = &am335x_prueth_pdata, }, 594*511f6c1aSRoger Quadros { /* sentinel */ } 595*511f6c1aSRoger Quadros }; 596*511f6c1aSRoger Quadros MODULE_DEVICE_TABLE(of, prueth_dt_match); 597*511f6c1aSRoger Quadros 598*511f6c1aSRoger Quadros static struct platform_driver prueth_driver = { 599*511f6c1aSRoger Quadros .probe = icssm_prueth_probe, 600*511f6c1aSRoger Quadros .remove = icssm_prueth_remove, 601*511f6c1aSRoger Quadros .driver = { 602*511f6c1aSRoger Quadros .name = "prueth", 603*511f6c1aSRoger Quadros .of_match_table = prueth_dt_match, 604*511f6c1aSRoger Quadros .pm = &prueth_dev_pm_ops, 605*511f6c1aSRoger Quadros }, 606*511f6c1aSRoger Quadros }; 607*511f6c1aSRoger Quadros module_platform_driver(prueth_driver); 608*511f6c1aSRoger Quadros 609*511f6c1aSRoger Quadros MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>"); 610*511f6c1aSRoger Quadros MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>"); 611*511f6c1aSRoger Quadros MODULE_DESCRIPTION("PRUSS ICSSM Ethernet Driver"); 612*511f6c1aSRoger Quadros MODULE_LICENSE("GPL"); 613