diff options
Diffstat (limited to 'drivers/net/wireless/ath/wcn36xx/dxe.c')
-rw-r--r-- | drivers/net/wireless/ath/wcn36xx/dxe.c | 805 |
1 files changed, 805 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c new file mode 100644 index 000000000000..ee25786b4447 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c | |||
@@ -0,0 +1,805 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | /* DXE - DMA transfer engine | ||
18 | * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. | ||
19 | * through low channels data packets are transfered | ||
20 | * through high channels managment packets are transfered | ||
21 | */ | ||
22 | |||
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
24 | |||
25 | #include <linux/interrupt.h> | ||
26 | #include "wcn36xx.h" | ||
27 | #include "txrx.h" | ||
28 | |||
29 | void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low) | ||
30 | { | ||
31 | struct wcn36xx_dxe_ch *ch = is_low ? | ||
32 | &wcn->dxe_tx_l_ch : | ||
33 | &wcn->dxe_tx_h_ch; | ||
34 | |||
35 | return ch->head_blk_ctl->bd_cpu_addr; | ||
36 | } | ||
37 | |||
38 | static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data) | ||
39 | { | ||
40 | wcn36xx_dbg(WCN36XX_DBG_DXE, | ||
41 | "wcn36xx_dxe_write_register: addr=%x, data=%x\n", | ||
42 | addr, data); | ||
43 | |||
44 | writel(data, wcn->mmio + addr); | ||
45 | } | ||
46 | |||
47 | static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data) | ||
48 | { | ||
49 | *data = readl(wcn->mmio + addr); | ||
50 | |||
51 | wcn36xx_dbg(WCN36XX_DBG_DXE, | ||
52 | "wcn36xx_dxe_read_register: addr=%x, data=%x\n", | ||
53 | addr, *data); | ||
54 | } | ||
55 | |||
56 | static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch) | ||
57 | { | ||
58 | struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next; | ||
59 | int i; | ||
60 | |||
61 | for (i = 0; i < ch->desc_num && ctl; i++) { | ||
62 | next = ctl->next; | ||
63 | kfree(ctl); | ||
64 | ctl = next; | ||
65 | } | ||
66 | } | ||
67 | |||
68 | static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) | ||
69 | { | ||
70 | struct wcn36xx_dxe_ctl *prev_ctl = NULL; | ||
71 | struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||
72 | int i; | ||
73 | |||
74 | for (i = 0; i < ch->desc_num; i++) { | ||
75 | cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL); | ||
76 | if (!cur_ctl) | ||
77 | goto out_fail; | ||
78 | |||
79 | cur_ctl->ctl_blk_order = i; | ||
80 | if (i == 0) { | ||
81 | ch->head_blk_ctl = cur_ctl; | ||
82 | ch->tail_blk_ctl = cur_ctl; | ||
83 | } else if (ch->desc_num - 1 == i) { | ||
84 | prev_ctl->next = cur_ctl; | ||
85 | cur_ctl->next = ch->head_blk_ctl; | ||
86 | } else { | ||
87 | prev_ctl->next = cur_ctl; | ||
88 | } | ||
89 | prev_ctl = cur_ctl; | ||
90 | } | ||
91 | |||
92 | return 0; | ||
93 | |||
94 | out_fail: | ||
95 | wcn36xx_dxe_free_ctl_block(ch); | ||
96 | return -ENOMEM; | ||
97 | } | ||
98 | |||
99 | int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) | ||
100 | { | ||
101 | int ret; | ||
102 | |||
103 | wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L; | ||
104 | wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H; | ||
105 | wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L; | ||
106 | wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H; | ||
107 | |||
108 | wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L; | ||
109 | wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H; | ||
110 | wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; | ||
111 | wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; | ||
112 | |||
113 | wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; | ||
114 | wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; | ||
115 | |||
116 | wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; | ||
117 | wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; | ||
118 | |||
119 | wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB; | ||
120 | wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB; | ||
121 | |||
122 | wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L; | ||
123 | wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H; | ||
124 | |||
125 | wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L; | ||
126 | wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H; | ||
127 | |||
128 | /* DXE control block allocation */ | ||
129 | ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch); | ||
130 | if (ret) | ||
131 | goto out_err; | ||
132 | ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch); | ||
133 | if (ret) | ||
134 | goto out_err; | ||
135 | ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch); | ||
136 | if (ret) | ||
137 | goto out_err; | ||
138 | ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch); | ||
139 | if (ret) | ||
140 | goto out_err; | ||
141 | |||
142 | /* Initialize SMSM state Clear TX Enable RING EMPTY STATE */ | ||
143 | ret = wcn->ctrl_ops->smsm_change_state( | ||
144 | WCN36XX_SMSM_WLAN_TX_ENABLE, | ||
145 | WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); | ||
146 | |||
147 | return 0; | ||
148 | |||
149 | out_err: | ||
150 | wcn36xx_err("Failed to allocate DXE control blocks\n"); | ||
151 | wcn36xx_dxe_free_ctl_blks(wcn); | ||
152 | return -ENOMEM; | ||
153 | } | ||
154 | |||
155 | void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) | ||
156 | { | ||
157 | wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch); | ||
158 | wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch); | ||
159 | wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch); | ||
160 | wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); | ||
161 | } | ||
162 | |||
163 | static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) | ||
164 | { | ||
165 | struct wcn36xx_dxe_desc *cur_dxe = NULL; | ||
166 | struct wcn36xx_dxe_desc *prev_dxe = NULL; | ||
167 | struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||
168 | size_t size; | ||
169 | int i; | ||
170 | |||
171 | size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); | ||
172 | wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr, | ||
173 | GFP_KERNEL); | ||
174 | if (!wcn_ch->cpu_addr) | ||
175 | return -ENOMEM; | ||
176 | |||
177 | memset(wcn_ch->cpu_addr, 0, size); | ||
178 | |||
179 | cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; | ||
180 | cur_ctl = wcn_ch->head_blk_ctl; | ||
181 | |||
182 | for (i = 0; i < wcn_ch->desc_num; i++) { | ||
183 | cur_ctl->desc = cur_dxe; | ||
184 | cur_ctl->desc_phy_addr = wcn_ch->dma_addr + | ||
185 | i * sizeof(struct wcn36xx_dxe_desc); | ||
186 | |||
187 | switch (wcn_ch->ch_type) { | ||
188 | case WCN36XX_DXE_CH_TX_L: | ||
189 | cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; | ||
190 | cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; | ||
191 | break; | ||
192 | case WCN36XX_DXE_CH_TX_H: | ||
193 | cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; | ||
194 | cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; | ||
195 | break; | ||
196 | case WCN36XX_DXE_CH_RX_L: | ||
197 | cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; | ||
198 | cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L; | ||
199 | break; | ||
200 | case WCN36XX_DXE_CH_RX_H: | ||
201 | cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; | ||
202 | cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H; | ||
203 | break; | ||
204 | } | ||
205 | if (0 == i) { | ||
206 | cur_dxe->phy_next_l = 0; | ||
207 | } else if ((0 < i) && (i < wcn_ch->desc_num - 1)) { | ||
208 | prev_dxe->phy_next_l = | ||
209 | cur_ctl->desc_phy_addr; | ||
210 | } else if (i == (wcn_ch->desc_num - 1)) { | ||
211 | prev_dxe->phy_next_l = | ||
212 | cur_ctl->desc_phy_addr; | ||
213 | cur_dxe->phy_next_l = | ||
214 | wcn_ch->head_blk_ctl->desc_phy_addr; | ||
215 | } | ||
216 | cur_ctl = cur_ctl->next; | ||
217 | prev_dxe = cur_dxe; | ||
218 | cur_dxe++; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch, | ||
225 | struct wcn36xx_dxe_mem_pool *pool) | ||
226 | { | ||
227 | int i, chunk_size = pool->chunk_size; | ||
228 | dma_addr_t bd_phy_addr = pool->phy_addr; | ||
229 | void *bd_cpu_addr = pool->virt_addr; | ||
230 | struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl; | ||
231 | |||
232 | for (i = 0; i < ch->desc_num; i++) { | ||
233 | /* Only every second dxe needs a bd pointer, | ||
234 | the other will point to the skb data */ | ||
235 | if (!(i & 1)) { | ||
236 | cur->bd_phy_addr = bd_phy_addr; | ||
237 | cur->bd_cpu_addr = bd_cpu_addr; | ||
238 | bd_phy_addr += chunk_size; | ||
239 | bd_cpu_addr += chunk_size; | ||
240 | } else { | ||
241 | cur->bd_phy_addr = 0; | ||
242 | cur->bd_cpu_addr = NULL; | ||
243 | } | ||
244 | cur = cur->next; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) | ||
249 | { | ||
250 | int reg_data = 0; | ||
251 | |||
252 | wcn36xx_dxe_read_register(wcn, | ||
253 | WCN36XX_DXE_INT_MASK_REG, | ||
254 | ®_data); | ||
255 | |||
256 | reg_data |= wcn_ch; | ||
257 | |||
258 | wcn36xx_dxe_write_register(wcn, | ||
259 | WCN36XX_DXE_INT_MASK_REG, | ||
260 | (int)reg_data); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) | ||
265 | { | ||
266 | struct wcn36xx_dxe_desc *dxe = ctl->desc; | ||
267 | struct sk_buff *skb; | ||
268 | |||
269 | skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC); | ||
270 | if (skb == NULL) | ||
271 | return -ENOMEM; | ||
272 | |||
273 | dxe->dst_addr_l = dma_map_single(NULL, | ||
274 | skb_tail_pointer(skb), | ||
275 | WCN36XX_PKT_SIZE, | ||
276 | DMA_FROM_DEVICE); | ||
277 | ctl->skb = skb; | ||
278 | |||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn, | ||
283 | struct wcn36xx_dxe_ch *wcn_ch) | ||
284 | { | ||
285 | int i; | ||
286 | struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||
287 | |||
288 | cur_ctl = wcn_ch->head_blk_ctl; | ||
289 | |||
290 | for (i = 0; i < wcn_ch->desc_num; i++) { | ||
291 | wcn36xx_dxe_fill_skb(cur_ctl); | ||
292 | cur_ctl = cur_ctl->next; | ||
293 | } | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn, | ||
299 | struct wcn36xx_dxe_ch *wcn_ch) | ||
300 | { | ||
301 | struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl; | ||
302 | int i; | ||
303 | |||
304 | for (i = 0; i < wcn_ch->desc_num; i++) { | ||
305 | kfree_skb(cur->skb); | ||
306 | cur = cur->next; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) | ||
311 | { | ||
312 | struct ieee80211_tx_info *info; | ||
313 | struct sk_buff *skb; | ||
314 | unsigned long flags; | ||
315 | |||
316 | spin_lock_irqsave(&wcn->dxe_lock, flags); | ||
317 | skb = wcn->tx_ack_skb; | ||
318 | wcn->tx_ack_skb = NULL; | ||
319 | spin_unlock_irqrestore(&wcn->dxe_lock, flags); | ||
320 | |||
321 | if (!skb) { | ||
322 | wcn36xx_warn("Spurious TX complete indication\n"); | ||
323 | return; | ||
324 | } | ||
325 | |||
326 | info = IEEE80211_SKB_CB(skb); | ||
327 | |||
328 | if (status == 1) | ||
329 | info->flags |= IEEE80211_TX_STAT_ACK; | ||
330 | |||
331 | wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); | ||
332 | |||
333 | ieee80211_tx_status_irqsafe(wcn->hw, skb); | ||
334 | ieee80211_wake_queues(wcn->hw); | ||
335 | } | ||
336 | |||
337 | static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) | ||
338 | { | ||
339 | struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl; | ||
340 | struct ieee80211_tx_info *info; | ||
341 | unsigned long flags; | ||
342 | |||
343 | /* | ||
344 | * Make at least one loop of do-while because in case ring is | ||
345 | * completely full head and tail are pointing to the same element | ||
346 | * and while-do will not make any cycles. | ||
347 | */ | ||
348 | do { | ||
349 | if (ctl->skb) { | ||
350 | dma_unmap_single(NULL, ctl->desc->src_addr_l, | ||
351 | ctl->skb->len, DMA_TO_DEVICE); | ||
352 | info = IEEE80211_SKB_CB(ctl->skb); | ||
353 | if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { | ||
354 | /* Keep frame until TX status comes */ | ||
355 | ieee80211_free_txskb(wcn->hw, ctl->skb); | ||
356 | } | ||
357 | spin_lock_irqsave(&ctl->skb_lock, flags); | ||
358 | if (wcn->queues_stopped) { | ||
359 | wcn->queues_stopped = false; | ||
360 | ieee80211_wake_queues(wcn->hw); | ||
361 | } | ||
362 | spin_unlock_irqrestore(&ctl->skb_lock, flags); | ||
363 | |||
364 | ctl->skb = NULL; | ||
365 | } | ||
366 | ctl = ctl->next; | ||
367 | } while (ctl != ch->head_blk_ctl && | ||
368 | !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); | ||
369 | |||
370 | ch->tail_blk_ctl = ctl; | ||
371 | } | ||
372 | |||
373 | static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) | ||
374 | { | ||
375 | struct wcn36xx *wcn = (struct wcn36xx *)dev; | ||
376 | int int_src, int_reason; | ||
377 | |||
378 | wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); | ||
379 | |||
380 | if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) { | ||
381 | wcn36xx_dxe_read_register(wcn, | ||
382 | WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H, | ||
383 | &int_reason); | ||
384 | |||
385 | /* TODO: Check int_reason */ | ||
386 | |||
387 | wcn36xx_dxe_write_register(wcn, | ||
388 | WCN36XX_DXE_0_INT_CLR, | ||
389 | WCN36XX_INT_MASK_CHAN_TX_H); | ||
390 | |||
391 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, | ||
392 | WCN36XX_INT_MASK_CHAN_TX_H); | ||
393 | wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n"); | ||
394 | reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); | ||
395 | } | ||
396 | |||
397 | if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { | ||
398 | wcn36xx_dxe_read_register(wcn, | ||
399 | WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L, | ||
400 | &int_reason); | ||
401 | /* TODO: Check int_reason */ | ||
402 | |||
403 | wcn36xx_dxe_write_register(wcn, | ||
404 | WCN36XX_DXE_0_INT_CLR, | ||
405 | WCN36XX_INT_MASK_CHAN_TX_L); | ||
406 | |||
407 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, | ||
408 | WCN36XX_INT_MASK_CHAN_TX_L); | ||
409 | wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n"); | ||
410 | reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); | ||
411 | } | ||
412 | |||
413 | return IRQ_HANDLED; | ||
414 | } | ||
415 | |||
416 | static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev) | ||
417 | { | ||
418 | struct wcn36xx *wcn = (struct wcn36xx *)dev; | ||
419 | |||
420 | disable_irq_nosync(wcn->rx_irq); | ||
421 | wcn36xx_dxe_rx_frame(wcn); | ||
422 | enable_irq(wcn->rx_irq); | ||
423 | return IRQ_HANDLED; | ||
424 | } | ||
425 | |||
426 | static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn) | ||
427 | { | ||
428 | int ret; | ||
429 | |||
430 | ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete, | ||
431 | IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn); | ||
432 | if (ret) { | ||
433 | wcn36xx_err("failed to alloc tx irq\n"); | ||
434 | goto out_err; | ||
435 | } | ||
436 | |||
437 | ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH, | ||
438 | "wcn36xx_rx", wcn); | ||
439 | if (ret) { | ||
440 | wcn36xx_err("failed to alloc rx irq\n"); | ||
441 | goto out_txirq; | ||
442 | } | ||
443 | |||
444 | enable_irq_wake(wcn->rx_irq); | ||
445 | |||
446 | return 0; | ||
447 | |||
448 | out_txirq: | ||
449 | free_irq(wcn->tx_irq, wcn); | ||
450 | out_err: | ||
451 | return ret; | ||
452 | |||
453 | } | ||
454 | |||
455 | static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, | ||
456 | struct wcn36xx_dxe_ch *ch) | ||
457 | { | ||
458 | struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl; | ||
459 | struct wcn36xx_dxe_desc *dxe = ctl->desc; | ||
460 | dma_addr_t dma_addr; | ||
461 | struct sk_buff *skb; | ||
462 | |||
463 | while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { | ||
464 | skb = ctl->skb; | ||
465 | dma_addr = dxe->dst_addr_l; | ||
466 | wcn36xx_dxe_fill_skb(ctl); | ||
467 | |||
468 | switch (ch->ch_type) { | ||
469 | case WCN36XX_DXE_CH_RX_L: | ||
470 | dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; | ||
471 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, | ||
472 | WCN36XX_DXE_INT_CH1_MASK); | ||
473 | break; | ||
474 | case WCN36XX_DXE_CH_RX_H: | ||
475 | dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; | ||
476 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, | ||
477 | WCN36XX_DXE_INT_CH3_MASK); | ||
478 | break; | ||
479 | default: | ||
480 | wcn36xx_warn("Unknown channel\n"); | ||
481 | } | ||
482 | |||
483 | dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE, | ||
484 | DMA_FROM_DEVICE); | ||
485 | wcn36xx_rx_skb(wcn, skb); | ||
486 | ctl = ctl->next; | ||
487 | dxe = ctl->desc; | ||
488 | } | ||
489 | |||
490 | ch->head_blk_ctl = ctl; | ||
491 | |||
492 | return 0; | ||
493 | } | ||
494 | |||
495 | void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn) | ||
496 | { | ||
497 | int int_src; | ||
498 | |||
499 | wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); | ||
500 | |||
501 | /* RX_LOW_PRI */ | ||
502 | if (int_src & WCN36XX_DXE_INT_CH1_MASK) { | ||
503 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, | ||
504 | WCN36XX_DXE_INT_CH1_MASK); | ||
505 | wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch)); | ||
506 | } | ||
507 | |||
508 | /* RX_HIGH_PRI */ | ||
509 | if (int_src & WCN36XX_DXE_INT_CH3_MASK) { | ||
510 | /* Clean up all the INT within this channel */ | ||
511 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, | ||
512 | WCN36XX_DXE_INT_CH3_MASK); | ||
513 | wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch)); | ||
514 | } | ||
515 | |||
516 | if (!int_src) | ||
517 | wcn36xx_warn("No DXE interrupt pending\n"); | ||
518 | } | ||
519 | |||
520 | int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) | ||
521 | { | ||
522 | size_t s; | ||
523 | void *cpu_addr; | ||
524 | |||
525 | /* Allocate BD headers for MGMT frames */ | ||
526 | |||
527 | /* Where this come from ask QC */ | ||
528 | wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + | ||
529 | 16 - (WCN36XX_BD_CHUNK_SIZE % 8); | ||
530 | |||
531 | s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; | ||
532 | cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr, | ||
533 | GFP_KERNEL); | ||
534 | if (!cpu_addr) | ||
535 | goto out_err; | ||
536 | |||
537 | wcn->mgmt_mem_pool.virt_addr = cpu_addr; | ||
538 | memset(cpu_addr, 0, s); | ||
539 | |||
540 | /* Allocate BD headers for DATA frames */ | ||
541 | |||
542 | /* Where this come from ask QC */ | ||
543 | wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + | ||
544 | 16 - (WCN36XX_BD_CHUNK_SIZE % 8); | ||
545 | |||
546 | s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; | ||
547 | cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr, | ||
548 | GFP_KERNEL); | ||
549 | if (!cpu_addr) | ||
550 | goto out_err; | ||
551 | |||
552 | wcn->data_mem_pool.virt_addr = cpu_addr; | ||
553 | memset(cpu_addr, 0, s); | ||
554 | |||
555 | return 0; | ||
556 | |||
557 | out_err: | ||
558 | wcn36xx_dxe_free_mem_pools(wcn); | ||
559 | wcn36xx_err("Failed to allocate BD mempool\n"); | ||
560 | return -ENOMEM; | ||
561 | } | ||
562 | |||
563 | void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn) | ||
564 | { | ||
565 | if (wcn->mgmt_mem_pool.virt_addr) | ||
566 | dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size * | ||
567 | WCN36XX_DXE_CH_DESC_NUMB_TX_H, | ||
568 | wcn->mgmt_mem_pool.virt_addr, | ||
569 | wcn->mgmt_mem_pool.phy_addr); | ||
570 | |||
571 | if (wcn->data_mem_pool.virt_addr) { | ||
572 | dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size * | ||
573 | WCN36XX_DXE_CH_DESC_NUMB_TX_L, | ||
574 | wcn->data_mem_pool.virt_addr, | ||
575 | wcn->data_mem_pool.phy_addr); | ||
576 | } | ||
577 | } | ||
578 | |||
579 | int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, | ||
580 | struct wcn36xx_vif *vif_priv, | ||
581 | struct sk_buff *skb, | ||
582 | bool is_low) | ||
583 | { | ||
584 | struct wcn36xx_dxe_ctl *ctl = NULL; | ||
585 | struct wcn36xx_dxe_desc *desc = NULL; | ||
586 | struct wcn36xx_dxe_ch *ch = NULL; | ||
587 | unsigned long flags; | ||
588 | |||
589 | ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; | ||
590 | |||
591 | ctl = ch->head_blk_ctl; | ||
592 | |||
593 | spin_lock_irqsave(&ctl->next->skb_lock, flags); | ||
594 | |||
595 | /* | ||
596 | * If skb is not null that means that we reached the tail of the ring | ||
597 | * hence ring is full. Stop queues to let mac80211 back off until ring | ||
598 | * has an empty slot again. | ||
599 | */ | ||
600 | if (NULL != ctl->next->skb) { | ||
601 | ieee80211_stop_queues(wcn->hw); | ||
602 | wcn->queues_stopped = true; | ||
603 | spin_unlock_irqrestore(&ctl->next->skb_lock, flags); | ||
604 | return -EBUSY; | ||
605 | } | ||
606 | spin_unlock_irqrestore(&ctl->next->skb_lock, flags); | ||
607 | |||
608 | ctl->skb = NULL; | ||
609 | desc = ctl->desc; | ||
610 | |||
611 | /* Set source address of the BD we send */ | ||
612 | desc->src_addr_l = ctl->bd_phy_addr; | ||
613 | |||
614 | desc->dst_addr_l = ch->dxe_wq; | ||
615 | desc->fr_len = sizeof(struct wcn36xx_tx_bd); | ||
616 | desc->ctrl = ch->ctrl_bd; | ||
617 | |||
618 | wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n"); | ||
619 | |||
620 | wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ", | ||
621 | (char *)desc, sizeof(*desc)); | ||
622 | wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, | ||
623 | "BD >>> ", (char *)ctl->bd_cpu_addr, | ||
624 | sizeof(struct wcn36xx_tx_bd)); | ||
625 | |||
626 | /* Set source address of the SKB we send */ | ||
627 | ctl = ctl->next; | ||
628 | ctl->skb = skb; | ||
629 | desc = ctl->desc; | ||
630 | if (ctl->bd_cpu_addr) { | ||
631 | wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n"); | ||
632 | return -EINVAL; | ||
633 | } | ||
634 | |||
635 | desc->src_addr_l = dma_map_single(NULL, | ||
636 | ctl->skb->data, | ||
637 | ctl->skb->len, | ||
638 | DMA_TO_DEVICE); | ||
639 | |||
640 | desc->dst_addr_l = ch->dxe_wq; | ||
641 | desc->fr_len = ctl->skb->len; | ||
642 | |||
643 | /* set dxe descriptor to VALID */ | ||
644 | desc->ctrl = ch->ctrl_skb; | ||
645 | |||
646 | wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ", | ||
647 | (char *)desc, sizeof(*desc)); | ||
648 | wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ", | ||
649 | (char *)ctl->skb->data, ctl->skb->len); | ||
650 | |||
651 | /* Move the head of the ring to the next empty descriptor */ | ||
652 | ch->head_blk_ctl = ctl->next; | ||
653 | |||
654 | /* | ||
655 | * When connected and trying to send data frame chip can be in sleep | ||
656 | * mode and writing to the register will not wake up the chip. Instead | ||
657 | * notify chip about new frame through SMSM bus. | ||
658 | */ | ||
659 | if (is_low && vif_priv->pw_state == WCN36XX_BMPS) { | ||
660 | wcn->ctrl_ops->smsm_change_state( | ||
661 | 0, | ||
662 | WCN36XX_SMSM_WLAN_TX_ENABLE); | ||
663 | } else { | ||
664 | /* indicate End Of Packet and generate interrupt on descriptor | ||
665 | * done. | ||
666 | */ | ||
667 | wcn36xx_dxe_write_register(wcn, | ||
668 | ch->reg_ctrl, ch->def_ctrl); | ||
669 | } | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | int wcn36xx_dxe_init(struct wcn36xx *wcn) | ||
675 | { | ||
676 | int reg_data = 0, ret; | ||
677 | |||
678 | reg_data = WCN36XX_DXE_REG_RESET; | ||
679 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); | ||
680 | |||
681 | /* Setting interrupt path */ | ||
682 | reg_data = WCN36XX_DXE_CCU_INT; | ||
683 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data); | ||
684 | |||
685 | /***************************************/ | ||
686 | /* Init descriptors for TX LOW channel */ | ||
687 | /***************************************/ | ||
688 | wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch); | ||
689 | wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool); | ||
690 | |||
691 | /* Write channel head to a NEXT register */ | ||
692 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L, | ||
693 | wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr); | ||
694 | |||
695 | /* Program DMA destination addr for TX LOW */ | ||
696 | wcn36xx_dxe_write_register(wcn, | ||
697 | WCN36XX_DXE_CH_DEST_ADDR_TX_L, | ||
698 | WCN36XX_DXE_WQ_TX_L); | ||
699 | |||
700 | wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); | ||
701 | wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); | ||
702 | |||
703 | /***************************************/ | ||
704 | /* Init descriptors for TX HIGH channel */ | ||
705 | /***************************************/ | ||
706 | wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch); | ||
707 | wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool); | ||
708 | |||
709 | /* Write channel head to a NEXT register */ | ||
710 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H, | ||
711 | wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr); | ||
712 | |||
713 | /* Program DMA destination addr for TX HIGH */ | ||
714 | wcn36xx_dxe_write_register(wcn, | ||
715 | WCN36XX_DXE_CH_DEST_ADDR_TX_H, | ||
716 | WCN36XX_DXE_WQ_TX_H); | ||
717 | |||
718 | wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); | ||
719 | |||
720 | /* Enable channel interrupts */ | ||
721 | wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); | ||
722 | |||
723 | /***************************************/ | ||
724 | /* Init descriptors for RX LOW channel */ | ||
725 | /***************************************/ | ||
726 | wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch); | ||
727 | |||
728 | /* For RX we need to preallocated buffers */ | ||
729 | wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch); | ||
730 | |||
731 | /* Write channel head to a NEXT register */ | ||
732 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L, | ||
733 | wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr); | ||
734 | |||
735 | /* Write DMA source address */ | ||
736 | wcn36xx_dxe_write_register(wcn, | ||
737 | WCN36XX_DXE_CH_SRC_ADDR_RX_L, | ||
738 | WCN36XX_DXE_WQ_RX_L); | ||
739 | |||
740 | /* Program preallocated destination address */ | ||
741 | wcn36xx_dxe_write_register(wcn, | ||
742 | WCN36XX_DXE_CH_DEST_ADDR_RX_L, | ||
743 | wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l); | ||
744 | |||
745 | /* Enable default control registers */ | ||
746 | wcn36xx_dxe_write_register(wcn, | ||
747 | WCN36XX_DXE_REG_CTL_RX_L, | ||
748 | WCN36XX_DXE_CH_DEFAULT_CTL_RX_L); | ||
749 | |||
750 | /* Enable channel interrupts */ | ||
751 | wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); | ||
752 | |||
753 | /***************************************/ | ||
754 | /* Init descriptors for RX HIGH channel */ | ||
755 | /***************************************/ | ||
756 | wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch); | ||
757 | |||
758 | /* For RX we need to prealocat buffers */ | ||
759 | wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch); | ||
760 | |||
761 | /* Write chanel head to a NEXT register */ | ||
762 | wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H, | ||
763 | wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr); | ||
764 | |||
765 | /* Write DMA source address */ | ||
766 | wcn36xx_dxe_write_register(wcn, | ||
767 | WCN36XX_DXE_CH_SRC_ADDR_RX_H, | ||
768 | WCN36XX_DXE_WQ_RX_H); | ||
769 | |||
770 | /* Program preallocated destination address */ | ||
771 | wcn36xx_dxe_write_register(wcn, | ||
772 | WCN36XX_DXE_CH_DEST_ADDR_RX_H, | ||
773 | wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l); | ||
774 | |||
775 | /* Enable default control registers */ | ||
776 | wcn36xx_dxe_write_register(wcn, | ||
777 | WCN36XX_DXE_REG_CTL_RX_H, | ||
778 | WCN36XX_DXE_CH_DEFAULT_CTL_RX_H); | ||
779 | |||
780 | /* Enable channel interrupts */ | ||
781 | wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); | ||
782 | |||
783 | ret = wcn36xx_dxe_request_irqs(wcn); | ||
784 | if (ret < 0) | ||
785 | goto out_err; | ||
786 | |||
787 | return 0; | ||
788 | |||
789 | out_err: | ||
790 | return ret; | ||
791 | } | ||
792 | |||
793 | void wcn36xx_dxe_deinit(struct wcn36xx *wcn) | ||
794 | { | ||
795 | free_irq(wcn->tx_irq, wcn); | ||
796 | free_irq(wcn->rx_irq, wcn); | ||
797 | |||
798 | if (wcn->tx_ack_skb) { | ||
799 | ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); | ||
800 | wcn->tx_ack_skb = NULL; | ||
801 | } | ||
802 | |||
803 | wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch); | ||
804 | wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch); | ||
805 | } | ||