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(&current->mm->mmap_sem);
745 	res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
746 			     0, dio->pages, NULL);
747 	up_read(&current->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