1/********************************************************************** 2 * Author: Cavium Networks 3 * 4 * Contact: support@caviumnetworks.com 5 * This file is part of the OCTEON SDK 6 * 7 * Copyright (c) 2003-2007 Cavium Networks 8 * 9 * This file is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License, Version 2, as 11 * published by the Free Software Foundation. 12 * 13 * This file is distributed in the hope that it will be useful, but 14 * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty 15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or 16 * NONINFRINGEMENT. See the GNU General Public License for more 17 * details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this file; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * or visit http://www.gnu.org/licenses/. 23 * 24 * This file may also be available under a different license from Cavium. 25 * Contact Cavium Networks for more information 26**********************************************************************/ 27#include <linux/kernel.h> 28#include <linux/ethtool.h> 29#include <linux/phy.h> 30#include <linux/ratelimit.h> 31#include <linux/of_mdio.h> 32 33#include <net/dst.h> 34 35#include <asm/octeon/octeon.h> 36 37#include "ethernet-defines.h" 38#include "octeon-ethernet.h" 39#include "ethernet-mdio.h" 40#include "ethernet-util.h" 41 42#include <asm/octeon/cvmx-helper-board.h> 43 44#include <asm/octeon/cvmx-smix-defs.h> 45 46static void cvm_oct_get_drvinfo(struct net_device *dev, 47 struct ethtool_drvinfo *info) 48{ 49 strlcpy(info->driver, "cavium-ethernet", sizeof(info->driver)); 50 strlcpy(info->version, OCTEON_ETHERNET_VERSION, sizeof(info->version)); 51 strlcpy(info->bus_info, "Builtin", sizeof(info->bus_info)); 52} 53 54static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 55{ 56 struct octeon_ethernet *priv = netdev_priv(dev); 57 58 if (priv->phydev) 59 return phy_ethtool_gset(priv->phydev, cmd); 60 61 return -EINVAL; 62} 63 64static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) 65{ 66 struct octeon_ethernet *priv = netdev_priv(dev); 67 68 if (!capable(CAP_NET_ADMIN)) 69 return -EPERM; 70 71 if (priv->phydev) 72 return phy_ethtool_sset(priv->phydev, cmd); 73 74 return -EINVAL; 75} 76 77static int cvm_oct_nway_reset(struct net_device *dev) 78{ 79 struct octeon_ethernet *priv = netdev_priv(dev); 80 81 if (!capable(CAP_NET_ADMIN)) 82 return -EPERM; 83 84 if (priv->phydev) 85 return phy_start_aneg(priv->phydev); 86 87 return -EINVAL; 88} 89 90const struct ethtool_ops cvm_oct_ethtool_ops = { 91 .get_drvinfo = cvm_oct_get_drvinfo, 92 .get_settings = cvm_oct_get_settings, 93 .set_settings = cvm_oct_set_settings, 94 .nway_reset = cvm_oct_nway_reset, 95 .get_link = ethtool_op_get_link, 96}; 97 98/** 99 * cvm_oct_ioctl - IOCTL support for PHY control 100 * @dev: Device to change 101 * @rq: the request 102 * @cmd: the command 103 * 104 * Returns Zero on success 105 */ 106int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 107{ 108 struct octeon_ethernet *priv = netdev_priv(dev); 109 110 if (!netif_running(dev)) 111 return -EINVAL; 112 113 if (!priv->phydev) 114 return -EINVAL; 115 116 return phy_mii_ioctl(priv->phydev, rq, cmd); 117} 118 119static void cvm_oct_note_carrier(struct octeon_ethernet *priv, 120 cvmx_helper_link_info_t li) 121{ 122 if (li.s.link_up) { 123 pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d\n", 124 netdev_name(priv->netdev), li.s.speed, 125 (li.s.full_duplex) ? "Full" : "Half", 126 priv->port); 127 } else { 128 pr_notice_ratelimited("%s: Link down\n", 129 netdev_name(priv->netdev)); 130 } 131} 132 133void cvm_oct_adjust_link(struct net_device *dev) 134{ 135 struct octeon_ethernet *priv = netdev_priv(dev); 136 cvmx_helper_link_info_t link_info; 137 138 if (priv->last_link != priv->phydev->link) { 139 priv->last_link = priv->phydev->link; 140 link_info.u64 = 0; 141 link_info.s.link_up = priv->last_link ? 1 : 0; 142 link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0; 143 link_info.s.speed = priv->phydev->speed; 144 145 cvmx_helper_link_set(priv->port, link_info); 146 cvm_oct_note_carrier(priv, link_info); 147 } 148} 149 150int cvm_oct_common_stop(struct net_device *dev) 151{ 152 struct octeon_ethernet *priv = netdev_priv(dev); 153 cvmx_helper_link_info_t link_info; 154 155 priv->poll = NULL; 156 157 if (priv->phydev) 158 phy_disconnect(priv->phydev); 159 priv->phydev = NULL; 160 161 if (priv->last_link) { 162 link_info.u64 = 0; 163 priv->last_link = 0; 164 165 cvmx_helper_link_set(priv->port, link_info); 166 cvm_oct_note_carrier(priv, link_info); 167 } 168 return 0; 169} 170 171/** 172 * cvm_oct_phy_setup_device - setup the PHY 173 * 174 * @dev: Device to setup 175 * 176 * Returns Zero on success, negative on failure 177 */ 178int cvm_oct_phy_setup_device(struct net_device *dev) 179{ 180 struct octeon_ethernet *priv = netdev_priv(dev); 181 struct device_node *phy_node; 182 183 if (!priv->of_node) 184 goto no_phy; 185 186 phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); 187 if (!phy_node) 188 goto no_phy; 189 190 priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, 191 PHY_INTERFACE_MODE_GMII); 192 193 if (priv->phydev == NULL) 194 return -ENODEV; 195 196 priv->last_link = 0; 197 phy_start_aneg(priv->phydev); 198 199 return 0; 200no_phy: 201 /* If there is no phy, assume a direct MAC connection and that 202 * the link is up. 203 */ 204 netif_carrier_on(dev); 205 return 0; 206} 207