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

Commit d0412d96 authored by James Chapman's avatar James Chapman Committed by Jeff Garzik
Browse files

[PATCH] mv643xx_eth: use MII library for ethtool functions



Use the common ethtool support functions of the MII library.
Add generic MII ioctl handler.
Add PHY parameter speed/duplex/negotiation initialization and modification.

Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Signed-off-by: default avatarDale Farnsworth <dale@farnsworth.org>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent c28a4f89
Loading
Loading
Loading
Loading
+210 −141
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ static void ethernet_phy_set(unsigned int eth_port_num, int phy_addr);
static int ethernet_phy_detect(unsigned int eth_port_num);
static int mv643xx_mdio_read(struct net_device *dev, int phy_id, int location);
static void mv643xx_mdio_write(struct net_device *dev, int phy_id, int location, int val);
static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static struct ethtool_ops mv643xx_ethtool_ops;

static char mv643xx_driver_name[] = "mv643xx_eth";
@@ -457,6 +458,56 @@ static int mv643xx_eth_receive_queue(struct net_device *dev)
	return received_packets;
}

/* Set the mv643xx port configuration register for the speed/duplex mode. */
static void mv643xx_eth_update_pscr(struct net_device *dev,
				    struct ethtool_cmd *ecmd)
{
	struct mv643xx_private *mp = netdev_priv(dev);
	int port_num = mp->port_num;
	u32 o_pscr, n_pscr;
	unsigned int channels;

	o_pscr = mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num));
	n_pscr = o_pscr;

	/* clear speed, duplex and rx buffer size fields */
	n_pscr &= ~(MV643XX_ETH_SET_MII_SPEED_TO_100  |
		   MV643XX_ETH_SET_GMII_SPEED_TO_1000 |
		   MV643XX_ETH_SET_FULL_DUPLEX_MODE   |
		   MV643XX_ETH_MAX_RX_PACKET_MASK);

	if (ecmd->duplex == DUPLEX_FULL)
		n_pscr |= MV643XX_ETH_SET_FULL_DUPLEX_MODE;

	if (ecmd->speed == SPEED_1000)
		n_pscr |= MV643XX_ETH_SET_GMII_SPEED_TO_1000 |
			  MV643XX_ETH_MAX_RX_PACKET_9700BYTE;
	else {
		if (ecmd->speed == SPEED_100)
			n_pscr |= MV643XX_ETH_SET_MII_SPEED_TO_100;
		n_pscr |= MV643XX_ETH_MAX_RX_PACKET_1522BYTE;
	}

	if (n_pscr != o_pscr) {
		if ((o_pscr & MV643XX_ETH_SERIAL_PORT_ENABLE) == 0)
			mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
								n_pscr);
		else {
			channels = mv643xx_eth_port_disable_tx(port_num);

			o_pscr &= ~MV643XX_ETH_SERIAL_PORT_ENABLE;
			mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
								o_pscr);
			mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
								n_pscr);
			mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
								n_pscr);
			if (channels)
				mv643xx_eth_port_enable_tx(port_num, channels);
		}
	}
}

/*
 * mv643xx_eth_int_handler
 *
@@ -539,13 +590,19 @@ static irqreturn_t mv643xx_eth_int_handler(int irq, void *dev_id,
	}
	/* PHY status changed */
	if (eth_int_cause_ext & (BIT16 | BIT20)) {
		struct ethtool_cmd cmd;

		if (mii_link_ok(&mp->mii)) {
			mii_ethtool_gset(&mp->mii, &cmd);
			mv643xx_eth_update_pscr(dev, &cmd);
			if (!netif_carrier_ok(dev)) {
				netif_carrier_on(dev);
				if (mp->tx_ring_size > mp->tx_desc_count +
							MAX_DESCS_PER_SKB) {
					netif_wake_queue(dev);
					/* Start TX queue */
				mv643xx_eth_port_enable_tx(port_num,
						mp->port_tx_queue_command);
					mv643xx_eth_port_enable_tx(port_num, mp->port_tx_queue_command);
				}
			}
		} else if (netif_carrier_ok(dev)) {
			netif_stop_queue(dev);
@@ -729,6 +786,34 @@ static void ether_init_tx_desc_ring(struct mv643xx_private *mp)
	mp->port_tx_queue_command = 1;
}

static int mv643xx_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct mv643xx_private *mp = netdev_priv(dev);
	int err;

	spin_lock_irq(&mp->lock);
	err = mii_ethtool_sset(&mp->mii, cmd);
	spin_unlock_irq(&mp->lock);

	return err;
}

static int mv643xx_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
	struct mv643xx_private *mp = netdev_priv(dev);
	int err;

	spin_lock_irq(&mp->lock);
	err = mii_ethtool_gset(&mp->mii, cmd);
	spin_unlock_irq(&mp->lock);

	/* The PHY may support 1000baseT_Half, but the mv643xx does not */
	cmd->supported &= ~SUPPORTED_1000baseT_Half;
	cmd->advertising &= ~ADVERTISED_1000baseT_Half;

	return err;
}

/*
 * mv643xx_eth_open
 *
@@ -842,6 +927,10 @@ static int mv643xx_eth_open(struct net_device *dev)

	mv643xx_eth_rx_task(dev);	/* Fill RX ring with skb's */

	/* Clear any pending ethernet port interrupts */
	mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
	mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);

	eth_port_start(dev);

	/* Interrupt Coalescing */
@@ -854,16 +943,13 @@ static int mv643xx_eth_open(struct net_device *dev)
	mp->tx_int_coal =
		eth_port_set_tx_coal(port_num, 133000000, MV643XX_TX_COAL);

	/* Clear any pending ethernet port interrupts */
	mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0);
	mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0);

	/* Unmask phy and link status changes interrupts */
	mv_write(MV643XX_ETH_INTERRUPT_EXTEND_MASK_REG(port_num),
						INT_UNMASK_ALL_EXT);

	/* Unmask RX buffer and TX end interrupt */
	mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), INT_UNMASK_ALL);

	return 0;

out_free_tx_skb:
@@ -1318,6 +1404,35 @@ static void mv643xx_netpoll(struct net_device *netdev)
}
#endif

static void mv643xx_init_ethtool_cmd(struct net_device *dev, int phy_address,
				     int speed, int duplex,
				     struct ethtool_cmd *cmd)
{
	struct mv643xx_private *mp = netdev_priv(dev);

	memset(cmd, 0, sizeof(*cmd));

	cmd->port = PORT_MII;
	cmd->transceiver = XCVR_INTERNAL;
	cmd->phy_address = phy_address;

	if (speed == 0) {
		cmd->autoneg = AUTONEG_ENABLE;
		/* mii lib checks, but doesn't use speed on AUTONEG_ENABLE */
		cmd->speed = SPEED_100;
		cmd->advertising = ADVERTISED_10baseT_Half  |
				   ADVERTISED_10baseT_Full  |
				   ADVERTISED_100baseT_Half |
				   ADVERTISED_100baseT_Full;
		if (mp->mii.supports_gmii)
			cmd->advertising |= ADVERTISED_1000baseT_Full;
	} else {
		cmd->autoneg = AUTONEG_DISABLE;
		cmd->speed = speed;
		cmd->duplex = duplex;
	}
}

/*/
 * mv643xx_eth_probe
 *
@@ -1338,6 +1453,10 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
	u8 *p;
	struct resource *res;
	int err;
	struct ethtool_cmd cmd;
	u32 pscr;
	int duplex;
	int speed;

	dev = alloc_etherdev(sizeof(struct mv643xx_private));
	if (!dev)
@@ -1375,6 +1494,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
	dev->tx_queue_len = mp->tx_ring_size;
	dev->base_addr = 0;
	dev->change_mtu = mv643xx_eth_change_mtu;
	dev->do_ioctl = mv643xx_eth_do_ioctl;
	SET_ETHTOOL_OPS(dev, &mv643xx_ethtool_ops);

#ifdef MV643XX_CHECKSUM_OFFLOAD_TX
@@ -1452,10 +1572,35 @@ static int mv643xx_eth_probe(struct platform_device *pdev)
		pr_debug("MV643xx ethernet port %d: "
					"No PHY detected at addr %d\n",
					port_num, ethernet_phy_get(port_num));
		return err;
		goto out;
	}

	pscr = mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num));
	pscr &= ~MV643XX_ETH_SERIAL_PORT_ENABLE;
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), pscr);
	pscr = mp->port_serial_control;
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), pscr);

	if (!(pscr & MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLX) &&
	    !(pscr & MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII))
		speed = 0;
	else if (pscr & MV643XX_ETH_PORT_STATUS_GMII_1000)
		speed = SPEED_1000;
	else if (pscr & MV643XX_ETH_PORT_STATUS_MII_100)
		speed = SPEED_100;
	else
		speed = SPEED_10;

	if (pscr & MV643XX_ETH_PORT_STATUS_FULL_DUPLEX)
		duplex = DUPLEX_FULL;
	else
		duplex = DUPLEX_HALF;

	ethernet_phy_reset(mp->port_num);
	mp->mii.supports_gmii = mii_check_gmii_support(&mp->mii);
	mv643xx_init_ethtool_cmd(dev, mp->mii.phy_id, speed, duplex, &cmd);
	mv643xx_eth_update_pscr(dev, &cmd);
	mv643xx_set_settings(dev, &cmd);

	err = register_netdev(dev);
	if (err)
@@ -1775,8 +1920,6 @@ static void eth_port_init(struct mv643xx_private *mp)
	eth_port_reset(mp->port_num);

	eth_port_init_mac_tables(mp->port_num);

	ethernet_phy_reset(mp->port_num);
}

/*
@@ -1811,6 +1954,8 @@ static void eth_port_start(struct net_device *dev)
	struct mv643xx_private *mp = netdev_priv(dev);
	unsigned int port_num = mp->port_num;
	int tx_curr_desc, rx_curr_desc;
	u32 pscr;
	struct ethtool_cmd ethtool_cmd;

	/* Assignment of Tx CTRP of given queue */
	tx_curr_desc = mp->tx_curr_desc_q;
@@ -1828,31 +1973,35 @@ static void eth_port_start(struct net_device *dev)
	/* Assign port configuration and command. */
	mv_write(MV643XX_ETH_PORT_CONFIG_REG(port_num), mp->port_config);

	mv_write(MV643XX_ETH_PORT_CONFIG_EXTEND_REG(port_num),
						mp->port_config_extend);
	pscr = mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num));
	pscr &= ~MV643XX_ETH_SERIAL_PORT_ENABLE;
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), pscr);

	pscr &= ~MV643XX_ETH_FORCE_LINK_PASS;
	pscr |= MV643XX_ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL |
		MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII    |
		MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLX     |
		MV643XX_ETH_DO_NOT_FORCE_LINK_FAIL	   |
		MV643XX_ETH_SERIAL_PORT_CONTROL_RESERVED;

	/* Increase the Rx side buffer size if supporting GigE */
	if (mp->port_serial_control & MV643XX_ETH_SET_GMII_SPEED_TO_1000)
		mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
			(mp->port_serial_control & 0xfff1ffff) | (0x5 << 17));
	else
		mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
						mp->port_serial_control);
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), pscr);

	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num),
		mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num)) |
						MV643XX_ETH_SERIAL_PORT_ENABLE);
	pscr |= MV643XX_ETH_SERIAL_PORT_ENABLE;
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), pscr);

	/* Assign port SDMA configuration */
	mv_write(MV643XX_ETH_SDMA_CONFIG_REG(port_num),
							mp->port_sdma_config);
	mv_write(MV643XX_ETH_SDMA_CONFIG_REG(port_num), mp->port_sdma_config);

	/* Enable port Rx. */
	mv643xx_eth_port_enable_rx(port_num, mp->port_rx_queue_command);

	/* Disable port bandwidth limits by clearing MTU register */
	mv_write(MV643XX_ETH_MAXIMUM_TRANSMIT_UNIT(port_num), 0);

	/* save phy settings across reset */
	mv643xx_get_settings(dev, &ethtool_cmd);
	ethernet_phy_reset(mp->port_num);
	mv643xx_set_settings(dev, &ethtool_cmd);
}

/*
@@ -2324,6 +2473,12 @@ static void ethernet_phy_reset(unsigned int eth_port_num)
	eth_port_read_smi_reg(eth_port_num, 0, &phy_reg_data);
	phy_reg_data |= 0x8000;	/* Set bit 15 to reset the PHY */
	eth_port_write_smi_reg(eth_port_num, 0, phy_reg_data);

	/* wait for PHY to come out of reset */
	do {
		udelay(1);
		eth_port_read_smi_reg(eth_port_num, 0, &phy_reg_data);
	} while (phy_reg_data & 0x8000);
}

static void mv643xx_eth_port_enable_tx(unsigned int port_num,
@@ -2417,20 +2572,13 @@ static void eth_port_reset(unsigned int port_num)

	/* Reset the Enable bit in the Configuration Register */
	reg_data = mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num));
	reg_data &= ~MV643XX_ETH_SERIAL_PORT_ENABLE;
	reg_data &= ~(MV643XX_ETH_SERIAL_PORT_ENABLE		|
			MV643XX_ETH_DO_NOT_FORCE_LINK_FAIL	|
			MV643XX_ETH_FORCE_LINK_PASS);
	mv_write(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num), reg_data);
}


static int eth_port_autoneg_supported(unsigned int eth_port_num)
{
	unsigned int phy_reg_data0;

	eth_port_read_smi_reg(eth_port_num, 0, &phy_reg_data0);

	return phy_reg_data0 & 0x1000;
}

/*
 * eth_port_read_smi_reg - Read PHY registers
 *
@@ -2989,111 +3137,6 @@ static const struct mv643xx_stats mv643xx_gstrings_stats[] = {
#define MV643XX_STATS_LEN	\
	sizeof(mv643xx_gstrings_stats) / sizeof(struct mv643xx_stats)

static int
mv643xx_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
{
	struct mv643xx_private *mp = netdev->priv;
	int port_num = mp->port_num;
	int autoneg = eth_port_autoneg_supported(port_num);
	int mode_10_bit;
	int auto_duplex;
	int half_duplex = 0;
	int full_duplex = 0;
	int auto_speed;
	int speed_10 = 0;
	int speed_100 = 0;
	int speed_1000 = 0;

	u32 pcs = mv_read(MV643XX_ETH_PORT_SERIAL_CONTROL_REG(port_num));
	u32 psr = mv_read(MV643XX_ETH_PORT_STATUS_REG(port_num));

	mode_10_bit = psr & MV643XX_ETH_PORT_STATUS_MODE_10_BIT;

	if (mode_10_bit) {
		ecmd->supported = SUPPORTED_10baseT_Half;
	} else {
		ecmd->supported = (SUPPORTED_10baseT_Half		|
				   SUPPORTED_10baseT_Full		|
				   SUPPORTED_100baseT_Half		|
				   SUPPORTED_100baseT_Full		|
				   SUPPORTED_1000baseT_Full		|
				   (autoneg ? SUPPORTED_Autoneg : 0)	|
				   SUPPORTED_TP);

		auto_duplex = !(pcs & MV643XX_ETH_DISABLE_AUTO_NEG_FOR_DUPLX);
		auto_speed = !(pcs & MV643XX_ETH_DISABLE_AUTO_NEG_SPEED_GMII);

		ecmd->advertising = ADVERTISED_TP;

		if (autoneg) {
			ecmd->advertising |= ADVERTISED_Autoneg;

			if (auto_duplex) {
				half_duplex = 1;
				full_duplex = 1;
			} else {
				if (pcs & MV643XX_ETH_SET_FULL_DUPLEX_MODE)
					full_duplex = 1;
				else
					half_duplex = 1;
			}

			if (auto_speed) {
				speed_10 = 1;
				speed_100 = 1;
				speed_1000 = 1;
			} else {
				if (pcs & MV643XX_ETH_SET_GMII_SPEED_TO_1000)
					speed_1000 = 1;
				else if (pcs & MV643XX_ETH_SET_MII_SPEED_TO_100)
					speed_100 = 1;
				else
					speed_10 = 1;
			}

			if (speed_10 & half_duplex)
				ecmd->advertising |= ADVERTISED_10baseT_Half;
			if (speed_10 & full_duplex)
				ecmd->advertising |= ADVERTISED_10baseT_Full;
			if (speed_100 & half_duplex)
				ecmd->advertising |= ADVERTISED_100baseT_Half;
			if (speed_100 & full_duplex)
				ecmd->advertising |= ADVERTISED_100baseT_Full;
			if (speed_1000)
				ecmd->advertising |= ADVERTISED_1000baseT_Full;
		}
	}

	ecmd->port = PORT_TP;
	ecmd->phy_address = ethernet_phy_get(port_num);

	ecmd->transceiver = XCVR_EXTERNAL;

	if (netif_carrier_ok(netdev)) {
		if (mode_10_bit)
			ecmd->speed = SPEED_10;
		else {
			if (psr & MV643XX_ETH_PORT_STATUS_GMII_1000)
				ecmd->speed = SPEED_1000;
			else if (psr & MV643XX_ETH_PORT_STATUS_MII_100)
				ecmd->speed = SPEED_100;
			else
				ecmd->speed = SPEED_10;
		}

		if (psr & MV643XX_ETH_PORT_STATUS_FULL_DUPLEX)
			ecmd->duplex = DUPLEX_FULL;
		else
			ecmd->duplex = DUPLEX_HALF;
	} else {
		ecmd->speed = -1;
		ecmd->duplex = -1;
	}

	ecmd->autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
	return 0;
}

static void mv643xx_get_drvinfo(struct net_device *netdev,
				struct ethtool_drvinfo *drvinfo)
{
@@ -3140,15 +3183,41 @@ static void mv643xx_get_strings(struct net_device *netdev, uint32_t stringset,
	}
}

static u32 mv643xx_eth_get_link(struct net_device *dev)
{
	struct mv643xx_private *mp = netdev_priv(dev);

	return mii_link_ok(&mp->mii);
}

static int mv643xx_eth_nway_restart(struct net_device *dev)
{
	struct mv643xx_private *mp = netdev_priv(dev);

	return mii_nway_restart(&mp->mii);
}

static int mv643xx_eth_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
	struct mv643xx_private *mp = netdev_priv(dev);

	return generic_mii_ioctl(&mp->mii, if_mii(ifr), cmd, NULL);
}

static struct ethtool_ops mv643xx_ethtool_ops = {
	.get_settings           = mv643xx_get_settings,
	.set_settings           = mv643xx_set_settings,
	.get_drvinfo            = mv643xx_get_drvinfo,
	.get_link               = ethtool_op_get_link,
	.get_link               = mv643xx_eth_get_link,
	.get_sg			= ethtool_op_get_sg,
	.set_sg			= ethtool_op_set_sg,
	.get_strings            = mv643xx_get_strings,
	.get_stats_count        = mv643xx_get_stats_count,
	.get_ethtool_stats      = mv643xx_get_ethtool_stats,
	.get_strings            = mv643xx_get_strings,
	.get_stats_count        = mv643xx_get_stats_count,
	.get_ethtool_stats      = mv643xx_get_ethtool_stats,
	.nway_reset		= mv643xx_eth_nway_restart,
};

/************* End ethtool support *************************/
+3 −0
Original line number Diff line number Diff line
@@ -1214,6 +1214,7 @@ struct mv64xxx_i2c_pdata {
#define MV643XX_ETH_FORCE_BP_MODE_NO_JAM		0
#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX		(1<<7)
#define MV643XX_ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR	(1<<8)
#define MV643XX_ETH_SERIAL_PORT_CONTROL_RESERVED	(1<<9)
#define MV643XX_ETH_FORCE_LINK_FAIL			0
#define MV643XX_ETH_DO_NOT_FORCE_LINK_FAIL		(1<<10)
#define MV643XX_ETH_RETRANSMIT_16_ATTEMPTS		0
@@ -1243,6 +1244,8 @@ struct mv64xxx_i2c_pdata {
#define MV643XX_ETH_SET_MII_SPEED_TO_10			0
#define MV643XX_ETH_SET_MII_SPEED_TO_100		(1<<24)

#define MV643XX_ETH_MAX_RX_PACKET_MASK			(0x7<<17)

#define	MV643XX_ETH_PORT_SERIAL_CONTROL_DEFAULT_VALUE		\
		MV643XX_ETH_DO_NOT_FORCE_LINK_PASS	|	\
		MV643XX_ETH_ENABLE_AUTO_NEG_FOR_DUPLX	|	\