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 31#include <net/dst.h> 32 33#include <asm/octeon/octeon.h> 34 35#include "ethernet-defines.h" 36#include "octeon-ethernet.h" 37#include "ethernet-mdio.h" 38#include "ethernet-util.h" 39 40#include "cvmx-helper-board.h" 41 42#include "cvmx-smix-defs.h" 43 44static void cvm_oct_get_drvinfo(struct net_device *dev, 45 struct ethtool_drvinfo *info) 46{ 47 strcpy(info->driver, "cavium-ethernet"); 48 strcpy(info->version, OCTEON_ETHERNET_VERSION); 49 strcpy(info->bus_info, "Builtin"); 50} 51 52static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) 53{ 54 struct octeon_ethernet *priv = netdev_priv(dev); 55 56 if (priv->phydev) 57 return phy_ethtool_gset(priv->phydev, cmd); 58 59 return -EINVAL; 60} 61 62static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) 63{ 64 struct octeon_ethernet *priv = netdev_priv(dev); 65 66 if (!capable(CAP_NET_ADMIN)) 67 return -EPERM; 68 69 if (priv->phydev) 70 return phy_ethtool_sset(priv->phydev, cmd); 71 72 return -EINVAL; 73} 74 75static int cvm_oct_nway_reset(struct net_device *dev) 76{ 77 struct octeon_ethernet *priv = netdev_priv(dev); 78 79 if (!capable(CAP_NET_ADMIN)) 80 return -EPERM; 81 82 if (priv->phydev) 83 return phy_start_aneg(priv->phydev); 84 85 return -EINVAL; 86} 87 88const struct ethtool_ops cvm_oct_ethtool_ops = { 89 .get_drvinfo = cvm_oct_get_drvinfo, 90 .get_settings = cvm_oct_get_settings, 91 .set_settings = cvm_oct_set_settings, 92 .nway_reset = cvm_oct_nway_reset, 93 .get_link = ethtool_op_get_link, 94 .get_sg = ethtool_op_get_sg, 95 .get_tx_csum = ethtool_op_get_tx_csum, 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_adjust_link(struct net_device *dev) 120{ 121 struct octeon_ethernet *priv = netdev_priv(dev); 122 cvmx_helper_link_info_t link_info; 123 124 if (priv->last_link != priv->phydev->link) { 125 priv->last_link = priv->phydev->link; 126 link_info.u64 = 0; 127 link_info.s.link_up = priv->last_link ? 1 : 0; 128 link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0; 129 link_info.s.speed = priv->phydev->speed; 130 cvmx_helper_link_set( priv->port, link_info); 131 if (priv->last_link) { 132 netif_carrier_on(dev); 133 if (priv->queue != -1) 134 DEBUGPRINT("%s: %u Mbps %s duplex, " 135 "port %2d, queue %2d\n", 136 dev->name, priv->phydev->speed, 137 priv->phydev->duplex ? 138 "Full" : "Half", 139 priv->port, priv->queue); 140 else 141 DEBUGPRINT("%s: %u Mbps %s duplex, " 142 "port %2d, POW\n", 143 dev->name, priv->phydev->speed, 144 priv->phydev->duplex ? 145 "Full" : "Half", 146 priv->port); 147 } else { 148 netif_carrier_off(dev); 149 DEBUGPRINT("%s: Link down\n", dev->name); 150 } 151 } 152} 153 154 155/** 156 * cvm_oct_phy_setup_device - setup the PHY 157 * 158 * @dev: Device to setup 159 * 160 * Returns Zero on success, negative on failure 161 */ 162int cvm_oct_phy_setup_device(struct net_device *dev) 163{ 164 struct octeon_ethernet *priv = netdev_priv(dev); 165 166 int phy_addr = cvmx_helper_board_get_mii_address(priv->port); 167 if (phy_addr != -1) { 168 char phy_id[20]; 169 170 snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr); 171 172 priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0, 173 PHY_INTERFACE_MODE_GMII); 174 175 if (IS_ERR(priv->phydev)) { 176 priv->phydev = NULL; 177 return -1; 178 } 179 priv->last_link = 0; 180 phy_start_aneg(priv->phydev); 181 } 182 return 0; 183} 184