* Re: net/fec on i.MX28: failure after network cable unplug or device down
2011-10-10 13:17 net/fec on i.MX28: failure after network cable unplug or device down Uwe Kleine-König
@ 2011-10-10 13:52 ` Lothar Waßmann
0 siblings, 0 replies; 2+ messages in thread
From: Lothar Waßmann @ 2011-10-10 13:52 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: netdev, Shawn Guo, Wolfram Sang
[-- Attachment #1: message body and .signature --]
[-- Type: text/plain, Size: 846 bytes --]
Hi,
Uwe Kleine-König writes:
> Hello,
>
> I currently see the problem on two different i.MX28 based system
> (Freescale's mx28evk and a Karo TX28) that after unplugging and
> reconnection of the network cable or ifconfig down; ifconfig up; the
> network is dead. That means nothing is sent or received anymore.
>
I already solved this problem (and some others too), but wasn't able
to send any patch yet.
The FEC of the i.MX28 requires the ETHER_EN bit in the ECR to be set
for the MII interface to work. Resetting the interface upon shutdown
is required though, because otherwise the receiver will remain active
and use the stale DMA descriptors in memory to store received packets!
Furthermore the (R)MII mode must be configured correctly in the RCR
register.
The current state of my patch is appended for reference.
Lothar Waßmann
--
[-- Attachment #2: fec-cleanup --]
[-- Type: application/octet-stream, Size: 3610 bytes --]
Index: linux/drivers/net/fec.c
===================================================================
--- linux.orig/drivers/net/fec.c 2011-09-13 09:58:38.000000000 +0200
+++ linux/drivers/net/fec.c 2011-09-13 09:58:41.000000000 +0200
@@ -486,10 +486,16 @@
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
}
+/* prevent the FEC from transmitting/receiving packets, but leave
+ * the MII interface active to detect link change
+ */
static void
fec_stop(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
+ u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
/* We cannot expect a graceful transmit stop without link !!! */
if (fep->link) {
@@ -504,8 +510,33 @@
udelay(10);
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
+
+ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) {
+ /* i.MX28 requires ETHER_EN enabled to be able to
+ * access the PHY registers and the RMII mode properly set to
+ * be able to detect link changes
+ */
+ writel(2, fep->hwp + FEC_ECNTRL);
+ writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
+ }
}
+/* shutdown the FEC and the MII interface */
+static void
+fec_shutdown(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+
+ /* We cannot expect a graceful transmit stop without link !!! */
+ if (fep->link) {
+ writel(1, fep->hwp + FEC_X_CNTRL); /* Graceful transmit stop */
+ udelay(10);
+ if (!(readl(fep->hwp + FEC_IEVENT) & FEC_ENET_GRA))
+ printk("fec_stop : Graceful transmit stop did not complete !\n");
+ }
+
+ writel(0, fep->hwp + FEC_ECNTRL);
+}
static void
fec_timeout(struct net_device *ndev)
@@ -789,7 +820,7 @@
iap = (unsigned char *)FEC_FLASHMAC;
#else
if (pdata)
- memcpy(iap, pdata->mac, ETH_ALEN);
+ iap = (unsigned char *)&pdata->mac;
#endif
}
@@ -836,6 +867,8 @@
if (phy_dev->link) {
if (fep->full_duplex != phy_dev->duplex) {
fec_restart(ndev, phy_dev->duplex);
+ /* prevent unnecessary second fec_restart() below */
+ fep->link = phy_dev->link;
status_change = 1;
}
}
@@ -1052,8 +1085,6 @@
static void fec_enet_mii_remove(struct fec_enet_private *fep)
{
- if (fep->phy_dev)
- phy_disconnect(fep->phy_dev);
mdiobus_unregister(fep->mii_bus);
kfree(fep->mii_bus->irq);
mdiobus_free(fep->mii_bus);
@@ -1184,6 +1215,8 @@
fec_enet_open(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
+ const struct platform_device_id *id_entry =
+ platform_get_device_id(fep->pdev);
int ret;
/* I should reset the ring buffers here, but I don't yet know
@@ -1214,7 +1247,7 @@
/* Don't know what to do yet. */
fep->opened = 0;
netif_stop_queue(ndev);
- fec_stop(ndev);
+ fec_shutdown(ndev);
if (fep->phy_dev) {
phy_stop(fep->phy_dev);
@@ -1518,7 +1551,7 @@
}
}
- fep->clk = clk_get(&pdev->dev, "fec_clk");
+ fep->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(fep->clk)) {
ret = PTR_ERR(fep->clk);
goto failed_clk;
@@ -1570,13 +1603,18 @@
struct net_device *ndev = platform_get_drvdata(pdev);
struct fec_enet_private *fep = netdev_priv(ndev);
struct resource *r;
+ int i;
- fec_stop(ndev);
+ unregister_netdev(ndev);
fec_enet_mii_remove(fep);
+ for (i = 0; i < 3; i++) {
+ int irq = platform_get_irq(pdev, i);
+ if (irq > 0)
+ free_irq(irq, ndev);
+ }
clk_disable(fep->clk);
clk_put(fep->clk);
iounmap(fep->hwp);
- unregister_netdev(ndev);
free_netdev(ndev);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
[-- Attachment #3: message body text --]
[-- Type: text/plain, Size: 367 bytes --]
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________
^ permalink raw reply [flat|nested] 2+ messages in thread