Commit | Line | Data |
---|---|---|
7963eb43 JW |
1 | /*************************************************************************** |
2 | * Copyright (c) 2005-2009, Broadcom Corporation. | |
3 | * | |
4 | * Name: crystalhd_misc . c | |
5 | * | |
6 | * Description: | |
7 | * BCM70012 Linux driver misc routines. | |
8 | * | |
9 | * HISTORY: | |
10 | * | |
11 | ********************************************************************** | |
12 | * This file is part of the crystalhd device driver. | |
13 | * | |
14 | * This driver is free software; you can redistribute it and/or modify | |
15 | * it under the terms of the GNU General Public License as published by | |
16 | * the Free Software Foundation, version 2 of the License. | |
17 | * | |
18 | * This driver is distributed in the hope that it will be useful, | |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 | * GNU General Public License for more details. | |
22 | * | |
23 | * You should have received a copy of the GNU General Public License | |
24 | * along with this driver. If not, see <http://www.gnu.org/licenses/>. | |
25 | **********************************************************************/ | |
26 | ||
5a0e3ad6 TH |
27 | #include <linux/slab.h> |
28 | ||
7963eb43 JW |
29 | #include "crystalhd_misc.h" |
30 | #include "crystalhd_lnx.h" | |
31 | ||
32 | uint32_t g_linklog_level; | |
33 | ||
34 | static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off) | |
35 | { | |
36 | crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19)); | |
37 | return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF))); | |
38 | } | |
39 | ||
40 | static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val) | |
41 | { | |
42 | crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19)); | |
43 | bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val); | |
44 | } | |
45 | ||
abfc768d | 46 | static inline enum BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt) |
7963eb43 JW |
47 | { |
48 | return BC_STS_SUCCESS; | |
49 | } | |
50 | ||
abfc768d | 51 | static struct crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp) |
7963eb43 JW |
52 | { |
53 | unsigned long flags = 0; | |
abfc768d | 54 | struct crystalhd_dio_req *temp = NULL; |
7963eb43 JW |
55 | |
56 | if (!adp) { | |
57 | BCMLOG_ERR("Invalid Arg!!\n"); | |
58 | return temp; | |
59 | } | |
60 | ||
61 | spin_lock_irqsave(&adp->lock, flags); | |
62 | temp = adp->ua_map_free_head; | |
63 | if (temp) | |
64 | adp->ua_map_free_head = adp->ua_map_free_head->next; | |
65 | spin_unlock_irqrestore(&adp->lock, flags); | |
66 | ||
67 | return temp; | |
68 | } | |
69 | ||
abfc768d | 70 | static void crystalhd_free_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio) |
7963eb43 JW |
71 | { |
72 | unsigned long flags = 0; | |
73 | ||
74 | if (!adp || !dio) | |
75 | return; | |
76 | spin_lock_irqsave(&adp->lock, flags); | |
77 | dio->sig = crystalhd_dio_inv; | |
78 | dio->page_cnt = 0; | |
79 | dio->fb_size = 0; | |
80 | memset(&dio->uinfo, 0, sizeof(dio->uinfo)); | |
81 | dio->next = adp->ua_map_free_head; | |
82 | adp->ua_map_free_head = dio; | |
83 | spin_unlock_irqrestore(&adp->lock, flags); | |
84 | } | |
85 | ||
abfc768d | 86 | static struct crystalhd_elem *crystalhd_alloc_elem(struct crystalhd_adp *adp) |
7963eb43 JW |
87 | { |
88 | unsigned long flags = 0; | |
abfc768d | 89 | struct crystalhd_elem *temp = NULL; |
7963eb43 JW |
90 | |
91 | if (!adp) | |
92 | return temp; | |
93 | spin_lock_irqsave(&adp->lock, flags); | |
94 | temp = adp->elem_pool_head; | |
95 | if (temp) { | |
96 | adp->elem_pool_head = adp->elem_pool_head->flink; | |
97 | memset(temp, 0, sizeof(*temp)); | |
98 | } | |
99 | spin_unlock_irqrestore(&adp->lock, flags); | |
100 | ||
101 | return temp; | |
102 | } | |
abfc768d | 103 | static void crystalhd_free_elem(struct crystalhd_adp *adp, struct crystalhd_elem *elem) |
7963eb43 JW |
104 | { |
105 | unsigned long flags = 0; | |
106 | ||
107 | if (!adp || !elem) | |
108 | return; | |
109 | spin_lock_irqsave(&adp->lock, flags); | |
110 | elem->flink = adp->elem_pool_head; | |
111 | adp->elem_pool_head = elem; | |
112 | spin_unlock_irqrestore(&adp->lock, flags); | |
113 | } | |
114 | ||
115 | static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page, | |
116 | unsigned int len, unsigned int offset) | |
117 | { | |
118 | sg_set_page(sg, page, len, offset); | |
119 | #ifdef CONFIG_X86_64 | |
120 | sg->dma_length = len; | |
121 | #endif | |
122 | } | |
123 | ||
124 | static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries) | |
125 | { | |
126 | /* http://lkml.org/lkml/2007/11/27/68 */ | |
127 | sg_init_table(sg, entries); | |
128 | } | |
129 | ||
130 | /*========================== Extern ========================================*/ | |
131 | /** | |
132 | * bc_dec_reg_rd - Read 7412's device register. | |
133 | * @adp: Adapter instance | |
134 | * @reg_off: Register offset. | |
135 | * | |
136 | * Return: | |
137 | * 32bit value read | |
138 | * | |
139 | * 7412's device register read routine. This interface use | |
140 | * 7412's device access range mapped from BAR-2 (4M) of PCIe | |
141 | * configuration space. | |
142 | */ | |
143 | uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off) | |
144 | { | |
145 | if (!adp || (reg_off > adp->pci_mem_len)) { | |
146 | BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off); | |
147 | return 0; | |
148 | } | |
149 | ||
150 | return readl(adp->addr + reg_off); | |
151 | } | |
152 | ||
153 | /** | |
154 | * bc_dec_reg_wr - Write 7412's device register | |
155 | * @adp: Adapter instance | |
156 | * @reg_off: Register offset. | |
157 | * @val: Dword value to be written. | |
158 | * | |
159 | * Return: | |
160 | * none. | |
161 | * | |
162 | * 7412's device register write routine. This interface use | |
163 | * 7412's device access range mapped from BAR-2 (4M) of PCIe | |
164 | * configuration space. | |
165 | */ | |
166 | void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val) | |
167 | { | |
168 | if (!adp || (reg_off > adp->pci_mem_len)) { | |
169 | BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off); | |
170 | return; | |
171 | } | |
172 | writel(val, adp->addr + reg_off); | |
173 | udelay(8); | |
174 | } | |
175 | ||
176 | /** | |
177 | * crystalhd_reg_rd - Read Link's device register. | |
178 | * @adp: Adapter instance | |
179 | * @reg_off: Register offset. | |
180 | * | |
181 | * Return: | |
182 | * 32bit value read | |
183 | * | |
184 | * Link device register read routine. This interface use | |
185 | * Link's device access range mapped from BAR-1 (64K) of PCIe | |
186 | * configuration space. | |
187 | * | |
188 | */ | |
189 | uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off) | |
190 | { | |
191 | if (!adp || (reg_off > adp->pci_i2o_len)) { | |
192 | BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off); | |
193 | return 0; | |
194 | } | |
195 | return readl(adp->i2o_addr + reg_off); | |
196 | } | |
197 | ||
198 | /** | |
199 | * crystalhd_reg_wr - Write Link's device register | |
200 | * @adp: Adapter instance | |
201 | * @reg_off: Register offset. | |
202 | * @val: Dword value to be written. | |
203 | * | |
204 | * Return: | |
205 | * none. | |
206 | * | |
207 | * Link device register write routine. This interface use | |
208 | * Link's device access range mapped from BAR-1 (64K) of PCIe | |
209 | * configuration space. | |
210 | * | |
211 | */ | |
212 | void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val) | |
213 | { | |
214 | if (!adp || (reg_off > adp->pci_i2o_len)) { | |
215 | BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off); | |
216 | return; | |
217 | } | |
218 | writel(val, adp->i2o_addr + reg_off); | |
219 | } | |
220 | ||
221 | /** | |
222 | * crystalhd_mem_rd - Read data from 7412's DRAM area. | |
223 | * @adp: Adapter instance | |
224 | * @start_off: Start offset. | |
225 | * @dw_cnt: Count in dwords. | |
226 | * @rd_buff: Buffer to copy the data from dram. | |
227 | * | |
228 | * Return: | |
229 | * Status. | |
230 | * | |
231 | * 7412's Dram read routine. | |
232 | */ | |
abfc768d | 233 | enum BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off, |
7963eb43 JW |
234 | uint32_t dw_cnt, uint32_t *rd_buff) |
235 | { | |
236 | uint32_t ix = 0; | |
237 | ||
238 | if (!adp || !rd_buff || | |
239 | (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) { | |
641b63f9 | 240 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
241 | return BC_STS_INV_ARG; |
242 | } | |
243 | for (ix = 0; ix < dw_cnt; ix++) | |
244 | rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4))); | |
245 | ||
246 | return BC_STS_SUCCESS; | |
247 | } | |
248 | ||
249 | /** | |
250 | * crystalhd_mem_wr - Write data to 7412's DRAM area. | |
251 | * @adp: Adapter instance | |
252 | * @start_off: Start offset. | |
253 | * @dw_cnt: Count in dwords. | |
254 | * @wr_buff: Data Buffer to be written. | |
255 | * | |
256 | * Return: | |
257 | * Status. | |
258 | * | |
259 | * 7412's Dram write routine. | |
260 | */ | |
abfc768d | 261 | enum BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off, |
7963eb43 JW |
262 | uint32_t dw_cnt, uint32_t *wr_buff) |
263 | { | |
264 | uint32_t ix = 0; | |
265 | ||
266 | if (!adp || !wr_buff || | |
267 | (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) { | |
641b63f9 | 268 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
269 | return BC_STS_INV_ARG; |
270 | } | |
271 | ||
272 | for (ix = 0; ix < dw_cnt; ix++) | |
273 | crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]); | |
274 | ||
275 | return BC_STS_SUCCESS; | |
276 | } | |
277 | /** | |
278 | * crystalhd_pci_cfg_rd - PCIe config read | |
279 | * @adp: Adapter instance | |
280 | * @off: PCI config space offset. | |
281 | * @len: Size -- Byte, Word & dword. | |
282 | * @val: Value read | |
283 | * | |
284 | * Return: | |
285 | * Status. | |
286 | * | |
287 | * Get value from Link's PCIe config space. | |
288 | */ | |
abfc768d | 289 | enum BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off, |
7963eb43 JW |
290 | uint32_t len, uint32_t *val) |
291 | { | |
abfc768d | 292 | enum BC_STATUS sts = BC_STS_SUCCESS; |
7963eb43 JW |
293 | int rc = 0; |
294 | ||
295 | if (!adp || !val) { | |
641b63f9 | 296 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
297 | return BC_STS_INV_ARG; |
298 | } | |
299 | ||
300 | switch (len) { | |
301 | case 1: | |
302 | rc = pci_read_config_byte(adp->pdev, off, (u8 *)val); | |
303 | break; | |
304 | case 2: | |
305 | rc = pci_read_config_word(adp->pdev, off, (u16 *)val); | |
306 | break; | |
307 | case 4: | |
308 | rc = pci_read_config_dword(adp->pdev, off, (u32 *)val); | |
309 | break; | |
310 | default: | |
311 | rc = -EINVAL; | |
312 | sts = BC_STS_INV_ARG; | |
313 | BCMLOG_ERR("Invalid len:%d\n", len); | |
314 | }; | |
315 | ||
316 | if (rc && (sts == BC_STS_SUCCESS)) | |
317 | sts = BC_STS_ERROR; | |
318 | ||
319 | return sts; | |
320 | } | |
321 | ||
322 | /** | |
323 | * crystalhd_pci_cfg_wr - PCIe config write | |
324 | * @adp: Adapter instance | |
325 | * @off: PCI config space offset. | |
326 | * @len: Size -- Byte, Word & dword. | |
327 | * @val: Value to be written | |
328 | * | |
329 | * Return: | |
330 | * Status. | |
331 | * | |
332 | * Set value to Link's PCIe config space. | |
333 | */ | |
abfc768d | 334 | enum BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off, |
7963eb43 JW |
335 | uint32_t len, uint32_t val) |
336 | { | |
abfc768d | 337 | enum BC_STATUS sts = BC_STS_SUCCESS; |
7963eb43 JW |
338 | int rc = 0; |
339 | ||
340 | if (!adp || !val) { | |
641b63f9 | 341 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
342 | return BC_STS_INV_ARG; |
343 | } | |
344 | ||
345 | switch (len) { | |
346 | case 1: | |
347 | rc = pci_write_config_byte(adp->pdev, off, (u8)val); | |
348 | break; | |
349 | case 2: | |
350 | rc = pci_write_config_word(adp->pdev, off, (u16)val); | |
351 | break; | |
352 | case 4: | |
353 | rc = pci_write_config_dword(adp->pdev, off, val); | |
354 | break; | |
355 | default: | |
356 | rc = -EINVAL; | |
357 | sts = BC_STS_INV_ARG; | |
358 | BCMLOG_ERR("Invalid len:%d\n", len); | |
359 | }; | |
360 | ||
361 | if (rc && (sts == BC_STS_SUCCESS)) | |
362 | sts = BC_STS_ERROR; | |
363 | ||
364 | return sts; | |
365 | } | |
366 | ||
367 | /** | |
368 | * bc_kern_dma_alloc - Allocate memory for Dma rings | |
369 | * @adp: Adapter instance | |
370 | * @sz: Size of the memory to allocate. | |
371 | * @phy_addr: Physical address of the memory allocated. | |
372 | * Typedef to system's dma_addr_t (u64) | |
373 | * | |
374 | * Return: | |
375 | * Pointer to allocated memory.. | |
376 | * | |
377 | * Wrapper to Linux kernel interface. | |
378 | * | |
379 | */ | |
380 | void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz, | |
381 | dma_addr_t *phy_addr) | |
382 | { | |
383 | void *temp = NULL; | |
384 | ||
385 | if (!adp || !sz || !phy_addr) { | |
386 | BCMLOG_ERR("Invalide Arg..\n"); | |
387 | return temp; | |
388 | } | |
389 | ||
390 | temp = pci_alloc_consistent(adp->pdev, sz, phy_addr); | |
391 | if (temp) | |
392 | memset(temp, 0, sz); | |
393 | ||
394 | return temp; | |
395 | } | |
396 | ||
397 | /** | |
398 | * bc_kern_dma_free - Release Dma ring memory. | |
399 | * @adp: Adapter instance | |
400 | * @sz: Size of the memory to allocate. | |
401 | * @ka: Kernel virtual address returned during _dio_alloc() | |
402 | * @phy_addr: Physical address of the memory allocated. | |
403 | * Typedef to system's dma_addr_t (u64) | |
404 | * | |
405 | * Return: | |
406 | * none. | |
407 | */ | |
408 | void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka, | |
409 | dma_addr_t phy_addr) | |
410 | { | |
411 | if (!adp || !ka || !sz || !phy_addr) { | |
412 | BCMLOG_ERR("Invalide Arg..\n"); | |
413 | return; | |
414 | } | |
415 | ||
416 | pci_free_consistent(adp->pdev, sz, ka, phy_addr); | |
417 | } | |
418 | ||
419 | /** | |
420 | * crystalhd_create_dioq - Create Generic DIO queue | |
421 | * @adp: Adapter instance | |
422 | * @dioq_hnd: Handle to the dio queue created | |
423 | * @cb : Optional - Call back To free the element. | |
424 | * @cbctx: Context to pass to callback. | |
425 | * | |
426 | * Return: | |
427 | * status | |
428 | * | |
429 | * Initialize Generic DIO queue to hold any data. Callback | |
430 | * will be used to free elements while deleting the queue. | |
431 | */ | |
abfc768d LD |
432 | enum BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp, |
433 | struct crystalhd_dioq **dioq_hnd, | |
7963eb43 JW |
434 | crystalhd_data_free_cb cb, void *cbctx) |
435 | { | |
abfc768d | 436 | struct crystalhd_dioq *dioq = NULL; |
7963eb43 JW |
437 | |
438 | if (!adp || !dioq_hnd) { | |
439 | BCMLOG_ERR("Invalid arg!!\n"); | |
440 | return BC_STS_INV_ARG; | |
441 | } | |
442 | ||
443 | dioq = kzalloc(sizeof(*dioq), GFP_KERNEL); | |
444 | if (!dioq) | |
445 | return BC_STS_INSUFF_RES; | |
446 | ||
447 | spin_lock_init(&dioq->lock); | |
448 | dioq->sig = BC_LINK_DIOQ_SIG; | |
abfc768d LD |
449 | dioq->head = (struct crystalhd_elem *)&dioq->head; |
450 | dioq->tail = (struct crystalhd_elem *)&dioq->head; | |
7963eb43 JW |
451 | crystalhd_create_event(&dioq->event); |
452 | dioq->adp = adp; | |
453 | dioq->data_rel_cb = cb; | |
454 | dioq->cb_context = cbctx; | |
455 | *dioq_hnd = dioq; | |
456 | ||
457 | return BC_STS_SUCCESS; | |
458 | } | |
459 | ||
460 | /** | |
461 | * crystalhd_delete_dioq - Delete Generic DIO queue | |
462 | * @adp: Adapter instance | |
463 | * @dioq: DIOQ instance.. | |
464 | * | |
465 | * Return: | |
466 | * None. | |
467 | * | |
468 | * Release Generic DIO queue. This function will remove | |
469 | * all the entries from the Queue and will release data | |
470 | * by calling the call back provided during creation. | |
471 | * | |
472 | */ | |
abfc768d | 473 | void crystalhd_delete_dioq(struct crystalhd_adp *adp, struct crystalhd_dioq *dioq) |
7963eb43 JW |
474 | { |
475 | void *temp; | |
476 | ||
477 | if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG)) | |
478 | return; | |
479 | ||
480 | do { | |
481 | temp = crystalhd_dioq_fetch(dioq); | |
482 | if (temp && dioq->data_rel_cb) | |
483 | dioq->data_rel_cb(dioq->cb_context, temp); | |
484 | } while (temp); | |
485 | dioq->sig = 0; | |
486 | kfree(dioq); | |
487 | } | |
488 | ||
489 | /** | |
490 | * crystalhd_dioq_add - Add new DIO request element. | |
491 | * @ioq: DIO queue instance | |
492 | * @t: DIO request to be added. | |
493 | * @wake: True - Wake up suspended process. | |
494 | * @tag: Special tag to assign - For search and get. | |
495 | * | |
496 | * Return: | |
497 | * Status. | |
498 | * | |
499 | * Insert new element to Q tail. | |
500 | */ | |
abfc768d | 501 | enum BC_STATUS crystalhd_dioq_add(struct crystalhd_dioq *ioq, void *data, |
7963eb43 JW |
502 | bool wake, uint32_t tag) |
503 | { | |
504 | unsigned long flags = 0; | |
abfc768d | 505 | struct crystalhd_elem *tmp; |
7963eb43 JW |
506 | |
507 | if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) { | |
508 | BCMLOG_ERR("Invalid arg!!\n"); | |
509 | return BC_STS_INV_ARG; | |
510 | } | |
511 | ||
512 | tmp = crystalhd_alloc_elem(ioq->adp); | |
513 | if (!tmp) { | |
514 | BCMLOG_ERR("No free elements.\n"); | |
515 | return BC_STS_INSUFF_RES; | |
516 | } | |
517 | ||
518 | tmp->data = data; | |
519 | tmp->tag = tag; | |
520 | spin_lock_irqsave(&ioq->lock, flags); | |
abfc768d | 521 | tmp->flink = (struct crystalhd_elem *)&ioq->head; |
7963eb43 JW |
522 | tmp->blink = ioq->tail; |
523 | tmp->flink->blink = tmp; | |
524 | tmp->blink->flink = tmp; | |
525 | ioq->count++; | |
526 | spin_unlock_irqrestore(&ioq->lock, flags); | |
527 | ||
528 | if (wake) | |
529 | crystalhd_set_event(&ioq->event); | |
530 | ||
531 | return BC_STS_SUCCESS; | |
532 | } | |
533 | ||
534 | /** | |
535 | * crystalhd_dioq_fetch - Fetch element from head. | |
536 | * @ioq: DIO queue instance | |
537 | * | |
538 | * Return: | |
539 | * data element from the head.. | |
540 | * | |
541 | * Remove an element from Queue. | |
542 | */ | |
abfc768d | 543 | void *crystalhd_dioq_fetch(struct crystalhd_dioq *ioq) |
7963eb43 JW |
544 | { |
545 | unsigned long flags = 0; | |
abfc768d LD |
546 | struct crystalhd_elem *tmp; |
547 | struct crystalhd_elem *ret = NULL; | |
7963eb43 JW |
548 | void *data = NULL; |
549 | ||
550 | if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) { | |
551 | BCMLOG_ERR("Invalid arg!!\n"); | |
552 | return data; | |
553 | } | |
554 | ||
555 | spin_lock_irqsave(&ioq->lock, flags); | |
556 | tmp = ioq->head; | |
abfc768d | 557 | if (tmp != (struct crystalhd_elem *)&ioq->head) { |
7963eb43 JW |
558 | ret = tmp; |
559 | tmp->flink->blink = tmp->blink; | |
560 | tmp->blink->flink = tmp->flink; | |
561 | ioq->count--; | |
562 | } | |
563 | spin_unlock_irqrestore(&ioq->lock, flags); | |
564 | if (ret) { | |
565 | data = ret->data; | |
566 | crystalhd_free_elem(ioq->adp, ret); | |
567 | } | |
568 | ||
569 | return data; | |
570 | } | |
571 | /** | |
572 | * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element | |
573 | * @ioq: DIO queue instance | |
574 | * @tag: Tag to search for. | |
575 | * | |
576 | * Return: | |
577 | * element from the head.. | |
578 | * | |
579 | * Search TAG and remove the element. | |
580 | */ | |
abfc768d | 581 | void *crystalhd_dioq_find_and_fetch(struct crystalhd_dioq *ioq, uint32_t tag) |
7963eb43 JW |
582 | { |
583 | unsigned long flags = 0; | |
abfc768d LD |
584 | struct crystalhd_elem *tmp; |
585 | struct crystalhd_elem *ret = NULL; | |
7963eb43 JW |
586 | void *data = NULL; |
587 | ||
588 | if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) { | |
589 | BCMLOG_ERR("Invalid arg!!\n"); | |
590 | return data; | |
591 | } | |
592 | ||
593 | spin_lock_irqsave(&ioq->lock, flags); | |
594 | tmp = ioq->head; | |
abfc768d | 595 | while (tmp != (struct crystalhd_elem *)&ioq->head) { |
7963eb43 JW |
596 | if (tmp->tag == tag) { |
597 | ret = tmp; | |
598 | tmp->flink->blink = tmp->blink; | |
599 | tmp->blink->flink = tmp->flink; | |
600 | ioq->count--; | |
601 | break; | |
602 | } | |
603 | tmp = tmp->flink; | |
604 | } | |
605 | spin_unlock_irqrestore(&ioq->lock, flags); | |
606 | ||
607 | if (ret) { | |
608 | data = ret->data; | |
609 | crystalhd_free_elem(ioq->adp, ret); | |
610 | } | |
611 | ||
612 | return data; | |
613 | } | |
614 | ||
615 | /** | |
616 | * crystalhd_dioq_fetch_wait - Fetch element from Head. | |
617 | * @ioq: DIO queue instance | |
618 | * @to_secs: Wait timeout in seconds.. | |
619 | * | |
620 | * Return: | |
621 | * element from the head.. | |
622 | * | |
623 | * Return element from head if Q is not empty. Wait for new element | |
624 | * if Q is empty for Timeout seconds. | |
625 | */ | |
abfc768d | 626 | void *crystalhd_dioq_fetch_wait(struct crystalhd_dioq *ioq, uint32_t to_secs, |
7963eb43 JW |
627 | uint32_t *sig_pend) |
628 | { | |
629 | unsigned long flags = 0; | |
630 | int rc = 0, count; | |
631 | void *tmp = NULL; | |
632 | ||
633 | if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) { | |
634 | BCMLOG_ERR("Invalid arg!!\n"); | |
635 | return tmp; | |
636 | } | |
637 | ||
638 | count = to_secs; | |
639 | spin_lock_irqsave(&ioq->lock, flags); | |
640 | while ((ioq->count == 0) && count) { | |
641 | spin_unlock_irqrestore(&ioq->lock, flags); | |
642 | ||
643 | crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0); | |
644 | if (rc == 0) { | |
645 | goto out; | |
646 | } else if (rc == -EINTR) { | |
647 | BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n"); | |
648 | *sig_pend = 1; | |
649 | return tmp; | |
650 | } | |
651 | spin_lock_irqsave(&ioq->lock, flags); | |
652 | count--; | |
653 | } | |
654 | spin_unlock_irqrestore(&ioq->lock, flags); | |
655 | ||
656 | out: | |
657 | return crystalhd_dioq_fetch(ioq); | |
658 | } | |
659 | ||
660 | /** | |
661 | * crystalhd_map_dio - Map user address for DMA | |
662 | * @adp: Adapter instance | |
663 | * @ubuff: User buffer to map. | |
664 | * @ubuff_sz: User buffer size. | |
665 | * @uv_offset: UV buffer offset. | |
666 | * @en_422mode: TRUE:422 FALSE:420 Capture mode. | |
667 | * @dir_tx: TRUE for Tx (To device from host) | |
668 | * @dio_hnd: Handle to mapped DIO request. | |
669 | * | |
670 | * Return: | |
671 | * Status. | |
672 | * | |
673 | * This routine maps user address and lock pages for DMA. | |
674 | * | |
675 | */ | |
abfc768d | 676 | enum BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff, |
7963eb43 JW |
677 | uint32_t ubuff_sz, uint32_t uv_offset, |
678 | bool en_422mode, bool dir_tx, | |
abfc768d | 679 | struct crystalhd_dio_req **dio_hnd) |
7963eb43 | 680 | { |
abfc768d | 681 | struct crystalhd_dio_req *dio; |
7963eb43 JW |
682 | /* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */ |
683 | unsigned long start = 0, end = 0, uaddr = 0, count = 0; | |
684 | unsigned long spsz = 0, uv_start = 0; | |
685 | int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0; | |
686 | ||
687 | if (!adp || !ubuff || !ubuff_sz || !dio_hnd) { | |
641b63f9 | 688 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
689 | return BC_STS_INV_ARG; |
690 | } | |
691 | /* Compute pages */ | |
692 | uaddr = (unsigned long)ubuff; | |
693 | count = (unsigned long)ubuff_sz; | |
694 | end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; | |
695 | start = uaddr >> PAGE_SHIFT; | |
696 | nr_pages = end - start; | |
697 | ||
698 | if (!count || ((uaddr + count) < uaddr)) { | |
699 | BCMLOG_ERR("User addr overflow!!\n"); | |
700 | return BC_STS_INV_ARG; | |
701 | } | |
702 | ||
703 | dio = crystalhd_alloc_dio(adp); | |
704 | if (!dio) { | |
705 | BCMLOG_ERR("dio pool empty..\n"); | |
706 | return BC_STS_INSUFF_RES; | |
707 | } | |
708 | ||
709 | if (dir_tx) { | |
710 | rw = WRITE; | |
711 | dio->direction = DMA_TO_DEVICE; | |
712 | } else { | |
713 | rw = READ; | |
714 | dio->direction = DMA_FROM_DEVICE; | |
715 | } | |
716 | ||
717 | if (nr_pages > dio->max_pages) { | |
718 | BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n", | |
719 | dio->max_pages, nr_pages); | |
720 | crystalhd_unmap_dio(adp, dio); | |
721 | return BC_STS_INSUFF_RES; | |
722 | } | |
723 | ||
724 | if (uv_offset) { | |
725 | uv_start = (uaddr + (unsigned long)uv_offset) >> PAGE_SHIFT; | |
726 | dio->uinfo.uv_sg_ix = uv_start - start; | |
727 | dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK); | |
728 | } | |
729 | ||
730 | dio->fb_size = ubuff_sz & 0x03; | |
731 | if (dio->fb_size) { | |
732 | res = copy_from_user(dio->fb_va, | |
733 | (void *)(uaddr + count - dio->fb_size), | |
734 | dio->fb_size); | |
735 | if (res) { | |
736 | BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n", | |
737 | res, dio->fb_size, | |
738 | (void *)(uaddr + count-dio->fb_size)); | |
739 | crystalhd_unmap_dio(adp, dio); | |
740 | return BC_STS_INSUFF_RES; | |
741 | } | |
742 | } | |
743 | ||
744 | down_read(¤t->mm->mmap_sem); | |
745 | res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ, | |
746 | 0, dio->pages, NULL); | |
747 | up_read(¤t->mm->mmap_sem); | |
748 | ||
749 | /* Save for release..*/ | |
750 | dio->sig = crystalhd_dio_locked; | |
751 | if (res < nr_pages) { | |
752 | BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res); | |
753 | dio->page_cnt = res; | |
754 | crystalhd_unmap_dio(adp, dio); | |
755 | return BC_STS_ERROR; | |
756 | } | |
757 | ||
758 | dio->page_cnt = nr_pages; | |
759 | /* Get scatter/gather */ | |
760 | crystalhd_init_sg(dio->sg, dio->page_cnt); | |
761 | crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK); | |
762 | if (nr_pages > 1) { | |
763 | dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset; | |
764 | ||
765 | #ifdef CONFIG_X86_64 | |
766 | dio->sg[0].dma_length = dio->sg[0].length; | |
767 | #endif | |
768 | count -= dio->sg[0].length; | |
769 | for (i = 1; i < nr_pages; i++) { | |
770 | if (count < 4) { | |
771 | spsz = count; | |
772 | skip_fb_sg = 1; | |
773 | } else { | |
774 | spsz = (count < PAGE_SIZE) ? | |
775 | (count & ~0x03) : PAGE_SIZE; | |
776 | } | |
777 | crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0); | |
778 | count -= spsz; | |
779 | } | |
780 | } else { | |
781 | if (count < 4) { | |
782 | dio->sg[0].length = count; | |
783 | skip_fb_sg = 1; | |
784 | } else { | |
785 | dio->sg[0].length = count - dio->fb_size; | |
786 | } | |
787 | #ifdef CONFIG_X86_64 | |
788 | dio->sg[0].dma_length = dio->sg[0].length; | |
789 | #endif | |
790 | } | |
791 | dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg, | |
792 | dio->page_cnt, dio->direction); | |
793 | if (dio->sg_cnt <= 0) { | |
641b63f9 | 794 | BCMLOG_ERR("sg map %d-%d\n", dio->sg_cnt, dio->page_cnt); |
7963eb43 JW |
795 | crystalhd_unmap_dio(adp, dio); |
796 | return BC_STS_ERROR; | |
797 | } | |
798 | if (dio->sg_cnt && skip_fb_sg) | |
799 | dio->sg_cnt -= 1; | |
800 | dio->sig = crystalhd_dio_sg_mapped; | |
801 | /* Fill in User info.. */ | |
802 | dio->uinfo.xfr_len = ubuff_sz; | |
803 | dio->uinfo.xfr_buff = ubuff; | |
804 | dio->uinfo.uv_offset = uv_offset; | |
805 | dio->uinfo.b422mode = en_422mode; | |
806 | dio->uinfo.dir_tx = dir_tx; | |
807 | ||
808 | *dio_hnd = dio; | |
809 | ||
810 | return BC_STS_SUCCESS; | |
811 | } | |
812 | ||
813 | /** | |
814 | * crystalhd_unmap_sgl - Release mapped resources | |
815 | * @adp: Adapter instance | |
816 | * @dio: DIO request instance | |
817 | * | |
818 | * Return: | |
819 | * Status. | |
820 | * | |
821 | * This routine is to unmap the user buffer pages. | |
822 | */ | |
abfc768d | 823 | enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio) |
7963eb43 JW |
824 | { |
825 | struct page *page = NULL; | |
826 | int j = 0; | |
827 | ||
828 | if (!adp || !dio) { | |
641b63f9 | 829 | BCMLOG_ERR("Invalid arg\n"); |
7963eb43 JW |
830 | return BC_STS_INV_ARG; |
831 | } | |
832 | ||
833 | if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) { | |
834 | for (j = 0; j < dio->page_cnt; j++) { | |
835 | page = dio->pages[j]; | |
836 | if (page) { | |
837 | if (!PageReserved(page) && | |
838 | (dio->direction == DMA_FROM_DEVICE)) | |
839 | SetPageDirty(page); | |
840 | page_cache_release(page); | |
841 | } | |
842 | } | |
843 | } | |
844 | if (dio->sig == crystalhd_dio_sg_mapped) | |
845 | pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction); | |
846 | ||
847 | crystalhd_free_dio(adp, dio); | |
848 | ||
849 | return BC_STS_SUCCESS; | |
850 | } | |
851 | ||
852 | /** | |
853 | * crystalhd_create_dio_pool - Allocate mem pool for DIO management. | |
854 | * @adp: Adapter instance | |
855 | * @max_pages: Max pages for size calculation. | |
856 | * | |
857 | * Return: | |
858 | * system error. | |
859 | * | |
860 | * This routine creates a memory pool to hold dio context for | |
861 | * for HW Direct IO operation. | |
862 | */ | |
863 | int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages) | |
864 | { | |
865 | uint32_t asz = 0, i = 0; | |
866 | uint8_t *temp; | |
abfc768d | 867 | struct crystalhd_dio_req *dio; |
7963eb43 JW |
868 | |
869 | if (!adp || !max_pages) { | |
870 | BCMLOG_ERR("Invalid Arg!!\n"); | |
871 | return -EINVAL; | |
872 | } | |
873 | ||
874 | /* Get dma memory for fill byte handling..*/ | |
875 | adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte", | |
876 | adp->pdev, 8, 8, 0); | |
877 | if (!adp->fill_byte_pool) { | |
878 | BCMLOG_ERR("failed to create fill byte pool\n"); | |
879 | return -ENOMEM; | |
880 | } | |
881 | ||
882 | /* Get the max size from user based on 420/422 modes */ | |
883 | asz = (sizeof(*dio->pages) * max_pages) + | |
884 | (sizeof(*dio->sg) * max_pages) + sizeof(*dio); | |
885 | ||
886 | BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n", | |
887 | BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool); | |
888 | ||
889 | for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) { | |
32414878 | 890 | temp = kzalloc(asz, GFP_KERNEL); |
7963eb43 JW |
891 | if ((temp) == NULL) { |
892 | BCMLOG_ERR("Failed to alloc %d mem\n", asz); | |
893 | return -ENOMEM; | |
894 | } | |
895 | ||
abfc768d | 896 | dio = (struct crystalhd_dio_req *)temp; |
7963eb43 JW |
897 | temp += sizeof(*dio); |
898 | dio->pages = (struct page **)temp; | |
899 | temp += (sizeof(*dio->pages) * max_pages); | |
900 | dio->sg = (struct scatterlist *)temp; | |
901 | dio->max_pages = max_pages; | |
902 | dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL, | |
903 | &dio->fb_pa); | |
904 | if (!dio->fb_va) { | |
905 | BCMLOG_ERR("fill byte alloc failed.\n"); | |
906 | return -ENOMEM; | |
907 | } | |
908 | ||
909 | crystalhd_free_dio(adp, dio); | |
910 | } | |
911 | ||
912 | return 0; | |
913 | } | |
914 | ||
915 | /** | |
916 | * crystalhd_destroy_dio_pool - Release DIO mem pool. | |
917 | * @adp: Adapter instance | |
918 | * | |
919 | * Return: | |
920 | * none. | |
921 | * | |
922 | * This routine releases dio memory pool during close. | |
923 | */ | |
924 | void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp) | |
925 | { | |
abfc768d | 926 | struct crystalhd_dio_req *dio; |
7963eb43 JW |
927 | int count = 0; |
928 | ||
929 | if (!adp) { | |
930 | BCMLOG_ERR("Invalid Arg!!\n"); | |
931 | return; | |
932 | } | |
933 | ||
934 | do { | |
935 | dio = crystalhd_alloc_dio(adp); | |
936 | if (dio) { | |
937 | if (dio->fb_va) | |
938 | pci_pool_free(adp->fill_byte_pool, | |
939 | dio->fb_va, dio->fb_pa); | |
940 | count++; | |
941 | kfree(dio); | |
942 | } | |
943 | } while (dio); | |
944 | ||
945 | if (adp->fill_byte_pool) { | |
946 | pci_pool_destroy(adp->fill_byte_pool); | |
947 | adp->fill_byte_pool = NULL; | |
948 | } | |
949 | ||
641b63f9 | 950 | BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count); |
7963eb43 JW |
951 | } |
952 | ||
953 | /** | |
954 | * crystalhd_create_elem_pool - List element pool creation. | |
955 | * @adp: Adapter instance | |
956 | * @pool_size: Number of elements in the pool. | |
957 | * | |
958 | * Return: | |
959 | * 0 - success, <0 error | |
960 | * | |
961 | * Create general purpose list element pool to hold pending, | |
962 | * and active requests. | |
963 | */ | |
21b0838c AP |
964 | int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp, |
965 | uint32_t pool_size) | |
7963eb43 JW |
966 | { |
967 | uint32_t i; | |
abfc768d | 968 | struct crystalhd_elem *temp; |
7963eb43 JW |
969 | |
970 | if (!adp || !pool_size) | |
971 | return -EINVAL; | |
972 | ||
973 | for (i = 0; i < pool_size; i++) { | |
974 | temp = kzalloc(sizeof(*temp), GFP_KERNEL); | |
975 | if (!temp) { | |
641b63f9 | 976 | BCMLOG_ERR("kalloc failed\n"); |
7963eb43 JW |
977 | return -ENOMEM; |
978 | } | |
979 | crystalhd_free_elem(adp, temp); | |
980 | } | |
981 | BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size); | |
982 | return 0; | |
983 | } | |
984 | ||
985 | /** | |
986 | * crystalhd_delete_elem_pool - List element pool deletion. | |
987 | * @adp: Adapter instance | |
988 | * | |
989 | * Return: | |
990 | * none | |
991 | * | |
992 | * Delete general purpose list element pool. | |
993 | */ | |
994 | void crystalhd_delete_elem_pool(struct crystalhd_adp *adp) | |
995 | { | |
abfc768d | 996 | struct crystalhd_elem *temp; |
7963eb43 JW |
997 | int dbg_cnt = 0; |
998 | ||
999 | if (!adp) | |
1000 | return; | |
1001 | ||
1002 | do { | |
1003 | temp = crystalhd_alloc_elem(adp); | |
1004 | if (temp) { | |
1005 | kfree(temp); | |
1006 | dbg_cnt++; | |
1007 | } | |
1008 | } while (temp); | |
1009 | ||
1010 | BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt); | |
1011 | } | |
1012 | ||
1013 | /*================ Debug support routines.. ================================*/ | |
1014 | void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount) | |
1015 | { | |
1016 | uint32_t i, k = 1; | |
1017 | ||
1018 | for (i = 0; i < dwcount; i++) { | |
1019 | if (k == 1) | |
1020 | BCMLOG(BCMLOG_DATA, "0x%08X : ", off); | |
1021 | ||
1022 | BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff)); | |
1023 | ||
1024 | buff += sizeof(uint32_t); | |
1025 | off += sizeof(uint32_t); | |
1026 | k++; | |
1027 | if ((i == dwcount - 1) || (k > 4)) { | |
1028 | BCMLOG(BCMLOG_DATA, "\n"); | |
1029 | k = 1; | |
1030 | } | |
1031 | } | |
1032 | } |