Subject: FEC MAC with ethernet switch chip

From: Peter Rusko <[email-masked]>
Date: Thu, 22 Mar 2012 17:46:33 +0100

Hi all,

I'm trying to get a Marvell ethernet switch (88E6065) working with the
FEC driver (on an i.mx28 cpu). The original driver doesn't support it
because of the multiple ports/phys. It probes the bus and assigns the hw
with the first address to fec0 and the second to fec1. This results the
loss of fec1 when I disconnect the cable from the first port (the phy
driver brings the connection down).

I have two solutions, but don't really know which one is better.

1. There is a DSA (Distributed Switch Architecture) interface used in
the kernel for these chips. These drivers don't need PHY's so I simply
disabled the PHY driver (and made it configurable through
platform_data). An attached patch shows this simple solution. It works
perfectly, but I still have to configure the chip. (btw, I had serious
problems adding the platform_dev because it needs a reference to a
struct ndev. Is there a good solution for this?)

2. A PHY driver would solve the problem, though it's not a PHY.

Which one should I use? Or is there a better one?

Regards,
-- 
Ruskó Péter
Fejlesztőmérnök

Prolan Zrt. / Prolan Co.
Hungary 2011 Budakalász, Szentendrei út 1-3.
Tel./Phone: +36 20 954 3118
Fax: +36 26 540 420
E-mail: [email-masked]
Web: www.prolan.hu
Timezone:CET
diff --git a/drivers/net/ethernet/freescale/fec.c b/drivers/net/ethernet/freescale/fec.c
index a13eb30..293432f 100644
--- a/drivers/net/ethernet/freescale/fec.c
+++ b/drivers/net/ethernet/freescale/fec.c
@@ -239,6 +239,7 @@ struct fec_enet_private {
 	int	mii_timeout;
 	uint	phy_speed;
 	phy_interface_t	phy_interface;
+	unsigned phy_type;
 	int	link;
 	int	full_duplex;
 	struct	completion mdio_done;
@@ -978,6 +979,17 @@ static int fec_enet_mii_probe(struct net_device *ndev)
 
 	fep->phy_dev = NULL;
 
+	if (fep->phy_type == FEC_PHY_SWITCH) {
+		fep->link = 1;
+		netif_carrier_on(ndev); // always up
+
+		fep->full_duplex = 1; // full duplex
+
+		fec_restart(ndev, fep->full_duplex);
+
+		return 0;
+	}
+
 	/* check for attached phy */
 	for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) {
 		if ((fep->mii_bus->phy_mask & (1 << phy_id)))
@@ -1170,8 +1182,20 @@ static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
 	if (!netif_running(ndev))
 		return -EINVAL;
 
-	if (!phydev)
-		return -ENODEV;
+	if (!phydev) {
+		// On i.mx28, this is not necessarily needed
+		// TODO: remove?
+		struct mii_ioctl_data *mii_data = if_mii(rq);
+
+		if (cmd == SIOCGMIIREG)
+			mdiobus_write(fep->mii_bus, mii_data->phy_id,
+					mii_data->reg_num, mii_data->val_in);
+		else if (cmd == SIOCSMIIREG)
+			mii_data->val_out = mdiobus_read(fep->mii_bus,
+					mii_data->phy_id, mii_data->reg_num);
+		else return -ENODEV;
+		return 0;
+	}
 
 	return phy_mii_ioctl(phydev, rq, cmd);
 }
@@ -1274,7 +1298,8 @@ fec_enet_open(struct net_device *ndev)
 		fec_enet_free_buffers(ndev);
 		return ret;
 	}
-	phy_start(fep->phy_dev);
+	if (fep->phy_type == FEC_PHY_NORMAL)
+		phy_start(fep->phy_dev);
 	netif_start_queue(ndev);
 	fep->opened = 1;
 	return 0;
@@ -1598,6 +1623,9 @@ fec_probe(struct platform_device *pdev)
 		fep->phy_interface = ret;
 	}
 
+	if (pdata)
+		fep->phy_type = pdata->phy_type;
+
 	fec_reset_phy(pdev);
 
 	for (i = 0; i < FEC_IRQ_NUM; i++) {
diff --git a/include/linux/fec.h b/include/linux/fec.h
index bcff455..701fb2a 100644
--- a/include/linux/fec.h
+++ b/include/linux/fec.h
@@ -14,10 +14,15 @@
 #ifndef __LINUX_FEC_H__
 #define __LINUX_FEC_H__
 
+#define FEC_PHY_NORMAL 0
+#define FEC_PHY_SWITCH 1
+#define FEC_PHY_NONE 2
+
 #include <linux/phy.h>
 
 struct fec_platform_data {
 	phy_interface_t phy;
+	unsigned phy_type;
 	unsigned char mac[ETH_ALEN];
 };