diff options
-rw-r--r-- | drivers/net/wireless/p54/p54spi.c | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index e830a2e68450..40aeb4387dc9 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 */ |