Commit e0d07278 authored by Jim Quinlan's avatar Jim Quinlan Committed by Christoph Hellwig

dma-mapping: introduce DMA range map, supplanting dma_pfn_offset

The new field 'dma_range_map' in struct device is used to facilitate the
use of single or multiple offsets between mapping regions of cpu addrs and
dma addrs.  It subsumes the role of "dev->dma_pfn_offset" which was only
capable of holding a single uniform offset and had no region bounds
checking.

The function of_dma_get_range() has been modified so that it takes a single
argument -- the device node -- and returns a map, NULL, or an error code.
The map is an array that holds the information regarding the DMA regions.
Each range entry contains the address offset, the cpu_start address, the
dma_start address, and the size of the region.

of_dma_configure() is the typical manner to set range offsets but there are
a number of ad hoc assignments to "dev->dma_pfn_offset" in the kernel
driver code.  These cases now invoke the function
dma_direct_set_offset(dev, cpu_addr, dma_addr, size).
Signed-off-by: default avatarJim Quinlan <james.quinlan@broadcom.com>
[hch: various interface cleanups]
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Tested-by: default avatarMathieu Poirier <mathieu.poirier@linaro.org>
Tested-by: default avatarNathan Chancellor <natechancellor@gmail.com>
parent 6eb0233e
...@@ -12,8 +12,8 @@ ...@@ -12,8 +12,8 @@
#ifndef __arch_pfn_to_dma #ifndef __arch_pfn_to_dma
static inline dma_addr_t pfn_to_dma(struct device *dev, unsigned long pfn) static inline dma_addr_t pfn_to_dma(struct device *dev, unsigned long pfn)
{ {
if (dev) if (dev && dev->dma_range_map)
pfn -= dev->dma_pfn_offset; pfn = PFN_DOWN(translate_phys_to_dma(dev, PFN_PHYS(pfn)));
return (dma_addr_t)__pfn_to_bus(pfn); return (dma_addr_t)__pfn_to_bus(pfn);
} }
...@@ -21,9 +21,8 @@ static inline unsigned long dma_to_pfn(struct device *dev, dma_addr_t addr) ...@@ -21,9 +21,8 @@ static inline unsigned long dma_to_pfn(struct device *dev, dma_addr_t addr)
{ {
unsigned long pfn = __bus_to_pfn(addr); unsigned long pfn = __bus_to_pfn(addr);
if (dev) if (dev && dev->dma_range_map)
pfn += dev->dma_pfn_offset; pfn = PFN_DOWN(translate_dma_to_phys(dev, PFN_PHYS(pfn)));
return pfn; return pfn;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
*/ */
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/dma-mapping.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/of_address.h> #include <linux/of_address.h>
...@@ -25,8 +26,6 @@ ...@@ -25,8 +26,6 @@
#include "keystone.h" #include "keystone.h"
#ifdef CONFIG_ARM_LPAE #ifdef CONFIG_ARM_LPAE
static unsigned long keystone_dma_pfn_offset __read_mostly;
static int keystone_platform_notifier(struct notifier_block *nb, static int keystone_platform_notifier(struct notifier_block *nb,
unsigned long event, void *data) unsigned long event, void *data)
{ {
...@@ -39,9 +38,12 @@ static int keystone_platform_notifier(struct notifier_block *nb, ...@@ -39,9 +38,12 @@ static int keystone_platform_notifier(struct notifier_block *nb,
return NOTIFY_BAD; return NOTIFY_BAD;
if (!dev->of_node) { if (!dev->of_node) {
dev->dma_pfn_offset = keystone_dma_pfn_offset; int ret = dma_direct_set_offset(dev, KEYSTONE_HIGH_PHYS_START,
dev_err(dev, "set dma_pfn_offset%08lx\n", KEYSTONE_LOW_PHYS_START,
dev->dma_pfn_offset); KEYSTONE_HIGH_PHYS_SIZE);
dev_err(dev, "set dma_offset%08llx%s\n",
KEYSTONE_HIGH_PHYS_START - KEYSTONE_LOW_PHYS_START,
ret ? " failed" : "");
} }
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -54,11 +56,8 @@ static struct notifier_block platform_nb = { ...@@ -54,11 +56,8 @@ static struct notifier_block platform_nb = {
static void __init keystone_init(void) static void __init keystone_init(void)
{ {
#ifdef CONFIG_ARM_LPAE #ifdef CONFIG_ARM_LPAE
if (PHYS_OFFSET >= KEYSTONE_HIGH_PHYS_START) { if (PHYS_OFFSET >= KEYSTONE_HIGH_PHYS_START)
keystone_dma_pfn_offset = PFN_DOWN(KEYSTONE_HIGH_PHYS_START -
KEYSTONE_LOW_PHYS_START);
bus_register_notifier(&platform_bus_type, &platform_nb); bus_register_notifier(&platform_bus_type, &platform_nb);
}
#endif #endif
keystone_pm_runtime_init(); keystone_pm_runtime_init();
} }
......
...@@ -41,6 +41,10 @@ ...@@ -41,6 +41,10 @@
__phys_to_pfn(__dma); \ __phys_to_pfn(__dma); \
}) })
#define __arch_dma_to_virt(dev, addr) ({ (void *) (is_lbus_device(dev) ? \
lbus_to_virt(addr) : \
__phys_to_virt(addr)); })
#define __arch_virt_to_dma(dev, addr) ({ unsigned long __addr = (unsigned long)(addr); \ #define __arch_virt_to_dma(dev, addr) ({ unsigned long __addr = (unsigned long)(addr); \
(dma_addr_t) (is_lbus_device(dev) ? \ (dma_addr_t) (is_lbus_device(dev) ? \
virt_to_lbus(__addr) : \ virt_to_lbus(__addr) : \
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/async.h> #include <linux/async.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/sh_clk.h> #include <linux/sh_clk.h>
...@@ -31,6 +32,8 @@ struct sh7786_pcie_port { ...@@ -31,6 +32,8 @@ struct sh7786_pcie_port {
static struct sh7786_pcie_port *sh7786_pcie_ports; static struct sh7786_pcie_port *sh7786_pcie_ports;
static unsigned int nr_ports; static unsigned int nr_ports;
static unsigned long dma_pfn_offset; static unsigned long dma_pfn_offset;
size_t memsize;
u64 memstart;
static struct sh7786_pcie_hwops { static struct sh7786_pcie_hwops {
int (*core_init)(void); int (*core_init)(void);
...@@ -301,7 +304,6 @@ static int __init pcie_init(struct sh7786_pcie_port *port) ...@@ -301,7 +304,6 @@ static int __init pcie_init(struct sh7786_pcie_port *port)
struct pci_channel *chan = port->hose; struct pci_channel *chan = port->hose;
unsigned int data; unsigned int data;
phys_addr_t memstart, memend; phys_addr_t memstart, memend;
size_t memsize;
int ret, i, win; int ret, i, win;
/* Begin initialization */ /* Begin initialization */
...@@ -368,8 +370,6 @@ static int __init pcie_init(struct sh7786_pcie_port *port) ...@@ -368,8 +370,6 @@ static int __init pcie_init(struct sh7786_pcie_port *port)
memstart = ALIGN_DOWN(memstart, memsize); memstart = ALIGN_DOWN(memstart, memsize);
memsize = roundup_pow_of_two(memend - memstart); memsize = roundup_pow_of_two(memend - memstart);
dma_pfn_offset = memstart >> PAGE_SHIFT;
/* /*
* If there's more than 512MB of memory, we need to roll over to * If there's more than 512MB of memory, we need to roll over to
* LAR1/LAMR1. * LAR1/LAMR1.
...@@ -487,7 +487,8 @@ int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin) ...@@ -487,7 +487,8 @@ int pcibios_map_platform_irq(const struct pci_dev *pdev, u8 slot, u8 pin)
void pcibios_bus_add_device(struct pci_dev *pdev) void pcibios_bus_add_device(struct pci_dev *pdev)
{ {
pdev->dev.dma_pfn_offset = dma_pfn_offset; dma_direct_set_offset(&pdev->dev, __pa(memory_start),
__pa(memory_start) - memstart, memsize);
} }
static int __init sh7786_pcie_core_init(void) static int __init sh7786_pcie_core_init(void)
......
...@@ -133,7 +133,7 @@ static void sta2x11_map_ep(struct pci_dev *pdev) ...@@ -133,7 +133,7 @@ static void sta2x11_map_ep(struct pci_dev *pdev)
struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev); struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
u32 amba_base, max_amba_addr; u32 amba_base, max_amba_addr;
int i; int i, ret;
if (!instance) if (!instance)
return; return;
...@@ -141,7 +141,9 @@ static void sta2x11_map_ep(struct pci_dev *pdev) ...@@ -141,7 +141,9 @@ static void sta2x11_map_ep(struct pci_dev *pdev)
pci_read_config_dword(pdev, AHB_BASE(0), &amba_base); pci_read_config_dword(pdev, AHB_BASE(0), &amba_base);
max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1; max_amba_addr = amba_base + STA2X11_AMBA_SIZE - 1;
dev->dma_pfn_offset = PFN_DOWN(-amba_base); ret = dma_direct_set_offset(dev, 0, amba_base, STA2X11_AMBA_SIZE);
if (ret)
dev_err(dev, "sta2x11: could not set DMA offset\n");
dev->bus_dma_limit = max_amba_addr; dev->bus_dma_limit = max_amba_addr;
pci_set_consistent_dma_mask(pdev, max_amba_addr); pci_set_consistent_dma_mask(pdev, max_amba_addr);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dma-mapping.h>
#define IORT_TYPE_MASK(type) (1 << (type)) #define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP) #define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
...@@ -1184,8 +1185,9 @@ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size) ...@@ -1184,8 +1185,9 @@ void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *dma_size)
*dma_addr = dmaaddr; *dma_addr = dmaaddr;
*dma_size = size; *dma_size = size;
dev->dma_pfn_offset = PFN_DOWN(offset); ret = dma_direct_set_offset(dev, dmaaddr + offset, dmaaddr, size);
dev_dbg(dev, "dma_pfn_offset(%#08llx)\n", offset);
dev_dbg(dev, "dma_offset(%#08llx)%s\n", offset, ret ? " failed!" : "");
} }
static void __init acpi_iort_register_irq(int hwirq, const char *name, static void __init acpi_iort_register_irq(int hwirq, const char *name,
......
...@@ -1792,6 +1792,8 @@ static void device_release(struct kobject *kobj) ...@@ -1792,6 +1792,8 @@ static void device_release(struct kobject *kobj)
*/ */
devres_release_all(dev); devres_release_all(dev);
kfree(dev->dma_range_map);
if (dev->release) if (dev->release)
dev->release(dev); dev->release(dev);
else if (dev->type && dev->type->release) else if (dev->type && dev->type->release)
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_graph.h> #include <linux/of_graph.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reset.h> #include <linux/reset.h>
...@@ -811,8 +812,13 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, ...@@ -811,8 +812,13 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
* because of an old DT, we need to set the DMA offset by hand * because of an old DT, we need to set the DMA offset by hand
* on our device since the RAM mapping is at 0 for the DMA bus, * on our device since the RAM mapping is at 0 for the DMA bus,
* unlike the CPU. * unlike the CPU.
*
* XXX(hch): this has no business in a driver and needs to move
* to the device tree.
*/ */
drm->dev->dma_pfn_offset = PHYS_PFN_OFFSET; ret = dma_direct_set_offset(drm->dev, PHYS_OFFSET, 0, SZ_4G);
if (ret)
return ret;
} }
backend->engine.node = dev->of_node; backend->engine.node = dev->of_node;
......
...@@ -751,7 +751,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg) ...@@ -751,7 +751,7 @@ arm_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg)
if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS) if (cfg->oas > ARM_LPAE_MAX_ADDR_BITS)
return NULL; return NULL;
if (!selftest_running && cfg->iommu_dev->dma_pfn_offset) { if (!selftest_running && cfg->iommu_dev->dma_range_map) {
dev_err(cfg->iommu_dev, "Cannot accommodate DMA offset for IOMMU page tables\n"); dev_err(cfg->iommu_dev, "Cannot accommodate DMA offset for IOMMU page tables\n");
return NULL; return NULL;
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
...@@ -182,8 +183,14 @@ static int sun4i_csi_probe(struct platform_device *pdev) ...@@ -182,8 +183,14 @@ static int sun4i_csi_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
} else { } else {
/*
* XXX(hch): this has no business in a driver and needs to move
* to the device tree.
*/
#ifdef PHYS_PFN_OFFSET #ifdef PHYS_PFN_OFFSET
csi->dev->dma_pfn_offset = PHYS_PFN_OFFSET; ret = dma_direct_set_offset(csi->dev, PHYS_OFFSET, 0, SZ_4G);
if (ret)
return ret;
#endif #endif
} }
......
...@@ -899,8 +899,15 @@ static int sun6i_csi_probe(struct platform_device *pdev) ...@@ -899,8 +899,15 @@ static int sun6i_csi_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
sdev->dev = &pdev->dev; sdev->dev = &pdev->dev;
/* The DMA bus has the memory mapped at 0 */ /*
sdev->dev->dma_pfn_offset = PHYS_OFFSET >> PAGE_SHIFT; * The DMA bus has the memory mapped at 0.
*
* XXX(hch): this has no business in a driver and needs to move
* to the device tree.
*/
ret = dma_direct_set_offset(sdev->dev, PHYS_OFFSET, 0, SZ_4G);
if (ret)
return ret;
ret = sun6i_csi_resource_request(sdev, pdev); ret = sun6i_csi_resource_request(sdev, pdev);
if (ret) if (ret)
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/dma-direct.h> /* for bus_dma_region */
#include "of_private.h" #include "of_private.h"
...@@ -937,33 +938,33 @@ void __iomem *of_io_request_and_map(struct device_node *np, int index, ...@@ -937,33 +938,33 @@ void __iomem *of_io_request_and_map(struct device_node *np, int index,
} }
EXPORT_SYMBOL(of_io_request_and_map); EXPORT_SYMBOL(of_io_request_and_map);
#ifdef CONFIG_HAS_DMA
/** /**
* of_dma_get_range - Get DMA range info * of_dma_get_range - Get DMA range info and put it into a map array
* @np: device node to get DMA range info * @np: device node to get DMA range info
* @dma_addr: pointer to store initial DMA address of DMA range * @map: dma range structure to return
* @paddr: pointer to store initial CPU address of DMA range
* @size: pointer to store size of DMA range
* *
* Look in bottom up direction for the first "dma-ranges" property * Look in bottom up direction for the first "dma-ranges" property
* and parse it. * and parse it. Put the information into a DMA offset map array.
* dma-ranges format: *
* dma-ranges format:
* DMA addr (dma_addr) : naddr cells * DMA addr (dma_addr) : naddr cells
* CPU addr (phys_addr_t) : pna cells * CPU addr (phys_addr_t) : pna cells
* size : nsize cells * size : nsize cells
* *
* It returns -ENODEV if "dma-ranges" property was not found * It returns -ENODEV if "dma-ranges" property was not found for this
* for this device in DT. * device in the DT.
*/ */
int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *size) int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
{ {
struct device_node *node = of_node_get(np); struct device_node *node = of_node_get(np);
const __be32 *ranges = NULL; const __be32 *ranges = NULL;
int len;
int ret = 0;
bool found_dma_ranges = false; bool found_dma_ranges = false;
struct of_range_parser parser; struct of_range_parser parser;
struct of_range range; struct of_range range;
u64 dma_start = U64_MAX, dma_end = 0, dma_offset = 0; struct bus_dma_region *r;
int len, num_ranges = 0;
int ret = 0;
while (node) { while (node) {
ranges = of_get_property(node, "dma-ranges", &len); ranges = of_get_property(node, "dma-ranges", &len);
...@@ -989,49 +990,39 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz ...@@ -989,49 +990,39 @@ int of_dma_get_range(struct device_node *np, u64 *dma_addr, u64 *paddr, u64 *siz
} }
of_dma_range_parser_init(&parser, node); of_dma_range_parser_init(&parser, node);
for_each_of_range(&parser, &range)
num_ranges++;
r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL);
if (!r) {
ret = -ENOMEM;
goto out;
}
/*
* Record all info in the generic DMA ranges array for struct device.
*/
*map = r;
of_dma_range_parser_init(&parser, node);
for_each_of_range(&parser, &range) { for_each_of_range(&parser, &range) {
pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n", pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
range.bus_addr, range.cpu_addr, range.size); range.bus_addr, range.cpu_addr, range.size);
if (dma_offset && range.cpu_addr - range.bus_addr != dma_offset) {
pr_warn("Can't handle multiple dma-ranges with different offsets on node(%pOF)\n", node);
/* Don't error out as we'd break some existing DTs */
continue;
}
if (range.cpu_addr == OF_BAD_ADDR) { if (range.cpu_addr == OF_BAD_ADDR) {
pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n", pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
range.bus_addr, node); range.bus_addr, node);
continue; continue;
} }
dma_offset = range.cpu_addr - range.bus_addr; r->cpu_start = range.cpu_addr;
r->dma_start = range.bus_addr;
/* Take lower and upper limits */ r->size = range.size;
if (range.bus_addr < dma_start) r->offset = range.cpu_addr - range.bus_addr;
dma_start = range.bus_addr; r++;
if (range.bus_addr + range.size > dma_end)
dma_end = range.bus_addr + range.size;
}
if (dma_start >= dma_end) {
ret = -EINVAL;
pr_debug("Invalid DMA ranges configuration on node(%pOF)\n",
node);
goto out;
} }
*dma_addr = dma_start;
*size = dma_end - dma_start;
*paddr = dma_start + dma_offset;
pr_debug("final: dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
*dma_addr, *paddr, *size);
out: out:
of_node_put(node); of_node_put(node);
return ret; return ret;
} }
#endif /* CONFIG_HAS_DMA */
/** /**
* of_dma_is_coherent - Check if device is coherent * of_dma_is_coherent - Check if device is coherent
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_iommu.h> #include <linux/of_iommu.h>
#include <linux/dma-mapping.h> #include <linux/dma-direct.h> /* for bus_dma_region */
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
...@@ -90,14 +90,14 @@ int of_device_add(struct platform_device *ofdev) ...@@ -90,14 +90,14 @@ int of_device_add(struct platform_device *ofdev)
int of_dma_configure_id(struct device *dev, struct device_node *np, int of_dma_configure_id(struct device *dev, struct device_node *np,
bool force_dma, const u32 *id) bool force_dma, const u32 *id)
{ {
u64 dma_addr, paddr, size = 0;
int ret;
bool coherent;
unsigned long offset;
const struct iommu_ops *iommu; const struct iommu_ops *iommu;
u64 mask, end; const struct bus_dma_region *map = NULL;
dma_addr_t dma_start = 0;
u64 mask, end, size = 0;
bool coherent;
int ret;
ret = of_dma_get_range(np, &dma_addr, &paddr, &size); ret = of_dma_get_range(np, &map);
if (ret < 0) { if (ret < 0) {
/* /*
* For legacy reasons, we have to assume some devices need * For legacy reasons, we have to assume some devices need
...@@ -106,26 +106,35 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, ...@@ -106,26 +106,35 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
*/ */
if (!force_dma) if (!force_dma)
return ret == -ENODEV ? 0 : ret; return ret == -ENODEV ? 0 : ret;
dma_addr = offset = 0;
} else { } else {
offset = PFN_DOWN(paddr - dma_addr); const struct bus_dma_region *r = map;
dma_addr_t dma_end = 0;
/* Determine the overall bounds of all DMA regions */
for (dma_start = ~(dma_addr_t)0; r->size; r++) {
/* Take lower and upper limits */
if (r->dma_start < dma_start)
dma_start = r->dma_start;
if (r->dma_start + r->size > dma_end)
dma_end = r->dma_start + r->size;
}
size = dma_end - dma_start;
/* /*
* Add a work around to treat the size as mask + 1 in case * Add a work around to treat the size as mask + 1 in case
* it is defined in DT as a mask. * it is defined in DT as a mask.
*/ */
if (size & 1) { if (size & 1) {
dev_warn(dev, "Invalid size 0x%llx for dma-range\n", dev_warn(dev, "Invalid size 0x%llx for dma-range(s)\n",
size); size);
size = size + 1; size = size + 1;
} }
if (!size) { if (!size) {
dev_err(dev, "Adjusted size 0x%llx invalid\n", size); dev_err(dev, "Adjusted size 0x%llx invalid\n", size);
kfree(map);
return -EINVAL; return -EINVAL;
} }
dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset);
} }
/* /*
...@@ -144,13 +153,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, ...@@ -144,13 +153,11 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
else if (!size) else if (!size)
size = 1ULL << 32; size = 1ULL << 32;
dev->dma_pfn_offset = offset;
/* /*
* Limit coherent and dma mask based on size and default mask * Limit coherent and dma mask based on size and default mask
* set by the driver. * set by the driver.
*/ */
end = dma_addr + size - 1; end = dma_start + size - 1;
mask = DMA_BIT_MASK(ilog2(end) + 1); mask = DMA_BIT_MASK(ilog2(end) + 1);
dev->coherent_dma_mask &= mask; dev->coherent_dma_mask &= mask;
*dev->dma_mask &= mask; *dev->dma_mask &= mask;
...@@ -163,14 +170,17 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, ...@@ -163,14 +170,17 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
coherent ? " " : " not "); coherent ? " " : " not ");
iommu = of_iommu_configure(dev, np, id); iommu = of_iommu_configure(dev, np, id);
if (PTR_ERR(iommu) == -EPROBE_DEFER) if (PTR_ERR(iommu) == -EPROBE_DEFER) {
kfree(map);
return -EPROBE_DEFER; return -EPROBE_DEFER;
}
dev_dbg(dev, "device is%sbehind an iommu\n", dev_dbg(dev, "device is%sbehind an iommu\n",
iommu ? " " : " not "); iommu ? " " : " not ");
arch_setup_dma_ops(dev, dma_addr, size, iommu, coherent); arch_setup_dma_ops(dev, dma_start, size, iommu, coherent);
dev->dma_range_map = map;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_dma_configure_id); EXPORT_SYMBOL_GPL(of_dma_configure_id);
......
...@@ -157,12 +157,13 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np, ...@@ -157,12 +157,13 @@ extern void __of_sysfs_remove_bin_file(struct device_node *np,
extern int of_bus_n_addr_cells(struct device_node *np); extern int of_bus_n_addr_cells(struct device_node *np);
extern int of_bus_n_size_cells(struct device_node *np); extern int of_bus_n_size_cells(struct device_node *np);
#ifdef CONFIG_OF_ADDRESS struct bus_dma_region;
extern int of_dma_get_range(struct device_node *np, u64 *dma_addr, #if defined(CONFIG_OF_ADDRESS) && defined(CONFIG_HAS_DMA)
u64 *paddr, u64 *size); int of_dma_get_range(struct device_node *np,
const struct bus_dma_region **map);
#else #else
static inline int of_dma_get_range(struct device_node *np, u64 *dma_addr, static inline int of_dma_get_range(struct device_node *np,
u64 *paddr, u64 *size) const struct bus_dma_region **map)
{ {
return -ENODEV; return -ENODEV;
} }
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/memblock.h> #include <linux/memblock.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/dma-direct.h> /* to test phys_to_dma/dma_to_phys */
#include <linux/err.h> #include <linux/err.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/hashtable.h> #include <linux/hashtable.h>
...@@ -869,10 +870,11 @@ static void __init of_unittest_changeset(void) ...@@ -869,10 +870,11 @@ static void __init of_unittest_changeset(void)
} }
static void __init of_unittest_dma_ranges_one(const char *path, static void __init of_unittest_dma_ranges_one(const char *path,
u64 expect_dma_addr, u64 expect_paddr, u64 expect_size) u64 expect_dma_addr, u64 expect_paddr)
{ {
#ifdef CONFIG_HAS_DMA
struct device_node *np; struct device_node *np;
u64 dma_addr, paddr, size; const struct bus_dma_region *map = NULL;
int rc; int rc;
np = of_find_node_by_path(path); np = of_find_node_by_path(path);
...@@ -881,28 +883,40 @@ static void __init of_unittest_dma_ranges_one(const char *path, ...@@ -881,28 +883,40 @@ static void __init of_unittest_dma_ranges_one(const char *path,
return; return;
} }
rc = of_dma_get_range(np, &dma_addr, &paddr, &size); rc = of_dma_get_range(np, &map);
unittest(!rc, "of_dma_get_range failed on node %pOF rc=%i\n", np, rc); unittest(!rc, "of_dma_get_range failed on node %pOF rc=%i\n", np, rc);
if (!rc) { if (!rc) {
unittest(size == expect_size, phys_addr_t paddr;
"of_dma_get_range wrong size on node %pOF size=%llx\n", np, size); dma_addr_t dma_addr;
struct device dev_bogus;
dev_bogus.dma_range_map = map;
paddr = dma_to_phys(&dev_bogus, expect_dma_addr);
dma_addr = phys_to_dma(&dev_bogus, expect_paddr);
unittest(paddr == expect_paddr, unittest(paddr == expect_paddr,
"of_dma_get_range wrong phys addr (%llx) on node %pOF", paddr, np); "of_dma_get_range: wrong phys addr %pap (expecting %llx) on node %pOF\n",
&paddr, expect_paddr, np);
unittest(dma_addr == expect_dma_addr, unittest(dma_addr == expect_dma_addr,
"of_dma_get_range wrong DMA addr (%llx) on node %pOF", dma_addr, np); "of_dma_get_range: wrong DMA addr %pad (expecting %llx) on node %pOF\n",
&dma_addr, expect_dma_addr, np);
kfree(map);
} }
of_node_put(np); of_node_put(np);
#endif
} }
static void __init of_unittest_parse_dma_ranges(void) static void __init of_unittest_parse_dma_ranges(void)
{ {
of_unittest_dma_ranges_one("/testcase-data/address-tests/device@70000000", of_unittest_dma_ranges_one("/testcase-data/address-tests/device@70000000",
0x0, 0x20000000, 0x40000000); 0x0, 0x20000000);
of_unittest_dma_ranges_one("/testcase-data/address-tests/bus@80000000/device@1000", of_unittest_dma_ranges_one("/testcase-data/address-tests/bus@80000000/device@1000",
0x100000000, 0x20000000, 0x2000000000); 0x100000000, 0x20000000);
of_unittest_dma_ranges_one("/testcase-data/address-tests/pci@90000000", of_unittest_dma_ranges_one("/testcase-data/address-tests/pci@90000000",
0x80000000, 0x20000000, 0x10000000); 0x80000000, 0x20000000);
} }
static void __init of_unittest_pci_dma_ranges(void) static void __init of_unittest_pci_dma_ranges(void)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dma-direct.h> /* XXX: pokes into bus_dma_range */
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
...@@ -458,6 +459,25 @@ static void rproc_rvdev_release(struct device *dev) ...@@ -458,6 +459,25 @@ static void rproc_rvdev_release(struct device *dev)
kfree(rvdev); kfree(rvdev);
} }
static int copy_dma_range_map(struct device *to, struct device *from)
{
const struct bus_dma_region *map = from->dma_range_map, *new_map, *r;
int num_ranges = 0;
if (!map)
return 0;
for (r = map; r->size; r++)
num_ranges++;
new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)),
GFP_KERNEL);
if (!new_map)
return -ENOMEM;
to->dma_range_map = new_map;
return 0;
}
/** /**
* rproc_handle_vdev() - handle a vdev fw resource * rproc_handle_vdev() - handle a vdev fw resource
* @rproc: the remote processor * @rproc: the remote processor
...@@ -529,7 +549,9 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc, ...@@ -529,7 +549,9 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
/* Initialise vdev subdevice */ /* Initialise vdev subdevice */
snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index); snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index);
rvdev->dev.parent = &rproc->dev; rvdev->dev.parent = &rproc->dev;
rvdev->dev.dma_pfn_offset = rproc->dev.parent->dma_pfn_offset; ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent);
if (ret)
return ret;
rvdev->dev.release = rproc_rvdev_release; rvdev->dev.release = rproc_rvdev_release;
dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name); dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name);
dev_set_drvdata(&rvdev->dev, rvdev); dev_set_drvdata(&rvdev->dev, rvdev);
......
...@@ -227,11 +227,17 @@ int cedrus_hw_probe(struct cedrus_dev *dev) ...@@ -227,11 +227,17 @@ int cedrus_hw_probe(struct cedrus_dev *dev)
* the RAM offset to the physcal addresses. * the RAM offset to the physcal addresses.
* *
* This information will eventually be obtained from device-tree. * This information will eventually be obtained from device-tree.
*
* XXX(hch): this has no business in a driver and needs to move
* to the device tree.
*/ */
#ifdef PHYS_PFN_OFFSET #ifdef PHYS_PFN_OFFSET
if (!(variant->quirks & CEDRUS_QUIRK_NO_DMA_OFFSET)) if (!(variant->quirks & CEDRUS_QUIRK_NO_DMA_OFFSET)) {
dev->dev->dma_pfn_offset = PHYS_PFN_OFFSET; ret = dma_direct_set_offset(dev->dev, PHYS_OFFSET, 0, SZ_4G);
if (ret)
return ret;
}
#endif #endif
ret = of_reserved_mem_device_init(dev->dev); ret = of_reserved_mem_device_init(dev->dev);
......
...@@ -466,7 +466,7 @@ struct dev_links_info { ...@@ -466,7 +466,7 @@ struct dev_links_info {
* such descriptors. * such descriptors.
* @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller * @bus_dma_limit: Limit of an upstream bridge or bus which imposes a smaller
* DMA limit than the device itself supports. * DMA limit than the device itself supports.
* @dma_pfn_offset: offset of DMA memory range relatively of RAM * @dma_range_map: map for DMA memory ranges relative to that of RAM
* @dma_parms: A low level driver may set these to teach IOMMU code about * @dma_parms: A low level driver may set these to teach IOMMU code about
* segment limitations. * segment limitations.
* @dma_pools: Dma pools (if dma'ble device). * @dma_pools: Dma pools (if dma'ble device).
...@@ -561,7 +561,7 @@ struct device { ...@@ -561,7 +561,7 @@ struct device {
64 bit addresses for consistent 64 bit addresses for consistent
allocations such descriptors. */ allocations such descriptors. */
u64 bus_dma_limit; /* upstream dma constraint */ u64 bus_dma_limit; /* upstream dma constraint */
unsigned long dma_pfn_offset; const struct bus_dma_region *dma_range_map;
struct device_dma_parameters *dma_parms; struct device_dma_parameters *dma_parms;
......
...@@ -14,6 +14,41 @@ ...@@ -14,6 +14,41 @@
extern unsigned int zone_dma_bits; extern unsigned int zone_dma_bits;
/*
* Record the mapping of CPU physical to DMA addresses for a given region.
*/
struct bus_dma_region {
phys_addr_t cpu_start;
dma_addr_t dma_start;
u64 size;
u64 offset;
};
static inline dma_addr_t translate_phys_to_dma(struct device *dev,
phys_addr_t paddr)
{
const struct bus_dma_region *m;
for (m = dev->dma_range_map; m->size; m++)
if (paddr >= m->cpu_start && paddr - m->cpu_start < m->size)
return (dma_addr_t)paddr - m->offset;
/* make sure dma_capable fails when no translation is available */
return DMA_MAPPING_ERROR;
}
static inline phys_addr_t translate_dma_to_phys(struct device *dev,
dma_addr_t dma_addr)
{
const struct bus_dma_region *m;
for (m = dev->dma_range_map; m->size; m++)
if (dma_addr >= m->dma_start && dma_addr - m->dma_start < m->size)
return (phys_addr_t)dma_addr + m->offset;
return (phys_addr_t)-1;
}
#ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA #ifdef CONFIG_ARCH_HAS_PHYS_TO_DMA
#include <asm/dma-direct.h> #include <asm/dma-direct.h>
#ifndef phys_to_dma_unencrypted #ifndef phys_to_dma_unencrypted
...@@ -23,9 +58,9 @@ extern unsigned int zone_dma_bits; ...@@ -23,9 +58,9 @@ extern unsigned int zone_dma_bits;
static inline dma_addr_t phys_to_dma_unencrypted(struct device *dev, static inline dma_addr_t phys_to_dma_unencrypted(struct device *dev,
phys_addr_t paddr) phys_addr_t paddr)
{ {
dma_addr_t dev_addr = (dma_addr_t)paddr; if (dev->dma_range_map)
return translate_phys_to_dma(dev, paddr);
return dev_addr - ((dma_addr_t)dev->dma_pfn_offset << PAGE_SHIFT); return paddr;
} }
/* /*
...@@ -39,10 +74,14 @@ static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) ...@@ -39,10 +74,14 @@ static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
return __sme_set(phys_to_dma_unencrypted(dev, paddr)); return __sme_set(phys_to_dma_unencrypted(dev, paddr));
} }
static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr) static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dma_addr)
{ {
phys_addr_t paddr = (phys_addr_t)dev_addr + phys_addr_t paddr;
((phys_addr_t)dev->dma_pfn_offset << PAGE_SHIFT);
if (dev->dma_range_map)
paddr = translate_dma_to_phys(dev, dma_addr);
else
paddr = dma_addr;
return __sme_clr(paddr); return __sme_clr(paddr);
} }
...@@ -62,6 +101,8 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size, ...@@ -62,6 +101,8 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size,
{ {
dma_addr_t end = addr + size - 1; dma_addr_t end = addr + size - 1;
if (addr == DMA_MAPPING_ERROR)
return false;
if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) && if (is_ram && !IS_ENABLED(CONFIG_ARCH_DMA_ADDR_T_64BIT) &&
min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn))) min(addr, end) < phys_to_dma(dev, PFN_PHYS(min_low_pfn)))
return false; return false;
......
...@@ -730,4 +730,11 @@ static inline int dma_mmap_wc(struct device *dev, ...@@ -730,4 +730,11 @@ static inline int dma_mmap_wc(struct device *dev,
#define dma_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) #define dma_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0)
#endif #endif
#endif /*
* Legacy interface to set up the dma offset map. Drivers really should not
* actually use it, but we have a few legacy cases left.
*/
int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
dma_addr_t dma_start, u64 size);
#endif /* _LINUX_DMA_MAPPING_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/dma-mapping.h> #include <linux/dma-direct.h>
struct dma_coherent_mem { struct dma_coherent_mem {
void *virt_base; void *virt_base;
...@@ -32,9 +32,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev, ...@@ -32,9 +32,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev,
struct dma_coherent_mem * mem) struct dma_coherent_mem * mem)
{ {
if (mem->use_dev_dma_pfn_offset) if (mem->use_dev_dma_pfn_offset)
return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT; return phys_to_dma(dev, PFN_PHYS(mem->pfn_base));
else return mem->device_base;
return mem->device_base;
} }
static int dma_init_coherent_memory(phys_addr_t phys_addr, static int dma_init_coherent_memory(phys_addr_t phys_addr,
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/pfn.h> #include <linux/pfn.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/set_memory.h> #include <linux/set_memory.h>
#include <linux/slab.h>
/* /*
* Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it * Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
...@@ -66,8 +67,12 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask, ...@@ -66,8 +67,12 @@ static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size) static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
{ {
return phys_to_dma_direct(dev, phys) + size - 1 <= dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
if (dma_addr == DMA_MAPPING_ERROR)
return false;
return dma_addr + size - 1 <=
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
} }
/* /*
...@@ -461,3 +466,45 @@ bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr) ...@@ -461,3 +466,45 @@ bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
return !dev_is_dma_coherent(dev) || return !dev_is_dma_coherent(dev) ||
is_swiotlb_buffer(dma_to_phys(dev, dma_addr)); is_swiotlb_buffer(dma_to_phys(dev, dma_addr));
} }
/**
* dma_direct_set_offset - Assign scalar offset for a single DMA range.
* @dev: device pointer; needed to "own" the alloced memory.
* @cpu_start: beginning of memory region covered by this offset.
* @dma_start: beginning of DMA/PCI region covered by this offset.
* @size: size of the region.
*
* This is for the simple case of a uniform offset which cannot
* be discovered by "dma-ranges".
*
* It returns -ENOMEM if out of memory, -EINVAL if a map
* already exists, 0 otherwise.
*
* Note: any call to this from a driver is a bug. The mapping needs
* to be described by the device tree or other firmware interfaces.
*/
int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
dma_addr_t dma_start, u64 size)
{
struct bus_dma_region *map;
u64 offset = (u64)cpu_start - (u64)dma_start;
if (dev->dma_range_map) {
dev_err(dev, "attempt to add DMA range to existing map\n");
return -EINVAL;
}
if (!offset)
return 0;
map = kcalloc(2, sizeof(*map), GFP_KERNEL);
if (!map)
return -ENOMEM;
map[0].cpu_start = cpu_start;
map[0].dma_start = dma_start;
map[0].offset = offset;
map[0].size = size;
dev->dma_range_map = map;
return 0;
}
EXPORT_SYMBOL_GPL(dma_direct_set_offset);
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