Commit a25859fd authored by Federico Vaga's avatar Federico Vaga Committed by Alessandro Rubini

zio: create DMA-sg list from an array of zio_blocks

Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
parent 8b590933
......@@ -42,6 +42,7 @@ fmc-adc-100m14b-y += fa-spec-irq.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-core.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-regtable.o
fmc-adc-100m14b-$(CONFIG_FMC_ADC_SVEC) += fa-svec-dma.o
fmc-adc-100m14b-y += zio-helpers.o
all modules:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) modules
......
This diff is collapsed.
......@@ -89,7 +89,6 @@ struct fa_spec_data {
/* DMA attributes */
unsigned int fa_dma_base;
unsigned int fa_irq_dma_base;
struct sg_table sgt;
struct fa_dma_item *items;
dma_addr_t dma_list_item;
unsigned int n_dma_err; /* statistics */
......
......@@ -324,6 +324,9 @@ struct fa_dev {
unsigned int fa_irq_adc_base;
unsigned int fa_utc_base;
/* DMA description */
struct zio_dma_sg *zdma;
/* carrier specific functions (init/exit/reset/readout/irq handling) */
struct fa_carrier_op *carrier_op;
/* carrier private data */
......
/*
* Copyright CERN 2014
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* handle DMA mapping
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include "zio-helpers.h"
static int zio_calculate_nents(struct zio_blocks_sg *sg_blocks,
unsigned int n_blocks)
{
int i, bytesleft;
void *bufp;
int mapbytes;
int nents = 0;
for (i = 0; i < n_blocks; ++i) {
bytesleft = sg_blocks[i].block->datalen;
bufp = sg_blocks[i].block->data;
sg_blocks[i].first_nent = nents;
while (bytesleft) {
nents++;
if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
mapbytes = bytesleft;
else
mapbytes = PAGE_SIZE - offset_in_page(bufp);
bufp += mapbytes;
bytesleft -= mapbytes;
}
}
return nents;
}
static void zio_dma_setup_scatter(struct zio_dma_sg *zdma)
{
struct scatterlist *sg;
int bytesleft = 0;
void *bufp = NULL;
int mapbytes;
int i, i_blk;
i_blk = 0;
for_each_sg(zdma->sgt.sgl, sg, zdma->sgt.nents, i) {
if (i_blk < zdma->n_blocks && i == zdma->sg_blocks[i_blk].first_nent) {
WARN(bytesleft, "unmapped byte in block %i\n",
i_blk - 1);
/*
* Configure the DMA for a new block, reset index and
* data pointer
*/
bytesleft = zdma->sg_blocks[i_blk].block->datalen;
bufp = zdma->sg_blocks[i_blk].block->data;
i_blk++; /* index the next block */
if (unlikely(i_blk > zdma->n_blocks))
BUG();
}
/*
* If there are less bytes left than what fits
* in the current page (plus page alignment offset)
* we just feed in this, else we stuff in as much
* as we can.
*/
if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
mapbytes = bytesleft;
else
mapbytes = PAGE_SIZE - offset_in_page(bufp);
/* Map the page */
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;
pr_debug("sg item (%p(+0x%lx), len:%d, left:%d)\n",
virt_to_page(bufp), offset_in_page(bufp),
mapbytes, bytesleft);
}
}
/*
* zio_alloc_scatterlist
* @chan: zio channel associated to this scatterlist
* @hwdev: low level device responsible of the DMA
* @blocks: array of zio_block to transfer
* @n_blocks: number of blocks to transfer
* @gfp: gfp flags for memory allocation
*
* The function allocates and initializes a scatterlist ready for DMA
* transfer
*/
struct zio_dma_sg *zio_dma_alloc_sg(struct device *hwdev,
struct zio_block **blocks, /* FIXME to array */
unsigned int n_blocks, gfp_t gfp)
{
struct zio_dma_sg *zdma;
unsigned int i, pages;
int err;
if (unlikely(!chan || !hwdev || !blocks || !n_blocks))
return -EINVAL;
/*
* Allocate a new zio_dma_sg structure that will contains all necessary
* information for DMA
*/
zdma = kzalloc(sizeof(struct zio_dma_sg), gfp);
if (!zdma)
return ERR_PTR(-ENOMEM);
/* Allocate a new list of blocks with sg information */
zdma->sg_blocks = kzalloc(sizeof(struct zio_blocks_sg) * n_blocks, gfp);
if (!zdma->sg_blocks) {
err = -ENOMEM;
goto out;
}
/* fill the zio_dma_sg structure */
zdma->hwdev = hwdev;
zdma->n_blocks = n_blocks;
for (i = 0; i < n_blocks; ++i)
zdma->sg_blocks[i].block = blocks[i];
/* calculate the number of necessary pages to transfer */
pages = zio_calculate_nents(zdma->sg_blocks, zdma->n_blocks);
if (!pages) {
err = -EINVAL;
goto out_calc_nents;
}
/* Create sglists for the transfers */
err = sg_alloc_table(&zdma->sgt, pages, gfp);
if (err)
goto out_alloc_sg;
/* Setup the scatter list for the provided block */
zio_dma_setup_scatter(zdma);
return zdma;
out_alloc_sg:
out_calc_nents:
kfree(zdma->sg_blocks);
out:
kfree(zdma);
return ERR_PTR(err);
}
EXPORT_SYMBOL(zio_dma_alloc_sg);
/*
* zio_free_scatterlist
* @zdma: zio DMA transfer descriptor
*
* It releases resources
*/
void zio_dma_free_sg(struct zio_dma_sg *zdma)
{
kfree(zdma->sg_blocks);
kfree(zdma);
}
EXPORT_SYMBOL(zio_dma_free_sg);
/*
* Copyright CERN 2014
* Author: Federico Vaga <federico.vaga@gmail.com>
*
* handle DMA mapping
*/
#ifndef ZIO_HELPERS_H_
#define ZIO_HELPERS_H_
#include <linux/zio.h>
#include <linux/scatterlist.h>
/*
* It describe a zio block to be mapped with sg
* @block: is the block to map
* @first_nent: it tells the index of the first DMA transfer corresponding to
* the start of this block
* @dev_mem_off: device memory offset where retrieve data for this block
*/
struct zio_blocks_sg {
struct zio_block *block;
unsigned int first_nent;
unsigned long dev_mem_off;
};
/*
* it describes the DMA sg mapping
* @hwdev: the low level driver which will do DMA
* @sg_blocks: one or more blocks to map
* @n_blocks: number of blocks to map
* @sgt: scatter gather table
*/
struct zio_dma_sg {
struct device *hwdev;
struct zio_blocks_sg *sg_blocks;
unsigned int n_blocks;
struct sg_table sgt;
};
/*
* It describe the current sg item
* @blk_index: current block index
* @page_index: current page index
* @is_first_nent_block: it tells if this description point to the first page
* transfer for current block
*/
struct zio_dma_sg_desc {
struct scatterlist *sg;
unsigned int blk_index;
unsigned int page_index;
int is_first_nent_block;
};
extern struct zio_dma_sg *zio_dma_alloc_sg(struct device *hwdev,
struct zio_block **blocks,
unsigned int n_blocks,
gfp_t gfp);
extern void zio_dma_free_sg(struct zio_dma_sg *zdma);
#endif /* ZIO_HELPERS_H_ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment