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
27 #include <linux/slab.h>
28
29 #include "crystalhd_misc.h"
30 #include "crystalhd_lnx.h"
31
32 uint32_t g_linklog_level;
33
crystalhd_dram_rd(struct crystalhd_adp * adp,uint32_t mem_off)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
crystalhd_dram_wr(struct crystalhd_adp * adp,uint32_t mem_off,uint32_t val)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
bc_chk_dram_range(struct crystalhd_adp * adp,uint32_t start_off,uint32_t cnt)46 static inline enum BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
47 {
48 return BC_STS_SUCCESS;
49 }
50
crystalhd_alloc_dio(struct crystalhd_adp * adp)51 static struct crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
52 {
53 unsigned long flags = 0;
54 struct crystalhd_dio_req *temp = NULL;
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
crystalhd_free_dio(struct crystalhd_adp * adp,struct crystalhd_dio_req * dio)70 static void crystalhd_free_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
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
crystalhd_alloc_elem(struct crystalhd_adp * adp)86 static struct crystalhd_elem *crystalhd_alloc_elem(struct crystalhd_adp *adp)
87 {
88 unsigned long flags = 0;
89 struct crystalhd_elem *temp = NULL;
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 }
crystalhd_free_elem(struct crystalhd_adp * adp,struct crystalhd_elem * elem)103 static void crystalhd_free_elem(struct crystalhd_adp *adp, struct crystalhd_elem *elem)
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
crystalhd_set_sg(struct scatterlist * sg,struct page * page,unsigned int len,unsigned int offset)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
crystalhd_init_sg(struct scatterlist * sg,unsigned int entries)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 */
bc_dec_reg_rd(struct crystalhd_adp * adp,uint32_t reg_off)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 */
bc_dec_reg_wr(struct crystalhd_adp * adp,uint32_t reg_off,uint32_t val)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 */
crystalhd_reg_rd(struct crystalhd_adp * adp,uint32_t reg_off)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 */
crystalhd_reg_wr(struct crystalhd_adp * adp,uint32_t reg_off,uint32_t val)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 */
crystalhd_mem_rd(struct crystalhd_adp * adp,uint32_t start_off,uint32_t dw_cnt,uint32_t * rd_buff)233 enum BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
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)) {
240 BCMLOG_ERR("Invalid arg\n");
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 */
crystalhd_mem_wr(struct crystalhd_adp * adp,uint32_t start_off,uint32_t dw_cnt,uint32_t * wr_buff)261 enum BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
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)) {
268 BCMLOG_ERR("Invalid arg\n");
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 */
crystalhd_pci_cfg_rd(struct crystalhd_adp * adp,uint32_t off,uint32_t len,uint32_t * val)289 enum BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
290 uint32_t len, uint32_t *val)
291 {
292 enum BC_STATUS sts = BC_STS_SUCCESS;
293 int rc = 0;
294
295 if (!adp || !val) {
296 BCMLOG_ERR("Invalid arg\n");
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 */
crystalhd_pci_cfg_wr(struct crystalhd_adp * adp,uint32_t off,uint32_t len,uint32_t val)334 enum BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
335 uint32_t len, uint32_t val)
336 {
337 enum BC_STATUS sts = BC_STS_SUCCESS;
338 int rc = 0;
339
340 if (!adp || !val) {
341 BCMLOG_ERR("Invalid arg\n");
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 */
bc_kern_dma_alloc(struct crystalhd_adp * adp,uint32_t sz,dma_addr_t * phy_addr)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 */
bc_kern_dma_free(struct crystalhd_adp * adp,uint32_t sz,void * ka,dma_addr_t phy_addr)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 */
crystalhd_create_dioq(struct crystalhd_adp * adp,struct crystalhd_dioq ** dioq_hnd,crystalhd_data_free_cb cb,void * cbctx)432 enum BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
433 struct crystalhd_dioq **dioq_hnd,
434 crystalhd_data_free_cb cb, void *cbctx)
435 {
436 struct crystalhd_dioq *dioq = NULL;
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;
449 dioq->head = (struct crystalhd_elem *)&dioq->head;
450 dioq->tail = (struct crystalhd_elem *)&dioq->head;
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 */
crystalhd_delete_dioq(struct crystalhd_adp * adp,struct crystalhd_dioq * dioq)473 void crystalhd_delete_dioq(struct crystalhd_adp *adp, struct crystalhd_dioq *dioq)
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 */
crystalhd_dioq_add(struct crystalhd_dioq * ioq,void * data,bool wake,uint32_t tag)501 enum BC_STATUS crystalhd_dioq_add(struct crystalhd_dioq *ioq, void *data,
502 bool wake, uint32_t tag)
503 {
504 unsigned long flags = 0;
505 struct crystalhd_elem *tmp;
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);
521 tmp->flink = (struct crystalhd_elem *)&ioq->head;
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 */
crystalhd_dioq_fetch(struct crystalhd_dioq * ioq)543 void *crystalhd_dioq_fetch(struct crystalhd_dioq *ioq)
544 {
545 unsigned long flags = 0;
546 struct crystalhd_elem *tmp;
547 struct crystalhd_elem *ret = NULL;
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;
557 if (tmp != (struct crystalhd_elem *)&ioq->head) {
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 */
crystalhd_dioq_find_and_fetch(struct crystalhd_dioq * ioq,uint32_t tag)581 void *crystalhd_dioq_find_and_fetch(struct crystalhd_dioq *ioq, uint32_t tag)
582 {
583 unsigned long flags = 0;
584 struct crystalhd_elem *tmp;
585 struct crystalhd_elem *ret = NULL;
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;
595 while (tmp != (struct crystalhd_elem *)&ioq->head) {
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 */
crystalhd_dioq_fetch_wait(struct crystalhd_dioq * ioq,uint32_t to_secs,uint32_t * sig_pend)626 void *crystalhd_dioq_fetch_wait(struct crystalhd_dioq *ioq, uint32_t to_secs,
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 */
crystalhd_map_dio(struct crystalhd_adp * adp,void * ubuff,uint32_t ubuff_sz,uint32_t uv_offset,bool en_422mode,bool dir_tx,struct crystalhd_dio_req ** dio_hnd)676 enum BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
677 uint32_t ubuff_sz, uint32_t uv_offset,
678 bool en_422mode, bool dir_tx,
679 struct crystalhd_dio_req **dio_hnd)
680 {
681 struct crystalhd_dio_req *dio;
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) {
688 BCMLOG_ERR("Invalid arg\n");
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) {
794 BCMLOG_ERR("sg map %d-%d\n", dio->sg_cnt, dio->page_cnt);
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 */
crystalhd_unmap_dio(struct crystalhd_adp * adp,struct crystalhd_dio_req * dio)823 enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
824 {
825 struct page *page = NULL;
826 int j = 0;
827
828 if (!adp || !dio) {
829 BCMLOG_ERR("Invalid arg\n");
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 */
crystalhd_create_dio_pool(struct crystalhd_adp * adp,uint32_t max_pages)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;
867 struct crystalhd_dio_req *dio;
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++) {
890 temp = kzalloc(asz, GFP_KERNEL);
891 if ((temp) == NULL) {
892 BCMLOG_ERR("Failed to alloc %d mem\n", asz);
893 return -ENOMEM;
894 }
895
896 dio = (struct crystalhd_dio_req *)temp;
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 */
crystalhd_destroy_dio_pool(struct crystalhd_adp * adp)924 void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
925 {
926 struct crystalhd_dio_req *dio;
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
950 BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count);
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 */
crystalhd_create_elem_pool(struct crystalhd_adp * adp,uint32_t pool_size)964 int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
965 uint32_t pool_size)
966 {
967 uint32_t i;
968 struct crystalhd_elem *temp;
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) {
976 BCMLOG_ERR("kalloc failed\n");
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 */
crystalhd_delete_elem_pool(struct crystalhd_adp * adp)994 void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
995 {
996 struct crystalhd_elem *temp;
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.. ================================*/
crystalhd_show_buffer(uint32_t off,uint8_t * buff,uint32_t dwcount)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 }
1033