mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 13:22:23 +00:00
6f51ee709e
The iomm-config branch contains work from Will Deacon, quoting his description: This series adds automatic IOMMU and DMA-mapping configuration for OF-based DMA masters described using the generic IOMMU devicetree bindings. Although there is plenty of future work around splitting up iommu_ops, adding default IOMMU domains and sorting out automatic IOMMU group creation for the platform_bus, this is already useful enough for people to port over their IOMMU drivers and start using the new probing infrastructure (indeed, Marek has patches queued for the Exynos IOMMU). The branch touches core ARM and IOMMU driver files, and the respective maintainers (Russell King and Joerg Roedel) agreed to have the contents merged through the arm-soc tree. The final version was ready just before the merge window, so we ended up delaying it a bit longer than the rest, but we don't expect to see regressions because this is just additional infrastructure that will get used in drivers starting in 3.20 but is unused so far. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIVAwUAVJCfoGCrR//JCVInAQIfvxAAhVeEKyhroIGiuCmylWK/TdXja+xO46g+ hkrijO0cPB5C7K45AW2a2aCUM0jSjr81dUprQ/uojr3xXxnJ59t7tDAXpKpFy8xi 5gb/wd/Cea90RtR1mUnNr/+P1sJKemcvmhCuib7111E5wd/s617bLd1+zgCuHguj g733GjDE7SUSTEStviDg963pn+l2IartjhRPhAKmGWiLZA7RiWe35pzDTZGCApnd yfZafXxn4IeUcxQUT6lAsW7xShzCUI2CZ8nZ4tG6YcyR2UNB5BVrPb1BAm6Eb28C 1WmyjnAAyXxc6pqPTalO+JctpS7ujjbtwlOOwgthKyKMfpFnqyavablDl6GvtHn8 NIa3HdnKQTXl9/nRXCvIjeWDyaZEZ5ueacfhMm4PWRSIkqKFVgwY18nNkOul9fuz 0UD9EuN0PPHV2hCIp9Kl3Jju5pi2EEzCt/Vn0YGsZTZuVOfREZ3izDtyKFg1tjif AJ5kFRc1X+6hXNDUWUOmLOnjBvupbq2axFbLeAzQxla/O/0pwHWhiuqXu3uB4six 1Hlgt7yI7pob86VcQKTCg1v8kOvQTEuL2BtUWkCpbyrVSafYRVKwlUNnQlmu5F3c sL14hhK9QSHyCmJ7yKchY104QVKmN8v3ks8PyUNoPxq57ChH4E6FVAZpMz08uF5V mIWREpeIPNw= =ELLq -----END PGP SIGNATURE----- Merge tag 'iommu-config-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC/iommu configuration update from Arnd Bergmann: "The iomm-config branch contains work from Will Deacon, quoting his description: This series adds automatic IOMMU and DMA-mapping configuration for OF-based DMA masters described using the generic IOMMU devicetree bindings. Although there is plenty of future work around splitting up iommu_ops, adding default IOMMU domains and sorting out automatic IOMMU group creation for the platform_bus, this is already useful enough for people to port over their IOMMU drivers and start using the new probing infrastructure (indeed, Marek has patches queued for the Exynos IOMMU). The branch touches core ARM and IOMMU driver files, and the respective maintainers (Russell King and Joerg Roedel) agreed to have the contents merged through the arm-soc tree. The final version was ready just before the merge window, so we ended up delaying it a bit longer than the rest, but we don't expect to see regressions because this is just additional infrastructure that will get used in drivers starting in 3.20 but is unused so far" * tag 'iommu-config-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: iommu: store DT-probed IOMMU data privately arm: dma-mapping: plumb our iommu mapping ops into arch_setup_dma_ops arm: call iommu_init before of_platform_populate dma-mapping: detect and configure IOMMU in of_dma_configure iommu: fix initialization without 'add_device' callback iommu: provide helper function to configure an IOMMU for an of master iommu: add new iommu_ops callback for adding an OF device dma-mapping: replace set_arch_dma_coherent_ops with arch_setup_dma_ops iommu: provide early initialisation hook for IOMMU drivers
339 lines
9.8 KiB
C
339 lines
9.8 KiB
C
#ifndef ASMARM_DMA_MAPPING_H
|
|
#define ASMARM_DMA_MAPPING_H
|
|
|
|
#ifdef __KERNEL__
|
|
|
|
#include <linux/mm_types.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/dma-attrs.h>
|
|
#include <linux/dma-debug.h>
|
|
|
|
#include <asm-generic/dma-coherent.h>
|
|
#include <asm/memory.h>
|
|
|
|
#include <xen/xen.h>
|
|
#include <asm/xen/hypervisor.h>
|
|
|
|
#define DMA_ERROR_CODE (~0)
|
|
extern struct dma_map_ops arm_dma_ops;
|
|
extern struct dma_map_ops arm_coherent_dma_ops;
|
|
|
|
static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
|
|
{
|
|
if (dev && dev->archdata.dma_ops)
|
|
return dev->archdata.dma_ops;
|
|
return &arm_dma_ops;
|
|
}
|
|
|
|
static inline struct dma_map_ops *get_dma_ops(struct device *dev)
|
|
{
|
|
if (xen_initial_domain())
|
|
return xen_dma_ops;
|
|
else
|
|
return __generic_dma_ops(dev);
|
|
}
|
|
|
|
static inline void set_dma_ops(struct device *dev, struct dma_map_ops *ops)
|
|
{
|
|
BUG_ON(!dev);
|
|
dev->archdata.dma_ops = ops;
|
|
}
|
|
|
|
#include <asm-generic/dma-mapping-common.h>
|
|
|
|
static inline int dma_set_mask(struct device *dev, u64 mask)
|
|
{
|
|
return get_dma_ops(dev)->set_dma_mask(dev, mask);
|
|
}
|
|
|
|
#ifdef __arch_page_to_dma
|
|
#error Please update to __arch_pfn_to_dma
|
|
#endif
|
|
|
|
/*
|
|
* dma_to_pfn/pfn_to_dma/dma_to_virt/virt_to_dma are architecture private
|
|
* functions used internally by the DMA-mapping API to provide DMA
|
|
* addresses. They must not be used by drivers.
|
|
*/
|
|
#ifndef __arch_pfn_to_dma
|
|
static inline dma_addr_t pfn_to_dma(struct device *dev, unsigned long pfn)
|
|
{
|
|
if (dev)
|
|
pfn -= dev->dma_pfn_offset;
|
|
return (dma_addr_t)__pfn_to_bus(pfn);
|
|
}
|
|
|
|
static inline unsigned long dma_to_pfn(struct device *dev, dma_addr_t addr)
|
|
{
|
|
unsigned long pfn = __bus_to_pfn(addr);
|
|
|
|
if (dev)
|
|
pfn += dev->dma_pfn_offset;
|
|
|
|
return pfn;
|
|
}
|
|
|
|
static inline void *dma_to_virt(struct device *dev, dma_addr_t addr)
|
|
{
|
|
if (dev) {
|
|
unsigned long pfn = dma_to_pfn(dev, addr);
|
|
|
|
return phys_to_virt(__pfn_to_phys(pfn));
|
|
}
|
|
|
|
return (void *)__bus_to_virt((unsigned long)addr);
|
|
}
|
|
|
|
static inline dma_addr_t virt_to_dma(struct device *dev, void *addr)
|
|
{
|
|
if (dev)
|
|
return pfn_to_dma(dev, virt_to_pfn(addr));
|
|
|
|
return (dma_addr_t)__virt_to_bus((unsigned long)(addr));
|
|
}
|
|
|
|
#else
|
|
static inline dma_addr_t pfn_to_dma(struct device *dev, unsigned long pfn)
|
|
{
|
|
return __arch_pfn_to_dma(dev, pfn);
|
|
}
|
|
|
|
static inline unsigned long dma_to_pfn(struct device *dev, dma_addr_t addr)
|
|
{
|
|
return __arch_dma_to_pfn(dev, addr);
|
|
}
|
|
|
|
static inline void *dma_to_virt(struct device *dev, dma_addr_t addr)
|
|
{
|
|
return __arch_dma_to_virt(dev, addr);
|
|
}
|
|
|
|
static inline dma_addr_t virt_to_dma(struct device *dev, void *addr)
|
|
{
|
|
return __arch_virt_to_dma(dev, addr);
|
|
}
|
|
#endif
|
|
|
|
/* The ARM override for dma_max_pfn() */
|
|
static inline unsigned long dma_max_pfn(struct device *dev)
|
|
{
|
|
return PHYS_PFN_OFFSET + dma_to_pfn(dev, *dev->dma_mask);
|
|
}
|
|
#define dma_max_pfn(dev) dma_max_pfn(dev)
|
|
|
|
#define arch_setup_dma_ops arch_setup_dma_ops
|
|
extern void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
|
|
struct iommu_ops *iommu, bool coherent);
|
|
|
|
#define arch_teardown_dma_ops arch_teardown_dma_ops
|
|
extern void arch_teardown_dma_ops(struct device *dev);
|
|
|
|
/* do not use this function in a driver */
|
|
static inline bool is_device_dma_coherent(struct device *dev)
|
|
{
|
|
return dev->archdata.dma_coherent;
|
|
}
|
|
|
|
static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
|
|
{
|
|
unsigned int offset = paddr & ~PAGE_MASK;
|
|
return pfn_to_dma(dev, __phys_to_pfn(paddr)) + offset;
|
|
}
|
|
|
|
static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr)
|
|
{
|
|
unsigned int offset = dev_addr & ~PAGE_MASK;
|
|
return __pfn_to_phys(dma_to_pfn(dev, dev_addr)) + offset;
|
|
}
|
|
|
|
static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
|
|
{
|
|
u64 limit, mask;
|
|
|
|
if (!dev->dma_mask)
|
|
return 0;
|
|
|
|
mask = *dev->dma_mask;
|
|
|
|
limit = (mask + 1) & ~mask;
|
|
if (limit && size > limit)
|
|
return 0;
|
|
|
|
if ((addr | (addr + size - 1)) & ~mask)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static inline void dma_mark_clean(void *addr, size_t size) { }
|
|
|
|
/*
|
|
* DMA errors are defined by all-bits-set in the DMA address.
|
|
*/
|
|
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
|
{
|
|
debug_dma_mapping_error(dev, dma_addr);
|
|
return dma_addr == DMA_ERROR_CODE;
|
|
}
|
|
|
|
/*
|
|
* Dummy noncoherent implementation. We don't provide a dma_cache_sync
|
|
* function so drivers using this API are highlighted with build warnings.
|
|
*/
|
|
static inline void *dma_alloc_noncoherent(struct device *dev, size_t size,
|
|
dma_addr_t *handle, gfp_t gfp)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void dma_free_noncoherent(struct device *dev, size_t size,
|
|
void *cpu_addr, dma_addr_t handle)
|
|
{
|
|
}
|
|
|
|
extern int dma_supported(struct device *dev, u64 mask);
|
|
|
|
extern int arm_dma_set_mask(struct device *dev, u64 dma_mask);
|
|
|
|
/**
|
|
* arm_dma_alloc - allocate consistent memory for DMA
|
|
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
|
* @size: required memory size
|
|
* @handle: bus-specific DMA address
|
|
* @attrs: optinal attributes that specific mapping properties
|
|
*
|
|
* Allocate some memory for a device for performing DMA. This function
|
|
* allocates pages, and will return the CPU-viewed address, and sets @handle
|
|
* to be the device-viewed address.
|
|
*/
|
|
extern void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
|
|
gfp_t gfp, struct dma_attrs *attrs);
|
|
|
|
#define dma_alloc_coherent(d, s, h, f) dma_alloc_attrs(d, s, h, f, NULL)
|
|
|
|
static inline void *dma_alloc_attrs(struct device *dev, size_t size,
|
|
dma_addr_t *dma_handle, gfp_t flag,
|
|
struct dma_attrs *attrs)
|
|
{
|
|
struct dma_map_ops *ops = get_dma_ops(dev);
|
|
void *cpu_addr;
|
|
BUG_ON(!ops);
|
|
|
|
cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs);
|
|
debug_dma_alloc_coherent(dev, size, *dma_handle, cpu_addr);
|
|
return cpu_addr;
|
|
}
|
|
|
|
/**
|
|
* arm_dma_free - free memory allocated by arm_dma_alloc
|
|
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
|
* @size: size of memory originally requested in dma_alloc_coherent
|
|
* @cpu_addr: CPU-view address returned from dma_alloc_coherent
|
|
* @handle: device-view address returned from dma_alloc_coherent
|
|
* @attrs: optinal attributes that specific mapping properties
|
|
*
|
|
* Free (and unmap) a DMA buffer previously allocated by
|
|
* arm_dma_alloc().
|
|
*
|
|
* References to memory and mappings associated with cpu_addr/handle
|
|
* during and after this call executing are illegal.
|
|
*/
|
|
extern void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
|
|
dma_addr_t handle, struct dma_attrs *attrs);
|
|
|
|
#define dma_free_coherent(d, s, c, h) dma_free_attrs(d, s, c, h, NULL)
|
|
|
|
static inline void dma_free_attrs(struct device *dev, size_t size,
|
|
void *cpu_addr, dma_addr_t dma_handle,
|
|
struct dma_attrs *attrs)
|
|
{
|
|
struct dma_map_ops *ops = get_dma_ops(dev);
|
|
BUG_ON(!ops);
|
|
|
|
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
|
|
ops->free(dev, size, cpu_addr, dma_handle, attrs);
|
|
}
|
|
|
|
/**
|
|
* arm_dma_mmap - map a coherent DMA allocation into user space
|
|
* @dev: valid struct device pointer, or NULL for ISA and EISA-like devices
|
|
* @vma: vm_area_struct describing requested user mapping
|
|
* @cpu_addr: kernel CPU-view address returned from dma_alloc_coherent
|
|
* @handle: device-view address returned from dma_alloc_coherent
|
|
* @size: size of memory originally requested in dma_alloc_coherent
|
|
* @attrs: optinal attributes that specific mapping properties
|
|
*
|
|
* Map a coherent DMA buffer previously allocated by dma_alloc_coherent
|
|
* into user space. The coherent DMA buffer must not be freed by the
|
|
* driver until the user space mapping has been released.
|
|
*/
|
|
extern int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
|
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
|
struct dma_attrs *attrs);
|
|
|
|
/*
|
|
* This can be called during early boot to increase the size of the atomic
|
|
* coherent DMA pool above the default value of 256KiB. It must be called
|
|
* before postcore_initcall.
|
|
*/
|
|
extern void __init init_dma_coherent_pool_size(unsigned long size);
|
|
|
|
/*
|
|
* For SA-1111, IXP425, and ADI systems the dma-mapping functions are "magic"
|
|
* and utilize bounce buffers as needed to work around limited DMA windows.
|
|
*
|
|
* On the SA-1111, a bug limits DMA to only certain regions of RAM.
|
|
* On the IXP425, the PCI inbound window is 64MB (256MB total RAM)
|
|
* On some ADI engineering systems, PCI inbound window is 32MB (12MB total RAM)
|
|
*
|
|
* The following are helper functions used by the dmabounce subystem
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* dmabounce_register_dev
|
|
*
|
|
* @dev: valid struct device pointer
|
|
* @small_buf_size: size of buffers to use with small buffer pool
|
|
* @large_buf_size: size of buffers to use with large buffer pool (can be 0)
|
|
* @needs_bounce_fn: called to determine whether buffer needs bouncing
|
|
*
|
|
* This function should be called by low-level platform code to register
|
|
* a device as requireing DMA buffer bouncing. The function will allocate
|
|
* appropriate DMA pools for the device.
|
|
*/
|
|
extern int dmabounce_register_dev(struct device *, unsigned long,
|
|
unsigned long, int (*)(struct device *, dma_addr_t, size_t));
|
|
|
|
/**
|
|
* dmabounce_unregister_dev
|
|
*
|
|
* @dev: valid struct device pointer
|
|
*
|
|
* This function should be called by low-level platform code when device
|
|
* that was previously registered with dmabounce_register_dev is removed
|
|
* from the system.
|
|
*
|
|
*/
|
|
extern void dmabounce_unregister_dev(struct device *);
|
|
|
|
|
|
|
|
/*
|
|
* The scatter list versions of the above methods.
|
|
*/
|
|
extern int arm_dma_map_sg(struct device *, struct scatterlist *, int,
|
|
enum dma_data_direction, struct dma_attrs *attrs);
|
|
extern void arm_dma_unmap_sg(struct device *, struct scatterlist *, int,
|
|
enum dma_data_direction, struct dma_attrs *attrs);
|
|
extern void arm_dma_sync_sg_for_cpu(struct device *, struct scatterlist *, int,
|
|
enum dma_data_direction);
|
|
extern void arm_dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
|
|
enum dma_data_direction);
|
|
extern int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
|
|
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
|
struct dma_attrs *attrs);
|
|
|
|
#endif /* __KERNEL__ */
|
|
#endif
|