• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/staging/crystalhd/
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
32uint32_t g_linklog_level;
33
34static 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
40static 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
46static 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
51static 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
70static 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
86static 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}
103static 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
115static 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
124static 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 */
143uint32_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 */
166void 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 */
189uint32_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 */
212void 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 */
233enum 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 */
261enum 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 */
289enum 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 */
334enum 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 */
380void *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 */
408void 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 */
432enum 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 */
473void 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 */
501enum 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 */
543void *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 */
581void *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 */
626void *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
656out:
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 */
676enum 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	unsigned long start = 0, end = 0, uaddr = 0, count = 0;
683	unsigned long spsz = 0, uv_start = 0;
684	int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
685
686	if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
687		BCMLOG_ERR("Invalid arg\n");
688		return BC_STS_INV_ARG;
689	}
690	/* Compute pages */
691	uaddr = (unsigned long)ubuff;
692	count = (unsigned long)ubuff_sz;
693	end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
694	start = uaddr >> PAGE_SHIFT;
695	nr_pages = end - start;
696
697	if (!count || ((uaddr + count) < uaddr)) {
698		BCMLOG_ERR("User addr overflow!!\n");
699		return BC_STS_INV_ARG;
700	}
701
702	dio = crystalhd_alloc_dio(adp);
703	if (!dio) {
704		BCMLOG_ERR("dio pool empty..\n");
705		return BC_STS_INSUFF_RES;
706	}
707
708	if (dir_tx) {
709		rw = WRITE;
710		dio->direction = DMA_TO_DEVICE;
711	} else {
712		rw = READ;
713		dio->direction = DMA_FROM_DEVICE;
714	}
715
716	if (nr_pages > dio->max_pages) {
717		BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
718			   dio->max_pages, nr_pages);
719		crystalhd_unmap_dio(adp, dio);
720		return BC_STS_INSUFF_RES;
721	}
722
723	if (uv_offset) {
724		uv_start = (uaddr + (unsigned long)uv_offset)  >> PAGE_SHIFT;
725		dio->uinfo.uv_sg_ix = uv_start - start;
726		dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
727	}
728
729	dio->fb_size = ubuff_sz & 0x03;
730	if (dio->fb_size) {
731		res = copy_from_user(dio->fb_va,
732				     (void *)(uaddr + count - dio->fb_size),
733				     dio->fb_size);
734		if (res) {
735			BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
736				   res, dio->fb_size,
737				   (void *)(uaddr + count-dio->fb_size));
738			crystalhd_unmap_dio(adp, dio);
739			return BC_STS_INSUFF_RES;
740		}
741	}
742
743	down_read(&current->mm->mmap_sem);
744	res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
745			     0, dio->pages, NULL);
746	up_read(&current->mm->mmap_sem);
747
748	/* Save for release..*/
749	dio->sig = crystalhd_dio_locked;
750	if (res < nr_pages) {
751		BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
752		dio->page_cnt = res;
753		crystalhd_unmap_dio(adp, dio);
754		return BC_STS_ERROR;
755	}
756
757	dio->page_cnt = nr_pages;
758	/* Get scatter/gather */
759	crystalhd_init_sg(dio->sg, dio->page_cnt);
760	crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
761	if (nr_pages > 1) {
762		dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
763
764#ifdef CONFIG_X86_64
765		dio->sg[0].dma_length = dio->sg[0].length;
766#endif
767		count -= dio->sg[0].length;
768		for (i = 1; i < nr_pages; i++) {
769			if (count < 4) {
770				spsz = count;
771				skip_fb_sg = 1;
772			} else {
773				spsz = (count < PAGE_SIZE) ?
774					(count & ~0x03) : PAGE_SIZE;
775			}
776			crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
777			count -= spsz;
778		}
779	} else {
780		if (count < 4) {
781			dio->sg[0].length = count;
782			skip_fb_sg = 1;
783		} else {
784			dio->sg[0].length = count - dio->fb_size;
785		}
786#ifdef CONFIG_X86_64
787		dio->sg[0].dma_length = dio->sg[0].length;
788#endif
789	}
790	dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
791				 dio->page_cnt, dio->direction);
792	if (dio->sg_cnt <= 0) {
793		BCMLOG_ERR("sg map %d-%d\n", dio->sg_cnt, dio->page_cnt);
794		crystalhd_unmap_dio(adp, dio);
795		return BC_STS_ERROR;
796	}
797	if (dio->sg_cnt && skip_fb_sg)
798		dio->sg_cnt -= 1;
799	dio->sig = crystalhd_dio_sg_mapped;
800	/* Fill in User info.. */
801	dio->uinfo.xfr_len   = ubuff_sz;
802	dio->uinfo.xfr_buff  = ubuff;
803	dio->uinfo.uv_offset = uv_offset;
804	dio->uinfo.b422mode  = en_422mode;
805	dio->uinfo.dir_tx    = dir_tx;
806
807	*dio_hnd = dio;
808
809	return BC_STS_SUCCESS;
810}
811
812/**
813 * crystalhd_unmap_sgl - Release mapped resources
814 * @adp: Adapter instance
815 * @dio: DIO request instance
816 *
817 * Return:
818 *	Status.
819 *
820 * This routine is to unmap the user buffer pages.
821 */
822enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
823{
824	struct page *page = NULL;
825	int j = 0;
826
827	if (!adp || !dio) {
828		BCMLOG_ERR("Invalid arg\n");
829		return BC_STS_INV_ARG;
830	}
831
832	if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
833		for (j = 0; j < dio->page_cnt; j++) {
834			page = dio->pages[j];
835			if (page) {
836				if (!PageReserved(page) &&
837				    (dio->direction == DMA_FROM_DEVICE))
838					SetPageDirty(page);
839				page_cache_release(page);
840			}
841		}
842	}
843	if (dio->sig == crystalhd_dio_sg_mapped)
844		pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
845
846	crystalhd_free_dio(adp, dio);
847
848	return BC_STS_SUCCESS;
849}
850
851/**
852 * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
853 * @adp: Adapter instance
854 * @max_pages: Max pages for size calculation.
855 *
856 * Return:
857 *	system error.
858 *
859 * This routine creates a memory pool to hold dio context for
860 * for HW Direct IO operation.
861 */
862int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
863{
864	uint32_t asz = 0, i = 0;
865	uint8_t	*temp;
866	struct crystalhd_dio_req *dio;
867
868	if (!adp || !max_pages) {
869		BCMLOG_ERR("Invalid Arg!!\n");
870		return -EINVAL;
871	}
872
873	/* Get dma memory for fill byte handling..*/
874	adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
875					      adp->pdev, 8, 8, 0);
876	if (!adp->fill_byte_pool) {
877		BCMLOG_ERR("failed to create fill byte pool\n");
878		return -ENOMEM;
879	}
880
881	/* Get the max size from user based on 420/422 modes */
882	asz =  (sizeof(*dio->pages) * max_pages) +
883	       (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
884
885	BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
886	       BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
887
888	for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
889		temp = kzalloc(asz, GFP_KERNEL);
890		if ((temp) == NULL) {
891			BCMLOG_ERR("Failed to alloc %d mem\n", asz);
892			return -ENOMEM;
893		}
894
895		dio = (struct crystalhd_dio_req *)temp;
896		temp += sizeof(*dio);
897		dio->pages = (struct page **)temp;
898		temp += (sizeof(*dio->pages) * max_pages);
899		dio->sg = (struct scatterlist *)temp;
900		dio->max_pages = max_pages;
901		dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
902					    &dio->fb_pa);
903		if (!dio->fb_va) {
904			BCMLOG_ERR("fill byte alloc failed.\n");
905			return -ENOMEM;
906		}
907
908		crystalhd_free_dio(adp, dio);
909	}
910
911	return 0;
912}
913
914/**
915 * crystalhd_destroy_dio_pool - Release DIO mem pool.
916 * @adp: Adapter instance
917 *
918 * Return:
919 *	none.
920 *
921 * This routine releases dio memory pool during close.
922 */
923void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
924{
925	struct crystalhd_dio_req *dio;
926	int count = 0;
927
928	if (!adp) {
929		BCMLOG_ERR("Invalid Arg!!\n");
930		return;
931	}
932
933	do {
934		dio = crystalhd_alloc_dio(adp);
935		if (dio) {
936			if (dio->fb_va)
937				pci_pool_free(adp->fill_byte_pool,
938					      dio->fb_va, dio->fb_pa);
939			count++;
940			kfree(dio);
941		}
942	} while (dio);
943
944	if (adp->fill_byte_pool) {
945		pci_pool_destroy(adp->fill_byte_pool);
946		adp->fill_byte_pool = NULL;
947	}
948
949	BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count);
950}
951
952/**
953 * crystalhd_create_elem_pool - List element pool creation.
954 * @adp: Adapter instance
955 * @pool_size: Number of elements in the pool.
956 *
957 * Return:
958 *	0 - success, <0 error
959 *
960 * Create general purpose list element pool to hold pending,
961 * and active requests.
962 */
963int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
964		uint32_t pool_size)
965{
966	uint32_t i;
967	struct crystalhd_elem *temp;
968
969	if (!adp || !pool_size)
970		return -EINVAL;
971
972	for (i = 0; i < pool_size; i++) {
973		temp = kzalloc(sizeof(*temp), GFP_KERNEL);
974		if (!temp) {
975			BCMLOG_ERR("kalloc failed\n");
976			return -ENOMEM;
977		}
978		crystalhd_free_elem(adp, temp);
979	}
980	BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
981	return 0;
982}
983
984/**
985 * crystalhd_delete_elem_pool - List element pool deletion.
986 * @adp: Adapter instance
987 *
988 * Return:
989 *	none
990 *
991 * Delete general purpose list element pool.
992 */
993void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
994{
995	struct crystalhd_elem *temp;
996	int dbg_cnt = 0;
997
998	if (!adp)
999		return;
1000
1001	do {
1002		temp = crystalhd_alloc_elem(adp);
1003		if (temp) {
1004			kfree(temp);
1005			dbg_cnt++;
1006		}
1007	} while (temp);
1008
1009	BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
1010}
1011
1012/*================ Debug support routines.. ================================*/
1013void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
1014{
1015	uint32_t i, k = 1;
1016
1017	for (i = 0; i < dwcount; i++) {
1018		if (k == 1)
1019			BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
1020
1021		BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
1022
1023		buff += sizeof(uint32_t);
1024		off  += sizeof(uint32_t);
1025		k++;
1026		if ((i == dwcount - 1) || (k > 4)) {
1027			BCMLOG(BCMLOG_DATA, "\n");
1028			k = 1;
1029		}
1030	}
1031}
1032