X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=kernel%2Fdrivers%2Fstaging%2Focteon%2Fethernet-mdio.c;fp=kernel%2Fdrivers%2Fstaging%2Focteon%2Fethernet-mdio.c;h=40dab11e5333829092d32e5988ccae3d686ac5e3;hb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;hp=0000000000000000000000000000000000000000;hpb=98260f3884f4a202f9ca5eabed40b1354c489b29;p=kvmfornfv.git diff --git a/kernel/drivers/staging/octeon/ethernet-mdio.c b/kernel/drivers/staging/octeon/ethernet-mdio.c new file mode 100644 index 000000000..40dab11e5 --- /dev/null +++ b/kernel/drivers/staging/octeon/ethernet-mdio.c @@ -0,0 +1,206 @@ +/********************************************************************** + * Author: Cavium Networks + * + * Contact: support@caviumnetworks.com + * This file is part of the OCTEON SDK + * + * Copyright (c) 2003-2007 Cavium Networks + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, but + * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * or visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium Networks for more information +**********************************************************************/ +#include +#include +#include +#include +#include + +#include + +#include + +#include "ethernet-defines.h" +#include "octeon-ethernet.h" +#include "ethernet-mdio.h" +#include "ethernet-util.h" + +#include + +#include + +static void cvm_oct_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, "cavium-ethernet", sizeof(info->driver)); + strlcpy(info->version, OCTEON_ETHERNET_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, "Builtin", sizeof(info->bus_info)); +} + +static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + + if (priv->phydev) + return phy_ethtool_gset(priv->phydev, cmd); + + return -EINVAL; +} + +static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (priv->phydev) + return phy_ethtool_sset(priv->phydev, cmd); + + return -EINVAL; +} + +static int cvm_oct_nway_reset(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (priv->phydev) + return phy_start_aneg(priv->phydev); + + return -EINVAL; +} + +const struct ethtool_ops cvm_oct_ethtool_ops = { + .get_drvinfo = cvm_oct_get_drvinfo, + .get_settings = cvm_oct_get_settings, + .set_settings = cvm_oct_set_settings, + .nway_reset = cvm_oct_nway_reset, + .get_link = ethtool_op_get_link, +}; + +/** + * cvm_oct_ioctl - IOCTL support for PHY control + * @dev: Device to change + * @rq: the request + * @cmd: the command + * + * Returns Zero on success + */ +int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + + if (!netif_running(dev)) + return -EINVAL; + + if (!priv->phydev) + return -EINVAL; + + return phy_mii_ioctl(priv->phydev, rq, cmd); +} + +static void cvm_oct_note_carrier(struct octeon_ethernet *priv, + cvmx_helper_link_info_t li) +{ + if (li.s.link_up) { + pr_notice_ratelimited("%s: %u Mbps %s duplex, port %d\n", + netdev_name(priv->netdev), li.s.speed, + (li.s.full_duplex) ? "Full" : "Half", + priv->port); + } else { + pr_notice_ratelimited("%s: Link down\n", + netdev_name(priv->netdev)); + } +} + +void cvm_oct_adjust_link(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + cvmx_helper_link_info_t link_info; + + if (priv->last_link != priv->phydev->link) { + priv->last_link = priv->phydev->link; + link_info.u64 = 0; + link_info.s.link_up = priv->last_link ? 1 : 0; + link_info.s.full_duplex = priv->phydev->duplex ? 1 : 0; + link_info.s.speed = priv->phydev->speed; + + cvmx_helper_link_set(priv->port, link_info); + cvm_oct_note_carrier(priv, link_info); + } +} + +int cvm_oct_common_stop(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + cvmx_helper_link_info_t link_info; + + priv->poll = NULL; + + if (priv->phydev) + phy_disconnect(priv->phydev); + priv->phydev = NULL; + + if (priv->last_link) { + link_info.u64 = 0; + priv->last_link = 0; + + cvmx_helper_link_set(priv->port, link_info); + cvm_oct_note_carrier(priv, link_info); + } + return 0; +} + +/** + * cvm_oct_phy_setup_device - setup the PHY + * + * @dev: Device to setup + * + * Returns Zero on success, negative on failure + */ +int cvm_oct_phy_setup_device(struct net_device *dev) +{ + struct octeon_ethernet *priv = netdev_priv(dev); + struct device_node *phy_node; + + if (!priv->of_node) + goto no_phy; + + phy_node = of_parse_phandle(priv->of_node, "phy-handle", 0); + if (!phy_node) + goto no_phy; + + priv->phydev = of_phy_connect(dev, phy_node, cvm_oct_adjust_link, 0, + PHY_INTERFACE_MODE_GMII); + + if (priv->phydev == NULL) + return -ENODEV; + + priv->last_link = 0; + phy_start_aneg(priv->phydev); + + return 0; +no_phy: + /* If there is no phy, assume a direct MAC connection and that + * the link is up. + */ + netif_carrier_on(dev); + return 0; +}