blob: 3aabf75b7d97573b70f40a98982e5caef48427cc [file] [log] [blame]
Jarod Wilson7963eb42010-01-04 18:02:27 -05001/***************************************************************************
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
Jorgyano Vieira01c32072012-02-25 21:58:21 -020027#include "crystalhd.h"
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090028
Jorgyano Vieira01c32072012-02-25 21:58:21 -020029#include <linux/slab.h>
Jarod Wilson7963eb42010-01-04 18:02:27 -050030
31uint32_t g_linklog_level;
32
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -070033static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp,
34 uint32_t mem_off)
Jarod Wilson7963eb42010-01-04 18:02:27 -050035{
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
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -070040static inline void crystalhd_dram_wr(struct crystalhd_adp *adp,
41 uint32_t mem_off, uint32_t val)
Jarod Wilson7963eb42010-01-04 18:02:27 -050042{
43 crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
44 bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
45}
46
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -070047static inline enum BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp,
48 uint32_t start_off, uint32_t cnt)
Jarod Wilson7963eb42010-01-04 18:02:27 -050049{
50 return BC_STS_SUCCESS;
51}
52
Lior Dotanabfc7682010-05-18 12:46:42 +030053static struct crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
Jarod Wilson7963eb42010-01-04 18:02:27 -050054{
55 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +030056 struct crystalhd_dio_req *temp = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -050057
58 if (!adp) {
59 BCMLOG_ERR("Invalid Arg!!\n");
60 return temp;
61 }
62
63 spin_lock_irqsave(&adp->lock, flags);
64 temp = adp->ua_map_free_head;
65 if (temp)
66 adp->ua_map_free_head = adp->ua_map_free_head->next;
67 spin_unlock_irqrestore(&adp->lock, flags);
68
69 return temp;
70}
71
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -070072static void crystalhd_free_dio(struct crystalhd_adp *adp,
73 struct crystalhd_dio_req *dio)
Jarod Wilson7963eb42010-01-04 18:02:27 -050074{
75 unsigned long flags = 0;
76
77 if (!adp || !dio)
78 return;
79 spin_lock_irqsave(&adp->lock, flags);
80 dio->sig = crystalhd_dio_inv;
81 dio->page_cnt = 0;
82 dio->fb_size = 0;
83 memset(&dio->uinfo, 0, sizeof(dio->uinfo));
84 dio->next = adp->ua_map_free_head;
85 adp->ua_map_free_head = dio;
86 spin_unlock_irqrestore(&adp->lock, flags);
87}
88
Lior Dotanabfc7682010-05-18 12:46:42 +030089static struct crystalhd_elem *crystalhd_alloc_elem(struct crystalhd_adp *adp)
Jarod Wilson7963eb42010-01-04 18:02:27 -050090{
91 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +030092 struct crystalhd_elem *temp = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -050093
94 if (!adp)
95 return temp;
96 spin_lock_irqsave(&adp->lock, flags);
97 temp = adp->elem_pool_head;
98 if (temp) {
99 adp->elem_pool_head = adp->elem_pool_head->flink;
100 memset(temp, 0, sizeof(*temp));
101 }
102 spin_unlock_irqrestore(&adp->lock, flags);
103
104 return temp;
105}
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700106static void crystalhd_free_elem(struct crystalhd_adp *adp,
107 struct crystalhd_elem *elem)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500108{
109 unsigned long flags = 0;
110
111 if (!adp || !elem)
112 return;
113 spin_lock_irqsave(&adp->lock, flags);
114 elem->flink = adp->elem_pool_head;
115 adp->elem_pool_head = elem;
116 spin_unlock_irqrestore(&adp->lock, flags);
117}
118
119static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
120 unsigned int len, unsigned int offset)
121{
122 sg_set_page(sg, page, len, offset);
123#ifdef CONFIG_X86_64
124 sg->dma_length = len;
125#endif
126}
127
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700128static inline void crystalhd_init_sg(struct scatterlist *sg,
129 unsigned int entries)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500130{
131 /* http://lkml.org/lkml/2007/11/27/68 */
132 sg_init_table(sg, entries);
133}
134
135/*========================== Extern ========================================*/
136/**
137 * bc_dec_reg_rd - Read 7412's device register.
138 * @adp: Adapter instance
139 * @reg_off: Register offset.
140 *
141 * Return:
142 * 32bit value read
143 *
144 * 7412's device register read routine. This interface use
145 * 7412's device access range mapped from BAR-2 (4M) of PCIe
146 * configuration space.
147 */
148uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
149{
150 if (!adp || (reg_off > adp->pci_mem_len)) {
151 BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
152 return 0;
153 }
154
155 return readl(adp->addr + reg_off);
156}
157
158/**
159 * bc_dec_reg_wr - Write 7412's device register
160 * @adp: Adapter instance
161 * @reg_off: Register offset.
162 * @val: Dword value to be written.
163 *
164 * Return:
165 * none.
166 *
167 * 7412's device register write routine. This interface use
168 * 7412's device access range mapped from BAR-2 (4M) of PCIe
169 * configuration space.
170 */
171void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
172{
173 if (!adp || (reg_off > adp->pci_mem_len)) {
174 BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
175 return;
176 }
177 writel(val, adp->addr + reg_off);
178 udelay(8);
179}
180
181/**
182 * crystalhd_reg_rd - Read Link's device register.
183 * @adp: Adapter instance
184 * @reg_off: Register offset.
185 *
186 * Return:
187 * 32bit value read
188 *
189 * Link device register read routine. This interface use
190 * Link's device access range mapped from BAR-1 (64K) of PCIe
191 * configuration space.
192 *
193 */
194uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
195{
196 if (!adp || (reg_off > adp->pci_i2o_len)) {
197 BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
198 return 0;
199 }
200 return readl(adp->i2o_addr + reg_off);
201}
202
203/**
204 * crystalhd_reg_wr - Write Link's device register
205 * @adp: Adapter instance
206 * @reg_off: Register offset.
207 * @val: Dword value to be written.
208 *
209 * Return:
210 * none.
211 *
212 * Link device register write routine. This interface use
213 * Link's device access range mapped from BAR-1 (64K) of PCIe
214 * configuration space.
215 *
216 */
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700217void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off,
218 uint32_t val)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500219{
220 if (!adp || (reg_off > adp->pci_i2o_len)) {
221 BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
222 return;
223 }
224 writel(val, adp->i2o_addr + reg_off);
225}
226
227/**
228 * crystalhd_mem_rd - Read data from 7412's DRAM area.
229 * @adp: Adapter instance
230 * @start_off: Start offset.
231 * @dw_cnt: Count in dwords.
232 * @rd_buff: Buffer to copy the data from dram.
233 *
234 * Return:
235 * Status.
236 *
237 * 7412's Dram read routine.
238 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300239enum BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500240 uint32_t dw_cnt, uint32_t *rd_buff)
241{
242 uint32_t ix = 0;
243
244 if (!adp || !rd_buff ||
245 (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100246 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500247 return BC_STS_INV_ARG;
248 }
249 for (ix = 0; ix < dw_cnt; ix++)
250 rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
251
252 return BC_STS_SUCCESS;
253}
254
255/**
256 * crystalhd_mem_wr - Write data to 7412's DRAM area.
257 * @adp: Adapter instance
258 * @start_off: Start offset.
259 * @dw_cnt: Count in dwords.
260 * @wr_buff: Data Buffer to be written.
261 *
262 * Return:
263 * Status.
264 *
265 * 7412's Dram write routine.
266 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300267enum BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500268 uint32_t dw_cnt, uint32_t *wr_buff)
269{
270 uint32_t ix = 0;
271
272 if (!adp || !wr_buff ||
273 (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100274 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500275 return BC_STS_INV_ARG;
276 }
277
278 for (ix = 0; ix < dw_cnt; ix++)
279 crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
280
281 return BC_STS_SUCCESS;
282}
283/**
284 * crystalhd_pci_cfg_rd - PCIe config read
285 * @adp: Adapter instance
286 * @off: PCI config space offset.
287 * @len: Size -- Byte, Word & dword.
288 * @val: Value read
289 *
290 * Return:
291 * Status.
292 *
293 * Get value from Link's PCIe config space.
294 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300295enum BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500296 uint32_t len, uint32_t *val)
297{
Lior Dotanabfc7682010-05-18 12:46:42 +0300298 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500299 int rc = 0;
300
301 if (!adp || !val) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100302 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500303 return BC_STS_INV_ARG;
304 }
305
306 switch (len) {
307 case 1:
308 rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
309 break;
310 case 2:
311 rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
312 break;
313 case 4:
314 rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
315 break;
316 default:
317 rc = -EINVAL;
318 sts = BC_STS_INV_ARG;
319 BCMLOG_ERR("Invalid len:%d\n", len);
Joe Perches95cd17c2011-04-10 14:31:35 -0700320 }
Jarod Wilson7963eb42010-01-04 18:02:27 -0500321
322 if (rc && (sts == BC_STS_SUCCESS))
323 sts = BC_STS_ERROR;
324
325 return sts;
326}
327
328/**
329 * crystalhd_pci_cfg_wr - PCIe config write
330 * @adp: Adapter instance
331 * @off: PCI config space offset.
332 * @len: Size -- Byte, Word & dword.
333 * @val: Value to be written
334 *
335 * Return:
336 * Status.
337 *
338 * Set value to Link's PCIe config space.
339 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300340enum BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500341 uint32_t len, uint32_t val)
342{
Lior Dotanabfc7682010-05-18 12:46:42 +0300343 enum BC_STATUS sts = BC_STS_SUCCESS;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500344 int rc = 0;
345
346 if (!adp || !val) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100347 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500348 return BC_STS_INV_ARG;
349 }
350
351 switch (len) {
352 case 1:
353 rc = pci_write_config_byte(adp->pdev, off, (u8)val);
354 break;
355 case 2:
356 rc = pci_write_config_word(adp->pdev, off, (u16)val);
357 break;
358 case 4:
359 rc = pci_write_config_dword(adp->pdev, off, val);
360 break;
361 default:
362 rc = -EINVAL;
363 sts = BC_STS_INV_ARG;
364 BCMLOG_ERR("Invalid len:%d\n", len);
Joe Perches95cd17c2011-04-10 14:31:35 -0700365 }
Jarod Wilson7963eb42010-01-04 18:02:27 -0500366
367 if (rc && (sts == BC_STS_SUCCESS))
368 sts = BC_STS_ERROR;
369
370 return sts;
371}
372
373/**
374 * bc_kern_dma_alloc - Allocate memory for Dma rings
375 * @adp: Adapter instance
376 * @sz: Size of the memory to allocate.
377 * @phy_addr: Physical address of the memory allocated.
378 * Typedef to system's dma_addr_t (u64)
379 *
380 * Return:
381 * Pointer to allocated memory..
382 *
383 * Wrapper to Linux kernel interface.
384 *
385 */
386void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
387 dma_addr_t *phy_addr)
388{
389 void *temp = NULL;
390
391 if (!adp || !sz || !phy_addr) {
Masanari Iidacc922722013-12-06 23:27:55 +0900392 BCMLOG_ERR("Invalid Arg..\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500393 return temp;
394 }
395
396 temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
397 if (temp)
398 memset(temp, 0, sz);
399
400 return temp;
401}
402
403/**
404 * bc_kern_dma_free - Release Dma ring memory.
405 * @adp: Adapter instance
406 * @sz: Size of the memory to allocate.
407 * @ka: Kernel virtual address returned during _dio_alloc()
408 * @phy_addr: Physical address of the memory allocated.
409 * Typedef to system's dma_addr_t (u64)
410 *
411 * Return:
412 * none.
413 */
414void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
415 dma_addr_t phy_addr)
416{
417 if (!adp || !ka || !sz || !phy_addr) {
Masanari Iidacc922722013-12-06 23:27:55 +0900418 BCMLOG_ERR("Invalid Arg..\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500419 return;
420 }
421
422 pci_free_consistent(adp->pdev, sz, ka, phy_addr);
423}
424
425/**
426 * crystalhd_create_dioq - Create Generic DIO queue
427 * @adp: Adapter instance
428 * @dioq_hnd: Handle to the dio queue created
429 * @cb : Optional - Call back To free the element.
430 * @cbctx: Context to pass to callback.
431 *
432 * Return:
433 * status
434 *
435 * Initialize Generic DIO queue to hold any data. Callback
436 * will be used to free elements while deleting the queue.
437 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300438enum BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
439 struct crystalhd_dioq **dioq_hnd,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500440 crystalhd_data_free_cb cb, void *cbctx)
441{
Lior Dotanabfc7682010-05-18 12:46:42 +0300442 struct crystalhd_dioq *dioq = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500443
444 if (!adp || !dioq_hnd) {
445 BCMLOG_ERR("Invalid arg!!\n");
446 return BC_STS_INV_ARG;
447 }
448
449 dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
450 if (!dioq)
451 return BC_STS_INSUFF_RES;
452
453 spin_lock_init(&dioq->lock);
454 dioq->sig = BC_LINK_DIOQ_SIG;
Lior Dotanabfc7682010-05-18 12:46:42 +0300455 dioq->head = (struct crystalhd_elem *)&dioq->head;
456 dioq->tail = (struct crystalhd_elem *)&dioq->head;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500457 crystalhd_create_event(&dioq->event);
458 dioq->adp = adp;
459 dioq->data_rel_cb = cb;
460 dioq->cb_context = cbctx;
461 *dioq_hnd = dioq;
462
463 return BC_STS_SUCCESS;
464}
465
466/**
467 * crystalhd_delete_dioq - Delete Generic DIO queue
468 * @adp: Adapter instance
469 * @dioq: DIOQ instance..
470 *
471 * Return:
472 * None.
473 *
474 * Release Generic DIO queue. This function will remove
475 * all the entries from the Queue and will release data
476 * by calling the call back provided during creation.
477 *
478 */
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700479void crystalhd_delete_dioq(struct crystalhd_adp *adp,
480 struct crystalhd_dioq *dioq)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500481{
482 void *temp;
483
484 if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
485 return;
486
487 do {
488 temp = crystalhd_dioq_fetch(dioq);
489 if (temp && dioq->data_rel_cb)
490 dioq->data_rel_cb(dioq->cb_context, temp);
491 } while (temp);
492 dioq->sig = 0;
493 kfree(dioq);
494}
495
496/**
497 * crystalhd_dioq_add - Add new DIO request element.
498 * @ioq: DIO queue instance
499 * @t: DIO request to be added.
500 * @wake: True - Wake up suspended process.
501 * @tag: Special tag to assign - For search and get.
502 *
503 * Return:
504 * Status.
505 *
506 * Insert new element to Q tail.
507 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300508enum BC_STATUS crystalhd_dioq_add(struct crystalhd_dioq *ioq, void *data,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500509 bool wake, uint32_t tag)
510{
511 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +0300512 struct crystalhd_elem *tmp;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500513
514 if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
515 BCMLOG_ERR("Invalid arg!!\n");
516 return BC_STS_INV_ARG;
517 }
518
519 tmp = crystalhd_alloc_elem(ioq->adp);
520 if (!tmp) {
521 BCMLOG_ERR("No free elements.\n");
522 return BC_STS_INSUFF_RES;
523 }
524
525 tmp->data = data;
526 tmp->tag = tag;
527 spin_lock_irqsave(&ioq->lock, flags);
Lior Dotanabfc7682010-05-18 12:46:42 +0300528 tmp->flink = (struct crystalhd_elem *)&ioq->head;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500529 tmp->blink = ioq->tail;
530 tmp->flink->blink = tmp;
531 tmp->blink->flink = tmp;
532 ioq->count++;
533 spin_unlock_irqrestore(&ioq->lock, flags);
534
535 if (wake)
536 crystalhd_set_event(&ioq->event);
537
538 return BC_STS_SUCCESS;
539}
540
541/**
542 * crystalhd_dioq_fetch - Fetch element from head.
543 * @ioq: DIO queue instance
544 *
545 * Return:
546 * data element from the head..
547 *
548 * Remove an element from Queue.
549 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300550void *crystalhd_dioq_fetch(struct crystalhd_dioq *ioq)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500551{
552 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +0300553 struct crystalhd_elem *tmp;
554 struct crystalhd_elem *ret = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500555 void *data = NULL;
556
557 if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
558 BCMLOG_ERR("Invalid arg!!\n");
559 return data;
560 }
561
562 spin_lock_irqsave(&ioq->lock, flags);
563 tmp = ioq->head;
Lior Dotanabfc7682010-05-18 12:46:42 +0300564 if (tmp != (struct crystalhd_elem *)&ioq->head) {
Jarod Wilson7963eb42010-01-04 18:02:27 -0500565 ret = tmp;
566 tmp->flink->blink = tmp->blink;
567 tmp->blink->flink = tmp->flink;
568 ioq->count--;
569 }
570 spin_unlock_irqrestore(&ioq->lock, flags);
571 if (ret) {
572 data = ret->data;
573 crystalhd_free_elem(ioq->adp, ret);
574 }
575
576 return data;
577}
578/**
579 * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
580 * @ioq: DIO queue instance
581 * @tag: Tag to search for.
582 *
583 * Return:
584 * element from the head..
585 *
586 * Search TAG and remove the element.
587 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300588void *crystalhd_dioq_find_and_fetch(struct crystalhd_dioq *ioq, uint32_t tag)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500589{
590 unsigned long flags = 0;
Lior Dotanabfc7682010-05-18 12:46:42 +0300591 struct crystalhd_elem *tmp;
592 struct crystalhd_elem *ret = NULL;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500593 void *data = NULL;
594
595 if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
596 BCMLOG_ERR("Invalid arg!!\n");
597 return data;
598 }
599
600 spin_lock_irqsave(&ioq->lock, flags);
601 tmp = ioq->head;
Lior Dotanabfc7682010-05-18 12:46:42 +0300602 while (tmp != (struct crystalhd_elem *)&ioq->head) {
Jarod Wilson7963eb42010-01-04 18:02:27 -0500603 if (tmp->tag == tag) {
604 ret = tmp;
605 tmp->flink->blink = tmp->blink;
606 tmp->blink->flink = tmp->flink;
607 ioq->count--;
608 break;
609 }
610 tmp = tmp->flink;
611 }
612 spin_unlock_irqrestore(&ioq->lock, flags);
613
614 if (ret) {
615 data = ret->data;
616 crystalhd_free_elem(ioq->adp, ret);
617 }
618
619 return data;
620}
621
622/**
623 * crystalhd_dioq_fetch_wait - Fetch element from Head.
624 * @ioq: DIO queue instance
625 * @to_secs: Wait timeout in seconds..
626 *
627 * Return:
628 * element from the head..
629 *
630 * Return element from head if Q is not empty. Wait for new element
631 * if Q is empty for Timeout seconds.
632 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300633void *crystalhd_dioq_fetch_wait(struct crystalhd_dioq *ioq, uint32_t to_secs,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500634 uint32_t *sig_pend)
635{
636 unsigned long flags = 0;
637 int rc = 0, count;
638 void *tmp = NULL;
639
640 if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
641 BCMLOG_ERR("Invalid arg!!\n");
642 return tmp;
643 }
644
645 count = to_secs;
646 spin_lock_irqsave(&ioq->lock, flags);
647 while ((ioq->count == 0) && count) {
648 spin_unlock_irqrestore(&ioq->lock, flags);
649
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700650 crystalhd_wait_on_event(&ioq->event,
651 (ioq->count > 0), 1000, rc, 0);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500652 if (rc == 0) {
653 goto out;
654 } else if (rc == -EINTR) {
655 BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
656 *sig_pend = 1;
657 return tmp;
658 }
659 spin_lock_irqsave(&ioq->lock, flags);
660 count--;
661 }
662 spin_unlock_irqrestore(&ioq->lock, flags);
663
664out:
665 return crystalhd_dioq_fetch(ioq);
666}
667
668/**
669 * crystalhd_map_dio - Map user address for DMA
670 * @adp: Adapter instance
671 * @ubuff: User buffer to map.
672 * @ubuff_sz: User buffer size.
673 * @uv_offset: UV buffer offset.
674 * @en_422mode: TRUE:422 FALSE:420 Capture mode.
675 * @dir_tx: TRUE for Tx (To device from host)
676 * @dio_hnd: Handle to mapped DIO request.
677 *
678 * Return:
679 * Status.
680 *
681 * This routine maps user address and lock pages for DMA.
682 *
683 */
Lior Dotanabfc7682010-05-18 12:46:42 +0300684enum BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
Jarod Wilson7963eb42010-01-04 18:02:27 -0500685 uint32_t ubuff_sz, uint32_t uv_offset,
686 bool en_422mode, bool dir_tx,
Lior Dotanabfc7682010-05-18 12:46:42 +0300687 struct crystalhd_dio_req **dio_hnd)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500688{
Lior Dotanabfc7682010-05-18 12:46:42 +0300689 struct crystalhd_dio_req *dio;
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700690 /* FIXME: jarod: should some of these
691 unsigned longs be uint32_t or uintptr_t? */
Jarod Wilson7963eb42010-01-04 18:02:27 -0500692 unsigned long start = 0, end = 0, uaddr = 0, count = 0;
693 unsigned long spsz = 0, uv_start = 0;
694 int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
695
696 if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100697 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500698 return BC_STS_INV_ARG;
699 }
700 /* Compute pages */
701 uaddr = (unsigned long)ubuff;
702 count = (unsigned long)ubuff_sz;
703 end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
704 start = uaddr >> PAGE_SHIFT;
705 nr_pages = end - start;
706
707 if (!count || ((uaddr + count) < uaddr)) {
708 BCMLOG_ERR("User addr overflow!!\n");
709 return BC_STS_INV_ARG;
710 }
711
712 dio = crystalhd_alloc_dio(adp);
713 if (!dio) {
714 BCMLOG_ERR("dio pool empty..\n");
715 return BC_STS_INSUFF_RES;
716 }
717
718 if (dir_tx) {
719 rw = WRITE;
720 dio->direction = DMA_TO_DEVICE;
721 } else {
722 rw = READ;
723 dio->direction = DMA_FROM_DEVICE;
724 }
725
726 if (nr_pages > dio->max_pages) {
727 BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
728 dio->max_pages, nr_pages);
729 crystalhd_unmap_dio(adp, dio);
730 return BC_STS_INSUFF_RES;
731 }
732
733 if (uv_offset) {
734 uv_start = (uaddr + (unsigned long)uv_offset) >> PAGE_SHIFT;
735 dio->uinfo.uv_sg_ix = uv_start - start;
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700736 dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) &
737 ~PAGE_MASK);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500738 }
739
740 dio->fb_size = ubuff_sz & 0x03;
741 if (dio->fb_size) {
742 res = copy_from_user(dio->fb_va,
Monam Agarwal6df1a832014-03-05 06:05:51 +0530743 (void __user *)(uaddr + count - dio->fb_size),
Jarod Wilson7963eb42010-01-04 18:02:27 -0500744 dio->fb_size);
745 if (res) {
746 BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
747 res, dio->fb_size,
748 (void *)(uaddr + count-dio->fb_size));
749 crystalhd_unmap_dio(adp, dio);
750 return BC_STS_INSUFF_RES;
751 }
752 }
753
754 down_read(&current->mm->mmap_sem);
755 res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
756 0, dio->pages, NULL);
757 up_read(&current->mm->mmap_sem);
758
759 /* Save for release..*/
760 dio->sig = crystalhd_dio_locked;
761 if (res < nr_pages) {
762 BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
763 dio->page_cnt = res;
764 crystalhd_unmap_dio(adp, dio);
765 return BC_STS_ERROR;
766 }
767
768 dio->page_cnt = nr_pages;
769 /* Get scatter/gather */
770 crystalhd_init_sg(dio->sg, dio->page_cnt);
771 crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
772 if (nr_pages > 1) {
773 dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
774
775#ifdef CONFIG_X86_64
776 dio->sg[0].dma_length = dio->sg[0].length;
777#endif
778 count -= dio->sg[0].length;
779 for (i = 1; i < nr_pages; i++) {
780 if (count < 4) {
781 spsz = count;
782 skip_fb_sg = 1;
783 } else {
784 spsz = (count < PAGE_SIZE) ?
785 (count & ~0x03) : PAGE_SIZE;
786 }
787 crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
788 count -= spsz;
789 }
790 } else {
791 if (count < 4) {
792 dio->sg[0].length = count;
793 skip_fb_sg = 1;
794 } else {
795 dio->sg[0].length = count - dio->fb_size;
796 }
797#ifdef CONFIG_X86_64
798 dio->sg[0].dma_length = dio->sg[0].length;
799#endif
800 }
801 dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
802 dio->page_cnt, dio->direction);
803 if (dio->sg_cnt <= 0) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100804 BCMLOG_ERR("sg map %d-%d\n", dio->sg_cnt, dio->page_cnt);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500805 crystalhd_unmap_dio(adp, dio);
806 return BC_STS_ERROR;
807 }
808 if (dio->sg_cnt && skip_fb_sg)
809 dio->sg_cnt -= 1;
810 dio->sig = crystalhd_dio_sg_mapped;
811 /* Fill in User info.. */
812 dio->uinfo.xfr_len = ubuff_sz;
813 dio->uinfo.xfr_buff = ubuff;
814 dio->uinfo.uv_offset = uv_offset;
815 dio->uinfo.b422mode = en_422mode;
816 dio->uinfo.dir_tx = dir_tx;
817
818 *dio_hnd = dio;
819
820 return BC_STS_SUCCESS;
821}
822
823/**
824 * crystalhd_unmap_sgl - Release mapped resources
825 * @adp: Adapter instance
826 * @dio: DIO request instance
827 *
828 * Return:
829 * Status.
830 *
831 * This routine is to unmap the user buffer pages.
832 */
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700833enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp,
834 struct crystalhd_dio_req *dio)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500835{
836 struct page *page = NULL;
837 int j = 0;
838
839 if (!adp || !dio) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100840 BCMLOG_ERR("Invalid arg\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500841 return BC_STS_INV_ARG;
842 }
843
844 if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
845 for (j = 0; j < dio->page_cnt; j++) {
846 page = dio->pages[j];
847 if (page) {
848 if (!PageReserved(page) &&
849 (dio->direction == DMA_FROM_DEVICE))
850 SetPageDirty(page);
851 page_cache_release(page);
852 }
853 }
854 }
855 if (dio->sig == crystalhd_dio_sg_mapped)
Amarjargal Gundjalamd7c94552013-05-13 03:18:07 -0700856 pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt,
857 dio->direction);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500858
859 crystalhd_free_dio(adp, dio);
860
861 return BC_STS_SUCCESS;
862}
863
864/**
865 * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
866 * @adp: Adapter instance
867 * @max_pages: Max pages for size calculation.
868 *
869 * Return:
870 * system error.
871 *
872 * This routine creates a memory pool to hold dio context for
873 * for HW Direct IO operation.
874 */
875int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
876{
877 uint32_t asz = 0, i = 0;
878 uint8_t *temp;
Lior Dotanabfc7682010-05-18 12:46:42 +0300879 struct crystalhd_dio_req *dio;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500880
881 if (!adp || !max_pages) {
882 BCMLOG_ERR("Invalid Arg!!\n");
883 return -EINVAL;
884 }
885
886 /* Get dma memory for fill byte handling..*/
887 adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
888 adp->pdev, 8, 8, 0);
889 if (!adp->fill_byte_pool) {
890 BCMLOG_ERR("failed to create fill byte pool\n");
891 return -ENOMEM;
892 }
893
894 /* Get the max size from user based on 420/422 modes */
895 asz = (sizeof(*dio->pages) * max_pages) +
896 (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
897
898 BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
899 BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
900
901 for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
Julia Lawall32414872010-05-11 20:26:57 +0200902 temp = kzalloc(asz, GFP_KERNEL);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500903 if ((temp) == NULL) {
904 BCMLOG_ERR("Failed to alloc %d mem\n", asz);
905 return -ENOMEM;
906 }
907
Lior Dotanabfc7682010-05-18 12:46:42 +0300908 dio = (struct crystalhd_dio_req *)temp;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500909 temp += sizeof(*dio);
910 dio->pages = (struct page **)temp;
911 temp += (sizeof(*dio->pages) * max_pages);
912 dio->sg = (struct scatterlist *)temp;
913 dio->max_pages = max_pages;
914 dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
915 &dio->fb_pa);
916 if (!dio->fb_va) {
917 BCMLOG_ERR("fill byte alloc failed.\n");
918 return -ENOMEM;
919 }
920
921 crystalhd_free_dio(adp, dio);
922 }
923
924 return 0;
925}
926
927/**
928 * crystalhd_destroy_dio_pool - Release DIO mem pool.
929 * @adp: Adapter instance
930 *
931 * Return:
932 * none.
933 *
934 * This routine releases dio memory pool during close.
935 */
936void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
937{
Lior Dotanabfc7682010-05-18 12:46:42 +0300938 struct crystalhd_dio_req *dio;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500939 int count = 0;
940
941 if (!adp) {
942 BCMLOG_ERR("Invalid Arg!!\n");
943 return;
944 }
945
946 do {
947 dio = crystalhd_alloc_dio(adp);
948 if (dio) {
949 if (dio->fb_va)
950 pci_pool_free(adp->fill_byte_pool,
951 dio->fb_va, dio->fb_pa);
952 count++;
953 kfree(dio);
954 }
955 } while (dio);
956
957 if (adp->fill_byte_pool) {
958 pci_pool_destroy(adp->fill_byte_pool);
959 adp->fill_byte_pool = NULL;
960 }
961
Lars Lindley641b63f2010-03-11 00:21:20 +0100962 BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count);
Jarod Wilson7963eb42010-01-04 18:02:27 -0500963}
964
965/**
966 * crystalhd_create_elem_pool - List element pool creation.
967 * @adp: Adapter instance
968 * @pool_size: Number of elements in the pool.
969 *
970 * Return:
971 * 0 - success, <0 error
972 *
973 * Create general purpose list element pool to hold pending,
974 * and active requests.
975 */
Bill Pemberton349affa2012-11-19 13:22:10 -0500976int crystalhd_create_elem_pool(struct crystalhd_adp *adp,
Ameya Palande21b08382010-02-24 20:18:28 +0200977 uint32_t pool_size)
Jarod Wilson7963eb42010-01-04 18:02:27 -0500978{
979 uint32_t i;
Lior Dotanabfc7682010-05-18 12:46:42 +0300980 struct crystalhd_elem *temp;
Jarod Wilson7963eb42010-01-04 18:02:27 -0500981
982 if (!adp || !pool_size)
983 return -EINVAL;
984
985 for (i = 0; i < pool_size; i++) {
986 temp = kzalloc(sizeof(*temp), GFP_KERNEL);
987 if (!temp) {
Lars Lindley641b63f2010-03-11 00:21:20 +0100988 BCMLOG_ERR("kalloc failed\n");
Jarod Wilson7963eb42010-01-04 18:02:27 -0500989 return -ENOMEM;
990 }
991 crystalhd_free_elem(adp, temp);
992 }
993 BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
994 return 0;
995}
996
997/**
998 * crystalhd_delete_elem_pool - List element pool deletion.
999 * @adp: Adapter instance
1000 *
1001 * Return:
1002 * none
1003 *
1004 * Delete general purpose list element pool.
1005 */
1006void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
1007{
Lior Dotanabfc7682010-05-18 12:46:42 +03001008 struct crystalhd_elem *temp;
Jarod Wilson7963eb42010-01-04 18:02:27 -05001009 int dbg_cnt = 0;
1010
1011 if (!adp)
1012 return;
1013
1014 do {
1015 temp = crystalhd_alloc_elem(adp);
1016 if (temp) {
1017 kfree(temp);
1018 dbg_cnt++;
1019 }
1020 } while (temp);
1021
1022 BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
1023}
1024
1025/*================ Debug support routines.. ================================*/
1026void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
1027{
1028 uint32_t i, k = 1;
1029
1030 for (i = 0; i < dwcount; i++) {
1031 if (k == 1)
1032 BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
1033
1034 BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
1035
1036 buff += sizeof(uint32_t);
1037 off += sizeof(uint32_t);
1038 k++;
1039 if ((i == dwcount - 1) || (k > 4)) {
1040 BCMLOG(BCMLOG_DATA, "\n");
1041 k = 1;
1042 }
1043 }
1044}