Skip to contentSkip to conversation

eeprom/at25: bugfix "not ready" timeout after write

PATCH In review David Brownell 3 participants 4 messages Jul 4, 2009 – Jul 18, 2009 2 messages not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 eeprom/at25: bugfix "not ready" timeout after write

From: Sebastian Heutling <heutling-U9zb0o/[email protected]>

Under certain circumstances msleep(1) within the loop, which waits for the EEPROM to be finished, might take longer than the timeout. On the next loop the status register might now return to be ready and therefore the loop finishes. The following check now tests if a timeout occurred and if so returns an error although the device reported it was ready.

This fix replaces testing the occurrence of the timeout by testing the "not ready" bit in the status register.

---
 drivers/misc/eeprom/at25.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/misc/eeprom/at25.c +2 -2
@@ -173,6 +173,7 @@ at25_ee_write(struct at25_data *at25, co
 		unsigned	segment;
 		unsigned	offset = (unsigned) off;
 		u8		*cp = bounce + 1;
+		int		sr;
 
 		*cp = AT25_WREN;
 		status = spi_write(at25->spi, cp, 1);
@@ -214,7 +215,6 @@ at25_ee_write(struct at25_data *at25, co
 		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
 		retries = 0;
 		do {
-			int	sr;
 
 			sr = spi_w8r8(at25->spi, AT25_RDSR);
 			if (sr < 0 || (sr & AT25_SR_nRDY)) {
@@ -228,7 +228,7 @@ at25_ee_write(struct at25_data *at25, co
 				break;
 		} while (retries++ < 3 || time_before_eq(jiffies, timeout));
 
-		if (time_after(jiffies, timeout)) {
+		if ((sr < 0) || (sr & AT25_SR_nRDY)) {
 			dev_err(&at25->spi->dev,
 				"write %d bytes offset %d, "
 				"timeout after %u msecs\n",

------------------------------------------------------------------------------

A message that is not in the archive (sent off-list or not yet received): 200907031836.28475.david-b-yBeKhBN/[email protected]

#2 lore raw
Quoted 37 lines · On Fri, 3 Jul 2009 18:36:27 -0700 David Brownell…

From: Sebastian Heutling <heutling-U9zb0o/[email protected]>

Under certain circumstances msleep(1) within the loop, which waits for the EEPROM to be finished, might take longer than the timeout. On the next loop the status register might now return to be ready and therefore the loop finishes. The following check now tests if a timeout occurred and if so returns an error although the device reported it was ready.

This fix replaces testing the occurrence of the timeout by testing the "not ready" bit in the status register.

---
 drivers/misc/eeprom/at25.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/misc/eeprom/at25.c +1 -1
@@ -173,6 +173,7 @@ at25_ee_write(struct at25_data *at25, co
 		unsigned	segment;
 		unsigned	offset = (unsigned) off;
 		u8		*cp = bounce + 1;
+		int		sr;
 
 		*cp = AT25_WREN;
 		status = spi_write(at25->spi, cp, 1);
@@ -214,7 +215,6 @@ at25_ee_write(struct at25_data *at25, co
 		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
 		retries = 0;
 		do {
-			int	sr;
 
 			sr = spi_w8r8(at25->spi, AT25_RDSR);
 			if (sr < 0 || (sr & AT25_SR_nRDY)) {

drivers/misc/eeprom/at25.c at25_ee_write(struct at25_data *at25, co · line 234

Quoted 8 lines
drivers/misc/eeprom/at25.c +1 -1
@@ -228,7 +228,7 @@ at25_ee_write(struct at25_data *at25, co
 				break;
 		} while (retries++ < 3 || time_before_eq(jiffies, timeout));
 
-		if (time_after(jiffies, timeout)) {
+		if ((sr < 0) || (sr & AT25_SR_nRDY)) {
 			dev_err(&at25->spi->dev,
 				"write %d bytes offset %d, "
 				"timeout after %u msecs\n",

Seems OK.

The code's a bit nonsensical though:

		/* Wait for non-busy status */
		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
		retries = 0;
		do {

			sr = spi_w8r8(at25->spi, AT25_RDSR);
			if (sr < 0 || (sr & AT25_SR_nRDY)) {
				dev_dbg(&at25->spi->dev,
					"rdsr --> %d (%02x)\n", sr, sr);
				/* at HZ=100, this is sloooow */
				msleep(1);
				continue;
			}
			if (!(sr & AT25_SR_nRDY))
				break;
		} while (retries++ < 3 || time_before_eq(jiffies, timeout));

EE_TIMEOUT is 25 msecs and `retries' is 3 msecs. So the time_before_eq() will never trigger unless there's a tremendous amount of CPU load or something. It seems a bit ripe for some rationalisation.

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge
This is your chance to win up to $100,000 in prizes! For a limited time, vendors submitting new applications to BlackBerry App World(TM) will have the opportunity to enter the BlackBerry Developer Challenge. See full prize details at: http://p.sf.net/sfu/Challenge

A message that is not in the archive (sent off-list or not yet received): 20090717173546.11f55599.akpm-de/[email protected]

#4 lore raw
Quoted 70 lines · Andrew Morton
Quoted 37 lines · On Fri, 3 Jul 2009 18:36:27 -0700 David Brownell…

From: Sebastian Heutling <heutling-U9zb0o/[email protected]>

Under certain circumstances msleep(1) within the loop, which waits for the EEPROM to be finished, might take longer than the timeout. On the next loop the status register might now return to be ready and therefore the loop finishes. The following check now tests if a timeout occurred and if so returns an error although the device reported it was ready.

This fix replaces testing the occurrence of the timeout by testing the "not ready" bit in the status register.

---
 drivers/misc/eeprom/at25.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/misc/eeprom/at25.c +1 -1
@@ -173,6 +173,7 @@ at25_ee_write(struct at25_data *at25, co
 		unsigned	segment;
 		unsigned	offset = (unsigned) off;
 		u8		*cp = bounce + 1;
+		int		sr;
 
 		*cp = AT25_WREN;
 		status = spi_write(at25->spi, cp, 1);
@@ -214,7 +215,6 @@ at25_ee_write(struct at25_data *at25, co
 		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
 		retries = 0;
 		do {
-			int	sr;
 
 			sr = spi_w8r8(at25->spi, AT25_RDSR);
 			if (sr < 0 || (sr & AT25_SR_nRDY)) {

drivers/misc/eeprom/at25.c at25_ee_write(struct at25_data *at25, co · line 234

Quoted 8 lines
drivers/misc/eeprom/at25.c +1 -1
@@ -228,7 +228,7 @@ at25_ee_write(struct at25_data *at25, co
 				break;
 		} while (retries++ < 3 || time_before_eq(jiffies, timeout));
 
-		if (time_after(jiffies, timeout)) {
+		if ((sr < 0) || (sr & AT25_SR_nRDY)) {
 			dev_err(&at25->spi->dev,
 				"write %d bytes offset %d, "
 				"timeout after %u msecs\n",

Seems OK.

The code's a bit nonsensical though:

		/* Wait for non-busy status */
		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
		retries = 0;
		do {

			sr = spi_w8r8(at25->spi, AT25_RDSR);
			if (sr < 0 || (sr & AT25_SR_nRDY)) {
				dev_dbg(&at25->spi->dev,
					"rdsr --> %d (%02x)\n", sr, sr);
				/* at HZ=100, this is sloooow */
				msleep(1);
				continue;
			}
			if (!(sr & AT25_SR_nRDY))
				break;
		} while (retries++ < 3 || time_before_eq(jiffies, timeout));
Quoted 3 lines

EE_TIMEOUT is 25 msecs and `retries' is 3 msecs. So the time_before_eq() will never trigger unless there's a tremendous amount of CPU load or something. It seems a bit ripe for some rationalisation.

Not 100% sure if this is applicable here, but here is the thread, when we discussed the write-routine for at24 (i2c).

http://lists.lm-sensors.org/pipermail/i2c/2008-April/003370.html

At least, this routine still uses retries, so the thread might be a bit useful.

Regards,

Wolfram

Signature · 2 lines
Pengutronix e.K.                           | Wolfram Sang                |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge
This is your chance to win up to $100,000 in prizes! For a limited time, vendors submitting new applications to BlackBerry App World(TM) will have the opportunity to enter the BlackBerry Developer Challenge. See full prize details at: http://p.sf.net/sfu/Challenge

_______________________________________________
spi-devel-general mailing list spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

#5 lore raw
In reply to a message not in the archive
Quoted 21 lines · Andrew Morton

Seems OK.

The code's a bit nonsensical though:

                /* Wait for non-busy status */
                timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT); retries = 0;
                do {

                        sr = spi_w8r8(at25->spi, AT25_RDSR);
                        if (sr < 0 || (sr & AT25_SR_nRDY)) { dev_dbg(&at25->spi->dev,
                                        "rdsr --> %d (%02x)\n", sr, sr); /* at HZ=100, this is sloooow */ msleep(1);
                                continue;
                        }
                        if (!(sr & AT25_SR_nRDY))
                                break;
                } while (retries++ < 3 || time_before_eq(jiffies, timeout));

Quoted 3 lines

EE_TIMEOUT is 25 msecs and `retries' is 3 msecs.  So the time_before_eq() will never trigger unless there's a tremendous amount of CPU load or something.  It seems a bit ripe for some rationalisation.

The retries check is to make sure it *does* retry a few times even in the face of load. Once that limit is met, *then* the EE_TIMOUT limit will apply.

Keep in mind also that wih HZ=100 (a common setting) the loops are "sloooow" -- 10+ msec each. The goal is to cope with a bunch of odd behaviors and still come out sane.

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge
This is your chance to win up to $100,000 in prizes! For a limited time, vendors submitting new applications to BlackBerry App World(TM) will have the opportunity to enter the BlackBerry Developer Challenge. See full prize details at: http://p.sf.net/sfu/Challenge