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

Commit 10096974 authored by Jeff Garzik's avatar Jeff Garzik Committed by David S. Miller
Browse files

[netdrvr] ns83820: add ethtool media support



Split out from patch authored by Dan Faerch <dan@hacker.dk>.

Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent 2808d2e8
Loading
Loading
Loading
Loading
+148 −3
Original line number Diff line number Diff line
#define VERSION "0.22"
#define VERSION "0.23"
/* ns83820.c by Benjamin LaHaise with contributions.
 *
 * Questions/comments/discussion to linux-ns83820@kvack.org.
@@ -1247,6 +1247,149 @@ static struct net_device_stats *ns83820_get_stats(struct net_device *ndev)
	return &dev->stats;
}

/* Let ethtool retrieve info */
static int ns83820_get_settings(struct net_device *ndev,
				struct ethtool_cmd *cmd)
{
	struct ns83820 *dev = PRIV(ndev);
	u32 cfg, tanar, tbicr;
	int have_optical = 0;
	int fullduplex   = 0;

	/*
	 * Here's the list of available ethtool commands from other drivers:
	 *	cmd->advertising =
	 *	cmd->speed =
	 *	cmd->duplex =
	 *	cmd->port = 0;
	 *	cmd->phy_address =
	 *	cmd->transceiver = 0;
	 *	cmd->autoneg =
	 *	cmd->maxtxpkt = 0;
	 *	cmd->maxrxpkt = 0;
	 */

	/* read current configuration */
	cfg   = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
	tanar = readl(dev->base + TANAR);
	tbicr = readl(dev->base + TBICR);

	if (dev->CFG_cache & CFG_TBI_EN) {
		/* we have an optical interface */
		have_optical = 1;
		fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;

	} else {
		/* We have copper */
		fullduplex = (cfg & CFG_DUPSTS) ? 1 : 0;
        }

	cmd->supported = SUPPORTED_Autoneg;

	/* we have optical interface */
	if (dev->CFG_cache & CFG_TBI_EN) {
		cmd->supported |= SUPPORTED_1000baseT_Half |
					SUPPORTED_1000baseT_Full |
					SUPPORTED_FIBRE;
		cmd->port       = PORT_FIBRE;
	} /* TODO: else copper related  support */

	cmd->duplex = fullduplex ? DUPLEX_FULL : DUPLEX_HALF;
	switch (cfg / CFG_SPDSTS0 & 3) {
	case 2:
		cmd->speed = SPEED_1000;
		break;
	case 1:
		cmd->speed = SPEED_100;
		break;
	default:
		cmd->speed = SPEED_10;
		break;
	}
	cmd->autoneg = (tbicr & TBICR_MR_AN_ENABLE) ? 1: 0;
	return 0;
}

/* Let ethool change settings*/
static int ns83820_set_settings(struct net_device *ndev,
				struct ethtool_cmd *cmd)
{
	struct ns83820 *dev = PRIV(ndev);
	u32 cfg, tanar;
	int have_optical = 0;
	int fullduplex   = 0;

	/* read current configuration */
	cfg = readl(dev->base + CFG) ^ SPDSTS_POLARITY;
	tanar = readl(dev->base + TANAR);

	if (dev->CFG_cache & CFG_TBI_EN) {
		/* we have optical */
		have_optical = 1;
		fullduplex   = (tanar & TANAR_FULL_DUP);

	} else {
		/* we have copper */
		fullduplex = cfg & CFG_DUPSTS;
	}

	spin_lock_irq(&dev->misc_lock);
	spin_lock(&dev->tx_lock);

	/* Set duplex */
	if (cmd->duplex != fullduplex) {
		if (have_optical) {
			/*set full duplex*/
			if (cmd->duplex == DUPLEX_FULL) {
				/* force full duplex */
				writel(readl(dev->base + TXCFG)
					| TXCFG_CSI | TXCFG_HBI | TXCFG_ATP,
					dev->base + TXCFG);
				writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
					dev->base + RXCFG);
				/* Light up full duplex LED */
				writel(readl(dev->base + GPIOR) | GPIOR_GP1_OUT,
					dev->base + GPIOR);
			} else {
				/*TODO: set half duplex */
			}

		} else {
			/*we have copper*/
			/* TODO: Set duplex for copper cards */
		}
		printk(KERN_INFO "%s: Duplex set via ethtool\n",
		ndev->name);
	}

	/* Set autonegotiation */
	if (1) {
		if (cmd->autoneg == AUTONEG_ENABLE) {
			/* restart auto negotiation */
			writel(TBICR_MR_AN_ENABLE | TBICR_MR_RESTART_AN,
				dev->base + TBICR);
			writel(TBICR_MR_AN_ENABLE, dev->base + TBICR);
				dev->linkstate = LINK_AUTONEGOTIATE;

			printk(KERN_INFO "%s: autoneg enabled via ethtool\n",
				ndev->name);
		} else {
			/* disable auto negotiation */
			writel(0x00000000, dev->base + TBICR);
		}

		printk(KERN_INFO "%s: autoneg %s via ethtool\n", ndev->name,
				cmd->autoneg ? "ENABLED" : "DISABLED");
	}

	phy_intr(ndev);
	spin_unlock(&dev->tx_lock);
	spin_unlock_irq(&dev->misc_lock);

	return 0;
}
/* end ethtool get/set support -df */

static void ns83820_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
{
	struct ns83820 *dev = PRIV(ndev);
@@ -1263,6 +1406,8 @@ static u32 ns83820_get_link(struct net_device *ndev)
}

static const struct ethtool_ops ops = {
	.get_settings    = ns83820_get_settings,
	.set_settings    = ns83820_set_settings,
	.get_drvinfo     = ns83820_get_drvinfo,
	.get_link        = ns83820_get_link
};