Following documents gives many details of DMA-APIs used in Linux :
Linux Documentation :
Documentation/DMA-API-HOWTO.txt
Documentation/DMA-API.txt
Documentation/Intel-IOMMU.txt
Online tutorials :
http://linuxkernelhacker.blogspot.in/2014/07/arm-dma-mapping-explained.html
http://www.linuxjournal.com/article/7104?page=0,0
Intel VT-D spec explaining IOMMU, IOTLB, DMA maps etc.
http://www.intel.in/content/dam/www/public/us/en/documents/product-specifications/vt-directed-io-spec.pdf
dma_map_ops for intel platform
struct dma_map_ops intel_dma_ops = {
.alloc = intel_alloc_coherent,
.free = intel_free_coherent,
.map_sg = intel_map_sg,
.unmap_sg = intel_unmap_sg,
.map_page = intel_map_page,
.unmap_page = intel_unmap_page,
.mapping_error = intel_mapping_error,
};
IOMMU hardware translates bus addresses(as seen by device) to physical addresses(RAM address).
dma_map_single(), takes the CPU virtual address, sets up any required IOMMU mapping and returns the bus address.
dma_set_mask_and_coherent(struct device *dev, u64 mask); -- Checks if device can address 32bits, 64 bits or 24 bits etc.
Consistent DMA mappings :
dma_alloc_coherent()
1. returns two values: the virtual address which you can use to access it from the CPU and dma_handle which you pass to the card.
2. The CPU virtual address returned is in multiples of pages.
intel_alloc_coherent()
..
vaddr = (void *)__get_free_pages(flags, order);
...
3. Can use map_single to generate the dma_handle(bus address as seen by device)
*dma_handle = __intel_map_single(dev, virt_to_bus(vaddr), size,
DMA_BIDIRECTIONAL,
dev->coherent_dma_mask);
dma_free_coherent()
dma_pool_create/dma_pool_alloc/dma_pool_free
These work much like a struct kmem_cache allocating small DMA-coherent memory regions
Streaming DMA mapping :
dma_addr_t
dma_map_single(struct device *dev, void *cpu_addr, size_t size,
enum dma_data_direction direction)
Maps a piece of processor virtual memory so it can be accessed by the device and returns the bus address of the memory.
dma_addr_t
dma_map_page(struct device *dev, struct page *page,
unsigned long offset, size_t size,
enum dma_data_direction direction)
void
dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction)
int
dma_map_sg(struct device *dev, struct scatterlist *sg,
int nents, enum dma_data_direction direction)
Returns: the number of bus address segments mapped (this may be shorter than <nents> passed in if some elements of the scatter/gather list are physically or virtually adjacent and an IOMMU maps them with a single entry).
virt_to_bus/bus_to_virt
virt_to_bus() and bus_to_virt() functions have been
superseded by the functionality provided by the PCI DMA interface
No comments:
Post a Comment