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 46 static 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 54 static 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 64 static 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 77 static 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 90 const 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 */ 106 int 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 119 static 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 printk_ratelimited("%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 printk_ratelimited("%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 printk_ratelimited("%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 */ 162 int cvm_oct_phy_setup_device(struct net_device *dev) 163 { 164 struct octeon_ethernet *priv = netdev_priv(dev); 165 struct device_node *phy_node; 166 167 if (!priv->of_node) 168 return 0; 169 170 phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); 171 if (!phy_node) 172 return 0; 173 174 priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, 175 PHY_INTERFACE_MODE_GMII); 176 177 if (priv->phydev == NULL) 178 return -ENODEV; 179 180 priv->last_link = 0; 181 phy_start_aneg(priv->phydev); 182 183 return 0; 184 } 185