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 "crystalhd.h"
28 
29 #include <linux/slab.h>
30 
31 uint32_t g_linklog_level;
32 
crystalhd_dram_rd(struct crystalhd_adp * adp,uint32_t mem_off)33 static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
34 {
35 	crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
36 	return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
37 }
38 
crystalhd_dram_wr(struct crystalhd_adp * adp,uint32_t mem_off,uint32_t val)39 static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
40 {
41 	crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
42 	bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
43 }
44 
bc_chk_dram_range(struct crystalhd_adp * adp,uint32_t start_off,uint32_t cnt)45 static inline enum BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
46 {
47 	return BC_STS_SUCCESS;
48 }
49 
crystalhd_alloc_dio(struct crystalhd_adp * adp)50 static struct crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
51 {
52 	unsigned long flags = 0;
53 	struct crystalhd_dio_req *temp = NULL;
54 
55 	if (!adp) {
56 		BCMLOG_ERR("Invalid Arg!!\n");
57 		return temp;
58 	}
59 
60 	spin_lock_irqsave(&adp->lock, flags);
61 	temp = adp->ua_map_free_head;
62 	if (temp)
63 		adp->ua_map_free_head = adp->ua_map_free_head->next;
64 	spin_unlock_irqrestore(&adp->lock, flags);
65 
66 	return temp;
67 }
68 
crystalhd_free_dio(struct crystalhd_adp * adp,struct crystalhd_dio_req * dio)69 static void crystalhd_free_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
70 {
71 	unsigned long flags = 0;
72 
73 	if (!adp || !dio)
74 		return;
75 	spin_lock_irqsave(&adp->lock, flags);
76 	dio->sig = crystalhd_dio_inv;
77 	dio->page_cnt = 0;
78 	dio->fb_size = 0;
79 	memset(&dio->uinfo, 0, sizeof(dio->uinfo));
80 	dio->next = adp->ua_map_free_head;
81 	adp->ua_map_free_head = dio;
82 	spin_unlock_irqrestore(&adp->lock, flags);
83 }
84 
crystalhd_alloc_elem(struct crystalhd_adp * adp)85 static struct crystalhd_elem *crystalhd_alloc_elem(struct crystalhd_adp *adp)
86 {
87 	unsigned long flags = 0;
88 	struct crystalhd_elem *temp = NULL;
89 
90 	if (!adp)
91 		return temp;
92 	spin_lock_irqsave(&adp->lock, flags);
93 	temp = adp->elem_pool_head;
94 	if (temp) {
95 		adp->elem_pool_head = adp->elem_pool_head->flink;
96 		memset(temp, 0, sizeof(*temp));
97 	}
98 	spin_unlock_irqrestore(&adp->lock, flags);
99 
100 	return temp;
101 }
crystalhd_free_elem(struct crystalhd_adp * adp,struct crystalhd_elem * elem)102 static void crystalhd_free_elem(struct crystalhd_adp *adp, struct crystalhd_elem *elem)
103 {
104 	unsigned long flags = 0;
105 
106 	if (!adp || !elem)
107 		return;
108 	spin_lock_irqsave(&adp->lock, flags);
109 	elem->flink = adp->elem_pool_head;
110 	adp->elem_pool_head = elem;
111 	spin_unlock_irqrestore(&adp->lock, flags);
112 }
113 
crystalhd_set_sg(struct scatterlist * sg,struct page * page,unsigned int len,unsigned int offset)114 static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
115 				  unsigned int len, unsigned int offset)
116 {
117 	sg_set_page(sg, page, len, offset);
118 #ifdef CONFIG_X86_64
119 	sg->dma_length = len;
120 #endif
121 }
122 
crystalhd_init_sg(struct scatterlist * sg,unsigned int entries)123 static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
124 {
125 	/* http://lkml.org/lkml/2007/11/27/68 */
126 	sg_init_table(sg, entries);
127 }
128 
129 /*========================== Extern ========================================*/
130 /**
131  * bc_dec_reg_rd - Read 7412's device register.
132  * @adp: Adapter instance
133  * @reg_off: Register offset.
134  *
135  * Return:
136  *	32bit value read
137  *
138  * 7412's device register read routine. This interface use
139  * 7412's device access range mapped from BAR-2 (4M) of PCIe
140  * configuration space.
141  */
bc_dec_reg_rd(struct crystalhd_adp * adp,uint32_t reg_off)142 uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
143 {
144 	if (!adp || (reg_off > adp->pci_mem_len)) {
145 		BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
146 		return 0;
147 	}
148 
149 	return readl(adp->addr + reg_off);
150 }
151 
152 /**
153  * bc_dec_reg_wr - Write 7412's device register
154  * @adp: Adapter instance
155  * @reg_off: Register offset.
156  * @val: Dword value to be written.
157  *
158  * Return:
159  *	none.
160  *
161  * 7412's device register write routine. This interface use
162  * 7412's device access range mapped from BAR-2 (4M) of PCIe
163  * configuration space.
164  */
bc_dec_reg_wr(struct crystalhd_adp * adp,uint32_t reg_off,uint32_t val)165 void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
166 {
167 	if (!adp || (reg_off > adp->pci_mem_len)) {
168 		BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
169 		return;
170 	}
171 	writel(val, adp->addr + reg_off);
172 	udelay(8);
173 }
174 
175 /**
176  * crystalhd_reg_rd - Read Link's device register.
177  * @adp: Adapter instance
178  * @reg_off: Register offset.
179  *
180  * Return:
181  *	32bit value read
182  *
183  * Link device register  read routine. This interface use
184  * Link's device access range mapped from BAR-1 (64K) of PCIe
185  * configuration space.
186  *
187  */
crystalhd_reg_rd(struct crystalhd_adp * adp,uint32_t reg_off)188 uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
189 {
190 	if (!adp || (reg_off > adp->pci_i2o_len)) {
191 		BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
192 		return 0;
193 	}
194 	return readl(adp->i2o_addr + reg_off);
195 }
196 
197 /**
198  * crystalhd_reg_wr - Write Link's device register
199  * @adp: Adapter instance
200  * @reg_off: Register offset.
201  * @val: Dword value to be written.
202  *
203  * Return:
204  *	none.
205  *
206  * Link device register  write routine. This interface use
207  * Link's device access range mapped from BAR-1 (64K) of PCIe
208  * configuration space.
209  *
210  */
crystalhd_reg_wr(struct crystalhd_adp * adp,uint32_t reg_off,uint32_t val)211 void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
212 {
213 	if (!adp || (reg_off > adp->pci_i2o_len)) {
214 		BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
215 		return;
216 	}
217 	writel(val, adp->i2o_addr + reg_off);
218 }
219 
220 /**
221  * crystalhd_mem_rd - Read data from 7412's DRAM area.
222  * @adp: Adapter instance
223  * @start_off: Start offset.
224  * @dw_cnt: Count in dwords.
225  * @rd_buff: Buffer to copy the data from dram.
226  *
227  * Return:
228  *	Status.
229  *
230  * 7412's Dram read routine.
231  */
crystalhd_mem_rd(struct crystalhd_adp * adp,uint32_t start_off,uint32_t dw_cnt,uint32_t * rd_buff)232 enum BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
233 			 uint32_t dw_cnt, uint32_t *rd_buff)
234 {
235 	uint32_t ix = 0;
236 
237 	if (!adp || !rd_buff ||
238 	    (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
239 		BCMLOG_ERR("Invalid arg\n");
240 		return BC_STS_INV_ARG;
241 	}
242 	for (ix = 0; ix < dw_cnt; ix++)
243 		rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
244 
245 	return BC_STS_SUCCESS;
246 }
247 
248 /**
249  * crystalhd_mem_wr - Write data to 7412's DRAM area.
250  * @adp: Adapter instance
251  * @start_off: Start offset.
252  * @dw_cnt: Count in dwords.
253  * @wr_buff: Data Buffer to be written.
254  *
255  * Return:
256  *	Status.
257  *
258  * 7412's Dram write routine.
259  */
crystalhd_mem_wr(struct crystalhd_adp * adp,uint32_t start_off,uint32_t dw_cnt,uint32_t * wr_buff)260 enum BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
261 			 uint32_t dw_cnt, uint32_t *wr_buff)
262 {
263 	uint32_t ix = 0;
264 
265 	if (!adp || !wr_buff ||
266 	    (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
267 		BCMLOG_ERR("Invalid arg\n");
268 		return BC_STS_INV_ARG;
269 	}
270 
271 	for (ix = 0; ix < dw_cnt; ix++)
272 		crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
273 
274 	return BC_STS_SUCCESS;
275 }
276 /**
277  * crystalhd_pci_cfg_rd - PCIe config read
278  * @adp: Adapter instance
279  * @off: PCI config space offset.
280  * @len: Size -- Byte, Word & dword.
281  * @val: Value read
282  *
283  * Return:
284  *	Status.
285  *
286  * Get value from Link's PCIe config space.
287  */
crystalhd_pci_cfg_rd(struct crystalhd_adp * adp,uint32_t off,uint32_t len,uint32_t * val)288 enum BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
289 			     uint32_t len, uint32_t *val)
290 {
291 	enum BC_STATUS sts = BC_STS_SUCCESS;
292 	int rc = 0;
293 
294 	if (!adp || !val) {
295 		BCMLOG_ERR("Invalid arg\n");
296 		return BC_STS_INV_ARG;
297 	}
298 
299 	switch (len) {
300 	case 1:
301 		rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
302 		break;
303 	case 2:
304 		rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
305 		break;
306 	case 4:
307 		rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
308 		break;
309 	default:
310 		rc = -EINVAL;
311 		sts = BC_STS_INV_ARG;
312 		BCMLOG_ERR("Invalid len:%d\n", len);
313 	}
314 
315 	if (rc && (sts == BC_STS_SUCCESS))
316 		sts = BC_STS_ERROR;
317 
318 	return sts;
319 }
320 
321 /**
322  * crystalhd_pci_cfg_wr - PCIe config write
323  * @adp: Adapter instance
324  * @off: PCI config space offset.
325  * @len: Size -- Byte, Word & dword.
326  * @val: Value to be written
327  *
328  * Return:
329  *	Status.
330  *
331  * Set value to Link's PCIe config space.
332  */
crystalhd_pci_cfg_wr(struct crystalhd_adp * adp,uint32_t off,uint32_t len,uint32_t val)333 enum BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
334 			     uint32_t len, uint32_t val)
335 {
336 	enum BC_STATUS sts = BC_STS_SUCCESS;
337 	int rc = 0;
338 
339 	if (!adp || !val) {
340 		BCMLOG_ERR("Invalid arg\n");
341 		return BC_STS_INV_ARG;
342 	}
343 
344 	switch (len) {
345 	case 1:
346 		rc = pci_write_config_byte(adp->pdev, off, (u8)val);
347 		break;
348 	case 2:
349 		rc = pci_write_config_word(adp->pdev, off, (u16)val);
350 		break;
351 	case 4:
352 		rc = pci_write_config_dword(adp->pdev, off, val);
353 		break;
354 	default:
355 		rc = -EINVAL;
356 		sts = BC_STS_INV_ARG;
357 		BCMLOG_ERR("Invalid len:%d\n", len);
358 	}
359 
360 	if (rc && (sts == BC_STS_SUCCESS))
361 		sts = BC_STS_ERROR;
362 
363 	return sts;
364 }
365 
366 /**
367  * bc_kern_dma_alloc - Allocate memory for Dma rings
368  * @adp: Adapter instance
369  * @sz: Size of the memory to allocate.
370  * @phy_addr: Physical address of the memory allocated.
371  *	   Typedef to system's dma_addr_t (u64)
372  *
373  * Return:
374  *  Pointer to allocated memory..
375  *
376  * Wrapper to Linux kernel interface.
377  *
378  */
bc_kern_dma_alloc(struct crystalhd_adp * adp,uint32_t sz,dma_addr_t * phy_addr)379 void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
380 			dma_addr_t *phy_addr)
381 {
382 	void *temp = NULL;
383 
384 	if (!adp || !sz || !phy_addr) {
385 		BCMLOG_ERR("Invalide Arg..\n");
386 		return temp;
387 	}
388 
389 	temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
390 	if (temp)
391 		memset(temp, 0, sz);
392 
393 	return temp;
394 }
395 
396 /**
397  * bc_kern_dma_free - Release Dma ring memory.
398  * @adp: Adapter instance
399  * @sz: Size of the memory to allocate.
400  * @ka: Kernel virtual address returned during _dio_alloc()
401  * @phy_addr: Physical address of the memory allocated.
402  *	   Typedef to system's dma_addr_t (u64)
403  *
404  * Return:
405  *     none.
406  */
bc_kern_dma_free(struct crystalhd_adp * adp,uint32_t sz,void * ka,dma_addr_t phy_addr)407 void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
408 		      dma_addr_t phy_addr)
409 {
410 	if (!adp || !ka || !sz || !phy_addr) {
411 		BCMLOG_ERR("Invalide Arg..\n");
412 		return;
413 	}
414 
415 	pci_free_consistent(adp->pdev, sz, ka, phy_addr);
416 }
417 
418 /**
419  * crystalhd_create_dioq - Create Generic DIO queue
420  * @adp: Adapter instance
421  * @dioq_hnd: Handle to the dio queue created
422  * @cb	: Optional - Call back To free the element.
423  * @cbctx: Context to pass to callback.
424  *
425  * Return:
426  *  status
427  *
428  * Initialize Generic DIO queue to hold any data. Callback
429  * will be used to free elements while deleting the queue.
430  */
crystalhd_create_dioq(struct crystalhd_adp * adp,struct crystalhd_dioq ** dioq_hnd,crystalhd_data_free_cb cb,void * cbctx)431 enum BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
432 			      struct crystalhd_dioq **dioq_hnd,
433 			      crystalhd_data_free_cb cb, void *cbctx)
434 {
435 	struct crystalhd_dioq *dioq = NULL;
436 
437 	if (!adp || !dioq_hnd) {
438 		BCMLOG_ERR("Invalid arg!!\n");
439 		return BC_STS_INV_ARG;
440 	}
441 
442 	dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
443 	if (!dioq)
444 		return BC_STS_INSUFF_RES;
445 
446 	spin_lock_init(&dioq->lock);
447 	dioq->sig = BC_LINK_DIOQ_SIG;
448 	dioq->head = (struct crystalhd_elem *)&dioq->head;
449 	dioq->tail = (struct crystalhd_elem *)&dioq->head;
450 	crystalhd_create_event(&dioq->event);
451 	dioq->adp = adp;
452 	dioq->data_rel_cb = cb;
453 	dioq->cb_context = cbctx;
454 	*dioq_hnd = dioq;
455 
456 	return BC_STS_SUCCESS;
457 }
458 
459 /**
460  * crystalhd_delete_dioq - Delete Generic DIO queue
461  * @adp: Adapter instance
462  * @dioq: DIOQ instance..
463  *
464  * Return:
465  *  None.
466  *
467  * Release Generic DIO queue. This function will remove
468  * all the entries from the Queue and will release data
469  * by calling the call back provided during creation.
470  *
471  */
crystalhd_delete_dioq(struct crystalhd_adp * adp,struct crystalhd_dioq * dioq)472 void crystalhd_delete_dioq(struct crystalhd_adp *adp, struct crystalhd_dioq *dioq)
473 {
474 	void *temp;
475 
476 	if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
477 		return;
478 
479 	do {
480 		temp = crystalhd_dioq_fetch(dioq);
481 		if (temp && dioq->data_rel_cb)
482 			dioq->data_rel_cb(dioq->cb_context, temp);
483 	} while (temp);
484 	dioq->sig = 0;
485 	kfree(dioq);
486 }
487 
488 /**
489  * crystalhd_dioq_add - Add new DIO request element.
490  * @ioq: DIO queue instance
491  * @t: DIO request to be added.
492  * @wake: True - Wake up suspended process.
493  * @tag: Special tag to assign - For search and get.
494  *
495  * Return:
496  *  Status.
497  *
498  * Insert new element to Q tail.
499  */
crystalhd_dioq_add(struct crystalhd_dioq * ioq,void * data,bool wake,uint32_t tag)500 enum BC_STATUS crystalhd_dioq_add(struct crystalhd_dioq *ioq, void *data,
501 			   bool wake, uint32_t tag)
502 {
503 	unsigned long flags = 0;
504 	struct crystalhd_elem *tmp;
505 
506 	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
507 		BCMLOG_ERR("Invalid arg!!\n");
508 		return BC_STS_INV_ARG;
509 	}
510 
511 	tmp = crystalhd_alloc_elem(ioq->adp);
512 	if (!tmp) {
513 		BCMLOG_ERR("No free elements.\n");
514 		return BC_STS_INSUFF_RES;
515 	}
516 
517 	tmp->data = data;
518 	tmp->tag = tag;
519 	spin_lock_irqsave(&ioq->lock, flags);
520 	tmp->flink = (struct crystalhd_elem *)&ioq->head;
521 	tmp->blink = ioq->tail;
522 	tmp->flink->blink = tmp;
523 	tmp->blink->flink = tmp;
524 	ioq->count++;
525 	spin_unlock_irqrestore(&ioq->lock, flags);
526 
527 	if (wake)
528 		crystalhd_set_event(&ioq->event);
529 
530 	return BC_STS_SUCCESS;
531 }
532 
533 /**
534  * crystalhd_dioq_fetch - Fetch element from head.
535  * @ioq: DIO queue instance
536  *
537  * Return:
538  *	data element from the head..
539  *
540  * Remove an element from Queue.
541  */
crystalhd_dioq_fetch(struct crystalhd_dioq * ioq)542 void *crystalhd_dioq_fetch(struct crystalhd_dioq *ioq)
543 {
544 	unsigned long flags = 0;
545 	struct crystalhd_elem *tmp;
546 	struct crystalhd_elem *ret = NULL;
547 	void *data = NULL;
548 
549 	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
550 		BCMLOG_ERR("Invalid arg!!\n");
551 		return data;
552 	}
553 
554 	spin_lock_irqsave(&ioq->lock, flags);
555 	tmp = ioq->head;
556 	if (tmp != (struct crystalhd_elem *)&ioq->head) {
557 		ret = tmp;
558 		tmp->flink->blink = tmp->blink;
559 		tmp->blink->flink = tmp->flink;
560 		ioq->count--;
561 	}
562 	spin_unlock_irqrestore(&ioq->lock, flags);
563 	if (ret) {
564 		data = ret->data;
565 		crystalhd_free_elem(ioq->adp, ret);
566 	}
567 
568 	return data;
569 }
570 /**
571  * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
572  * @ioq: DIO queue instance
573  * @tag: Tag to search for.
574  *
575  * Return:
576  *	element from the head..
577  *
578  * Search TAG and remove the element.
579  */
crystalhd_dioq_find_and_fetch(struct crystalhd_dioq * ioq,uint32_t tag)580 void *crystalhd_dioq_find_and_fetch(struct crystalhd_dioq *ioq, uint32_t tag)
581 {
582 	unsigned long flags = 0;
583 	struct crystalhd_elem *tmp;
584 	struct crystalhd_elem *ret = NULL;
585 	void *data = NULL;
586 
587 	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
588 		BCMLOG_ERR("Invalid arg!!\n");
589 		return data;
590 	}
591 
592 	spin_lock_irqsave(&ioq->lock, flags);
593 	tmp = ioq->head;
594 	while (tmp != (struct crystalhd_elem *)&ioq->head) {
595 		if (tmp->tag == tag) {
596 			ret = tmp;
597 			tmp->flink->blink = tmp->blink;
598 			tmp->blink->flink = tmp->flink;
599 			ioq->count--;
600 			break;
601 		}
602 		tmp = tmp->flink;
603 	}
604 	spin_unlock_irqrestore(&ioq->lock, flags);
605 
606 	if (ret) {
607 		data = ret->data;
608 		crystalhd_free_elem(ioq->adp, ret);
609 	}
610 
611 	return data;
612 }
613 
614 /**
615  * crystalhd_dioq_fetch_wait - Fetch element from Head.
616  * @ioq: DIO queue instance
617  * @to_secs: Wait timeout in seconds..
618  *
619  * Return:
620  *	element from the head..
621  *
622  * Return element from head if Q is not empty. Wait for new element
623  * if Q is empty for Timeout seconds.
624  */
crystalhd_dioq_fetch_wait(struct crystalhd_dioq * ioq,uint32_t to_secs,uint32_t * sig_pend)625 void *crystalhd_dioq_fetch_wait(struct crystalhd_dioq *ioq, uint32_t to_secs,
626 			      uint32_t *sig_pend)
627 {
628 	unsigned long flags = 0;
629 	int rc = 0, count;
630 	void *tmp = NULL;
631 
632 	if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
633 		BCMLOG_ERR("Invalid arg!!\n");
634 		return tmp;
635 	}
636 
637 	count = to_secs;
638 	spin_lock_irqsave(&ioq->lock, flags);
639 	while ((ioq->count == 0) && count) {
640 		spin_unlock_irqrestore(&ioq->lock, flags);
641 
642 		crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
643 		if (rc == 0) {
644 			goto out;
645 		} else if (rc == -EINTR) {
646 			BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
647 			*sig_pend = 1;
648 			return tmp;
649 		}
650 		spin_lock_irqsave(&ioq->lock, flags);
651 		count--;
652 	}
653 	spin_unlock_irqrestore(&ioq->lock, flags);
654 
655 out:
656 	return crystalhd_dioq_fetch(ioq);
657 }
658 
659 /**
660  * crystalhd_map_dio - Map user address for DMA
661  * @adp:	Adapter instance
662  * @ubuff:	User buffer to map.
663  * @ubuff_sz:	User buffer size.
664  * @uv_offset:	UV buffer offset.
665  * @en_422mode: TRUE:422 FALSE:420 Capture mode.
666  * @dir_tx:	TRUE for Tx (To device from host)
667  * @dio_hnd:	Handle to mapped DIO request.
668  *
669  * Return:
670  *	Status.
671  *
672  * This routine maps user address and lock pages for DMA.
673  *
674  */
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)675 enum BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
676 			  uint32_t ubuff_sz, uint32_t uv_offset,
677 			  bool en_422mode, bool dir_tx,
678 			  struct crystalhd_dio_req **dio_hnd)
679 {
680 	struct crystalhd_dio_req	*dio;
681 	/* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
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  */
crystalhd_unmap_dio(struct crystalhd_adp * adp,struct crystalhd_dio_req * dio)822 enum 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  */
crystalhd_create_dio_pool(struct crystalhd_adp * adp,uint32_t max_pages)862 int 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  */
crystalhd_destroy_dio_pool(struct crystalhd_adp * adp)923 void 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  */
crystalhd_create_elem_pool(struct crystalhd_adp * adp,uint32_t pool_size)963 int __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  */
crystalhd_delete_elem_pool(struct crystalhd_adp * adp)993 void 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.. ================================*/
crystalhd_show_buffer(uint32_t off,uint8_t * buff,uint32_t dwcount)1013 void 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