From f74d0f5cacb7023422098dd5c98e3ce06787ba56 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 25 Mar 2009 08:30:15 +0300 Subject: p54spi: mask value read from SPI_ADRS_DMA_WRITE_CTRL in p54spi_wait_bit Mask value read from SPI_ADRS_DMA_WRITE_CTRL in p54spi_wait_bit. Without this, 'fw_upload not allowed to DMA write' is observed at both N800 and N810. Signed-off-by: Max Filippov Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index d1fe577de3d4..55402729d21b 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -171,7 +171,7 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) for (i = 0; i < 2000; i++) { p54spi_spi_read(priv, reg, &buffer, sizeof(buffer)); - if (buffer == bits) + if ((buffer & bits) == bits) return 1; msleep(1); -- cgit v1.2.2 From 5e3af1d2d3e6c0011b4c22514d39c73dcbc6674f Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 25 Mar 2009 13:45:01 +0100 Subject: p54spi: fix p54spi_upload_firmware Instead of firmware itself p54_upload_firmware was sending to the device content of struct firmware and the following random garbage. Notice '&' before priv->firmware->data at p54spi_spi_write. But simple removing of '&' sign triggered BUG_ON at dma_cache_maint. Thus kmemdup - kfree workaround. Signed-off-by: Max Filippov Signed-off-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 55402729d21b..bed894ae04b5 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -228,8 +228,15 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev) static int p54spi_upload_firmware(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; - unsigned long fw_len, fw_addr; - long _fw_len; + unsigned long fw_len, _fw_len; + unsigned int offset = 0; + int err = 0; + u8 *fw; + + fw_len = priv->firmware->size; + fw = kmemdup(priv->firmware->data, fw_len, GFP_KERNEL); + if (!fw) + return -ENOMEM; /* stop the device */ p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( @@ -244,9 +251,6 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev) msleep(TARGET_BOOT_SLEEP); - fw_addr = ISL38XX_DEV_FIRMWARE_ADDR; - fw_len = priv->firmware->size; - while (fw_len > 0) { _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE); @@ -257,23 +261,20 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev) cpu_to_le32(HOST_ALLOWED)) == 0) { dev_err(&priv->spi->dev, "fw_upload not allowed " "to DMA write."); - return -EAGAIN; + err = -EAGAIN; + goto out; } p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(_fw_len)); - p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, - cpu_to_le32(fw_addr)); + p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, cpu_to_le32( + ISL38XX_DEV_FIRMWARE_ADDR + offset)); p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, - &priv->firmware->data, _fw_len); + (fw + offset), _fw_len); fw_len -= _fw_len; - fw_addr += _fw_len; - - /* FIXME: I think this doesn't work if firmware is large, - * this loop goes to second round. fw->data is not - * increased at all! */ + offset += _fw_len; } BUG_ON(fw_len != 0); @@ -292,7 +293,10 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev) p54spi_write16(priv, SPI_ADRS_DEV_CTRL_STAT, cpu_to_le16( SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT)); msleep(TARGET_BOOT_SLEEP); - return 0; + +out: + kfree(fw); + return err; } static void p54spi_power_off(struct p54s_priv *priv) -- cgit v1.2.2 From 4f5cab969bdbec1ab0c5b690282372b4978123ac Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Thu, 26 Mar 2009 06:38:25 +0300 Subject: p54spi: fix p54spi_tx_frame DMA transfer initiation and skb cleanup p54spi_tx_frame wasn't waiting for HOST_ALLOWED in SPI_ADRS_DMA_WRITE_CTRL. This resulted in frequent 'WR_READY timeout' on beacon resubmission. Also don't free skb on error path, as it gets freed on p54spi_wq_tx. Signed-off-by: Max Filippov Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 56 +++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 28 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index bed894ae04b5..f8af09610180 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -179,6 +179,25 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) return 0; } +static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, + const void *buf, size_t len) +{ + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, + cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); + + if (p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, + cpu_to_le32(HOST_ALLOWED)) == 0) { + dev_err(&priv->spi->dev, "spi_write_dma not allowed " + "to DMA write."); + return -EAGAIN; + } + + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len)); + p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base); + p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len); + return 0; +} + static int p54spi_request_firmware(struct ieee80211_hw *dev) { struct p54s_priv *priv = dev->priv; @@ -254,24 +273,11 @@ static int p54spi_upload_firmware(struct ieee80211_hw *dev) while (fw_len > 0) { _fw_len = min_t(long, fw_len, SPI_MAX_PACKET_SIZE); - p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); - - if (p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le32(HOST_ALLOWED)) == 0) { - dev_err(&priv->spi->dev, "fw_upload not allowed " - "to DMA write."); - err = -EAGAIN; + err = p54spi_spi_write_dma(priv, cpu_to_le32( + ISL38XX_DEV_FIRMWARE_ADDR + offset), + (fw + offset), _fw_len); + if (err < 0) goto out; - } - - p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, - cpu_to_le16(_fw_len)); - p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, cpu_to_le32( - ISL38XX_DEV_FIRMWARE_ADDR + offset)); - - p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, - (fw + offset), _fw_len); fw_len -= _fw_len; offset += _fw_len; @@ -418,27 +424,21 @@ static irqreturn_t p54spi_interrupt(int irq, void *config) static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - struct p54s_dma_regs dma_regs; unsigned long timeout; int ret = 0; u32 ints; p54spi_wakeup(priv); - dma_regs.cmd = cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE); - dma_regs.len = cpu_to_le16(skb->len); - dma_regs.addr = hdr->req_id; - - p54spi_spi_write(priv, SPI_ADRS_DMA_WRITE_CTRL, &dma_regs, - sizeof(dma_regs)); - - p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, skb->data, skb->len); + ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len); + if (ret < 0) + goto out; timeout = jiffies + 2 * HZ; ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); while (!(ints & SPI_HOST_INT_WR_READY)) { if (time_after(jiffies, timeout)) { - dev_err(&priv->spi->dev, "WR_READY timeout"); + dev_err(&priv->spi->dev, "WR_READY timeout\n"); ret = -1; goto out; } @@ -448,9 +448,9 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); p54spi_sleep(priv); -out: if (FREE_AFTER_TX(skb)) p54_free_skb(priv->hw, skb); +out: return ret; } -- cgit v1.2.2 From 9f201a87831af9458df1eda65941c955f2da87ed Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Fri, 27 Mar 2009 07:50:53 +0300 Subject: p54spi: compensate firmware alignment bug in p54spi_rx Firmware may insert up to 4 padding bytes after the lmac header, but it does not amend the size of SPI data transfer. Such packets has correct data size in header, thus referencing past the end of allocated skb. Put extra 4 bytes to the end of the received skb to compensate for this case. Signed-off-by: Max Filippov Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index f8af09610180..52023127cf37 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -395,7 +395,12 @@ static int p54spi_rx(struct p54s_priv *priv) return 0; } - skb = dev_alloc_skb(len); + + /* Firmware may insert up to 4 padding bytes after the lmac header, + * but it does not amend the size of SPI data transfer. + * Such packets has correct data size in header, thus referencing + * past the end of allocated skb. Reserve extra 4 bytes for this case */ + skb = dev_alloc_skb(len + 4); if (!skb) { dev_err(&priv->spi->dev, "could not alloc skb"); return 0; @@ -403,6 +408,9 @@ static int p54spi_rx(struct p54s_priv *priv) p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len); p54spi_sleep(priv); + /* Put additional bytes to compensate for the possible + * alignment-caused truncation */ + skb_put(skb, 4); if (p54_rx(priv->hw, skb) == 0) dev_kfree_skb(skb); -- cgit v1.2.2 From 87cbfd06889256cac945b37c7f62f4ce7f44b34a Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 6 Apr 2009 01:03:09 +0400 Subject: p54spi: get rid of busy-wait loops p54spi_wakeup and p54spi_tx_frame used busy-waiting loop to poll for 'ready' bits in SPI_ADRS_HOST_INTERRUPTS register. With this change in place 'WR_READY timeout' messages do not show anymore. Signed-off-by: Max Filippov Acked-by: Christian Lamparter Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 41 +++++++++++++-------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 52023127cf37..59a5e778bb08 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -167,14 +167,13 @@ static const struct p54spi_spi_reg p54spi_registers_array[] = static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) { int i; - __le32 buffer; for (i = 0; i < 2000; i++) { - p54spi_spi_read(priv, reg, &buffer, sizeof(buffer)); + __le32 buffer = p54spi_read32(priv, reg); if ((buffer & bits) == bits) return 1; - msleep(1); + msleep(0); } return 0; } @@ -185,10 +184,10 @@ static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); - if (p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le32(HOST_ALLOWED)) == 0) { + if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, + cpu_to_le32(HOST_ALLOWED))) { dev_err(&priv->spi->dev, "spi_write_dma not allowed " - "to DMA write."); + "to DMA write.\n"); return -EAGAIN; } @@ -330,21 +329,15 @@ static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val) static void p54spi_wakeup(struct p54s_priv *priv) { - unsigned long timeout; - u32 ints; - /* wake the chip */ p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, cpu_to_le32(SPI_TARGET_INT_WAKEUP)); /* And wait for the READY interrupt */ - timeout = jiffies + HZ; - - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); - while (!(ints & SPI_HOST_INT_READY)) { - if (time_after(jiffies, timeout)) - goto out; - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); + if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, + cpu_to_le32(SPI_HOST_INT_READY))) { + dev_err(&priv->spi->dev, "INT_READY timeout\n"); + goto out; } p54spi_int_ack(priv, SPI_HOST_INT_READY); @@ -432,9 +425,7 @@ static irqreturn_t p54spi_interrupt(int irq, void *config) static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) { struct p54_hdr *hdr = (struct p54_hdr *) skb->data; - unsigned long timeout; int ret = 0; - u32 ints; p54spi_wakeup(priv); @@ -442,15 +433,11 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) if (ret < 0) goto out; - timeout = jiffies + 2 * HZ; - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); - while (!(ints & SPI_HOST_INT_WR_READY)) { - if (time_after(jiffies, timeout)) { - dev_err(&priv->spi->dev, "WR_READY timeout\n"); - ret = -1; - goto out; - } - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); + if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, + cpu_to_le32(SPI_HOST_INT_WR_READY))) { + dev_err(&priv->spi->dev, "WR_READY timeout\n"); + ret = -1; + goto out; } p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); -- cgit v1.2.2 From 210dd1bb9b0329bcc1b4f8932022eddc6466980c Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 03:02:31 +0400 Subject: p54spi: fix incorrect access sequence to DMA_WRITE_CTRL in p54spi_spi_write_dma Host is not allowed to modify DMA_WRITE_CTRL register if bit HOST_ALLOWED in it is not set. Wait for HOST_ALLOWED first. Also get rid of timeout in p54spi_wait_bit as it's been playing a role of workaround for such an incorrect register access. Signed-off-by: Max Filippov Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 59a5e778bb08..272b7e176afc 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -172,8 +172,6 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) __le32 buffer = p54spi_read32(priv, reg); if ((buffer & bits) == bits) return 1; - - msleep(0); } return 0; } @@ -181,9 +179,6 @@ static int p54spi_wait_bit(struct p54s_priv *priv, u16 reg, __le32 bits) static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, const void *buf, size_t len) { - p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, - cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); - if (!p54spi_wait_bit(priv, SPI_ADRS_DMA_WRITE_CTRL, cpu_to_le32(HOST_ALLOWED))) { dev_err(&priv->spi->dev, "spi_write_dma not allowed " @@ -191,6 +186,9 @@ static int p54spi_spi_write_dma(struct p54s_priv *priv, __le32 base, return -EAGAIN; } + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_CTRL, + cpu_to_le16(SPI_DMA_WRITE_CTRL_ENABLE)); + p54spi_write16(priv, SPI_ADRS_DMA_WRITE_LEN, cpu_to_le16(len)); p54spi_write32(priv, SPI_ADRS_DMA_WRITE_BASE, base); p54spi_spi_write(priv, SPI_ADRS_DMA_DATA, buf, len); -- cgit v1.2.2 From 69712e926bad9c163d5ba5597f08084ee7902480 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 03:02:32 +0400 Subject: p54spi: cosmetic fixes: use even byte count in SPI write; drop unused interrupt status read When SPI write of odd length is requested, p54spi_write splits it into two parts: one for all data, except the last byte, and one for last byte and padding byte. Unfortunately, the length of first part is not amended. It works because all meaningful bytes have proper value and the last byte of odd length SPI write transaction is ignored. p54spi_work has dummy HOST_INTERRUPTS register read at the end. Drop it, as its result is not used and it has no side effects. Signed-off-by: Max Filippov Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 272b7e176afc..399190b3bed7 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -96,7 +96,7 @@ static void p54spi_spi_write(struct p54s_priv *priv, u8 address, spi_message_add_tail(&t[0], &m); t[1].tx_buf = buf; - t[1].len = len; + t[1].len = len & ~1; spi_message_add_tail(&t[1], &m); if (len % 2) { @@ -541,11 +541,6 @@ static void p54spi_work(struct work_struct *work) } ret = p54spi_wq_tx(priv); - if (ret < 0) - goto out; - - ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); - out: mutex_unlock(&priv->mutex); } -- cgit v1.2.2 From 465b63537f93e82a2606332830801acb64467ec9 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 03:02:33 +0400 Subject: p54spi: return status of p54spi_wakeup Return whether wakeup operation succeeded. Make use of this return value. Signed-off-by: Max Filippov Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 399190b3bed7..5038e8aa9353 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -325,7 +325,7 @@ static inline void p54spi_int_ack(struct p54s_priv *priv, u32 val) p54spi_write32(priv, SPI_ADRS_HOST_INT_ACK, cpu_to_le32(val)); } -static void p54spi_wakeup(struct p54s_priv *priv) +static int p54spi_wakeup(struct p54s_priv *priv) { /* wake the chip */ p54spi_write32(priv, SPI_ADRS_ARM_INTERRUPTS, @@ -335,13 +335,11 @@ static void p54spi_wakeup(struct p54s_priv *priv) if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, cpu_to_le32(SPI_HOST_INT_READY))) { dev_err(&priv->spi->dev, "INT_READY timeout\n"); - goto out; + return -EBUSY; } p54spi_int_ack(priv, SPI_HOST_INT_READY); - -out: - return; + return 0; } static inline void p54spi_sleep(struct p54s_priv *priv) @@ -374,7 +372,8 @@ static int p54spi_rx(struct p54s_priv *priv) struct sk_buff *skb; u16 len; - p54spi_wakeup(priv); + if (p54spi_wakeup(priv) < 0) + return -EBUSY; /* dummy read to flush SPI DMA controller bug */ p54spi_read16(priv, SPI_ADRS_GEN_PURP_1); @@ -425,7 +424,8 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) struct p54_hdr *hdr = (struct p54_hdr *) skb->data; int ret = 0; - p54spi_wakeup(priv); + if (p54spi_wakeup(priv) < 0) + return -EBUSY; ret = p54spi_spi_write_dma(priv, hdr->req_id, skb->data, skb->len); if (ret < 0) -- cgit v1.2.2 From 6edf534a3214e8fad943d7acd903603f0a5b9ac8 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 03:02:34 +0400 Subject: p54spi: always call p54spi_sleep in p54spi_tx_frame if p54spi_wakeup succeeded Put chip into sleep state, once it's been awaken. Also, propagate error code to the caller. Signed-off-by: Max Filippov Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 5038e8aa9353..e830a2e68450 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -434,16 +434,16 @@ static int p54spi_tx_frame(struct p54s_priv *priv, struct sk_buff *skb) if (!p54spi_wait_bit(priv, SPI_ADRS_HOST_INTERRUPTS, cpu_to_le32(SPI_HOST_INT_WR_READY))) { dev_err(&priv->spi->dev, "WR_READY timeout\n"); - ret = -1; + ret = -EAGAIN; goto out; } p54spi_int_ack(priv, SPI_HOST_INT_WR_READY); - p54spi_sleep(priv); if (FREE_AFTER_TX(skb)) p54_free_skb(priv->hw, skb); out: + p54spi_sleep(priv); return ret; } -- cgit v1.2.2 From ff561ac84e0bdfe1316ed355669dd73101609f24 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 03:02:35 +0400 Subject: 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 Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') 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) { struct sk_buff *skb; u16 len; + u16 rx_head[2]; +#define READAHEAD_SZ (sizeof(rx_head)-sizeof(u16)) if (p54spi_wakeup(priv) < 0) return -EBUSY; - /* dummy read to flush SPI DMA controller bug */ - p54spi_read16(priv, SPI_ADRS_GEN_PURP_1); - - len = p54spi_read16(priv, SPI_ADRS_DMA_DATA); + /* Read data size and first data word in one SPI transaction + * This is workaround for firmware/DMA bug, + * when first data word gets lost under high load. + */ + p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, rx_head, sizeof(rx_head)); + len = rx_head[0]; if (len == 0) { - dev_err(&priv->spi->dev, "rx request of zero bytes"); + p54spi_sleep(priv); + dev_err(&priv->spi->dev, "rx request of zero bytes\n"); return 0; } - /* Firmware may insert up to 4 padding bytes after the lmac header, * but it does not amend the size of SPI data transfer. * Such packets has correct data size in header, thus referencing * past the end of allocated skb. Reserve extra 4 bytes for this case */ skb = dev_alloc_skb(len + 4); if (!skb) { + p54spi_sleep(priv); dev_err(&priv->spi->dev, "could not alloc skb"); - return 0; + return -ENOMEM; } - p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, skb_put(skb, len), len); + if (len <= READAHEAD_SZ) { + memcpy(skb_put(skb, len), rx_head + 1, len); + } else { + memcpy(skb_put(skb, READAHEAD_SZ), rx_head + 1, READAHEAD_SZ); + p54spi_spi_read(priv, SPI_ADRS_DMA_DATA, + skb_put(skb, len - READAHEAD_SZ), + len - READAHEAD_SZ); + } p54spi_sleep(priv); /* Put additional bytes to compensate for the possible * alignment-caused truncation */ -- cgit v1.2.2 From 4de2dc74a1c7dedcfbe8290316f1a239a7c86554 Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Mon, 18 May 2009 21:28:55 +0400 Subject: p54spi: drop test for FW_STATE_RESET in p54spi_work Drop test for FW_STATE_RESET in p54spi_work as fw_state is never assigned this value. Signed-off-by: Roel Kluin Signed-off-by: Max Filippov Signed-off-by: John W. Linville --- drivers/net/wireless/p54/p54spi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/wireless/p54/p54spi.c') diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 40aeb4387dc9..83116baeb110 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -525,8 +525,7 @@ static void p54spi_work(struct work_struct *work) mutex_lock(&priv->mutex); - if (priv->fw_state == FW_STATE_OFF && - priv->fw_state == FW_STATE_RESET) + if (priv->fw_state == FW_STATE_OFF) goto out; ints = p54spi_read32(priv, SPI_ADRS_HOST_INTERRUPTS); -- cgit v1.2.2