aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/p54/p54spi.c
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2009-05-17 19:02:35 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-05-20 14:46:28 -0400
commitff561ac84e0bdfe1316ed355669dd73101609f24 (patch)
treef7415ab89e184c943cc9f61e87a5b92e46d2e819 /drivers/net/wireless/p54/p54spi.c
parent6edf534a3214e8fad943d7acd903603f0a5b9ac8 (diff)
p54spi: use firmware/DMA bug workaround that work under hight load in p54spi_rx
Under high load first data word, read after available data size is sometimes lost in p54spi_rx. It seems to depend on frequency of interrupts and latency of data read request relatively to 'data available' interrupt. The worst consequence of this bug is loss of packet transmission acknowledgement, which in turn causes overflow of tx queues and permanent link loss. Read data size and first data word in one SPI transaction. No packets from LMAC should have length less than 1 word, so this shouldn't interfere with the next read transaction. Also call p54spi_sleep if p54spi_wake succeeded. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/p54/p54spi.c')
-rw-r--r--drivers/net/wireless/p54/p54spi.c28
1 files changed, 20 insertions, 8 deletions
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index e830a2e6845..40aeb4387dc 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -371,32 +371,44 @@ static int p54spi_rx(struct p54s_priv *priv)
371{ 371{
372 struct sk_buff *skb; 372 struct sk_buff *skb;
373 u16 len; 373 u16 len;
374 u16 rx_head[2];
375#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16))
374 376
375 if (p54spi_wakeup(priv) < 0) 377 if (p54spi_wakeup(priv) < 0)
376 return -EBUSY; 378 return -EBUSY;
377 379
378 /* dummy read to flush SPI DMA controller bug */ 380 /* Read data size and first data word in one SPI transaction
379 p54spi_read16(priv, SPI_ADRS_GEN_PURP_1); 381 * This is workaround for firmware/DMA bug,
380 382 * when first data word gets lost under high load.
381 len = p54spi_read16(priv, SPI_ADRS_DMA_DATA); 383 */
384 p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head));
385 len = rx_head[0];
382 386
383 if (len == 0) { 387 if (len == 0) {
384 dev_err(&priv->spi->dev, "rx request of zero bytes"); 388 p54spi_sleep(priv);
389 dev_err(&priv->spi->dev, "rx request of zero bytes\n");
385 return 0; 390 return 0;
386 } 391 }
387 392
388
389 /* Firmware may insert up to 4 padding bytes after the lmac header, 393 /* Firmware may insert up to 4 padding bytes after the lmac header,
390 * but it does not amend the size of SPI data transfer. 394 * but it does not amend the size of SPI data transfer.
391 * Such packets has correct data size in header, thus referencing 395 * Such packets has correct data size in header, thus referencing
392 * past the end of allocated skb. Reserve extra 4 bytes for this case */ 396 * past the end of allocated skb. Reserve extra 4 bytes for this case */
393 skb = dev_alloc_skb(len + 4); 397 skb = dev_alloc_skb(len + 4);
394 if (!skb) { 398 if (!skb) {
399 p54spi_sleep(priv);
395 dev_err(&priv->spi->dev, "could not alloc skb"); 400 dev_err(&priv->spi->dev, "could not alloc skb");
396 return 0; 401 return -ENOMEM;
397 } 402 }
398 403
399 p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len); 404 if (len <= READAHEAD_SZ) {
405 memcpy(skb_put(skb, len), rx_head + 1, len);
406 } else {
407 memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ);
408 p54spi_spi_read(priv, SPI_ADRS_DMA_DATA,
409 skb_put(skb, len - READAHEAD_SZ),
410 len - READAHEAD_SZ);
411 }
400 p54spi_sleep(priv); 412 p54spi_sleep(priv);
401 /* Put additional bytes to compensate for the possible 413 /* Put additional bytes to compensate for the possible
402 * alignment-caused truncation */ 414 * alignment-caused truncation */