[PATCH] Enhanced ethtool API of CPSW driver in order to be able to address even more than one PHY.

Sven Fischer sven.fischer at snom.com
Thu Apr 21 12:17:18 EDT 2011


Linked ethtool ioctls ETHTOOL_GSET and ETHTOOL_SSET to the CPSW switch driver and added a new ioctl ETHTOOL_GPHYLINK.

The new ETHTOOL_GPHYLINK, which gets the PHY link status of a specific PHY, makes ETHTOOL_GLINK obsolete, because it was not working PHY specific.

ETHTOOL_GSET and ETHTOOL_SSET ioctls are now able to access a specific PHY as well.
---
 drivers/net/cpsw.c      |   53 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/phy/phy.c   |    9 ++++++++
 include/linux/ethtool.h |   12 +++++++++-
 include/linux/phy.h     |    1 +
 net/core/ethtool.c      |   28 ++++++++++++++++++++++++-
 5 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c
index df4d302..b1045b1 100644
--- a/drivers/net/cpsw.c
+++ b/drivers/net/cpsw.c
@@ -319,6 +319,54 @@ static void cpsw_adjust_link(struct net_device *ndev)
 	}
 }
 
+static u32 cpsw_slave_get_link(struct net_device *ndev,
+			       struct ethtool_link *cmd)
+{
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+	int port;
+
+	if (!priv || !cmd) return -EFAULT;
+	for (port = 0; port < priv->data.slaves; port++) {
+		struct cpsw_slave *slave = priv->slaves + port;
+		if (slave && slave->phy && (slave->phy->addr == cmd->phy_address)) {
+			return phy_ethtool_gphlnk(slave->phy, cmd);
+		}
+	}
+	return -EOPNOTSUPP;
+}
+
+static int cpsw_slave_get_settings(struct net_device *ndev,
+				   struct ethtool_cmd *cmd)
+{
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+	int port;
+	
+	if (!priv || !cmd) return -EFAULT;
+	for (port = 0; port < priv->data.slaves; port++) {
+		struct cpsw_slave *slave = priv->slaves + port;
+		if (slave && slave->phy && (slave->phy->addr == cmd->phy_address)) {
+			return phy_ethtool_gset(slave->phy, cmd);
+		}
+	}
+	return -EOPNOTSUPP;
+}
+
+static int cpsw_slave_set_settings(struct net_device *ndev,
+				   struct ethtool_cmd *cmd)
+{
+	struct cpsw_priv	*priv = netdev_priv(ndev);
+	int port;
+	
+	if (!priv || !cmd) return -EFAULT;
+	for (port = 0; port < priv->data.slaves; port++) {
+		struct cpsw_slave *slave = priv->slaves + port;
+		if (slave && slave->phy && (slave->phy->addr == cmd->phy_address)) {
+			return phy_ethtool_sset(slave->phy, cmd);
+		}
+	}
+	return -EOPNOTSUPP;
+}
+
 static inline int __show_stat(char *buf, int maxlen, const char* name, u32 val)
 {
 	static char *leader = "........................................";
@@ -718,6 +766,7 @@ static struct net_device_stats *cpsw_ndo_get_stats(struct net_device *ndev)
 	return &priv->stats;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
 static void cpsw_ndo_poll_controller(struct net_device *ndev)
 {
 	struct cpsw_priv *priv = netdev_priv(ndev);
@@ -726,6 +775,7 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev)
 	cpsw_interrupt(ndev->irq, priv);
 	cpdma_ctlr_int_ctrl(priv->dma, true);
 }
+#endif
 
 static const struct net_device_ops cpsw_netdev_ops = {
 	.ndo_open		= cpsw_ndo_open,
@@ -764,9 +814,12 @@ static void cpsw_set_msglevel(struct net_device *ndev, u32 value)
 
 static const struct ethtool_ops cpsw_ethtool_ops = {
 	.get_drvinfo	= cpsw_get_drvinfo,
+	.get_settings   = cpsw_slave_get_settings,
+	.set_settings   = cpsw_slave_set_settings,
 	.get_msglevel	= cpsw_get_msglevel,
 	.set_msglevel	= cpsw_set_msglevel,
 	.get_link	= ethtool_op_get_link,
+	.get_phy_link   = cpsw_slave_get_link,
 };
 
 static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv)
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 7670aac..c2bbe21 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -297,6 +297,15 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd)
 }
 EXPORT_SYMBOL(phy_ethtool_gset);
 
+int phy_ethtool_gphlnk(struct phy_device *phydev, struct ethtool_link *cmd)
+{
+	cmd->phy_address = phydev->addr;
+	cmd->link = phydev->link;
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_ethtool_gphlnk);
+
 /**
  * phy_mii_ioctl - generic PHY MII ioctl interface
  * @phydev: the phy_device struct
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 6628a50..8734985 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -50,6 +50,13 @@ static inline __u32 ethtool_cmd_speed(struct ethtool_cmd *ep)
 	return (ep->speed_hi << 16) | ep->speed;
 }
 
+/* Used for ETHTOOL_GPHYLINK */
+struct ethtool_link {
+	__u32	cmd;
+	__u8	phy_address;
+	__u8	link;
+};
+
 #define ETHTOOL_FWVERS_LEN	32
 #define ETHTOOL_BUSINFO_LEN	32
 /* these strings are set to whatever the driver author decides... */
@@ -570,6 +577,7 @@ void ethtool_ntuple_flush(struct net_device *dev);
  * set_msglevel: Set driver message level
  * nway_reset: Restart autonegotiation
  * get_link: Get link status
+ * get_phy_link: Get link status of a specific PHY; replaces get_link
  * get_eeprom: Read data from the device EEPROM
  * set_eeprom: Write data to the device EEPROM
  * get_coalesce: Get interrupt coalescing parameters
@@ -629,6 +637,7 @@ struct ethtool_ops {
 	void	(*set_msglevel)(struct net_device *, u32);
 	int	(*nway_reset)(struct net_device *);
 	u32	(*get_link)(struct net_device *);
+	u32	(*get_phy_link)(struct net_device *, struct ethtool_link *);
 	int	(*get_eeprom_len)(struct net_device *);
 	int	(*get_eeprom)(struct net_device *,
 			      struct ethtool_eeprom *, u8 *);
@@ -691,7 +700,7 @@ struct ethtool_ops {
 #define ETHTOOL_GMSGLVL		0x00000007 /* Get driver message level */
 #define ETHTOOL_SMSGLVL		0x00000008 /* Set driver msg level. */
 #define ETHTOOL_NWAY_RST	0x00000009 /* Restart autonegotiation. */
-#define ETHTOOL_GLINK		0x0000000a /* Get link status (ethtool_value) */
+#define ETHTOOL_GLINK		0x0000000a /* Get link status (ethtool_value), replaced by ETHTOOL_GPHYLINK */
 #define ETHTOOL_GEEPROM		0x0000000b /* Get EEPROM data */
 #define ETHTOOL_SEEPROM		0x0000000c /* Set EEPROM data. */
 #define ETHTOOL_GCOALESCE	0x0000000e /* Get coalesce config */
@@ -741,6 +750,7 @@ struct ethtool_ops {
 #define ETHTOOL_GSSET_INFO	0x00000037 /* Get string set info */
 #define ETHTOOL_GRXFHINDIR	0x00000038 /* Get RX flow hash indir'n table */
 #define ETHTOOL_SRXFHINDIR	0x00000039 /* Set RX flow hash indir'n table */
+#define ETHTOOL_GPHYLINK	0x0000003a /* Get phy link status, makes ETHTOOL_GLINK obsolete */
 
 /* compatibility with older code */
 #define SPARC_ETH_GSET		ETHTOOL_GSET
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7da5fa8..e73c139 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -508,6 +508,7 @@ void phy_start_machine(struct phy_device *phydev,
 void phy_stop_machine(struct phy_device *phydev);
 int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd);
 int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd);
+int phy_ethtool_gphlnk(struct phy_device *phydev, struct ethtool_link *cmd);
 int phy_mii_ioctl(struct phy_device *phydev,
 		struct ifreq *ifr, int cmd);
 int phy_start_interrupts(struct phy_device *phydev);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 956a9f4..409d13d 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -171,14 +171,37 @@ EXPORT_SYMBOL(ethtool_ntuple_flush);
 
 /* Handlers for each ethtool command */
 
+static int ethtool_get_phy_link(struct net_device *dev, void __user *useraddr)
+{
+	struct ethtool_link cmd;
+	int err;
+
+	if (!dev->ethtool_ops->get_phy_link)
+		return -EOPNOTSUPP;
+
+	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+		return -EFAULT;
+
+	err = dev->ethtool_ops->get_phy_link(dev, &cmd);
+	if (err < 0)
+		return err;
+
+	if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
+		return -EFAULT;
+	return 0;
+}
+
 static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
 {
-	struct ethtool_cmd cmd = { .cmd = ETHTOOL_GSET };
+	struct ethtool_cmd cmd;
 	int err;
 
 	if (!dev->ethtool_ops->get_settings)
 		return -EOPNOTSUPP;
 
+	if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+		return -EFAULT;
+
 	err = dev->ethtool_ops->get_settings(dev, &cmd);
 	if (err < 0)
 		return err;
@@ -1531,6 +1554,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
 		rc = ethtool_get_value(dev, useraddr, ethcmd,
 				       dev->ethtool_ops->get_link);
 		break;
+	case ETHTOOL_GPHYLINK:
+		rc = ethtool_get_phy_link(dev, useraddr);
+		break;
 	case ETHTOOL_GEEPROM:
 		rc = ethtool_get_eeprom(dev, useraddr);
 		break;
-- 
1.7.3.2




More information about the linux-arm-kernel mailing list