Root/drivers/staging/crystalhd/crystalhd_misc.c

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
31uint32_t g_linklog_level;
32
33static 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
39static 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
45static 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
50static 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
69static 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
85static 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}
102static 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
114static 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
123static 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 */
142uint32_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 */
165void 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 */
188uint32_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 */
211void 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 */
232enum 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 */
260enum 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 */
288enum 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 */
333enum 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 */
379void *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 */
407void 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 */
431enum 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 */
472void 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 */
500enum 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 */
542void *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 */
580void *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 */
625void *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
655out:
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 */
675enum 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 */
822enum BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, struct crystalhd_dio_req *dio)
823{
824    struct page *page = NULL;
825    int j = 0;
826
827    if (!adp || !dio) {
828        BCMLOG_ERR("Invalid arg\n");
829        return BC_STS_INV_ARG;
830    }
831
832    if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
833        for (j = 0; j < dio->page_cnt; j++) {
834            page = dio->pages[j];
835            if (page) {
836                if (!PageReserved(page) &&
837                    (dio->direction == DMA_FROM_DEVICE))
838                    SetPageDirty(page);
839                page_cache_release(page);
840            }
841        }
842    }
843    if (dio->sig == crystalhd_dio_sg_mapped)
844        pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
845
846    crystalhd_free_dio(adp, dio);
847
848    return BC_STS_SUCCESS;
849}
850
851/**
852 * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
853 * @adp: Adapter instance
854 * @max_pages: Max pages for size calculation.
855 *
856 * Return:
857 * system error.
858 *
859 * This routine creates a memory pool to hold dio context for
860 * for HW Direct IO operation.
861 */
862int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
863{
864    uint32_t asz = 0, i = 0;
865    uint8_t *temp;
866    struct crystalhd_dio_req *dio;
867
868    if (!adp || !max_pages) {
869        BCMLOG_ERR("Invalid Arg!!\n");
870        return -EINVAL;
871    }
872
873    /* Get dma memory for fill byte handling..*/
874    adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
875                          adp->pdev, 8, 8, 0);
876    if (!adp->fill_byte_pool) {
877        BCMLOG_ERR("failed to create fill byte pool\n");
878        return -ENOMEM;
879    }
880
881    /* Get the max size from user based on 420/422 modes */
882    asz = (sizeof(*dio->pages) * max_pages) +
883           (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
884
885    BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
886           BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
887
888    for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
889        temp = kzalloc(asz, GFP_KERNEL);
890        if ((temp) == NULL) {
891            BCMLOG_ERR("Failed to alloc %d mem\n", asz);
892            return -ENOMEM;
893        }
894
895        dio = (struct crystalhd_dio_req *)temp;
896        temp += sizeof(*dio);
897        dio->pages = (struct page **)temp;
898        temp += (sizeof(*dio->pages) * max_pages);
899        dio->sg = (struct scatterlist *)temp;
900        dio->max_pages = max_pages;
901        dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
902                        &dio->fb_pa);
903        if (!dio->fb_va) {
904            BCMLOG_ERR("fill byte alloc failed.\n");
905            return -ENOMEM;
906        }
907
908        crystalhd_free_dio(adp, dio);
909    }
910
911    return 0;
912}
913
914/**
915 * crystalhd_destroy_dio_pool - Release DIO mem pool.
916 * @adp: Adapter instance
917 *
918 * Return:
919 * none.
920 *
921 * This routine releases dio memory pool during close.
922 */
923void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
924{
925    struct crystalhd_dio_req *dio;
926    int count = 0;
927
928    if (!adp) {
929        BCMLOG_ERR("Invalid Arg!!\n");
930        return;
931    }
932
933    do {
934        dio = crystalhd_alloc_dio(adp);
935        if (dio) {
936            if (dio->fb_va)
937                pci_pool_free(adp->fill_byte_pool,
938                          dio->fb_va, dio->fb_pa);
939            count++;
940            kfree(dio);
941        }
942    } while (dio);
943
944    if (adp->fill_byte_pool) {
945        pci_pool_destroy(adp->fill_byte_pool);
946        adp->fill_byte_pool = NULL;
947    }
948
949    BCMLOG(BCMLOG_DBG, "Released dio pool %d\n", count);
950}
951
952/**
953 * crystalhd_create_elem_pool - List element pool creation.
954 * @adp: Adapter instance
955 * @pool_size: Number of elements in the pool.
956 *
957 * Return:
958 * 0 - success, <0 error
959 *
960 * Create general purpose list element pool to hold pending,
961 * and active requests.
962 */
963int __devinit crystalhd_create_elem_pool(struct crystalhd_adp *adp,
964        uint32_t pool_size)
965{
966    uint32_t i;
967    struct crystalhd_elem *temp;
968
969    if (!adp || !pool_size)
970        return -EINVAL;
971
972    for (i = 0; i < pool_size; i++) {
973        temp = kzalloc(sizeof(*temp), GFP_KERNEL);
974        if (!temp) {
975            BCMLOG_ERR("kalloc failed\n");
976            return -ENOMEM;
977        }
978        crystalhd_free_elem(adp, temp);
979    }
980    BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
981    return 0;
982}
983
984/**
985 * crystalhd_delete_elem_pool - List element pool deletion.
986 * @adp: Adapter instance
987 *
988 * Return:
989 * none
990 *
991 * Delete general purpose list element pool.
992 */
993void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
994{
995    struct crystalhd_elem *temp;
996    int dbg_cnt = 0;
997
998    if (!adp)
999        return;
1000
1001    do {
1002        temp = crystalhd_alloc_elem(adp);
1003        if (temp) {
1004            kfree(temp);
1005            dbg_cnt++;
1006        }
1007    } while (temp);
1008
1009    BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
1010}
1011
1012/*================ Debug support routines.. ================================*/
1013void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
1014{
1015    uint32_t i, k = 1;
1016
1017    for (i = 0; i < dwcount; i++) {
1018        if (k == 1)
1019            BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
1020
1021        BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
1022
1023        buff += sizeof(uint32_t);
1024        off += sizeof(uint32_t);
1025        k++;
1026        if ((i == dwcount - 1) || (k > 4)) {
1027            BCMLOG(BCMLOG_DATA, "\n");
1028            k = 1;
1029        }
1030    }
1031}
1032

Archive Download this file



interactive