/kernel/bmeas-dma.c
C | 298 lines | 170 code | 36 blank | 92 comment | 19 complexity | bb202c84f3a836313abf435f93ad238c MD5 | raw file
- /*
- * Copyright CERN 2013
- * Author: Daniel Oberson
- *
- * DMA of bmeas
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/types.h>
- #include <linux/list.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/dma-mapping.h>
- #include <linux/scatterlist.h>
- #include <linux/fmc.h>
- #include "bmeas.h"
- /*
- * calculate_nents
- *
- * It calculates the number of pages
- */
- static int calculate_n_pages(unsigned int n_data, unsigned int n_bytes_data)
- {
- int n_pages;
- n_pages = n_data*n_bytes_data/PAGE_SIZE;
- if (n_pages*PAGE_SIZE<n_data*n_bytes_data)
- n_pages += 1;
- return n_pages;
- }
- /*
- * setup_dma_scatter
- *
- * Initialize each element of the scatter list
- */
- static void setup_dma_scatter(struct bmeas_dev *bmeasdev,
- int size_dma_buffer)
- {
- struct scatterlist *sg;
- int bytesleft = 0;
- void *bufp = NULL;
- int mapbytes;
- int i;
- for_each_sg(bmeasdev->sgt.sgl, sg, bmeasdev->sgt.nents, i) {
- /*printk(KERN_DEBUG "Init sg number : %d \n",i);*/
- if (i==0)
- {
- bytesleft = size_dma_buffer;
- bufp = bmeasdev->dma_buffer;
- }
- if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
- {
- mapbytes = bytesleft;
- }
- else
- {
- mapbytes = PAGE_SIZE - offset_in_page(bufp);
- }
- //TODO Check if this test is needed
- if (is_vmalloc_addr(bufp))
- sg_set_page(sg, vmalloc_to_page(bufp), mapbytes,
- offset_in_page(bufp));
- else
- sg_set_buf(sg, bufp, mapbytes);
- /* Configure next values */
- bufp += mapbytes;
- bytesleft -= mapbytes;
- /*printk(KERN_DEBUG "sg item (%p(+0x%lx), len:%d, left:%d)\n",
- virt_to_page(bufp), offset_in_page(bufp),
- mapbytes, bytesleft);*/
- }
- }
- void cfg_start_dma(struct bmeas_dev *bmeasdev)
- {
- int i;
- struct scatterlist *sg;
- uint32_t dev_mem_offset;
- dma_addr_t tmp;
- unsigned long test;
-
- //Switch block number
- if (bmeasdev->block_nber==0)
- dev_mem_offset = 0;
- else
- dev_mem_offset = DEFAULT_DMA_DATA_LENGTH*SIZE_DMA_DATA;
- // Configure DMA items
- for_each_sg(bmeasdev->sgt.sgl, sg, bmeasdev->sgt.nents, i) {
- //printk(KERN_DEBUG "DMA item : %d \n",i);
- // Prepare DMA item
- bmeasdev->items[i].start_addr = dev_mem_offset;
- bmeasdev->items[i].dma_addr_l = sg_dma_address(sg) & 0xFFFFFFFF;
- bmeasdev->items[i].dma_addr_h = (uint64_t)sg_dma_address(sg) >> 32;
- bmeasdev->items[i].dma_len = sg_dma_len(sg);
- dev_mem_offset += bmeasdev->items[i].dma_len;
- if (!sg_is_last(sg)) {// more transfers
- // uint64_t so it works on 32 and 64 bit
- //printk(KERN_DEBUG "not last sg : %d \n",i);
- tmp = bmeasdev->dma_list_item;
- tmp += (sizeof(struct bmeasd_dma_item) * ( i + 1 ));
- bmeasdev->items[i].next_addr_l = ((uint64_t)tmp) & 0xFFFFFFFF;
- bmeasdev->items[i].next_addr_h = ((uint64_t)tmp) >> 32;
- bmeasdev->items[i].attribute = 0x1; // more items chained
- } else {
- //printk(KERN_DEBUG "No more transfer");
- bmeasdev->items[i].attribute = 0x0; // last item
- }
- // The first item is written on the device
- if (i == 0) {
- test = sizeof(struct bmeasd_dma_item);
- /*printk(KERN_DEBUG "bmeasdev->dma_list_item : 0x%llx \n",bmeasdev->dma_list_item);
- printk(KERN_DEBUG "sizeof(struct bmeasd_dma_item) : 0x%llx \n",test);
- printk(KERN_DEBUG "Start adress : 0x%08x \n"
- "DMA addr_l : 0x%08x \n"
- "DMA addr_h : 0x%08x \n"
- "DMA len : %d \n"
- "DMA n_addr_l : 0x%08x \n"
- "DMA n_addr_h : 0x%08x \n"
- "Attribut : %d \n",
- bmeasdev->items[i].start_addr,bmeasdev->items[i].dma_addr_l,bmeasdev->items[i].dma_addr_h,
- bmeasdev->items[i].dma_len,bmeasdev->items[i].next_addr_l,bmeasdev->items[i].next_addr_h,bmeasdev->items[i].attribute);
- */
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_ADDR,bmeasdev->items[i].start_addr);
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_ADDR_L,bmeasdev->items[i].dma_addr_l);
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_ADDR_H,bmeasdev->items[i].dma_addr_h);
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_LEN,bmeasdev->items[i].dma_len);
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_NEXT_L,bmeasdev->items[i].next_addr_l);
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_NEXT_H,bmeasdev->items[i].next_addr_h);
- // Chain another transfer or not
- bmeas_hardware_write(bmeasdev->fmc,
- BMEAS_DMA_BR_LAST,bmeasdev->items[i].attribute);
-
- }
- }
- }
- /*
- * bmeas_map_dma
- * @fmc: fmc_device
- *
- * n_data: number of data to transfer
- *
- * Map a scatter/gather table for the DMA transfer from the peak-detector.
- * The DMA controller can store a single item, but more then one transfer
- * could be necessary because one item is 4Kbytes.
- */
- int bmeas_map_dma(struct bmeas_dev *bmeasdev,
- unsigned int n_data, unsigned int n_bytes_data)
- {
- struct bmeasd_dma_item *items;
- unsigned int pages;
- int i, count, size, err;
- struct scatterlist *sg;
- uint32_t dev_mem_offset = 0;
- dma_addr_t tmp;
- unsigned long test;
- printk(KERN_DEBUG "BMeas_map_dma -> bmeasdev : %p \n",bmeasdev);
- //Init DMA buffer
- //bmeasdev->dma_buffer_len = n_data*n_bytes_data;
- bmeasdev->dma_buffer = kzalloc(bmeasdev->dma_buffer_len, GFP_ATOMIC);
-
- //Calculate number of pages
- pages = calculate_n_pages(n_data,n_bytes_data);
- // Create sglists for the transfers
- err = sg_alloc_table(&bmeasdev->sgt, pages, GFP_ATOMIC);
- if (err) {
- dev_err(&bmeasdev->fmc->dev, "Cannot allocate sg table (%i pages)\n", pages);
- goto out;
- }
-
- // Limited to 32-bit (kernel limit)
- size = sizeof(*items) * bmeasdev->sgt.nents;
- items = kzalloc(size, GFP_ATOMIC);
- if (!items) {
- dev_err(&bmeasdev->fmc->dev, "Cannot allocate coherent dma memory\n");
- goto out_mem;
- }
- bmeasdev->items = items;
- bmeasdev->dma_list_item = dma_map_single(bmeasdev->fmc->hwdev, items, size,
- DMA_TO_DEVICE);
- if (!bmeasdev->dma_list_item) {
- goto out_free;
- }
- //printk(KERN_DEBUG "Nents size : %08x \n",size);
-
- // Setup the scatter list for the provided block
- setup_dma_scatter(bmeasdev,bmeasdev->dma_buffer_len);
- //printk(KERN_DEBUG "Init dma_buffer for a size : %08x \n",bmeasdev->dma_buffer_len);
- // Map DMA buffers
- //printk(KERN_DEBUG "bmeasdev->fmc->hwdev : %08x bmeasdev->sgt.sgl : %08x bmeasdev->sgt.nents : %08x \n",
- // bmeasdev->fmc->hwdev,bmeasdev->sgt.sgl,bmeasdev->sgt.nents);
- count = dma_map_sg(bmeasdev->fmc->hwdev,bmeasdev->sgt.sgl,bmeasdev->sgt.nents,DMA_FROM_DEVICE);
- if (!count) {
- dev_err(&bmeasdev->fmc->dev, "cannot map dma memory\n");
- goto out_map;
- }
-
- return 0;
- out_map:
- dma_unmap_single(bmeasdev->fmc->hwdev, bmeasdev->dma_list_item, size,
- DMA_TO_DEVICE);
- out_free:
- kfree(bmeasdev->items);
- out_mem:
- sg_free_table(&bmeasdev->sgt);
- out:
- return -ENOMEM;
- }
- /*
- * bmeas_dma_done
- * @fmc: pointer to fmc device
- *
- *
- */
- void bmeas_dma_done(struct bmeas_dev *bmeasdev)
- {
- int size_dma_bufer,i;
- bmeasdev->dmatag+=1;
-
- /*printk(KERN_DEBUG "DMA done : \n");
- if (bmeasdev->dma_buffer != NULL)
- {
- // Sync data to CPU
- //dma_sync_sg_for_cpu(bmeasdev->fmc->hwdev,bmeasdev->sgt.sgl,bmeasdev->sgt.nents,DMA_FROM_DEVICE);
- for (i=0;i<bmeasdev->dma_buffer_len/SIZE_DMA_DATA;i++)
- {
- //printk(KERN_DEBUG "Buffer[%d] : %d \n",i,*(bmeasdev->dma_buffer+i));
- if ((i>=0)&(i<8))
- printk(KERN_DEBUG "Buffer[%d] : %d \n",i,*(bmeasdev->dma_buffer+i));
- if (i>bmeasdev->dma_buffer_len/SIZE_DMA_DATA-10)
- printk(KERN_DEBUG "Buffer[%d] : %d \n",i,*(bmeasdev->dma_buffer+i));
-
- }
- }*/
- }
- /*
- * bmeas_unmap_dma
- * @bmeasdev: pointer to bmeas device
- *
- * It unmaps DMA.
- */
- void bmeas_unmap_dma(struct bmeas_dev *bmeasdev)
- {
- unsigned int size;
- printk(KERN_DEBUG "Unmap DMA\n");
- if (bmeasdev->items != NULL)
- {
- size = sizeof(struct bmeasd_dma_item) * bmeasdev->sgt.nents;
- dma_unmap_single(bmeasdev->fmc->hwdev, bmeasdev->dma_list_item, size,
- DMA_TO_DEVICE);
- dma_unmap_sg(bmeasdev->fmc->hwdev, bmeasdev->sgt.sgl, bmeasdev->sgt.nents,
- DMA_FROM_DEVICE);
- kfree(bmeasdev->items);
- bmeasdev->items = NULL;
- bmeasdev->dma_list_item = 0;
- }
-
-
- if (bmeasdev->dma_buffer != NULL)
- {
- kfree(bmeasdev->dma_buffer);
- bmeasdev->dma_buffer = NULL;
- }
-
- if (bmeasdev->sgt.sgl != NULL)
- sg_free_table(&bmeasdev->sgt);
-
-
- }