ethernet-mdio.c (4119B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * This file is based on code from OCTEON SDK by Cavium Networks. 4 * 5 * Copyright (c) 2003-2007 Cavium Networks 6 */ 7 8#include <linux/kernel.h> 9#include <linux/ethtool.h> 10#include <linux/phy.h> 11#include <linux/ratelimit.h> 12#include <linux/of_mdio.h> 13#include <generated/utsrelease.h> 14#include <net/dst.h> 15 16#include "octeon-ethernet.h" 17#include "ethernet-defines.h" 18#include "ethernet-mdio.h" 19#include "ethernet-util.h" 20 21static void cvm_oct_get_drvinfo(struct net_device *dev, 22 struct ethtool_drvinfo *info) 23{ 24 strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); 25 strscpy(info->version, UTS_RELEASE, sizeof(info->version)); 26 strscpy(info->bus_info, "Builtin", sizeof(info->bus_info)); 27} 28 29static int cvm_oct_nway_reset(struct net_device *dev) 30{ 31 if (!capable(CAP_NET_ADMIN)) 32 return -EPERM; 33 34 if (dev->phydev) 35 return phy_start_aneg(dev->phydev); 36 37 return -EINVAL; 38} 39 40const struct ethtool_ops cvm_oct_ethtool_ops = { 41 .get_drvinfo = cvm_oct_get_drvinfo, 42 .nway_reset = cvm_oct_nway_reset, 43 .get_link = ethtool_op_get_link, 44 .get_link_ksettings = phy_ethtool_get_link_ksettings, 45 .set_link_ksettings = phy_ethtool_set_link_ksettings, 46}; 47 48/** 49 * cvm_oct_ioctl - IOCTL support for PHY control 50 * @dev: Device to change 51 * @rq: the request 52 * @cmd: the command 53 * 54 * Returns Zero on success 55 */ 56int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 57{ 58 if (!netif_running(dev)) 59 return -EINVAL; 60 61 if (!dev->phydev) 62 return -EINVAL; 63 64 return phy_mii_ioctl(dev->phydev, rq, cmd); 65} 66 67void cvm_oct_note_carrier(struct octeon_ethernet *priv, 68 union cvmx_helper_link_info li) 69{ 70 if (li.s.link_up) { 71 pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d, queue %d\n", 72 netdev_name(priv->netdev), li.s.speed, 73 (li.s.full_duplex) ? "Full" : "Half", 74 priv->port, priv->queue); 75 } else { 76 pr_notice_ratelimited("%s: Link down\n", 77 netdev_name(priv->netdev)); 78 } 79} 80 81void cvm_oct_adjust_link(struct net_device *dev) 82{ 83 struct octeon_ethernet *priv = netdev_priv(dev); 84 union cvmx_helper_link_info link_info; 85 86 link_info.u64 = 0; 87 link_info.s.link_up = dev->phydev->link ? 1 : 0; 88 link_info.s.full_duplex = dev->phydev->duplex ? 1 : 0; 89 link_info.s.speed = dev->phydev->speed; 90 priv->link_info = link_info.u64; 91 92 /* 93 * The polling task need to know about link status changes. 94 */ 95 if (priv->poll) 96 priv->poll(dev); 97 98 if (priv->last_link != dev->phydev->link) { 99 priv->last_link = dev->phydev->link; 100 cvmx_helper_link_set(priv->port, link_info); 101 cvm_oct_note_carrier(priv, link_info); 102 } 103} 104 105int cvm_oct_common_stop(struct net_device *dev) 106{ 107 struct octeon_ethernet *priv = netdev_priv(dev); 108 int interface = INTERFACE(priv->port); 109 union cvmx_helper_link_info link_info; 110 union cvmx_gmxx_prtx_cfg gmx_cfg; 111 int index = INDEX(priv->port); 112 113 gmx_cfg.u64 = cvmx_read_csr(CVMX_GMXX_PRTX_CFG(index, interface)); 114 gmx_cfg.s.en = 0; 115 cvmx_write_csr(CVMX_GMXX_PRTX_CFG(index, interface), gmx_cfg.u64); 116 117 priv->poll = NULL; 118 119 if (dev->phydev) 120 phy_disconnect(dev->phydev); 121 122 if (priv->last_link) { 123 link_info.u64 = 0; 124 priv->last_link = 0; 125 126 cvmx_helper_link_set(priv->port, link_info); 127 cvm_oct_note_carrier(priv, link_info); 128 } 129 return 0; 130} 131 132/** 133 * cvm_oct_phy_setup_device - setup the PHY 134 * 135 * @dev: Device to setup 136 * 137 * Returns Zero on success, negative on failure 138 */ 139int cvm_oct_phy_setup_device(struct net_device *dev) 140{ 141 struct octeon_ethernet *priv = netdev_priv(dev); 142 struct device_node *phy_node; 143 struct phy_device *phydev = NULL; 144 145 if (!priv->of_node) 146 goto no_phy; 147 148 phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); 149 if (!phy_node && of_phy_is_fixed_link(priv->of_node)) 150 phy_node = of_node_get(priv->of_node); 151 if (!phy_node) 152 goto no_phy; 153 154 phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, 155 priv->phy_mode); 156 of_node_put(phy_node); 157 158 if (!phydev) 159 return -EPROBE_DEFER; 160 161 priv->last_link = 0; 162 phy_start(phydev); 163 164 return 0; 165no_phy: 166 /* If there is no phy, assume a direct MAC connection and that 167 * the link is up. 168 */ 169 netif_carrier_on(dev); 170 return 0; 171}