aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/p54/p54spi.c
diff options
context:
space:
mode:
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 */