Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f6ed1b3b authored by David Daney's avatar David Daney Committed by Ralf Baechle
Browse files

Staging: octeon-ethernet: Convert to use PHY Abstraction Layer.



The octeon-ethernet driver shares an mdio bus with the octeon-mgmt
driver.  Here we convert the octeon-ethernet driver to use the PHY
Abstraction Layer.

Signed-off-by: default avatarDavid Daney <ddaney@caviumnetworks.com>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent d6aa60a1
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
config OCTEON_ETHERNET
	tristate "Cavium Networks Octeon Ethernet support"
	depends on CPU_CAVIUM_OCTEON
	select MII
	select PHYLIB
	select MDIO_OCTEON
	help
	  This driver supports the builtin ethernet ports on Cavium
	  Networks' products in the Octeon family. This driver supports the
+78 −126
Original line number Diff line number Diff line
@@ -26,7 +26,8 @@
**********************************************************************/
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>

#include <net/dst.h>

#include <asm/octeon/octeon.h>
@@ -34,86 +35,12 @@
#include "ethernet-defines.h"
#include "octeon-ethernet.h"
#include "ethernet-mdio.h"
#include "ethernet-util.h"

#include "cvmx-helper-board.h"

#include "cvmx-smix-defs.h"

DECLARE_MUTEX(mdio_sem);

/**
 * Perform an MII read. Called by the generic MII routines
 *
 * @dev:      Device to perform read for
 * @phy_id:   The MII phy id
 * @location: Register location to read
 * Returns Result from the read or zero on failure
 */
static int cvm_oct_mdio_read(struct net_device *dev, int phy_id, int location)
{
	union cvmx_smix_cmd smi_cmd;
	union cvmx_smix_rd_dat smi_rd;

	smi_cmd.u64 = 0;
	smi_cmd.s.phy_op = 1;
	smi_cmd.s.phy_adr = phy_id;
	smi_cmd.s.reg_adr = location;
	cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);

	do {
		if (!in_interrupt())
			yield();
		smi_rd.u64 = cvmx_read_csr(CVMX_SMIX_RD_DAT(0));
	} while (smi_rd.s.pending);

	if (smi_rd.s.val)
		return smi_rd.s.dat;
	else
		return 0;
}

static int cvm_oct_mdio_dummy_read(struct net_device *dev, int phy_id,
				   int location)
{
	return 0xffff;
}

/**
 * Perform an MII write. Called by the generic MII routines
 *
 * @dev:      Device to perform write for
 * @phy_id:   The MII phy id
 * @location: Register location to write
 * @val:      Value to write
 */
static void cvm_oct_mdio_write(struct net_device *dev, int phy_id, int location,
			       int val)
{
	union cvmx_smix_cmd smi_cmd;
	union cvmx_smix_wr_dat smi_wr;

	smi_wr.u64 = 0;
	smi_wr.s.dat = val;
	cvmx_write_csr(CVMX_SMIX_WR_DAT(0), smi_wr.u64);

	smi_cmd.u64 = 0;
	smi_cmd.s.phy_op = 0;
	smi_cmd.s.phy_adr = phy_id;
	smi_cmd.s.reg_adr = location;
	cvmx_write_csr(CVMX_SMIX_CMD(0), smi_cmd.u64);

	do {
		if (!in_interrupt())
			yield();
		smi_wr.u64 = cvmx_read_csr(CVMX_SMIX_WR_DAT(0));
	} while (smi_wr.s.pending);
}

static void cvm_oct_mdio_dummy_write(struct net_device *dev, int phy_id,
				     int location, int val)
{
}

static void cvm_oct_get_drvinfo(struct net_device *dev,
				struct ethtool_drvinfo *info)
{
@@ -125,49 +52,37 @@ static void cvm_oct_get_drvinfo(struct net_device *dev,
static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	int ret;

	down(&mdio_sem);
	ret = mii_ethtool_gset(&priv->mii_info, cmd);
	up(&mdio_sem);
	if (priv->phydev)
		return phy_ethtool_gset(priv->phydev, cmd);

	return ret;
	return -EINVAL;
}

static int cvm_oct_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	int ret;

	down(&mdio_sem);
	ret = mii_ethtool_sset(&priv->mii_info, cmd);
	up(&mdio_sem);
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	return ret;
	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);
	int ret;

	down(&mdio_sem);
	ret = mii_nway_restart(&priv->mii_info);
	up(&mdio_sem);

	return ret;
}

static u32 cvm_oct_get_link(struct net_device *dev)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	u32 ret;
	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	down(&mdio_sem);
	ret = mii_link_ok(&priv->mii_info);
	up(&mdio_sem);
	if (priv->phydev)
		return phy_start_aneg(priv->phydev);

	return ret;
	return -EINVAL;
}

const struct ethtool_ops cvm_oct_ethtool_ops = {
@@ -175,7 +90,7 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
	.get_settings = cvm_oct_get_settings,
	.set_settings = cvm_oct_set_settings,
	.nway_reset = cvm_oct_nway_reset,
	.get_link = cvm_oct_get_link,
	.get_link = ethtool_op_get_link,
	.get_sg = ethtool_op_get_sg,
	.get_tx_csum = ethtool_op_get_tx_csum,
};
@@ -191,41 +106,78 @@ const struct ethtool_ops cvm_oct_ethtool_ops = {
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	struct mii_ioctl_data *data = if_mii(rq);
	unsigned int duplex_chg;
	int ret;

	down(&mdio_sem);
	ret = generic_mii_ioctl(&priv->mii_info, data, cmd, &duplex_chg);
	up(&mdio_sem);
	if (!netif_running(dev))
		return -EINVAL;

	return ret;
	if (!priv->phydev)
		return -EINVAL;

	return phy_mii_ioctl(priv->phydev, if_mii(rq), cmd);
}

static 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);
		if (priv->last_link) {
			netif_carrier_on(dev);
			if (priv->queue != -1)
				DEBUGPRINT("%s: %u Mbps %s duplex, "
					   "port %2d, queue %2d\n",
					   dev->name, priv->phydev->speed,
					   priv->phydev->duplex ?
						"Full" : "Half",
					   priv->port, priv->queue);
			else
				DEBUGPRINT("%s: %u Mbps %s duplex, "
					   "port %2d, POW\n",
					   dev->name, priv->phydev->speed,
					   priv->phydev->duplex ?
						"Full" : "Half",
					   priv->port);
		} else {
			netif_carrier_off(dev);
			DEBUGPRINT("%s: Link down\n", dev->name);
		}
	}
}


/**
 * Setup the MDIO device structures
 * Setup the PHY
 *
 * @dev:    Device to setup
 *
 * Returns Zero on success, negative on failure
 */
int cvm_oct_mdio_setup_device(struct net_device *dev)
int cvm_oct_phy_setup_device(struct net_device *dev)
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	int phy_id = cvmx_helper_board_get_mii_address(priv->port);
	if (phy_id != -1) {
		priv->mii_info.dev = dev;
		priv->mii_info.phy_id = phy_id;
		priv->mii_info.phy_id_mask = 0xff;
		priv->mii_info.supports_gmii = 1;
		priv->mii_info.reg_num_mask = 0x1f;
		priv->mii_info.mdio_read = cvm_oct_mdio_read;
		priv->mii_info.mdio_write = cvm_oct_mdio_write;
	} else {
		/* Supply dummy MDIO routines so the kernel won't crash
		   if the user tries to read them */
		priv->mii_info.mdio_read = cvm_oct_mdio_dummy_read;
		priv->mii_info.mdio_write = cvm_oct_mdio_dummy_write;

	int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
	if (phy_addr != -1) {
		char phy_id[20];

		snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr);

		priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
					PHY_INTERFACE_MODE_GMII);

		if (IS_ERR(priv->phydev)) {
			priv->phydev = NULL;
			return -1;
		}
		priv->last_link = 0;
		phy_start_aneg(priv->phydev);
	}
	return 0;
}
+1 −1
Original line number Diff line number Diff line
@@ -43,4 +43,4 @@

extern const struct ethtool_ops cvm_oct_ethtool_ops;
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
int cvm_oct_mdio_setup_device(struct net_device *dev);
int cvm_oct_phy_setup_device(struct net_device *dev);
+0 −112
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@
 * Contact Cavium Networks for more information
**********************************************************************/
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <net/dst.h>
@@ -38,112 +37,6 @@
#include "cvmx-helper.h"
#include "cvmx-pip.h"

static unsigned long long cvm_oct_stats_read_switch(struct net_device *dev,
						    int phy_id, int offset)
{
	struct octeon_ethernet *priv = netdev_priv(dev);

	priv->mii_info.mdio_write(dev, phy_id, 0x1d, 0xcc00 | offset);
	return ((uint64_t) priv->mii_info.
		mdio_read(dev, phy_id,
			  0x1e) << 16) | (uint64_t) priv->mii_info.
	    mdio_read(dev, phy_id, 0x1f);
}

static int cvm_oct_stats_switch_show(struct seq_file *m, void *v)
{
	static const int ports[] = { 0, 1, 2, 3, 9, -1 };
	struct net_device *dev = cvm_oct_device[0];
	int index = 0;

	while (ports[index] != -1) {

		/* Latch port */
		struct octeon_ethernet *priv = netdev_priv(dev);

		priv->mii_info.mdio_write(dev, 0x1b, 0x1d,
					  0xdc00 | ports[index]);
		seq_printf(m, "\nSwitch Port %d\n", ports[index]);
		seq_printf(m, "InGoodOctets:   %12llu\t"
			   "OutOctets:      %12llu\t"
			   "64 Octets:      %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b,
						     0x00) |
			   (cvm_oct_stats_read_switch(dev, 0x1b, 0x01) << 32),
			   cvm_oct_stats_read_switch(dev, 0x1b,
						     0x0E) |
			   (cvm_oct_stats_read_switch(dev, 0x1b, 0x0F) << 32),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x08));

		seq_printf(m, "InBadOctets:    %12llu\t"
			   "OutUnicast:     %12llu\t"
			   "65-127 Octets:  %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x02),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x10),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x09));

		seq_printf(m, "InUnicast:      %12llu\t"
			   "OutBroadcasts:  %12llu\t"
			   "128-255 Octets: %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x04),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x13),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x0A));

		seq_printf(m, "InBroadcasts:   %12llu\t"
			   "OutMulticasts:  %12llu\t"
			   "256-511 Octets: %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x06),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x12),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x0B));

		seq_printf(m, "InMulticasts:   %12llu\t"
			   "OutPause:       %12llu\t"
			   "512-1023 Octets:%12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x07),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x15),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x0C));

		seq_printf(m, "InPause:        %12llu\t"
			   "Excessive:      %12llu\t"
			   "1024-Max Octets:%12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x16),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x11),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x0D));

		seq_printf(m, "InUndersize:    %12llu\t"
			   "Collisions:     %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x18),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1E));

		seq_printf(m, "InFragments:    %12llu\t"
			   "Deferred:       %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x19),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x05));

		seq_printf(m, "InOversize:     %12llu\t"
			   "Single:         %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1A),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x14));

		seq_printf(m, "InJabber:       %12llu\t"
			   "Multiple:       %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1B),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x17));

		seq_printf(m, "In RxErr:       %12llu\t"
			   "OutFCSErr:      %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1C),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x03));

		seq_printf(m, "InFCSErr:       %12llu\t"
			   "Late:           %12llu\n",
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1D),
			   cvm_oct_stats_read_switch(dev, 0x1b, 0x1F));
		index++;
	}
	return 0;
}

/**
 * User is reading /proc/octeon_ethernet_stats
 *
@@ -215,11 +108,6 @@ static int cvm_oct_stats_show(struct seq_file *m, void *v)
		}
	}

	if (cvm_oct_device[0]) {
		priv = netdev_priv(cvm_oct_device[0]);
		if (priv->imode == CVMX_HELPER_INTERFACE_MODE_GMII)
			cvm_oct_stats_switch_show(m, v);
	}
	return 0;
}

+28 −24
Original line number Diff line number Diff line
@@ -147,34 +147,38 @@ static void cvm_oct_rgmii_poll(struct net_device *dev)
		cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
			       gmxx_rxx_int_reg.u64);
	}

	if (priv->phydev == NULL) {
		link_info = cvmx_helper_link_autoconf(priv->port);
		priv->link_info = link_info.u64;
	}
	spin_unlock_irqrestore(&global_register_lock, flags);

	/* Tell Linux */
	if (priv->phydev == NULL) {
		/* Tell core. */
		if (link_info.s.link_up) {

			if (!netif_carrier_ok(dev))
				netif_carrier_on(dev);
			if (priv->queue != -1)
			DEBUGPRINT
			    ("%s: %u Mbps %s duplex, port %2d, queue %2d\n",
				DEBUGPRINT("%s: %u Mbps %s duplex, "
					   "port %2d, queue %2d\n",
					   dev->name, link_info.s.speed,
			     (link_info.s.full_duplex) ? "Full" : "Half",
					   (link_info.s.full_duplex) ?
						"Full" : "Half",
					   priv->port, priv->queue);
			else
			DEBUGPRINT("%s: %u Mbps %s duplex, port %2d, POW\n",
				DEBUGPRINT("%s: %u Mbps %s duplex, "
					   "port %2d, POW\n",
					   dev->name, link_info.s.speed,
				   (link_info.s.full_duplex) ? "Full" : "Half",
					   (link_info.s.full_duplex) ?
						"Full" : "Half",
					   priv->port);
		} else {

			if (netif_carrier_ok(dev))
				netif_carrier_off(dev);
			DEBUGPRINT("%s: Link down\n", dev->name);
		}
	}
}

static irqreturn_t cvm_oct_rgmii_rml_interrupt(int cpl, void *dev_id)
{
Loading