All of lore.kernel.org
 help / color / mirror / Atom feed
* [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
@ 2008-02-05 19:01 David Brownell
  2008-02-06 17:11 ` Claudio Lanconelli
  0 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-05 19:01 UTC (permalink / raw)
  To: lanconelli.claudio, netdev

From: David Brownell <dbrownell@users.sourceforge.net>

Prevent unaligned packet oops on enc28j60 packet RX.

Keep enc28j60 chips in low-power mode when they're not in use.
At typically 120 mA, these chips run hot even when idle.  Low
power mode cuts that power usage by a factor of around 100.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/net/enc28j60.c |   54 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 50 insertions(+), 4 deletions(-)

--- avr.orig/drivers/net/enc28j60.c	2008-02-05 10:04:22.000000000 -0800
+++ avr/drivers/net/enc28j60.c	2008-02-05 10:50:50.000000000 -0800
@@ -594,6 +594,43 @@ static void nolock_txfifo_init(struct en
 	nolock_regw_write(priv, ETXNDL, end);
 }
 
+/*
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive.
+ */
+static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
+{
+	int	tmp;
+
+	dev_dbg(&priv->spi->dev, "%s power...\n", is_low ? "low" : "high");
+
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		for (;;) {
+			tmp = nolock_regb_read(priv, ESTAT);
+			if (!(tmp & ESTAT_RXBUSY))
+				break;
+		}
+		for (;;) {
+			tmp = nolock_regb_read(priv, ECON1);
+			if (!(tmp & ECON1_TXRTS))
+				break;
+		}
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		for (;;) {
+			tmp = nolock_regb_read(priv, ESTAT);
+			if (tmp & ESTAT_CLKRDY)
+				break;
+		}
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+}
+
 static int enc28j60_hw_init(struct enc28j60_net *priv)
 {
 	u8 reg;
@@ -612,8 +649,8 @@ static int enc28j60_hw_init(struct enc28
 	priv->tx_retry_count = 0;
 	priv->max_pk_counter = 0;
 	priv->rxfilter = RXFILTER_NORMAL;
-	/* enable address auto increment */
-	nolock_regb_write(priv, ECON2, ECON2_AUTOINC);
+	/* enable address auto increment and voltage regulator powersave */
+	nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
 
 	nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
 	nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
@@ -690,7 +727,9 @@ static int enc28j60_hw_init(struct enc28
 
 static void enc28j60_hw_enable(struct enc28j60_net *priv)
 {
-	/* enable interrutps */
+	enc28j60_lowpower(priv, false);
+
+	/* enable interrupts */
 	if (netif_msg_hw(priv))
 		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
 			__FUNCTION__);
@@ -717,6 +756,8 @@ static void enc28j60_hw_disable(struct e
 	nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
 	priv->hw_enable = false;
 	mutex_unlock(&priv->lock);
+
+	enc28j60_lowpower(priv, true);
 }
 
 static int
@@ -734,6 +775,8 @@ enc28j60_setlink(struct net_device *ndev
 						"hw_reset() failed\n");
 				ret = -EINVAL;
 			}
+			enc28j60_lowpower(priv, true);
+
 		} else {
 			if (netif_msg_link(priv))
 				dev_warn(&ndev->dev,
@@ -900,7 +943,7 @@ static void enc28j60_hw_rx(struct net_de
 		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
 			ndev->stats.rx_frame_errors++;
 	} else {
-		skb = dev_alloc_skb(len);
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
 		if (!skb) {
 			if (netif_msg_rx_err(priv))
 				dev_err(&ndev->dev,
@@ -908,6 +951,7 @@ static void enc28j60_hw_rx(struct net_de
 			ndev->stats.rx_dropped++;
 		} else {
 			skb->dev = ndev;
+			skb_reserve(skb, NET_IP_ALIGN);
 			/* copy the packet from the receive buffer */
 			enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv),
 					len, skb_put(skb, len));
@@ -1536,6 +1580,8 @@ static int __devinit enc28j60_probe(stru
 	dev->watchdog_timeo = TX_TIMEOUT;
 	SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
 
+	enc28j60_lowpower(priv, true);
+
 	ret = register_netdev(dev);
 	if (ret) {
 		if (netif_msg_probe(priv))

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-05 19:01 [patch 2.6.24-git] net/enc28j60: oops fix, low power mode David Brownell
@ 2008-02-06 17:11 ` Claudio Lanconelli
  2008-02-06 17:33   ` [patch 2.6.24-git] net/enc28j60: oops fix David Brownell
                     ` (4 more replies)
  0 siblings, 5 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-06 17:11 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

David Brownell wrote:
> From: David Brownell <dbrownell@users.sourceforge.net>
>
> Prevent unaligned packet oops on enc28j60 packet RX.
>   
How can I reproduce the unaligned packet oops? Did you use any utilities 
to force this condition?
> Keep enc28j60 chips in low-power mode when they're not in use.
> At typically 120 mA, these chips run hot even when idle.  Low
> power mode cuts that power usage by a factor of around 100.
>   
Good idea, but with your patch applied, after some ifconfig down - 
ifconfig up cycle, the
enc28j60 is left in an unknown state and it doesn' Rx/Tx anything.
In such cases If I dump the counters with ifconfig I got rx error 
counter > 0 and the RX and TX packets counters are freezed.
Actually I don't know what causes the freeze, it needs investigation. 
The cause can be the rx error condition or the power down/up commands.
May be receiving packets while it's going to wakeup causes problems.

Can you split the patch in 2 parts, unaligned rx and power save?

> +/*
> + * Low power mode shrinks power consumption about 100x, so we'd like
> + * the chip to be in that mode whenever it's inactive.
> + */
> +static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
> +{
> +	int	tmp;
> +
> +	dev_dbg(&priv->spi->dev, "%s power...\n", is_low ? "low" : "high");
>   
use
if(netif_msg_drv(priv)) printk(...
instead of dev_dbg(), please.
Doing so we can switch on/off messages runtime using ethtool.

> +
> +	mutex_lock(&priv->lock);
> +	if (is_low) {
> +		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
> +		for (;;) {
> +			tmp = nolock_regb_read(priv, ESTAT);
> +			if (!(tmp & ESTAT_RXBUSY))
> +				break;
> +		}
>   
Avoid infinite waiting loops, please.
Look at enc28j60_phy_read() for example.
> +		for (;;) {
> +			tmp = nolock_regb_read(priv, ECON1);
> +			if (!(tmp & ECON1_TXRTS))
> +				break;
> +		}
>   
idem
> +		/* ECON2_VRPS was set during initialization */
> +		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
> +	} else {
> +		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
> +		for (;;) {
> +			tmp = nolock_regb_read(priv, ESTAT);
> +			if (tmp & ESTAT_CLKRDY)
> +				break;
> +		}
>   
idem


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix
  2008-02-06 17:11 ` Claudio Lanconelli
@ 2008-02-06 17:33   ` David Brownell
  2008-02-07 10:24     ` Claudio Lanconelli
  2008-02-06 18:19   ` [patch 2.6.24-git] net/enc28j60: low power mode David Brownell
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-06 17:33 UTC (permalink / raw)
  To: netdev, lanconelli.claudio

> > Prevent unaligned packet oops on enc28j60 packet RX.
>
> How can I reproduce the unaligned packet oops?

Use any architecture that doesn't support unaligned accesses
in kernel space.  I used AVR32 for this (it's got a pretty
solid SPI master controller driver). ARM isn't immune either,
and there are others.  That's why NET_IP_ALIGN exists.


> Did you use any utilities to force this condition?

"ping" ... the first RX packet oopsed.


> Can you split the patch in 2 parts, unaligned rx and power save?

Below is: unaligned access.

- Dave


=========== CUT HERE
Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
Not all architectures support unaligned accesses in kernel space.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/net/enc28j60.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletion(-)

--- a/drivers/net/enc28j60.c	2008-02-06 09:29:00.000000000 -0800
+++ b/drivers/net/enc28j60.c	2008-02-06 09:30:03.000000000 -0800
@@ -900,7 +900,7 @@ static void enc28j60_hw_rx(struct net_de
 		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
 			ndev->stats.rx_frame_errors++;
 	} else {
-		skb = dev_alloc_skb(len);
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
 		if (!skb) {
 			if (netif_msg_rx_err(priv))
 				dev_err(&ndev->dev,
@@ -908,6 +908,7 @@ static void enc28j60_hw_rx(struct net_de
 			ndev->stats.rx_dropped++;
 		} else {
 			skb->dev = ndev;
+			skb_reserve(skb, NET_IP_ALIGN);
 			/* copy the packet from the receive buffer */
 			enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv),
 					len, skb_put(skb, len));

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-06 17:11 ` Claudio Lanconelli
  2008-02-06 17:33   ` [patch 2.6.24-git] net/enc28j60: oops fix David Brownell
@ 2008-02-06 18:19   ` David Brownell
  2008-02-07 10:49     ` Claudio Lanconelli
  2008-02-07  5:56   ` [patch 2.6.24-git] net/enc28j60: oops fix, " David Brownell
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-06 18:19 UTC (permalink / raw)
  To: netdev, lanconelli.claudio

> > Keep enc28j60 chips in low-power mode when they're not in use.
> > At typically 120 mA, these chips run hot even when idle.  Low
> > power mode cuts that power usage by a factor of around 100.
>
> Good idea, but with your patch applied, after some ifconfig down - 
> ifconfig up cycle, the
> enc28j60 is left in an unknown state and it doesn' Rx/Tx anything.

The driver seemed to already have some goofage there:

  # ifconfig eth1 up
  net eth1: link down
  net eth1: link down
  net eth1: normal mode
  net eth1: multicast mode
  net eth1: multicast mode
  #

I'd normally expect it not to go down when told to go up, and
then only to do the multicast thing once ...


> In such cases If I dump the counters with ifconfig I got rx error 
> counter > 0 and the RX and TX packets counters are freezed.
> Actually I don't know what causes the freeze, it needs investigation. 
> The cause can be the rx error condition or the power down/up commands.
> May be receiving packets while it's going to wakeup causes problems.

The enc28j60_setlink() was odd too.  It insists the link be down
before changing duplex, then brings the link up ... so I had to
put it down again to maintain the "lowpower if not up" invariant.

But the way it brings the link up is to enc28j60_hw_init(), which
it also does when bringing the link up.  So there's no need to
bring the link up when changing duplex ...


> > +/*
> > + * Low power mode shrinks power consumption about 100x, so we'd like
> > + * the chip to be in that mode whenever it's inactive.
> > + */
> > +static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
> > +{
> > +	int	tmp;
> > +
> > +	dev_dbg(&priv->spi->dev, "%s power...\n", is_low ? "low" : "high");
>
> use
> if(netif_msg_drv(priv)) ...
> Doing so we can switch on/off messages runtime using ethtool.

OK, although there's still a role for "-DDEBUG" compile-time
mesage removal.


> printk(...  instead of dev_dbg(), please.

Actually, I had to resist sending a patch converting this driver
to stop abusing printk in all those locations it should have been
using dev_*() messaging instead.

And to use the dev_*() messaging correctly ... the above shouldn't
have said "net eth1", but either "eth1:" or "enc28j60 spi1.0".

The general policy in the kernel is to avoid using printk for driver
messaging.  Couple messages to the hardware, so that when there are
two instances it's easy to tell which chip is affected.  That happens
for free with the "driver and device" prefix of the dev_*() messages.
	
In absense of interface renaming, network devices sometimes use "eth1"
style message prefixes ... after a previous message coupling that to
the particular hardware.

This driver also abuses __FUNCTION__ (general policy:  don't use it)
and underuses the "-DDEBUG" compile-time string removal.


> > +
> > +	mutex_lock(&priv->lock);
> > +	if (is_low) {
> > +		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
> > +		for (;;) {
> > +			tmp = nolock_regb_read(priv, ESTAT);
> > +			if (!(tmp & ESTAT_RXBUSY))
> > +				break;
> > +		}
> >   
> Avoid infinite waiting loops, please.
> Look at enc28j60_phy_read() for example.

Then there should be a single routine for all such busy-wait loops,
so each such usage doesn't need to be open-coded.  Less space, and
more obviously correct.  I'll add one and make phy_read() use it too.


> > +		for (;;) {
> > +			tmp = nolock_regb_read(priv, ECON1);
> > +			if (!(tmp & ECON1_TXRTS))
> > +				break;
> > +		}
> >   
> idem
> > +		/* ECON2_VRPS was set during initialization */
> > +		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
> > +	} else {
> > +		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
> > +		for (;;) {
> > +			tmp = nolock_regb_read(priv, ESTAT);
> > +			if (tmp & ESTAT_CLKRDY)
> > +				break;
> > +		}
> >   
> idem
>

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-06 17:11 ` Claudio Lanconelli
  2008-02-06 17:33   ` [patch 2.6.24-git] net/enc28j60: oops fix David Brownell
  2008-02-06 18:19   ` [patch 2.6.24-git] net/enc28j60: low power mode David Brownell
@ 2008-02-07  5:56   ` David Brownell
  2008-02-07 10:53     ` Claudio Lanconelli
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: " David Brownell
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: section fix David Brownell
  4 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-07  5:56 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

On Wednesday 06 February 2008, Claudio Lanconelli wrote:
> Good idea, but with your patch applied, after some ifconfig down - 
> ifconfig up cycle, the
> enc28j60 is left in an unknown state and it doesn' Rx/Tx anything.

How long did that take?  I did about four dozen

	ifconfig eth1 up
	sleep 3
	ifconfig eth1 down

cycles ... it worked fine.  The "sleep" was to let the link
negotiation complete.

- Dave

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-06 17:11 ` Claudio Lanconelli
                     ` (2 preceding siblings ...)
  2008-02-07  5:56   ` [patch 2.6.24-git] net/enc28j60: oops fix, " David Brownell
@ 2008-02-07  6:08   ` David Brownell
  2008-02-07 11:21     ` Claudio Lanconelli
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: section fix David Brownell
  4 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-07  6:08 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

On Wednesday 06 February 2008, Claudio Lanconelli wrote:
> > +		for (;;) {
> > +			tmp = nolock_regb_read(priv, ESTAT);
> > +			if (!(tmp & ESTAT_RXBUSY))
> > +				break;
> > +		}
> >   
> Avoid infinite waiting loops, please.
> Look at enc28j60_phy_read() for example.

Updated patch is appended.

==========	CUT HERE
Keep enc28j60 chips in low-power mode when they're not in use.
At typically 120 mA, these chips run hot even when idle; this
low power mode cuts that power usage by a factor of around 100.

This version provides a generic routine to poll a register until
its masked value equals some value ... e.g. bit set or cleared.
It's basically what the previous wait_phy_ready() did, but this
version is generalized to support the handshaking needed to
enter and exit low power mode.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/net/enc28j60.c |   70 ++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 55 insertions(+), 15 deletions(-)

--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_ne
 	mutex_unlock(&priv->lock);
 }
 
-/*
- * Wait until the PHY operation is complete.
- */
-static int wait_phy_ready(struct enc28j60_net *priv)
+static unsigned long msec20_to_jiffies;
+
+static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val)
 {
-	unsigned long timeout = jiffies + 20 * HZ / 1000;
-	int ret = 1;
+	unsigned long timeout = jiffies + msec20_to_jiffies;
 
 	/* 20 msec timeout read */
-	while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) {
+	while ((nolock_regb_read(priv, reg) & mask) != val) {
 		if (time_after(jiffies, timeout)) {
 			if (netif_msg_drv(priv))
-				printk(KERN_DEBUG DRV_NAME
-					": PHY ready timeout!\n");
-			ret = 0;
-			break;
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
 		}
 		cpu_relax();
 	}
-	return ret;
+	return 0;
+}
+
+/*
+ * Wait until the PHY operation is complete.
+ */
+static int wait_phy_ready(struct enc28j60_net *priv)
+{
+	return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1;
 }
 
 /*
@@ -594,6 +599,31 @@ static void nolock_txfifo_init(struct en
 	nolock_regw_write(priv, ETXNDL, end);
 }
 
+/*
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive.
+ */
+static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
+{
+	if (netif_msg_drv(priv))
+		dev_dbg(&priv->spi->dev, "%s power...\n",
+				is_low ? "low" : "high");
+
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0);
+		poll_ready(priv, ECON1, ECON1_TXRTS, 0);
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+}
+
 static int enc28j60_hw_init(struct enc28j60_net *priv)
 {
 	u8 reg;
@@ -612,8 +642,8 @@ static int enc28j60_hw_init(struct enc28
 	priv->tx_retry_count = 0;
 	priv->max_pk_counter = 0;
 	priv->rxfilter = RXFILTER_NORMAL;
-	/* enable address auto increment */
-	nolock_regb_write(priv, ECON2, ECON2_AUTOINC);
+	/* enable address auto increment and voltage regulator powersave */
+	nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
 
 	nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
 	nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
@@ -690,7 +720,9 @@ static int enc28j60_hw_init(struct enc28
 
 static void enc28j60_hw_enable(struct enc28j60_net *priv)
 {
-	/* enable interrutps */
+	enc28j60_lowpower(priv, false);
+
+	/* enable interrupts */
 	if (netif_msg_hw(priv))
 		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
 			__FUNCTION__);
@@ -717,6 +749,8 @@ static void enc28j60_hw_disable(struct e
 	nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
 	priv->hw_enable = false;
 	mutex_unlock(&priv->lock);
+
+	enc28j60_lowpower(priv, true);
 }
 
 static int
@@ -734,6 +768,8 @@ enc28j60_setlink(struct net_device *ndev
 						"hw_reset() failed\n");
 				ret = -EINVAL;
 			}
+			enc28j60_lowpower(priv, true);
+
 		} else {
 			if (netif_msg_link(priv))
 				dev_warn(&ndev->dev,
@@ -1537,6 +1573,8 @@ static int __devinit enc28j60_probe(stru
 	dev->watchdog_timeo = TX_TIMEOUT;
 	SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
 
+	enc28j60_lowpower(priv, true);
+
 	ret = register_netdev(dev);
 	if (ret) {
 		if (netif_msg_probe(priv))
@@ -1582,6 +1620,8 @@ static struct spi_driver enc28j60_driver
 
 static int __init enc28j60_init(void)
 {
+	msec20_to_jiffies = msecs_to_jiffies(20);
+
 	return spi_register_driver(&enc28j60_driver);
 }
 



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: section fix
  2008-02-06 17:11 ` Claudio Lanconelli
                     ` (3 preceding siblings ...)
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: " David Brownell
@ 2008-02-07  6:08   ` David Brownell
  2008-02-07 11:13     ` Claudio Lanconelli
  4 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-07  6:08 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

Minor bugfixes to the enc28j60 driver ... wrong section marking and
indentation, and bogus use of spi_bus_type.  (Setting the bus type
of a driver is a SPI framework responsibility.)

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
Bonus patch.

 drivers/net/enc28j60.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -1555,7 +1555,7 @@ error_alloc:
 	return ret;
 }
 
-static int enc28j60_remove(struct spi_device *spi)
+static int __devexit enc28j60_remove(struct spi_device *spi)
 {
 	struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
 
@@ -1572,9 +1572,8 @@ static int enc28j60_remove(struct spi_de
 static struct spi_driver enc28j60_driver = {
 	.driver = {
 		   .name = DRV_NAME,
-		   .bus = &spi_bus_type,
 		   .owner = THIS_MODULE,
-		   },
+	 },
 	.probe = enc28j60_probe,
 	.remove = __devexit_p(enc28j60_remove),
 };

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix
  2008-02-06 17:33   ` [patch 2.6.24-git] net/enc28j60: oops fix David Brownell
@ 2008-02-07 10:24     ` Claudio Lanconelli
  2008-02-19 20:52       ` [RESEND/patch 2.6.25-rc2-git] " David Brownell
  2008-03-05  1:17       ` [RE(*2)SEND/patch " David Brownell
  0 siblings, 2 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-07 10:24 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

David Brownell wrote:
> Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
> Not all architectures support unaligned accesses in kernel space.
>
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
>   

Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>

> ---
>  drivers/net/enc28j60.c |    3 ++-
>  1 files changed, 2 insertions(+), 1 deletion(-)
>
> --- a/drivers/net/enc28j60.c	2008-02-06 09:29:00.000000000 -0800
> +++ b/drivers/net/enc28j60.c	2008-02-06 09:30:03.000000000 -0800
> @@ -900,7 +900,7 @@ static void enc28j60_hw_rx(struct net_de
>  		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
>  			ndev->stats.rx_frame_errors++;
>  	} else {
> -		skb = dev_alloc_skb(len);
> +		skb = dev_alloc_skb(len + NET_IP_ALIGN);
>  		if (!skb) {
>  			if (netif_msg_rx_err(priv))
>  				dev_err(&ndev->dev,
> @@ -908,6 +908,7 @@ static void enc28j60_hw_rx(struct net_de
>  			ndev->stats.rx_dropped++;
>  		} else {
>  			skb->dev = ndev;
> +			skb_reserve(skb, NET_IP_ALIGN);
>  			/* copy the packet from the receive buffer */
>  			enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv),
>  					len, skb_put(skb, len));
>
>
>   


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-06 18:19   ` [patch 2.6.24-git] net/enc28j60: low power mode David Brownell
@ 2008-02-07 10:49     ` Claudio Lanconelli
  0 siblings, 0 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-07 10:49 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

David Brownell wrote:
> The driver seemed to already have some goofage there:
>
>   # ifconfig eth1 up
>   net eth1: link down
>   net eth1: link down
>   net eth1: normal mode
>   net eth1: multicast mode
>   net eth1: multicast mode
>   #
>
>   
Without low power patch I have:

# ifconfig eth0 up
net eth0: link down
net eth0: normal mode
net eth0: multicast mode
net eth0: multicast mode
# net eth0: link up - Half duplex

> I'd normally expect it not to go down when told to go up, and
> then only to do the multicast thing once ...
>   
multicast is called by network stacks, no control by the driver. The driver
just print message.
I don't know why enc28j60_set_multicast_list() are called more than once.

When you do an ifconfig up the driver reset the chip, so you see link down
before link up message.

>
>   
>> In such cases If I dump the counters with ifconfig I got rx error 
>> counter > 0 and the RX and TX packets counters are freezed.
>> Actually I don't know what causes the freeze, it needs investigation. 
>> The cause can be the rx error condition or the power down/up commands.
>> May be receiving packets while it's going to wakeup causes problems.
>>     
>
> The enc28j60_setlink() was odd too.  It insists the link be down
> before changing duplex, then brings the link up ... so I had to
> put it down again to maintain the "lowpower if not up" invariant.
>
> But the way it brings the link up is to enc28j60_hw_init(), which
> it also does when bringing the link up.  So there's no need to
> bring the link up when changing duplex ...
>
>
>   
I don't follow you anymore.
To change duplex mode the link must be down.
enc28j60_setlink() reinitialize the chip with new duplex mode,
enc28j60_hw_init() never brings link up.

I propose you to add set_lowpower(true) in the enc28j60_probe() and in 
the enc28j60_net_close() after enc28j60_hw_disable().
Probably we don't need to set_lowpower(false) in enc28j60_net_open() since
it performs a soft reset with enc28j60_hw_init().
Do you agree?


>>
>> use
>> if(netif_msg_drv(priv)) ...
>> Doing so we can switch on/off messages runtime using ethtool.
>>     
>
> OK, although there's still a role for "-DDEBUG" compile-time
> mesage removal.
>
>   
It's ok to use

if(netif_msg_drv(priv)) dev_dbv(...


> This driver also abuses __FUNCTION__ (general policy:  don't use it)
>   
Why?
> Then there should be a single routine for all such busy-wait loops,
> so each such usage doesn't need to be open-coded.  Less space, and
> more obviously correct.  I'll add one and make phy_read() use it too.
>
>   
Ok


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-07  5:56   ` [patch 2.6.24-git] net/enc28j60: oops fix, " David Brownell
@ 2008-02-07 10:53     ` Claudio Lanconelli
  2008-02-10 17:54       ` David Brownell
  0 siblings, 1 reply; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-07 10:53 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

David Brownell wrote:
> How long did that take?  I did about four dozen
>
> 	ifconfig eth1 up
> 	sleep 3
> 	ifconfig eth1 down
>
> cycles ... it worked fine.  The "sleep" was to let the link
> negotiation complete.
>
>   
After a couple of :

ifconfig eth0 down
(wait just 1 second)
ifconfig eth0 up

the network is frozen.

If I do another
ifconfig eth0 down
(wait just 1 second)
ifconfig eth0 up

restarts.
It's random, no rule.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: section fix
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: section fix David Brownell
@ 2008-02-07 11:13     ` Claudio Lanconelli
  2008-02-19 20:54       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: low power mode David Brownell
                         ` (3 more replies)
  0 siblings, 4 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-07 11:13 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

David Brownell wrote:
> Minor bugfixes to the enc28j60 driver ... wrong section marking and
> indentation, and bogus use of spi_bus_type.  (Setting the bus type
> of a driver is a SPI framework responsibility.)
>
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
>   

Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
> ---
> Bonus patch.
>
>  drivers/net/enc28j60.c |    5 ++---
>  1 files changed, 2 insertions(+), 3 deletions(-)
>
> --- a/drivers/net/enc28j60.c
> +++ b/drivers/net/enc28j60.c
> @@ -1555,7 +1555,7 @@ error_alloc:
>  	return ret;
>  }
>  
> -static int enc28j60_remove(struct spi_device *spi)
> +static int __devexit enc28j60_remove(struct spi_device *spi)
>  {
>  	struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
>  
> @@ -1572,9 +1572,8 @@ static int enc28j60_remove(struct spi_de
>  static struct spi_driver enc28j60_driver = {
>  	.driver = {
>  		   .name = DRV_NAME,
> -		   .bus = &spi_bus_type,
>  		   .owner = THIS_MODULE,
> -		   },
> +	 },
>  	.probe = enc28j60_probe,
>  	.remove = __devexit_p(enc28j60_remove),
>  };
>
>
>   


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: " David Brownell
@ 2008-02-07 11:21     ` Claudio Lanconelli
  2008-02-10 17:45       ` David Brownell
  2008-02-10 17:46       ` David Brownell
  0 siblings, 2 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-07 11:21 UTC (permalink / raw)
  To: netdev; +Cc: David Brownell

Sorry,
let me repeat what I said in previous mail.
I propose you to add set_lowpower(true) in the enc28j60_probe() and in 
the enc28j60_net_close() after enc28j60_hw_disable().
Probably we don't need to set_lowpower(false) in enc28j60_net_open() since
it performs a soft reset with enc28j60_hw_init() (not sure).

Furthermore, as you suggested, we also need to remove hw_init() from the 
setlink()
because hw_init() is called when we bring link up.

--- enc28j60.c 20 Dec 2007 10:47:01 -0000 1.22
+++ enc28j60.c 7 Feb 2008 11:07:20 -0000
@@ -740,12 +740,6 @@
if (!priv->hw_enable) {
if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) {
priv->full_duplex = (duplex == DUPLEX_FULL);
- if (!enc28j60_hw_init(priv)) {
- if (netif_msg_drv(priv))
- dev_err(&ndev->dev,
- "hw_reset() failed\n");
- ret = -EINVAL;
- }
} else {
if (netif_msg_link(priv))
dev_warn(&ndev->dev,

Can you update your low power patch with these modifications?


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-07 11:21     ` Claudio Lanconelli
@ 2008-02-10 17:45       ` David Brownell
  2008-02-10 17:46       ` David Brownell
  1 sibling, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-02-10 17:45 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

On Thursday 07 February 2008, Claudio Lanconelli wrote:
> Sorry,
> let me repeat what I said in previous mail.
> I propose you to add set_lowpower(true) in the enc28j60_probe() 

As the current patch does...


> and in  the enc28j60_net_close() after enc28j60_hw_disable().
> Probably we don't need to set_lowpower(false) in enc28j60_net_open() since
> it performs a soft reset with enc28j60_hw_init() (not sure).

The current patch sets the device in low power mode in hw_disable(),
and takes it out of that mode in hw_enable().  I can move them; and
the only "soft" thing about this chip's reset is when it starts from
a protocol command not the reset command.


> Furthermore, as you suggested, we also need to remove hw_init() from the 
> setlink()
> because hw_init() is called when we bring link up.
> 
> --- enc28j60.c 20 Dec 2007 10:47:01 -0000 1.22
> +++ enc28j60.c 7 Feb 2008 11:07:20 -0000
> @@ -740,12 +740,6 @@
> if (!priv->hw_enable) {
> if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) {
> priv->full_duplex = (duplex == DUPLEX_FULL);
> - if (!enc28j60_hw_init(priv)) {
> - if (netif_msg_drv(priv))
> - dev_err(&ndev->dev,
> - "hw_reset() failed\n");
> - ret = -EINVAL;
> - }

Right.  Without the patch mangling presumably done by your mailer.  ;)


> } else {
> if (netif_msg_link(priv))
> dev_warn(&ndev->dev,
> 
> Can you update your low power patch with these modifications?
> 

Done -- see my next patch.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: low power mode
  2008-02-07 11:21     ` Claudio Lanconelli
  2008-02-10 17:45       ` David Brownell
@ 2008-02-10 17:46       ` David Brownell
  1 sibling, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-02-10 17:46 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

Keep enc28j60 chips in low-power mode when they're not in use.
At typically 120 mA, these chips run hot even when idle; this
low power mode cuts that power usage by a factor of around 100.

This version provides a generic routine to poll a register until
its masked value equals some value ... e.g. bit set or cleared.
It's basically what the previous wait_phy_ready() did, but this
version is generalized to support the handshaking needed to
enter and exit low power mode.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
---
 drivers/net/enc28j60.c |   81 ++++++++++++++++++++++++++++++++++---------------
 1 files changed, 57 insertions(+), 24 deletions(-)

--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_ne
 	mutex_unlock(&priv->lock);
 }
 
-/*
- * Wait until the PHY operation is complete.
- */
-static int wait_phy_ready(struct enc28j60_net *priv)
+static unsigned long msec20_to_jiffies;
+
+static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val)
 {
-	unsigned long timeout = jiffies + 20 * HZ / 1000;
-	int ret = 1;
+	unsigned long timeout = jiffies + msec20_to_jiffies;
 
 	/* 20 msec timeout read */
-	while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) {
+	while ((nolock_regb_read(priv, reg) & mask) != val) {
 		if (time_after(jiffies, timeout)) {
 			if (netif_msg_drv(priv))
-				printk(KERN_DEBUG DRV_NAME
-					": PHY ready timeout!\n");
-			ret = 0;
-			break;
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
 		}
 		cpu_relax();
 	}
-	return ret;
+	return 0;
+}
+
+/*
+ * Wait until the PHY operation is complete.
+ */
+static int wait_phy_ready(struct enc28j60_net *priv)
+{
+	return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1;
 }
 
 /*
@@ -594,6 +599,32 @@ static void nolock_txfifo_init(struct en
 	nolock_regw_write(priv, ETXNDL, end);
 }
 
+/*
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive.  (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
+{
+	if (netif_msg_drv(priv))
+		dev_dbg(&priv->spi->dev, "%s power...\n",
+				is_low ? "low" : "high");
+
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0);
+		poll_ready(priv, ECON1, ECON1_TXRTS, 0);
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+}
+
 static int enc28j60_hw_init(struct enc28j60_net *priv)
 {
 	u8 reg;
@@ -612,8 +643,8 @@ static int enc28j60_hw_init(struct enc28
 	priv->tx_retry_count = 0;
 	priv->max_pk_counter = 0;
 	priv->rxfilter = RXFILTER_NORMAL;
-	/* enable address auto increment */
-	nolock_regb_write(priv, ECON2, ECON2_AUTOINC);
+	/* enable address auto increment and voltage regulator powersave */
+	nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
 
 	nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
 	nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
@@ -690,7 +721,7 @@ static int enc28j60_hw_init(struct enc28
 
 static void enc28j60_hw_enable(struct enc28j60_net *priv)
 {
-	/* enable interrutps */
+	/* enable interrupts */
 	if (netif_msg_hw(priv))
 		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
 			__FUNCTION__);
@@ -726,15 +757,12 @@ enc28j60_setlink(struct net_device *ndev
 	int ret = 0;
 
 	if (!priv->hw_enable) {
-		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) {
+		/* link is in low power mode now; duplex setting
+		 * will take effect on next enc28j60_hw_init().
+		 */
+		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10)
 			priv->full_duplex = (duplex == DUPLEX_FULL);
-			if (!enc28j60_hw_init(priv)) {
-				if (netif_msg_drv(priv))
-					dev_err(&ndev->dev,
-						"hw_reset() failed\n");
-				ret = -EINVAL;
-			}
-		} else {
+		else {
 			if (netif_msg_link(priv))
 				dev_warn(&ndev->dev,
 					"unsupported link setting\n");
@@ -1307,7 +1335,7 @@ static int enc28j60_net_open(struct net_
 		}
 		return -EADDRNOTAVAIL;
 	}
-	/* Reset the hardware here */
+	/* Reset the hardware here (and take it out of low power mode) */
 	enc28j60_hw_disable(priv);
 	if (!enc28j60_hw_init(priv)) {
 		if (netif_msg_ifup(priv))
@@ -1337,6 +1365,7 @@ static int enc28j60_net_close(struct net
 		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__);
 
 	enc28j60_hw_disable(priv);
+	enc28j60_lowpower(priv, true);
 	netif_stop_queue(dev);
 
 	return 0;
@@ -1537,6 +1566,8 @@ static int __devinit enc28j60_probe(stru
 	dev->watchdog_timeo = TX_TIMEOUT;
 	SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
 
+	enc28j60_lowpower(priv, true);
+
 	ret = register_netdev(dev);
 	if (ret) {
 		if (netif_msg_probe(priv))
@@ -1582,6 +1613,8 @@ static struct spi_driver enc28j60_driver
 
 static int __init enc28j60_init(void)
 {
+	msec20_to_jiffies = msecs_to_jiffies(20);
+
 	return spi_register_driver(&enc28j60_driver);
 }
 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-07 10:53     ` Claudio Lanconelli
@ 2008-02-10 17:54       ` David Brownell
  2008-02-11 12:07         ` Claudio Lanconelli
  0 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-10 17:54 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

On Thursday 07 February 2008, Claudio Lanconelli wrote:
> David Brownell wrote:
> > How long did that take?  I did about four dozen
> >
> > 	ifconfig eth1 up
> > 	sleep 3
> > 	ifconfig eth1 down
> >
> > cycles ... it worked fine.  The "sleep" was to let the link
> > negotiation complete.
> >
> >   
> After a couple of :
> 
> ifconfig eth0 down
> (wait just 1 second)
> ifconfig eth0 up
> 
> the network is frozen.
> 
> If I do another
> ifconfig eth0 down
> (wait just 1 second)
> ifconfig eth0 up
> 
> restarts.
> It's random, no rule.

I write a shell loop to do that, and added a "ping -c2" too.
If that was done before the "sleep 1" no packets flowed.
Afterwards, no problem -- ever. 

(And outside the loop, "ethool -s eth1 duplex full".)

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-10 17:54       ` David Brownell
@ 2008-02-11 12:07         ` Claudio Lanconelli
  2008-02-11 20:23           ` David Brownell
  0 siblings, 1 reply; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-11 12:07 UTC (permalink / raw)
  To: David Brownell; +Cc: netdev

David Brownell wrote:

>> > and in  the enc28j60_net_close() after enc28j60_hw_disable().
>> > Probably we don't need to set_lowpower(false) in enc28j60_net_open() since
>> > it performs a soft reset with enc28j60_hw_init() (not sure).
>>     
>
> The current patch sets the device in low power mode in hw_disable(),
> and takes it out of that mode in hw_enable().  I can move them; and
> the only "soft" thing about this chip's reset is when it starts from
> a protocol command not the reset command.
>
>   
I want to mean the reset software command. It's functionally
equivalent to hardware system reset, but it seems to need exit low
power mode to work flawlessly.
I have tried your latest patch. Only after the following change it
works fine (no more rx errors during ifconfig up).
I added enc28j60_lowpower(false) just before enc28j60_hw_init()

@@ -1318,8 +1347,9 @@
         }
         return -EADDRNOTAVAIL;
     }
-    /* Reset the hardware here */
+    /* Reset the hardware here (and take it out of low power mode) */
     enc28j60_hw_disable(priv);
+    enc28j60_lowpower(priv, false);
     if (!enc28j60_hw_init(priv)) {
         if (netif_msg_ifup(priv))
             dev_err(&dev->dev, "hw_reset() failed\n");

With this addition you can add Acked-by line.
Thank you.
>> After a couple of :
>>
>> ifconfig eth0 down
>> (wait just 1 second)
>> ifconfig eth0 up
>>
>> the network is frozen.
>>
>> If I do another
>> ifconfig eth0 down
>> (wait just 1 second)
>> ifconfig eth0 up
>>
>> restarts.
>> It's random, no rule.
>>     
>
> I write a shell loop to do that, and added a "ping -c2" too.
> If that was done before the "sleep 1" no packets flowed.
> Afterwards, no problem -- ever. 
>
> (And outside the loop, "ethool -s eth1 duplex full".)
>
>
>   
I forgot to tell that during my test I have a web server running on the 
board and
a client continuously requesting a page.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-11 12:07         ` Claudio Lanconelli
@ 2008-02-11 20:23           ` David Brownell
  2008-02-14 10:28             ` Claudio Lanconelli
  0 siblings, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-02-11 20:23 UTC (permalink / raw)
  To: Claudio Lanconelli; +Cc: netdev

On Monday 11 February 2008, Claudio Lanconelli wrote:
> I have tried your latest patch. Only after the following change it
> works fine (no more rx errors during ifconfig up).

Hmm, what chip rev do you have?  Different errata and all.
ISTR mine is rev4; so, not the most current, but not the
oldest version either.


> I added enc28j60_lowpower(false) just before enc28j60_hw_init()

Hmm, I'd have expected it would go best *before* that, but
what you include below shows it going *after* ...

If there's some problem where reset doesn't work correctly
in low power mode, who knows what else would need manual
resetting.

 
> @@ -1318,8 +1347,9 @@
>          }
>          return -EADDRNOTAVAIL;
>      }
> -    /* Reset the hardware here */
> +    /* Reset the hardware here (and take it out of low power mode) */
>      enc28j60_hw_disable(priv);
> +    enc28j60_lowpower(priv, false);
>      if (!enc28j60_hw_init(priv)) {
>          if (netif_msg_ifup(priv))
>              dev_err(&dev->dev, "hw_reset() failed\n");
>
> With this addition you can add Acked-by line.

Better yet, since I can't reproduce the problem, why don't
you just update my latest patch with the relevant version
of this tweak, and then forward it as "From: " me and with
both our signoffs.  That's the usual way to cope with this
type of tweaking.  (Not all updates to your driver should
need your signoff, but then most patches shouldn't need
very many iterations either.)

- Dave



^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [patch 2.6.24-git] net/enc28j60: oops fix, low power mode
  2008-02-11 20:23           ` David Brownell
@ 2008-02-14 10:28             ` Claudio Lanconelli
  0 siblings, 0 replies; 26+ messages in thread
From: Claudio Lanconelli @ 2008-02-14 10:28 UTC (permalink / raw)
  To: David Brownell; +Cc: netdev

David Brownell wrote:
> On Monday 11 February 2008, Claudio Lanconelli wrote:
>   
>> I have tried your latest patch. Only after the following change it
>> works fine (no more rx errors during ifconfig up).
>>     
>
> Hmm, what chip rev do you have?  Different errata and all.
> ISTR mine is rev4; so, not the most current, but not the
> oldest version either.
>   
I use the same revision.

>> I added enc28j60_lowpower(false) just before enc28j60_hw_init()
>>     
>
> Hmm, I'd have expected it would go best *before* that, but
> what you include below shows it going *after* ...
>
> If there's some problem where reset doesn't work correctly
> in low power mode, who knows what else would need manual
> resetting.
>
>   
I don't know why it needs low power resume before reset.
I read in the errata tath clkready bit after reset doesn't work reliably.
May be something related to this, but undocumented.

> Better yet, since I can't reproduce the problem, why don't
> you just update my latest patch with the relevant version
> of this tweak, and then forward it as "From: " me and with
> both our signoffs.  That's the usual way to cope with this
> type of tweaking.  (Not all updates to your driver should
> need your signoff, but then most patches shouldn't need
> very many iterations either.)
>   
Done


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RESEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
  2008-02-07 10:24     ` Claudio Lanconelli
@ 2008-02-19 20:52       ` David Brownell
  2008-03-05  1:17       ` [RE(*2)SEND/patch " David Brownell
  1 sibling, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-02-19 20:52 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
Not all architectures support unaligned accesses in kernel space.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
Seems the enc28j60 patches didn't get picked up, ergo resend...
they're not in net-2.6.git, even this oops fix.

 drivers/net/enc28j60.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletion(-)

--- a/drivers/net/enc28j60.c	2008-02-06 09:29:00.000000000 -0800
+++ b/drivers/net/enc28j60.c	2008-02-06 09:30:03.000000000 -0800
@@ -900,7 +900,7 @@ static void enc28j60_hw_rx(struct net_de
 		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
 			ndev->stats.rx_frame_errors++;
 	} else {
-		skb = dev_alloc_skb(len);
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
 		if (!skb) {
 			if (netif_msg_rx_err(priv))
 				dev_err(&ndev->dev,
@@ -908,6 +908,7 @@ static void enc28j60_hw_rx(struct net_de
 			ndev->stats.rx_dropped++;
 		} else {
 			skb->dev = ndev;
+			skb_reserve(skb, NET_IP_ALIGN);
 			/* copy the packet from the receive buffer */
 			enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv),
 					len, skb_put(skb, len));

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RESEND/patch 2.6.25-rc2-git] net/enc28j60: low power mode
  2008-02-07 11:13     ` Claudio Lanconelli
@ 2008-02-19 20:54       ` David Brownell
  2008-02-19 20:56       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: section fix David Brownell
                         ` (2 subsequent siblings)
  3 siblings, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-02-19 20:54 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Keep enc28j60 chips in low-power mode when they're not in use.
At typically 120 mA, these chips run hot even when idle; this
low power mode cuts that power usage by a factor of around 100.

This version provides a generic routine to poll a register until
its masked value equals some value ... e.g. bit set or cleared.
It's basically what the previous wait_phy_ready() did, but this
version is generalized to support the handshaking needed to
enter and exit low power mode.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
 drivers/net/enc28j60.c |   82 ++++++++++++++++++++++++++++++++++---------------
 1 files changed, 58 insertions(+), 24 deletions(-)

--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_ne
 	mutex_unlock(&priv->lock);
 }
 
-/*
- * Wait until the PHY operation is complete.
- */
-static int wait_phy_ready(struct enc28j60_net *priv)
+static unsigned long msec20_to_jiffies;
+
+static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val)
 {
-	unsigned long timeout = jiffies + 20 * HZ / 1000;
-	int ret = 1;
+	unsigned long timeout = jiffies + msec20_to_jiffies;
 
 	/* 20 msec timeout read */
-	while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) {
+	while ((nolock_regb_read(priv, reg) & mask) != val) {
 		if (time_after(jiffies, timeout)) {
 			if (netif_msg_drv(priv))
-				printk(KERN_DEBUG DRV_NAME
-					": PHY ready timeout!\n");
-			ret = 0;
-			break;
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
 		}
 		cpu_relax();
 	}
-	return ret;
+	return 0;
+}
+
+/*
+ * Wait until the PHY operation is complete.
+ */
+static int wait_phy_ready(struct enc28j60_net *priv)
+{
+	return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1;
 }
 
 /*
@@ -594,6 +599,32 @@ static void nolock_txfifo_init(struct en
 	nolock_regw_write(priv, ETXNDL, end);
 }
 
+/*
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive.  (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
+{
+	if (netif_msg_drv(priv))
+		dev_dbg(&priv->spi->dev, "%s power...\n",
+				is_low ? "low" : "high");
+
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0);
+		poll_ready(priv, ECON1, ECON1_TXRTS, 0);
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+}
+
 static int enc28j60_hw_init(struct enc28j60_net *priv)
 {
 	u8 reg;
@@ -612,8 +643,8 @@ static int enc28j60_hw_init(struct enc28
 	priv->tx_retry_count = 0;
 	priv->max_pk_counter = 0;
 	priv->rxfilter = RXFILTER_NORMAL;
-	/* enable address auto increment */
-	nolock_regb_write(priv, ECON2, ECON2_AUTOINC);
+	/* enable address auto increment and voltage regulator powersave */
+	nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
 
 	nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
 	nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
@@ -690,7 +721,7 @@ static int enc28j60_hw_init(struct enc28
 
 static void enc28j60_hw_enable(struct enc28j60_net *priv)
 {
-	/* enable interrutps */
+	/* enable interrupts */
 	if (netif_msg_hw(priv))
 		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
 			__FUNCTION__);
@@ -726,15 +757,12 @@ enc28j60_setlink(struct net_device *ndev
 	int ret = 0;
 
 	if (!priv->hw_enable) {
-		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) {
+		/* link is in low power mode now; duplex setting
+		 * will take effect on next enc28j60_hw_init().
+		 */
+		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10)
 			priv->full_duplex = (duplex == DUPLEX_FULL);
-			if (!enc28j60_hw_init(priv)) {
-				if (netif_msg_drv(priv))
-					dev_err(&ndev->dev,
-						"hw_reset() failed\n");
-				ret = -EINVAL;
-			}
-		} else {
+		else {
 			if (netif_msg_link(priv))
 				dev_warn(&ndev->dev,
 					"unsupported link setting\n");
@@ -1307,7 +1335,8 @@ static int enc28j60_net_open(struct net_
 		}
 		return -EADDRNOTAVAIL;
 	}
-	/* Reset the hardware here */
+	/* Reset the hardware here (and take it out of low power mode) */
+	enc28j60_lowpower(priv, false);
 	enc28j60_hw_disable(priv);
 	if (!enc28j60_hw_init(priv)) {
 		if (netif_msg_ifup(priv))
@@ -1337,6 +1366,7 @@ static int enc28j60_net_close(struct net
 		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__);
 
 	enc28j60_hw_disable(priv);
+	enc28j60_lowpower(priv, true);
 	netif_stop_queue(dev);
 
 	return 0;
@@ -1537,6 +1567,8 @@ static int __devinit enc28j60_probe(stru
 	dev->watchdog_timeo = TX_TIMEOUT;
 	SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
 
+	enc28j60_lowpower(priv, true);
+
 	ret = register_netdev(dev);
 	if (ret) {
 		if (netif_msg_probe(priv))
@@ -1582,6 +1614,8 @@ static struct spi_driver enc28j60_driver
 
 static int __init enc28j60_init(void)
 {
+	msec20_to_jiffies = msecs_to_jiffies(20);
+
 	return spi_register_driver(&enc28j60_driver);
 }
 

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RESEND/patch 2.6.25-rc2-git] net/enc28j60: section fix
  2008-02-07 11:13     ` Claudio Lanconelli
  2008-02-19 20:54       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: low power mode David Brownell
@ 2008-02-19 20:56       ` David Brownell
  2008-04-19  2:08       ` [RESEND/patch 2.6.25] " David Brownell
  2008-04-19  2:08       ` [RESEND/patch 2.6.25] net/enc28j60: low power mode David Brownell
  3 siblings, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-02-19 20:56 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Minor bugfixes to the enc28j60 driver ... wrong section marking,
indentation, and bogus use of spi_bus_type.  There's still major
overuse of printk; essentially every place it's used should switch
to dev_*() messaging primitives.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
 drivers/net/enc28j60.c |    5 ++---
 1 files changed, 2 insertions(+), 3 deletions(-)

--- a/drivers/net/enc28j60.c	2008-01-29 11:07:14.000000000 -0800
+++ b/drivers/net/enc28j60.c	2008-01-29 12:43:18.000000000 -0800
@@ -1555,7 +1555,7 @@ error_alloc:
 	return ret;
 }
 
-static int enc28j60_remove(struct spi_device *spi)
+static int __devexit enc28j60_remove(struct spi_device *spi)
 {
 	struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
 
@@ -1572,9 +1572,8 @@ static int enc28j60_remove(struct spi_de
 static struct spi_driver enc28j60_driver = {
 	.driver = {
 		   .name = DRV_NAME,
-		   .bus = &spi_bus_type,
 		   .owner = THIS_MODULE,
-		   },
+	 },
 	.probe = enc28j60_probe,
 	.remove = __devexit_p(enc28j60_remove),
 };

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RE(*2)SEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
  2008-02-07 10:24     ` Claudio Lanconelli
  2008-02-19 20:52       ` [RESEND/patch 2.6.25-rc2-git] " David Brownell
@ 2008-03-05  1:17       ` David Brownell
  2008-03-06  2:52         ` David Miller
  1 sibling, 1 reply; 26+ messages in thread
From: David Brownell @ 2008-03-05  1:17 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
Not all architectures support unaligned accesses in kernel space.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
Seems the enc28j60 patches didn't get picked up, ergo (2x) resend...
they're not in net-2.6.git, even this oops fix (which I'd expect
would be prioritized for 2.6.25)

 drivers/net/enc28j60.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletion(-)

--- a/drivers/net/enc28j60.c	2008-02-06 09:29:00.000000000 -0800
+++ b/drivers/net/enc28j60.c	2008-02-06 09:30:03.000000000 -0800
@@ -900,7 +900,7 @@ static void enc28j60_hw_rx(struct net_de
 		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
 			ndev->stats.rx_frame_errors++;
 	} else {
-		skb = dev_alloc_skb(len);
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
 		if (!skb) {
 			if (netif_msg_rx_err(priv))
 				dev_err(&ndev->dev,
@@ -908,6 +908,7 @@ static void enc28j60_hw_rx(struct net_de
 			ndev->stats.rx_dropped++;
 		} else {
 			skb->dev = ndev;
+			skb_reserve(skb, NET_IP_ALIGN);
 			/* copy the packet from the receive buffer */
 			enc28j60_mem_read(priv, priv->next_pk_ptr + sizeof(rsv),
 					len, skb_put(skb, len));

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RE(*2)SEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
  2008-03-05  1:17       ` [RE(*2)SEND/patch " David Brownell
@ 2008-03-06  2:52         ` David Miller
  2008-03-06  3:05           ` David Brownell
  0 siblings, 1 reply; 26+ messages in thread
From: David Miller @ 2008-03-06  2:52 UTC (permalink / raw)
  To: david-b; +Cc: netdev, lanconelli.claudio

From: David Brownell <david-b@pacbell.net>
Date: Tue, 4 Mar 2008 17:17:32 -0800

> Seems the enc28j60 patches didn't get picked up, ergo (2x) resend...
> they're not in net-2.6.git, even this oops fix (which I'd expect
> would be prioritized for 2.6.25)

We prioritize them about the same as bootup hard hangs with EHCI.

> Subject: [RE(*2)SEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
> 
> Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
> Not all architectures support unaligned accesses in kernel space.
> 
> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>

Applied.

^ permalink raw reply	[flat|nested] 26+ messages in thread

* Re: [RE(*2)SEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
  2008-03-06  2:52         ` David Miller
@ 2008-03-06  3:05           ` David Brownell
  0 siblings, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-03-06  3:05 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, lanconelli.claudio

On Wednesday 05 March 2008, David Miller wrote:
> From: David Brownell <david-b@pacbell.net>
> Date: Tue, 4 Mar 2008 17:17:32 -0800
> 
> > Seems the enc28j60 patches didn't get picked up, ergo (2x) resend...
> > they're not in net-2.6.git, even this oops fix (which I'd expect
> > would be prioritized for 2.6.25)
> 
> We prioritize them about the same as bootup hard hangs with EHCI.

I don't know about any of those that are unfixed ...


> > Subject: [RE(*2)SEND/patch 2.6.25-rc2-git] net/enc28j60: oops fix
> > 
> > Prevent oops on enc28j60 packet RX:  make sure buffers are aligned.
> > Not all architectures support unaligned accesses in kernel space.
> > 
> > Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
> > Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
> 
> Applied.

Thanks.


^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RESEND/patch 2.6.25] net/enc28j60: section fix
  2008-02-07 11:13     ` Claudio Lanconelli
  2008-02-19 20:54       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: low power mode David Brownell
  2008-02-19 20:56       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: section fix David Brownell
@ 2008-04-19  2:08       ` David Brownell
  2008-04-19  2:08       ` [RESEND/patch 2.6.25] net/enc28j60: low power mode David Brownell
  3 siblings, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-04-19  2:08 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Minor bugfixes to the enc28j60 driver ... wrong section marking,
indentation, and bogus use of spi_bus_type. 

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Acked-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
 drivers/net/enc28j60.c |    5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

--- g26.orig/drivers/net/enc28j60.c	2008-02-22 19:48:13.000000000 -0800
+++ g26/drivers/net/enc28j60.c	2008-02-22 19:48:14.000000000 -0800
@@ -1556,7 +1556,7 @@ error_alloc:
 	return ret;
 }
 
-static int enc28j60_remove(struct spi_device *spi)
+static int __devexit enc28j60_remove(struct spi_device *spi)
 {
 	struct enc28j60_net *priv = dev_get_drvdata(&spi->dev);
 
@@ -1573,9 +1573,8 @@ static int enc28j60_remove(struct spi_de
 static struct spi_driver enc28j60_driver = {
 	.driver = {
 		   .name = DRV_NAME,
-		   .bus = &spi_bus_type,
 		   .owner = THIS_MODULE,
-		   },
+	 },
 	.probe = enc28j60_probe,
 	.remove = __devexit_p(enc28j60_remove),
 };

^ permalink raw reply	[flat|nested] 26+ messages in thread

* [RESEND/patch 2.6.25] net/enc28j60: low power mode
  2008-02-07 11:13     ` Claudio Lanconelli
                         ` (2 preceding siblings ...)
  2008-04-19  2:08       ` [RESEND/patch 2.6.25] " David Brownell
@ 2008-04-19  2:08       ` David Brownell
  3 siblings, 0 replies; 26+ messages in thread
From: David Brownell @ 2008-04-19  2:08 UTC (permalink / raw)
  To: netdev; +Cc: Claudio Lanconelli

Keep enc28j60 chips in low-power mode when they're not in use.
At typically 120 mA, these chips run hot even when idle; this
low power mode cuts that power usage by a factor of around 100.

This version provides a generic routine to poll a register until
its masked value equals some value ... e.g. bit set or cleared.
It's basically what the previous wait_phy_ready() did, but this
version is generalized to support the handshaking needed to
enter and exit low power mode.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Claudio Lanconelli <lanconelli.claudio@eptar.com>
---
 drivers/net/enc28j60.c |   82 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 58 insertions(+), 24 deletions(-)

--- g26.orig/drivers/net/enc28j60.c	2008-02-22 19:48:14.000000000 -0800
+++ g26/drivers/net/enc28j60.c	2008-02-22 19:48:21.000000000 -0800
@@ -400,26 +400,31 @@ enc28j60_packet_write(struct enc28j60_ne
 	mutex_unlock(&priv->lock);
 }
 
-/*
- * Wait until the PHY operation is complete.
- */
-static int wait_phy_ready(struct enc28j60_net *priv)
+static unsigned long msec20_to_jiffies;
+
+static int poll_ready(struct enc28j60_net *priv, u8 reg, u8 mask, u8 val)
 {
-	unsigned long timeout = jiffies + 20 * HZ / 1000;
-	int ret = 1;
+	unsigned long timeout = jiffies + msec20_to_jiffies;
 
 	/* 20 msec timeout read */
-	while (nolock_regb_read(priv, MISTAT) & MISTAT_BUSY) {
+	while ((nolock_regb_read(priv, reg) & mask) != val) {
 		if (time_after(jiffies, timeout)) {
 			if (netif_msg_drv(priv))
-				printk(KERN_DEBUG DRV_NAME
-					": PHY ready timeout!\n");
-			ret = 0;
-			break;
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
 		}
 		cpu_relax();
 	}
-	return ret;
+	return 0;
+}
+
+/*
+ * Wait until the PHY operation is complete.
+ */
+static int wait_phy_ready(struct enc28j60_net *priv)
+{
+	return poll_ready(priv, MISTAT, MISTAT_BUSY, 0) ? 0 : 1;
 }
 
 /*
@@ -594,6 +599,32 @@ static void nolock_txfifo_init(struct en
 	nolock_regw_write(priv, ETXNDL, end);
 }
 
+/*
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive.  (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc28j60_lowpower(struct enc28j60_net *priv, bool is_low)
+{
+	if (netif_msg_drv(priv))
+		dev_dbg(&priv->spi->dev, "%s power...\n",
+				is_low ? "low" : "high");
+
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		poll_ready(priv, ESTAT, ESTAT_RXBUSY, 0);
+		poll_ready(priv, ECON1, ECON1_TXRTS, 0);
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		poll_ready(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+}
+
 static int enc28j60_hw_init(struct enc28j60_net *priv)
 {
 	u8 reg;
@@ -612,8 +643,8 @@ static int enc28j60_hw_init(struct enc28
 	priv->tx_retry_count = 0;
 	priv->max_pk_counter = 0;
 	priv->rxfilter = RXFILTER_NORMAL;
-	/* enable address auto increment */
-	nolock_regb_write(priv, ECON2, ECON2_AUTOINC);
+	/* enable address auto increment and voltage regulator powersave */
+	nolock_regb_write(priv, ECON2, ECON2_AUTOINC | ECON2_VRPS);
 
 	nolock_rxfifo_init(priv, RXSTART_INIT, RXEND_INIT);
 	nolock_txfifo_init(priv, TXSTART_INIT, TXEND_INIT);
@@ -690,7 +721,7 @@ static int enc28j60_hw_init(struct enc28
 
 static void enc28j60_hw_enable(struct enc28j60_net *priv)
 {
-	/* enable interrutps */
+	/* enable interrupts */
 	if (netif_msg_hw(priv))
 		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
 			__FUNCTION__);
@@ -726,15 +757,12 @@ enc28j60_setlink(struct net_device *ndev
 	int ret = 0;
 
 	if (!priv->hw_enable) {
-		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10) {
+		/* link is in low power mode now; duplex setting
+		 * will take effect on next enc28j60_hw_init().
+		 */
+		if (autoneg == AUTONEG_DISABLE && speed == SPEED_10)
 			priv->full_duplex = (duplex == DUPLEX_FULL);
-			if (!enc28j60_hw_init(priv)) {
-				if (netif_msg_drv(priv))
-					dev_err(&ndev->dev,
-						"hw_reset() failed\n");
-				ret = -EINVAL;
-			}
-		} else {
+		else {
 			if (netif_msg_link(priv))
 				dev_warn(&ndev->dev,
 					"unsupported link setting\n");
@@ -1307,7 +1335,8 @@ static int enc28j60_net_open(struct net_
 		}
 		return -EADDRNOTAVAIL;
 	}
-	/* Reset the hardware here */
+	/* Reset the hardware here (and take it out of low power mode) */
+	enc28j60_lowpower(priv, false);
 	enc28j60_hw_disable(priv);
 	if (!enc28j60_hw_init(priv)) {
 		if (netif_msg_ifup(priv))
@@ -1337,6 +1366,7 @@ static int enc28j60_net_close(struct net
 		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __FUNCTION__);
 
 	enc28j60_hw_disable(priv);
+	enc28j60_lowpower(priv, true);
 	netif_stop_queue(dev);
 
 	return 0;
@@ -1537,6 +1567,8 @@ static int __devinit enc28j60_probe(stru
 	dev->watchdog_timeo = TX_TIMEOUT;
 	SET_ETHTOOL_OPS(dev, &enc28j60_ethtool_ops);
 
+	enc28j60_lowpower(priv, true);
+
 	ret = register_netdev(dev);
 	if (ret) {
 		if (netif_msg_probe(priv))
@@ -1581,6 +1613,8 @@ static struct spi_driver enc28j60_driver
 
 static int __init enc28j60_init(void)
 {
+	msec20_to_jiffies = msecs_to_jiffies(20);
+
 	return spi_register_driver(&enc28j60_driver);
 }
 

^ permalink raw reply	[flat|nested] 26+ messages in thread

end of thread, other threads:[~2008-04-19  2:38 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-05 19:01 [patch 2.6.24-git] net/enc28j60: oops fix, low power mode David Brownell
2008-02-06 17:11 ` Claudio Lanconelli
2008-02-06 17:33   ` [patch 2.6.24-git] net/enc28j60: oops fix David Brownell
2008-02-07 10:24     ` Claudio Lanconelli
2008-02-19 20:52       ` [RESEND/patch 2.6.25-rc2-git] " David Brownell
2008-03-05  1:17       ` [RE(*2)SEND/patch " David Brownell
2008-03-06  2:52         ` David Miller
2008-03-06  3:05           ` David Brownell
2008-02-06 18:19   ` [patch 2.6.24-git] net/enc28j60: low power mode David Brownell
2008-02-07 10:49     ` Claudio Lanconelli
2008-02-07  5:56   ` [patch 2.6.24-git] net/enc28j60: oops fix, " David Brownell
2008-02-07 10:53     ` Claudio Lanconelli
2008-02-10 17:54       ` David Brownell
2008-02-11 12:07         ` Claudio Lanconelli
2008-02-11 20:23           ` David Brownell
2008-02-14 10:28             ` Claudio Lanconelli
2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: " David Brownell
2008-02-07 11:21     ` Claudio Lanconelli
2008-02-10 17:45       ` David Brownell
2008-02-10 17:46       ` David Brownell
2008-02-07  6:08   ` [patch 2.6.24-git] net/enc28j60: section fix David Brownell
2008-02-07 11:13     ` Claudio Lanconelli
2008-02-19 20:54       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: low power mode David Brownell
2008-02-19 20:56       ` [RESEND/patch 2.6.25-rc2-git] net/enc28j60: section fix David Brownell
2008-04-19  2:08       ` [RESEND/patch 2.6.25] " David Brownell
2008-04-19  2:08       ` [RESEND/patch 2.6.25] net/enc28j60: low power mode David Brownell

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.