diff options
Diffstat (limited to 'drivers/net/wimax/i2400m/sdio-rx.c')
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-rx.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c index 321beadf6e47..8adf6c9b6f8f 100644 --- a/drivers/net/wimax/i2400m/sdio-rx.c +++ b/drivers/net/wimax/i2400m/sdio-rx.c | |||
@@ -53,6 +53,7 @@ | |||
53 | * i2400ms_irq() | 53 | * i2400ms_irq() |
54 | * i2400ms_rx() | 54 | * i2400ms_rx() |
55 | * __i2400ms_rx_get_size() | 55 | * __i2400ms_rx_get_size() |
56 | * i2400m_is_boot_barker() | ||
56 | * i2400m_rx() | 57 | * i2400m_rx() |
57 | * | 58 | * |
58 | * i2400ms_rx_setup() | 59 | * i2400ms_rx_setup() |
@@ -138,6 +139,11 @@ void i2400ms_rx(struct i2400ms *i2400ms) | |||
138 | ret = rx_size; | 139 | ret = rx_size; |
139 | goto error_get_size; | 140 | goto error_get_size; |
140 | } | 141 | } |
142 | /* | ||
143 | * Hardware quirk: make sure to clear the INTR status register | ||
144 | * AFTER getting the data transfer size. | ||
145 | */ | ||
146 | sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); | ||
141 | 147 | ||
142 | ret = -ENOMEM; | 148 | ret = -ENOMEM; |
143 | skb = alloc_skb(rx_size, GFP_ATOMIC); | 149 | skb = alloc_skb(rx_size, GFP_ATOMIC); |
@@ -153,25 +159,34 @@ void i2400ms_rx(struct i2400ms *i2400ms) | |||
153 | } | 159 | } |
154 | 160 | ||
155 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ | 161 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ |
156 | if (i2400m->boot_mode == 1) { | 162 | if (unlikely(i2400m->boot_mode == 1)) { |
157 | spin_lock(&i2400m->rx_lock); | 163 | spin_lock(&i2400m->rx_lock); |
158 | i2400ms->bm_ack_size = rx_size; | 164 | i2400ms->bm_ack_size = rx_size; |
159 | spin_unlock(&i2400m->rx_lock); | 165 | spin_unlock(&i2400m->rx_lock); |
160 | memcpy(i2400m->bm_ack_buf, skb->data, rx_size); | 166 | memcpy(i2400m->bm_ack_buf, skb->data, rx_size); |
161 | wake_up(&i2400ms->bm_wfa_wq); | 167 | wake_up(&i2400ms->bm_wfa_wq); |
162 | dev_err(dev, "RX: SDIO boot mode message\n"); | 168 | d_printf(5, dev, "RX: SDIO boot mode message\n"); |
163 | kfree_skb(skb); | 169 | kfree_skb(skb); |
164 | } else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER, | 170 | goto out; |
165 | sizeof(i2400m_NBOOT_BARKER)) | 171 | } |
166 | || !memcmp(skb->data, i2400m_SBOOT_BARKER, | 172 | ret = -EIO; |
167 | sizeof(i2400m_SBOOT_BARKER)))) { | 173 | if (unlikely(rx_size < sizeof(__le32))) { |
168 | ret = i2400m_dev_reset_handle(i2400m); | 174 | dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size); |
175 | goto error_bad_size; | ||
176 | } | ||
177 | if (likely(i2400m_is_d2h_barker(skb->data))) { | ||
178 | skb_put(skb, rx_size); | ||
179 | i2400m_rx(i2400m, skb); | ||
180 | } else if (unlikely(i2400m_is_boot_barker(i2400m, | ||
181 | skb->data, rx_size))) { | ||
182 | ret = i2400m_dev_reset_handle(i2400m, "device rebooted"); | ||
169 | dev_err(dev, "RX: SDIO reboot barker\n"); | 183 | dev_err(dev, "RX: SDIO reboot barker\n"); |
170 | kfree_skb(skb); | 184 | kfree_skb(skb); |
171 | } else { | 185 | } else { |
172 | skb_put(skb, rx_size); | 186 | i2400m_unknown_barker(i2400m, skb->data, rx_size); |
173 | i2400m_rx(i2400m, skb); | 187 | kfree_skb(skb); |
174 | } | 188 | } |
189 | out: | ||
175 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); | 190 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); |
176 | return; | 191 | return; |
177 | 192 | ||
@@ -179,6 +194,7 @@ error_memcpy_fromio: | |||
179 | kfree_skb(skb); | 194 | kfree_skb(skb); |
180 | error_alloc_skb: | 195 | error_alloc_skb: |
181 | error_get_size: | 196 | error_get_size: |
197 | error_bad_size: | ||
182 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); | 198 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); |
183 | return; | 199 | return; |
184 | } | 200 | } |
@@ -209,7 +225,6 @@ void i2400ms_irq(struct sdio_func *func) | |||
209 | dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); | 225 | dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n"); |
210 | goto error_no_irq; | 226 | goto error_no_irq; |
211 | } | 227 | } |
212 | sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret); | ||
213 | i2400ms_rx(i2400ms); | 228 | i2400ms_rx(i2400ms); |
214 | error_no_irq: | 229 | error_no_irq: |
215 | d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); | 230 | d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms); |
@@ -234,6 +249,13 @@ int i2400ms_rx_setup(struct i2400ms *i2400ms) | |||
234 | init_waitqueue_head(&i2400ms->bm_wfa_wq); | 249 | init_waitqueue_head(&i2400ms->bm_wfa_wq); |
235 | spin_lock(&i2400m->rx_lock); | 250 | spin_lock(&i2400m->rx_lock); |
236 | i2400ms->bm_wait_result = -EINPROGRESS; | 251 | i2400ms->bm_wait_result = -EINPROGRESS; |
252 | /* | ||
253 | * Before we are about to enable the RX interrupt, make sure | ||
254 | * bm_ack_size is cleared to -EINPROGRESS which indicates | ||
255 | * no RX interrupt happened yet or the previous interrupt | ||
256 | * has been handled, we are ready to take the new interrupt | ||
257 | */ | ||
258 | i2400ms->bm_ack_size = -EINPROGRESS; | ||
237 | spin_unlock(&i2400m->rx_lock); | 259 | spin_unlock(&i2400m->rx_lock); |
238 | 260 | ||
239 | sdio_claim_host(func); | 261 | sdio_claim_host(func); |