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