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 Original line Diff line number Diff line
config OCTEON_ETHERNET
config OCTEON_ETHERNET
	tristate "Cavium Networks Octeon Ethernet support"
	tristate "Cavium Networks Octeon Ethernet support"
	depends on CPU_CAVIUM_OCTEON
	depends on CPU_CAVIUM_OCTEON
	select MII
	select PHYLIB
	select MDIO_OCTEON
	help
	help
	  This driver supports the builtin ethernet ports on Cavium
	  This driver supports the builtin ethernet ports on Cavium
	  Networks' products in the Octeon family. This driver supports the
	  Networks' products in the Octeon family. This driver supports the
+78 −126
Original line number Original line Diff line number Diff line
@@ -26,7 +26,8 @@
**********************************************************************/
**********************************************************************/
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/ethtool.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>

#include <net/dst.h>
#include <net/dst.h>


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


#include "cvmx-helper-board.h"
#include "cvmx-helper-board.h"


#include "cvmx-smix-defs.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,
static void cvm_oct_get_drvinfo(struct net_device *dev,
				struct ethtool_drvinfo *info)
				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)
static int cvm_oct_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	struct octeon_ethernet *priv = netdev_priv(dev);
	int ret;


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


	return ret;
	return -EINVAL;
}
}


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


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


	return ret;
	if (priv->phydev)
		return phy_ethtool_sset(priv->phydev, cmd);

	return -EINVAL;
}
}


static int cvm_oct_nway_reset(struct net_device *dev)
static int cvm_oct_nway_reset(struct net_device *dev)
{
{
	struct octeon_ethernet *priv = netdev_priv(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)
	if (!capable(CAP_NET_ADMIN))
{
		return -EPERM;
	struct octeon_ethernet *priv = netdev_priv(dev);
	u32 ret;


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


	return ret;
	return -EINVAL;
}
}


const struct ethtool_ops cvm_oct_ethtool_ops = {
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,
	.get_settings = cvm_oct_get_settings,
	.set_settings = cvm_oct_set_settings,
	.set_settings = cvm_oct_set_settings,
	.nway_reset = cvm_oct_nway_reset,
	.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_sg = ethtool_op_get_sg,
	.get_tx_csum = ethtool_op_get_tx_csum,
	.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)
int cvm_oct_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
{
	struct octeon_ethernet *priv = netdev_priv(dev);
	struct octeon_ethernet *priv = netdev_priv(dev);
	struct mii_ioctl_data *data = if_mii(rq);
	unsigned int duplex_chg;
	int ret;


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


	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
 * @dev:    Device to setup
 *
 *
 * Returns Zero on success, negative on failure
 * 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);
	struct octeon_ethernet *priv = netdev_priv(dev);
	int phy_id = cvmx_helper_board_get_mii_address(priv->port);

	if (phy_id != -1) {
	int phy_addr = cvmx_helper_board_get_mii_address(priv->port);
		priv->mii_info.dev = dev;
	if (phy_addr != -1) {
		priv->mii_info.phy_id = phy_id;
		char phy_id[20];
		priv->mii_info.phy_id_mask = 0xff;

		priv->mii_info.supports_gmii = 1;
		snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT, "0", phy_addr);
		priv->mii_info.reg_num_mask = 0x1f;

		priv->mii_info.mdio_read = cvm_oct_mdio_read;
		priv->phydev = phy_connect(dev, phy_id, cvm_oct_adjust_link, 0,
		priv->mii_info.mdio_write = cvm_oct_mdio_write;
					PHY_INTERFACE_MODE_GMII);
	} else {

		/* Supply dummy MDIO routines so the kernel won't crash
		if (IS_ERR(priv->phydev)) {
		   if the user tries to read them */
			priv->phydev = NULL;
		priv->mii_info.mdio_read = cvm_oct_mdio_dummy_read;
			return -1;
		priv->mii_info.mdio_write = cvm_oct_mdio_dummy_write;
		}
		priv->last_link = 0;
		phy_start_aneg(priv->phydev);
	}
	}
	return 0;
	return 0;
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -43,4 +43,4 @@


extern const struct ethtool_ops cvm_oct_ethtool_ops;
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_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 Original line Diff line number Diff line
@@ -25,7 +25,6 @@
 * Contact Cavium Networks for more information
 * Contact Cavium Networks for more information
**********************************************************************/
**********************************************************************/
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/seq_file.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/proc_fs.h>
#include <net/dst.h>
#include <net/dst.h>
@@ -38,112 +37,6 @@
#include "cvmx-helper.h"
#include "cvmx-helper.h"
#include "cvmx-pip.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
 * 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;
	return 0;
}
}


+28 −24
Original line number Original line 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),
		cvmx_write_csr(CVMX_GMXX_RXX_INT_REG(index, interface),
			       gmxx_rxx_int_reg.u64);
			       gmxx_rxx_int_reg.u64);
	}
	}

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


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

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

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


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