Merge branches 'iommu/fixes', 'dma-debug', 'x86/amd', 'x86/vt-d', 'arm/tegra' and 'arm/omap' into next
This commit is contained in:
commit
9c6ecf6a3a
42 changed files with 765 additions and 594 deletions
|
@ -468,11 +468,46 @@ To map a single region, you do:
|
|||
size_t size = buffer->len;
|
||||
|
||||
dma_handle = dma_map_single(dev, addr, size, direction);
|
||||
if (dma_mapping_error(dma_handle)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling;
|
||||
}
|
||||
|
||||
and to unmap it:
|
||||
|
||||
dma_unmap_single(dev, dma_handle, size, direction);
|
||||
|
||||
You should call dma_mapping_error() as dma_map_single() could fail and return
|
||||
error. Not all dma implementations support dma_mapping_error() interface.
|
||||
However, it is a good practice to call dma_mapping_error() interface, which
|
||||
will invoke the generic mapping error check interface. Doing so will ensure
|
||||
that the mapping code will work correctly on all dma implementations without
|
||||
any dependency on the specifics of the underlying implementation. Using the
|
||||
returned address without checking for errors could result in failures ranging
|
||||
from panics to silent data corruption. Couple of example of incorrect ways to
|
||||
check for errors that make assumptions about the underlying dma implementation
|
||||
are as follows and these are applicable to dma_map_page() as well.
|
||||
|
||||
Incorrect example 1:
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
dma_handle = dma_map_single(dev, addr, size, direction);
|
||||
if ((dma_handle & 0xffff != 0) || (dma_handle >= 0x1000000)) {
|
||||
goto map_error;
|
||||
}
|
||||
|
||||
Incorrect example 2:
|
||||
dma_addr_t dma_handle;
|
||||
|
||||
dma_handle = dma_map_single(dev, addr, size, direction);
|
||||
if (dma_handle == DMA_ERROR_CODE) {
|
||||
goto map_error;
|
||||
}
|
||||
|
||||
You should call dma_unmap_single when the DMA activity is finished, e.g.
|
||||
from the interrupt which told you that the DMA transfer is done.
|
||||
|
||||
|
@ -489,6 +524,14 @@ Specifically:
|
|||
size_t size = buffer->len;
|
||||
|
||||
dma_handle = dma_map_page(dev, page, offset, size, direction);
|
||||
if (dma_mapping_error(dma_handle)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
|
@ -496,6 +539,12 @@ Specifically:
|
|||
|
||||
Here, "offset" means byte offset within the given page.
|
||||
|
||||
You should call dma_mapping_error() as dma_map_page() could fail and return
|
||||
error as outlined under the dma_map_single() discussion.
|
||||
|
||||
You should call dma_unmap_page when the DMA activity is finished, e.g.
|
||||
from the interrupt which told you that the DMA transfer is done.
|
||||
|
||||
With scatterlists, you map a region gathered from several regions by:
|
||||
|
||||
int i, count = dma_map_sg(dev, sglist, nents, direction);
|
||||
|
@ -578,6 +627,14 @@ to use the dma_sync_*() interfaces.
|
|||
dma_addr_t mapping;
|
||||
|
||||
mapping = dma_map_single(cp->dev, buffer, len, DMA_FROM_DEVICE);
|
||||
if (dma_mapping_error(dma_handle)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling;
|
||||
}
|
||||
|
||||
cp->rx_buf = buffer;
|
||||
cp->rx_len = len;
|
||||
|
@ -658,6 +715,75 @@ failure can be determined by:
|
|||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling;
|
||||
}
|
||||
|
||||
- unmap pages that are already mapped, when mapping error occurs in the middle
|
||||
of a multiple page mapping attempt. These example are applicable to
|
||||
dma_map_page() as well.
|
||||
|
||||
Example 1:
|
||||
dma_addr_t dma_handle1;
|
||||
dma_addr_t dma_handle2;
|
||||
|
||||
dma_handle1 = dma_map_single(dev, addr, size, direction);
|
||||
if (dma_mapping_error(dev, dma_handle1)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling1;
|
||||
}
|
||||
dma_handle2 = dma_map_single(dev, addr, size, direction);
|
||||
if (dma_mapping_error(dev, dma_handle2)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling2;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
map_error_handling2:
|
||||
dma_unmap_single(dma_handle1);
|
||||
map_error_handling1:
|
||||
|
||||
Example 2: (if buffers are allocated a loop, unmap all mapped buffers when
|
||||
mapping error is detected in the middle)
|
||||
|
||||
dma_addr_t dma_addr;
|
||||
dma_addr_t array[DMA_BUFFERS];
|
||||
int save_index = 0;
|
||||
|
||||
for (i = 0; i < DMA_BUFFERS; i++) {
|
||||
|
||||
...
|
||||
|
||||
dma_addr = dma_map_single(dev, addr, size, direction);
|
||||
if (dma_mapping_error(dev, dma_addr)) {
|
||||
/*
|
||||
* reduce current DMA mapping usage,
|
||||
* delay and try again later or
|
||||
* reset driver.
|
||||
*/
|
||||
goto map_error_handling;
|
||||
}
|
||||
array[i].dma_addr = dma_addr;
|
||||
save_index++;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
map_error_handling:
|
||||
|
||||
for (i = 0; i < save_index; i++) {
|
||||
|
||||
...
|
||||
|
||||
dma_unmap_single(array[i].dma_addr);
|
||||
}
|
||||
|
||||
Networking drivers must call dev_kfree_skb to free the socket buffer
|
||||
|
|
|
@ -678,3 +678,15 @@ out of dma_debug_entries. These entries are preallocated at boot. The number
|
|||
of preallocated entries is defined per architecture. If it is too low for you
|
||||
boot with 'dma_debug_entries=<your_desired_number>' to overwrite the
|
||||
architectural default.
|
||||
|
||||
void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr);
|
||||
|
||||
dma-debug interface debug_dma_mapping_error() to debug drivers that fail
|
||||
to check dma mapping errors on addresses returned by dma_map_single() and
|
||||
dma_map_page() interfaces. This interface clears a flag set by
|
||||
debug_dma_map_page() to indicate that dma_mapping_error() has been called by
|
||||
the driver. When driver does unmap, debug_dma_unmap() checks the flag and if
|
||||
this flag is still set, prints warning message that includes call trace that
|
||||
leads up to the unmap. This interface can be called from dma_mapping_error()
|
||||
routines to enable dma mapping error check debugging.
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ static inline dma_addr_t virt_to_dma(struct device *dev, void *addr)
|
|||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -184,8 +184,6 @@ obj-$(CONFIG_HW_PERF_EVENTS) += pmu.o
|
|||
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o
|
||||
mailbox_mach-objs := mailbox.o
|
||||
|
||||
obj-$(CONFIG_OMAP_IOMMU) += iommu2.o
|
||||
|
||||
iommu-$(CONFIG_OMAP_IOMMU) := omap-iommu.o
|
||||
obj-y += $(iommu-m) $(iommu-y)
|
||||
|
||||
|
|
|
@ -1316,16 +1316,6 @@ static struct clk dmic_fck = {
|
|||
.clkdm_name = "abe_clkdm",
|
||||
};
|
||||
|
||||
static struct clk dsp_fck = {
|
||||
.name = "dsp_fck",
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.enable_reg = OMAP4430_CM_TESLA_TESLA_CLKCTRL,
|
||||
.enable_bit = OMAP4430_MODULEMODE_HWCTRL,
|
||||
.clkdm_name = "tesla_clkdm",
|
||||
.parent = &dpll_iva_m4x2_ck,
|
||||
.recalc = &followparent_recalc,
|
||||
};
|
||||
|
||||
static struct clk dss_sys_clk = {
|
||||
.name = "dss_sys_clk",
|
||||
.ops = &clkops_omap2_dflt,
|
||||
|
@ -1696,16 +1686,6 @@ static struct clk i2c4_fck = {
|
|||
.recalc = &followparent_recalc,
|
||||
};
|
||||
|
||||
static struct clk ipu_fck = {
|
||||
.name = "ipu_fck",
|
||||
.ops = &clkops_omap2_dflt,
|
||||
.enable_reg = OMAP4430_CM_DUCATI_DUCATI_CLKCTRL,
|
||||
.enable_bit = OMAP4430_MODULEMODE_HWCTRL,
|
||||
.clkdm_name = "ducati_clkdm",
|
||||
.parent = &ducati_clk_mux_ck,
|
||||
.recalc = &followparent_recalc,
|
||||
};
|
||||
|
||||
static struct clk iss_ctrlclk = {
|
||||
.name = "iss_ctrlclk",
|
||||
.ops = &clkops_omap2_dflt,
|
||||
|
@ -3151,7 +3131,6 @@ static struct omap_clk omap44xx_clks[] = {
|
|||
CLK(NULL, "div_ts_ck", &div_ts_ck, CK_446X),
|
||||
CLK(NULL, "dmic_sync_mux_ck", &dmic_sync_mux_ck, CK_443X),
|
||||
CLK(NULL, "dmic_fck", &dmic_fck, CK_443X),
|
||||
CLK(NULL, "dsp_fck", &dsp_fck, CK_443X),
|
||||
CLK(NULL, "dss_sys_clk", &dss_sys_clk, CK_443X),
|
||||
CLK(NULL, "dss_tv_clk", &dss_tv_clk, CK_443X),
|
||||
CLK(NULL, "dss_48mhz_clk", &dss_48mhz_clk, CK_443X),
|
||||
|
@ -3183,7 +3162,6 @@ static struct omap_clk omap44xx_clks[] = {
|
|||
CLK(NULL, "i2c2_fck", &i2c2_fck, CK_443X),
|
||||
CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X),
|
||||
CLK(NULL, "i2c4_fck", &i2c4_fck, CK_443X),
|
||||
CLK(NULL, "ipu_fck", &ipu_fck, CK_443X),
|
||||
CLK(NULL, "iss_ctrlclk", &iss_ctrlclk, CK_443X),
|
||||
CLK(NULL, "iss_fck", &iss_fck, CK_443X),
|
||||
CLK(NULL, "iva_fck", &iva_fck, CK_443X),
|
||||
|
|
|
@ -127,7 +127,7 @@ static struct platform_device omap2cam_device = {
|
|||
|
||||
#if defined(CONFIG_IOMMU_API)
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
static struct resource omap3isp_resources[] = {
|
||||
{
|
||||
|
@ -214,7 +214,7 @@ static struct platform_device omap3isp_device = {
|
|||
};
|
||||
|
||||
static struct omap_iommu_arch_data omap3_isp_iommu = {
|
||||
.name = "isp",
|
||||
.name = "mmu_isp",
|
||||
};
|
||||
|
||||
int omap3_init_camera(struct isp_platform_data *pdata)
|
||||
|
|
|
@ -12,153 +12,60 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
#include <plat/omap_hwmod.h>
|
||||
#include <plat/omap_device.h>
|
||||
|
||||
#include "soc.h"
|
||||
#include "common.h"
|
||||
static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct iommu_platform_data *pdata;
|
||||
struct omap_mmu_dev_attr *a = (struct omap_mmu_dev_attr *)oh->dev_attr;
|
||||
static int i;
|
||||
|
||||
struct iommu_device {
|
||||
resource_size_t base;
|
||||
int irq;
|
||||
struct iommu_platform_data pdata;
|
||||
struct resource res[2];
|
||||
};
|
||||
static struct iommu_device *devices;
|
||||
static int num_iommu_devices;
|
||||
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP3
|
||||
static struct iommu_device omap3_devices[] = {
|
||||
{
|
||||
.base = 0x480bd400,
|
||||
.irq = 24 + OMAP_INTC_START,
|
||||
.pdata = {
|
||||
.name = "isp",
|
||||
.nr_tlb_entries = 8,
|
||||
.clk_name = "cam_ick",
|
||||
.da_start = 0x0,
|
||||
.da_end = 0xFFFFF000,
|
||||
},
|
||||
},
|
||||
#if defined(CONFIG_OMAP_IOMMU_IVA2)
|
||||
{
|
||||
.base = 0x5d000000,
|
||||
.irq = 28 + OMAP_INTC_START,
|
||||
.pdata = {
|
||||
.name = "iva2",
|
||||
.nr_tlb_entries = 32,
|
||||
.clk_name = "iva2_ck",
|
||||
.da_start = 0x11000000,
|
||||
.da_end = 0xFFFFF000,
|
||||
},
|
||||
},
|
||||
#endif
|
||||
};
|
||||
#define NR_OMAP3_IOMMU_DEVICES ARRAY_SIZE(omap3_devices)
|
||||
static struct platform_device *omap3_iommu_pdev[NR_OMAP3_IOMMU_DEVICES];
|
||||
#else
|
||||
#define omap3_devices NULL
|
||||
#define NR_OMAP3_IOMMU_DEVICES 0
|
||||
#define omap3_iommu_pdev NULL
|
||||
#endif
|
||||
pdata->name = oh->name;
|
||||
pdata->nr_tlb_entries = a->nr_tlb_entries;
|
||||
pdata->da_start = a->da_start;
|
||||
pdata->da_end = a->da_end;
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP4
|
||||
static struct iommu_device omap4_devices[] = {
|
||||
{
|
||||
.base = OMAP4_MMU1_BASE,
|
||||
.irq = 100 + OMAP44XX_IRQ_GIC_START,
|
||||
.pdata = {
|
||||
.name = "ducati",
|
||||
.nr_tlb_entries = 32,
|
||||
.clk_name = "ipu_fck",
|
||||
.da_start = 0x0,
|
||||
.da_end = 0xFFFFF000,
|
||||
},
|
||||
},
|
||||
{
|
||||
.base = OMAP4_MMU2_BASE,
|
||||
.irq = 28 + OMAP44XX_IRQ_GIC_START,
|
||||
.pdata = {
|
||||
.name = "tesla",
|
||||
.nr_tlb_entries = 32,
|
||||
.clk_name = "dsp_fck",
|
||||
.da_start = 0x0,
|
||||
.da_end = 0xFFFFF000,
|
||||
},
|
||||
},
|
||||
};
|
||||
#define NR_OMAP4_IOMMU_DEVICES ARRAY_SIZE(omap4_devices)
|
||||
static struct platform_device *omap4_iommu_pdev[NR_OMAP4_IOMMU_DEVICES];
|
||||
#else
|
||||
#define omap4_devices NULL
|
||||
#define NR_OMAP4_IOMMU_DEVICES 0
|
||||
#define omap4_iommu_pdev NULL
|
||||
#endif
|
||||
if (oh->rst_lines_cnt == 1) {
|
||||
pdata->reset_name = oh->rst_lines->name;
|
||||
pdata->assert_reset = omap_device_assert_hardreset;
|
||||
pdata->deassert_reset = omap_device_deassert_hardreset;
|
||||
}
|
||||
|
||||
static struct platform_device **omap_iommu_pdev;
|
||||
pdev = omap_device_build("omap-iommu", i, oh, pdata, sizeof(*pdata),
|
||||
NULL, 0, 0);
|
||||
|
||||
kfree(pdata);
|
||||
|
||||
if (IS_ERR(pdev)) {
|
||||
pr_err("%s: device build err: %ld\n", __func__, PTR_ERR(pdev));
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init omap_iommu_init(void)
|
||||
{
|
||||
int i, err;
|
||||
struct resource res[] = {
|
||||
{ .flags = IORESOURCE_MEM },
|
||||
{ .flags = IORESOURCE_IRQ },
|
||||
};
|
||||
|
||||
if (cpu_is_omap34xx()) {
|
||||
devices = omap3_devices;
|
||||
omap_iommu_pdev = omap3_iommu_pdev;
|
||||
num_iommu_devices = NR_OMAP3_IOMMU_DEVICES;
|
||||
} else if (cpu_is_omap44xx()) {
|
||||
devices = omap4_devices;
|
||||
omap_iommu_pdev = omap4_iommu_pdev;
|
||||
num_iommu_devices = NR_OMAP4_IOMMU_DEVICES;
|
||||
} else
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < num_iommu_devices; i++) {
|
||||
struct platform_device *pdev;
|
||||
const struct iommu_device *d = &devices[i];
|
||||
|
||||
pdev = platform_device_alloc("omap-iommu", i);
|
||||
if (!pdev) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
res[0].start = d->base;
|
||||
res[0].end = d->base + MMU_REG_SIZE - 1;
|
||||
res[1].start = res[1].end = d->irq;
|
||||
|
||||
err = platform_device_add_resources(pdev, res,
|
||||
ARRAY_SIZE(res));
|
||||
if (err)
|
||||
goto err_out;
|
||||
err = platform_device_add_data(pdev, &d->pdata,
|
||||
sizeof(d->pdata));
|
||||
if (err)
|
||||
goto err_out;
|
||||
err = platform_device_add(pdev);
|
||||
if (err)
|
||||
goto err_out;
|
||||
omap_iommu_pdev[i] = pdev;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
while (i--)
|
||||
platform_device_put(omap_iommu_pdev[i]);
|
||||
return err;
|
||||
return omap_hwmod_for_each_by_class("mmu", omap_iommu_dev_init, NULL);
|
||||
}
|
||||
/* must be ready before omap3isp is probed */
|
||||
subsys_initcall(omap_iommu_init);
|
||||
|
||||
static void __exit omap_iommu_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_iommu_devices; i++)
|
||||
platform_device_unregister(omap_iommu_pdev[i]);
|
||||
/* Do nothing */
|
||||
}
|
||||
module_exit(omap_iommu_exit);
|
||||
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
#include <plat/mmc.h>
|
||||
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
#include <plat/dmtimer.h>
|
||||
#include <plat/iommu.h>
|
||||
|
||||
#include "am35xx.h"
|
||||
|
||||
|
|
|
@ -28,10 +28,10 @@
|
|||
#include <plat/dma.h>
|
||||
#include <linux/platform_data/spi-omap2-mcspi.h>
|
||||
#include <linux/platform_data/asoc-ti-mcbsp.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
#include <plat/mmc.h>
|
||||
#include <plat/dmtimer.h>
|
||||
#include <plat/common.h>
|
||||
#include <plat/iommu.h>
|
||||
|
||||
#include "omap_hwmod_common_data.h"
|
||||
#include "cm1_44xx.h"
|
||||
|
@ -651,7 +651,7 @@ static struct omap_hwmod omap44xx_dsp_hwmod = {
|
|||
.mpu_irqs = omap44xx_dsp_irqs,
|
||||
.rst_lines = omap44xx_dsp_resets,
|
||||
.rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_resets),
|
||||
.main_clk = "dsp_fck",
|
||||
.main_clk = "dpll_iva_m4x2_ck",
|
||||
.prcm = {
|
||||
.omap4 = {
|
||||
.clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET,
|
||||
|
@ -1678,7 +1678,7 @@ static struct omap_hwmod omap44xx_ipu_hwmod = {
|
|||
.mpu_irqs = omap44xx_ipu_irqs,
|
||||
.rst_lines = omap44xx_ipu_resets,
|
||||
.rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_resets),
|
||||
.main_clk = "ipu_fck",
|
||||
.main_clk = "ducati_clk_mux_ck",
|
||||
.prcm = {
|
||||
.omap4 = {
|
||||
.clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET,
|
||||
|
|
|
@ -1,96 +0,0 @@
|
|||
/*
|
||||
* omap iommu: omap2 architecture specific definitions
|
||||
*
|
||||
* Copyright (C) 2008-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_IOMMU2_H
|
||||
#define __MACH_IOMMU2_H
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
/*
|
||||
* MMU Register offsets
|
||||
*/
|
||||
#define MMU_REVISION 0x00
|
||||
#define MMU_SYSCONFIG 0x10
|
||||
#define MMU_SYSSTATUS 0x14
|
||||
#define MMU_IRQSTATUS 0x18
|
||||
#define MMU_IRQENABLE 0x1c
|
||||
#define MMU_WALKING_ST 0x40
|
||||
#define MMU_CNTL 0x44
|
||||
#define MMU_FAULT_AD 0x48
|
||||
#define MMU_TTB 0x4c
|
||||
#define MMU_LOCK 0x50
|
||||
#define MMU_LD_TLB 0x54
|
||||
#define MMU_CAM 0x58
|
||||
#define MMU_RAM 0x5c
|
||||
#define MMU_GFLUSH 0x60
|
||||
#define MMU_FLUSH_ENTRY 0x64
|
||||
#define MMU_READ_CAM 0x68
|
||||
#define MMU_READ_RAM 0x6c
|
||||
#define MMU_EMU_FAULT_AD 0x70
|
||||
|
||||
#define MMU_REG_SIZE 256
|
||||
|
||||
/*
|
||||
* MMU Register bit definitions
|
||||
*/
|
||||
#define MMU_LOCK_BASE_SHIFT 10
|
||||
#define MMU_LOCK_BASE_MASK (0x1f << MMU_LOCK_BASE_SHIFT)
|
||||
#define MMU_LOCK_BASE(x) \
|
||||
((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
|
||||
|
||||
#define MMU_LOCK_VICT_SHIFT 4
|
||||
#define MMU_LOCK_VICT_MASK (0x1f << MMU_LOCK_VICT_SHIFT)
|
||||
#define MMU_LOCK_VICT(x) \
|
||||
((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
|
||||
|
||||
#define MMU_CAM_VATAG_SHIFT 12
|
||||
#define MMU_CAM_VATAG_MASK \
|
||||
((~0UL >> MMU_CAM_VATAG_SHIFT) << MMU_CAM_VATAG_SHIFT)
|
||||
#define MMU_CAM_P (1 << 3)
|
||||
#define MMU_CAM_V (1 << 2)
|
||||
#define MMU_CAM_PGSZ_MASK 3
|
||||
#define MMU_CAM_PGSZ_1M (0 << 0)
|
||||
#define MMU_CAM_PGSZ_64K (1 << 0)
|
||||
#define MMU_CAM_PGSZ_4K (2 << 0)
|
||||
#define MMU_CAM_PGSZ_16M (3 << 0)
|
||||
|
||||
#define MMU_RAM_PADDR_SHIFT 12
|
||||
#define MMU_RAM_PADDR_MASK \
|
||||
((~0UL >> MMU_RAM_PADDR_SHIFT) << MMU_RAM_PADDR_SHIFT)
|
||||
#define MMU_RAM_ENDIAN_SHIFT 9
|
||||
#define MMU_RAM_ENDIAN_MASK (1 << MMU_RAM_ENDIAN_SHIFT)
|
||||
#define MMU_RAM_ENDIAN_BIG (1 << MMU_RAM_ENDIAN_SHIFT)
|
||||
#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT)
|
||||
#define MMU_RAM_ELSZ_SHIFT 7
|
||||
#define MMU_RAM_ELSZ_MASK (3 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_16 (1 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_32 (2 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_NONE (3 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_MIXED_SHIFT 6
|
||||
#define MMU_RAM_MIXED_MASK (1 << MMU_RAM_MIXED_SHIFT)
|
||||
#define MMU_RAM_MIXED MMU_RAM_MIXED_MASK
|
||||
|
||||
/*
|
||||
* register accessors
|
||||
*/
|
||||
static inline u32 iommu_read_reg(struct omap_iommu *obj, size_t offs)
|
||||
{
|
||||
return __raw_readl(obj->regbase + offs);
|
||||
}
|
||||
|
||||
static inline void iommu_write_reg(struct omap_iommu *obj, u32 val, size_t offs)
|
||||
{
|
||||
__raw_writel(val, obj->regbase + offs);
|
||||
}
|
||||
|
||||
#endif /* __MACH_IOMMU2_H */
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* omap iommu: simple virtual address space management
|
||||
*
|
||||
* Copyright (C) 2008-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __IOMMU_MMAP_H
|
||||
#define __IOMMU_MMAP_H
|
||||
|
||||
#include <linux/iommu.h>
|
||||
|
||||
struct iovm_struct {
|
||||
struct omap_iommu *iommu; /* iommu object which this belongs to */
|
||||
u32 da_start; /* area definition */
|
||||
u32 da_end;
|
||||
u32 flags; /* IOVMF_: see below */
|
||||
struct list_head list; /* linked in ascending order */
|
||||
const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */
|
||||
void *va; /* mpu side mapped address */
|
||||
};
|
||||
|
||||
/*
|
||||
* IOVMF_FLAGS: attribute for iommu virtual memory area(iovma)
|
||||
*
|
||||
* lower 16 bit is used for h/w and upper 16 bit is for s/w.
|
||||
*/
|
||||
#define IOVMF_SW_SHIFT 16
|
||||
|
||||
/*
|
||||
* iovma: h/w flags derived from cam and ram attribute
|
||||
*/
|
||||
#define IOVMF_CAM_MASK (~((1 << 10) - 1))
|
||||
#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK)
|
||||
|
||||
#define IOVMF_PGSZ_MASK (3 << 0)
|
||||
#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M
|
||||
#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K
|
||||
#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K
|
||||
#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M
|
||||
|
||||
#define IOVMF_ENDIAN_MASK (1 << 9)
|
||||
#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG
|
||||
#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE
|
||||
|
||||
#define IOVMF_ELSZ_MASK (3 << 7)
|
||||
#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8
|
||||
#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16
|
||||
#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32
|
||||
#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE
|
||||
|
||||
#define IOVMF_MIXED_MASK (1 << 6)
|
||||
#define IOVMF_MIXED MMU_RAM_MIXED
|
||||
|
||||
/*
|
||||
* iovma: s/w flags, used for mapping and umapping internally.
|
||||
*/
|
||||
#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT)
|
||||
#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT)
|
||||
#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT)
|
||||
|
||||
/* "superpages" is supported just with physically linear pages */
|
||||
#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT))
|
||||
#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT))
|
||||
#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT))
|
||||
|
||||
#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT))
|
||||
|
||||
|
||||
extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
|
||||
const struct sg_table *sgt, u32 flags);
|
||||
extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain,
|
||||
struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev,
|
||||
u32 da, size_t bytes, u32 flags);
|
||||
extern void
|
||||
omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
|
||||
const u32 da);
|
||||
extern void *omap_da_to_va(struct device *dev, u32 da);
|
||||
|
||||
#endif /* __IOMMU_MMAP_H */
|
|
@ -50,6 +50,7 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr)
|
|||
static inline int dma_mapping_error(struct device *dev, dma_addr_t dev_addr)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
debug_dma_mapping_error(dev, dev_addr);
|
||||
return ops->mapping_error(dev, dev_addr);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask)
|
|||
*/
|
||||
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
return dma_addr == ~0;
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
|
|||
static inline int dma_mapping_error(struct device *dev, dma_addr_t daddr)
|
||||
{
|
||||
struct dma_map_ops *ops = platform_dma_get_ops(dev);
|
||||
debug_dma_mapping_error(dev, daddr);
|
||||
return ops->mapping_error(dev, daddr);
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,8 @@ static inline void __dma_sync(unsigned long paddr,
|
|||
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
if (ops->mapping_error)
|
||||
return ops->mapping_error(dev, dma_addr);
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ static inline int dma_supported(struct device *dev, u64 mask)
|
|||
static inline int dma_mapping_error(struct device *dev, u64 mask)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
debug_dma_mapping_error(dev, mask);
|
||||
return ops->mapping_error(dev, mask);
|
||||
}
|
||||
|
||||
|
|
|
@ -172,6 +172,7 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
|||
{
|
||||
struct dma_map_ops *dma_ops = get_dma_ops(dev);
|
||||
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
if (dma_ops->mapping_error)
|
||||
return dma_ops->mapping_error(dev, dma_addr);
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
|||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
if (ops->mapping_error)
|
||||
return ops->mapping_error(dev, dma_addr);
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
|
|||
static inline int
|
||||
dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
return get_dma_ops(dev)->mapping_error(dev, dma_addr);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
|
|||
static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
debug_dma_mapping_error(dev, dma_addr);
|
||||
if (ops->mapping_error)
|
||||
return ops->mapping_error(dev, dma_addr);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
|||
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o
|
||||
obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
|
||||
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
|
||||
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
|
||||
|
|
|
@ -57,17 +57,9 @@
|
|||
* physically contiguous memory regions it is mapping into page sizes
|
||||
* that we support.
|
||||
*
|
||||
* Traditionally the IOMMU core just handed us the mappings directly,
|
||||
* after making sure the size is an order of a 4KiB page and that the
|
||||
* mapping has natural alignment.
|
||||
*
|
||||
* To retain this behavior, we currently advertise that we support
|
||||
* all page sizes that are an order of 4KiB.
|
||||
*
|
||||
* If at some point we'd like to utilize the IOMMU core's new behavior,
|
||||
* we could change this to advertise the real page sizes we support.
|
||||
* 512GB Pages are not supported due to a hardware bug
|
||||
*/
|
||||
#define AMD_IOMMU_PGSIZES (~0xFFFUL)
|
||||
#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38))
|
||||
|
||||
static DEFINE_RWLOCK(amd_iommu_devtable_lock);
|
||||
|
||||
|
@ -140,6 +132,9 @@ static void free_dev_data(struct iommu_dev_data *dev_data)
|
|||
list_del(&dev_data->dev_data_list);
|
||||
spin_unlock_irqrestore(&dev_data_list_lock, flags);
|
||||
|
||||
if (dev_data->group)
|
||||
iommu_group_put(dev_data->group);
|
||||
|
||||
kfree(dev_data);
|
||||
}
|
||||
|
||||
|
@ -274,13 +269,160 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
|
|||
*from = to;
|
||||
}
|
||||
|
||||
static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
|
||||
{
|
||||
while (!bus->self) {
|
||||
if (!pci_is_root_bus(bus))
|
||||
bus = bus->parent;
|
||||
else
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
|
||||
|
||||
static struct pci_dev *get_isolation_root(struct pci_dev *pdev)
|
||||
{
|
||||
struct pci_dev *dma_pdev = pdev;
|
||||
|
||||
/* Account for quirked devices */
|
||||
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
|
||||
|
||||
/*
|
||||
* If it's a multifunction device that does not support our
|
||||
* required ACS flags, add to the same group as function 0.
|
||||
*/
|
||||
if (dma_pdev->multifunction &&
|
||||
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
|
||||
swap_pci_ref(&dma_pdev,
|
||||
pci_get_slot(dma_pdev->bus,
|
||||
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
|
||||
0)));
|
||||
|
||||
/*
|
||||
* Devices on the root bus go through the iommu. If that's not us,
|
||||
* find the next upstream device and test ACS up to the root bus.
|
||||
* Finding the next device may require skipping virtual buses.
|
||||
*/
|
||||
while (!pci_is_root_bus(dma_pdev->bus)) {
|
||||
struct pci_bus *bus = find_hosted_bus(dma_pdev->bus);
|
||||
if (IS_ERR(bus))
|
||||
break;
|
||||
|
||||
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
|
||||
break;
|
||||
|
||||
swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
|
||||
}
|
||||
|
||||
return dma_pdev;
|
||||
}
|
||||
|
||||
static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev)
|
||||
{
|
||||
struct iommu_group *group = iommu_group_get(&pdev->dev);
|
||||
int ret;
|
||||
|
||||
if (!group) {
|
||||
group = iommu_group_alloc();
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
WARN_ON(&pdev->dev != dev);
|
||||
}
|
||||
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
iommu_group_put(group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data,
|
||||
struct device *dev)
|
||||
{
|
||||
if (!dev_data->group) {
|
||||
struct iommu_group *group = iommu_group_alloc();
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
dev_data->group = group;
|
||||
}
|
||||
|
||||
return iommu_group_add_device(dev_data->group, dev);
|
||||
}
|
||||
|
||||
static int init_iommu_group(struct device *dev)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct iommu_group *group;
|
||||
struct pci_dev *dma_pdev;
|
||||
int ret;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
if (group) {
|
||||
iommu_group_put(group);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_data = find_dev_data(get_device_id(dev));
|
||||
if (!dev_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev_data->alias_data) {
|
||||
u16 alias;
|
||||
struct pci_bus *bus;
|
||||
|
||||
if (dev_data->alias_data->group)
|
||||
goto use_group;
|
||||
|
||||
/*
|
||||
* If the alias device exists, it's effectively just a first
|
||||
* level quirk for finding the DMA source.
|
||||
*/
|
||||
alias = amd_iommu_alias_table[dev_data->devid];
|
||||
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
|
||||
if (dma_pdev) {
|
||||
dma_pdev = get_isolation_root(dma_pdev);
|
||||
goto use_pdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the alias is virtual, try to find a parent device
|
||||
* and test whether the IOMMU group is actualy rooted above
|
||||
* the alias. Be careful to also test the parent device if
|
||||
* we think the alias is the root of the group.
|
||||
*/
|
||||
bus = pci_find_bus(0, alias >> 8);
|
||||
if (!bus)
|
||||
goto use_group;
|
||||
|
||||
bus = find_hosted_bus(bus);
|
||||
if (IS_ERR(bus) || !bus->self)
|
||||
goto use_group;
|
||||
|
||||
dma_pdev = get_isolation_root(pci_dev_get(bus->self));
|
||||
if (dma_pdev != bus->self || (dma_pdev->multifunction &&
|
||||
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS)))
|
||||
goto use_pdev;
|
||||
|
||||
pci_dev_put(dma_pdev);
|
||||
goto use_group;
|
||||
}
|
||||
|
||||
dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev)));
|
||||
use_pdev:
|
||||
ret = use_pdev_iommu_group(dma_pdev, dev);
|
||||
pci_dev_put(dma_pdev);
|
||||
return ret;
|
||||
use_group:
|
||||
return use_dev_data_iommu_group(dev_data->alias_data, dev);
|
||||
}
|
||||
|
||||
static int iommu_init_device(struct device *dev)
|
||||
{
|
||||
struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct iommu_group *group;
|
||||
u16 alias;
|
||||
int ret;
|
||||
|
||||
|
@ -303,61 +445,9 @@ static int iommu_init_device(struct device *dev)
|
|||
return -ENOTSUPP;
|
||||
}
|
||||
dev_data->alias_data = alias_data;
|
||||
|
||||
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
|
||||
}
|
||||
|
||||
if (dma_pdev == NULL)
|
||||
dma_pdev = pci_dev_get(pdev);
|
||||
|
||||
/* Account for quirked devices */
|
||||
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
|
||||
|
||||
/*
|
||||
* If it's a multifunction device that does not support our
|
||||
* required ACS flags, add to the same group as function 0.
|
||||
*/
|
||||
if (dma_pdev->multifunction &&
|
||||
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
|
||||
swap_pci_ref(&dma_pdev,
|
||||
pci_get_slot(dma_pdev->bus,
|
||||
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
|
||||
0)));
|
||||
|
||||
/*
|
||||
* Devices on the root bus go through the iommu. If that's not us,
|
||||
* find the next upstream device and test ACS up to the root bus.
|
||||
* Finding the next device may require skipping virtual buses.
|
||||
*/
|
||||
while (!pci_is_root_bus(dma_pdev->bus)) {
|
||||
struct pci_bus *bus = dma_pdev->bus;
|
||||
|
||||
while (!bus->self) {
|
||||
if (!pci_is_root_bus(bus))
|
||||
bus = bus->parent;
|
||||
else
|
||||
goto root_bus;
|
||||
}
|
||||
|
||||
if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
|
||||
break;
|
||||
|
||||
swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
|
||||
}
|
||||
|
||||
root_bus:
|
||||
group = iommu_group_get(&dma_pdev->dev);
|
||||
pci_dev_put(dma_pdev);
|
||||
if (!group) {
|
||||
group = iommu_group_alloc();
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
}
|
||||
|
||||
ret = iommu_group_add_device(group, dev);
|
||||
|
||||
iommu_group_put(group);
|
||||
|
||||
ret = init_iommu_group(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -426,6 +426,7 @@ struct iommu_dev_data {
|
|||
struct iommu_dev_data *alias_data;/* The alias dev_data */
|
||||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
atomic_t bind; /* Domain attach reference count */
|
||||
struct iommu_group *group; /* IOMMU group for virtual aliases */
|
||||
u16 devid; /* PCI Device ID */
|
||||
bool iommu_v2; /* Device can make use of IOMMUv2 */
|
||||
bool passthrough; /* Default for device is pt_domain */
|
||||
|
|
|
@ -2320,8 +2320,39 @@ static int domain_add_dev_info(struct dmar_domain *domain,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool device_has_rmrr(struct pci_dev *dev)
|
||||
{
|
||||
struct dmar_rmrr_unit *rmrr;
|
||||
int i;
|
||||
|
||||
for_each_rmrr_units(rmrr) {
|
||||
for (i = 0; i < rmrr->devices_cnt; i++) {
|
||||
/*
|
||||
* Return TRUE if this RMRR contains the device that
|
||||
* is passed in.
|
||||
*/
|
||||
if (rmrr->devices[i] == dev)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
|
||||
{
|
||||
|
||||
/*
|
||||
* We want to prevent any device associated with an RMRR from
|
||||
* getting placed into the SI Domain. This is done because
|
||||
* problems exist when devices are moved in and out of domains
|
||||
* and their respective RMRR info is lost. We exempt USB devices
|
||||
* from this process due to their usage of RMRRs that are known
|
||||
* to not be needed after BIOS hand-off to OS.
|
||||
*/
|
||||
if (device_has_rmrr(pdev) &&
|
||||
(pdev->class >> 8) != PCI_CLASS_SERIAL_USB)
|
||||
return 0;
|
||||
|
||||
if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
|
||||
return 1;
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@
|
|||
#include <linux/uaccess.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include <plat/iovmm.h>
|
||||
|
||||
#include <plat/iopgtable.h>
|
||||
#include "omap-iopgtable.h"
|
||||
#include "omap-iommu.h"
|
||||
|
||||
#define MAXCOLUMN 100 /* for short messages */
|
||||
|
||||
|
|
|
@ -16,17 +16,20 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
#include <plat/iopgtable.h>
|
||||
#include "omap-iopgtable.h"
|
||||
#include "omap-iommu.h"
|
||||
|
||||
#define for_each_iotlb_cr(obj, n, __i, cr) \
|
||||
for (__i = 0; \
|
||||
|
@ -51,6 +54,21 @@ struct omap_iommu_domain {
|
|||
spinlock_t lock;
|
||||
};
|
||||
|
||||
#define MMU_LOCK_BASE_SHIFT 10
|
||||
#define MMU_LOCK_BASE_MASK (0x1f << MMU_LOCK_BASE_SHIFT)
|
||||
#define MMU_LOCK_BASE(x) \
|
||||
((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
|
||||
|
||||
#define MMU_LOCK_VICT_SHIFT 4
|
||||
#define MMU_LOCK_VICT_MASK (0x1f << MMU_LOCK_VICT_SHIFT)
|
||||
#define MMU_LOCK_VICT(x) \
|
||||
((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
|
||||
|
||||
struct iotlb_lock {
|
||||
short base;
|
||||
short vict;
|
||||
};
|
||||
|
||||
/* accommodate the difference between omap1 and omap2/3 */
|
||||
static const struct iommu_functions *arch_iommu;
|
||||
|
||||
|
@ -125,31 +143,44 @@ EXPORT_SYMBOL_GPL(omap_iommu_arch_version);
|
|||
static int iommu_enable(struct omap_iommu *obj)
|
||||
{
|
||||
int err;
|
||||
struct platform_device *pdev = to_platform_device(obj->dev);
|
||||
struct iommu_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!obj)
|
||||
if (!obj || !pdata)
|
||||
return -EINVAL;
|
||||
|
||||
if (!arch_iommu)
|
||||
return -ENODEV;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
if (pdata->deassert_reset) {
|
||||
err = pdata->deassert_reset(pdev, pdata->reset_name);
|
||||
if (err) {
|
||||
dev_err(obj->dev, "deassert_reset failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
|
||||
err = arch_iommu->enable(obj);
|
||||
|
||||
clk_disable(obj->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void iommu_disable(struct omap_iommu *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
struct platform_device *pdev = to_platform_device(obj->dev);
|
||||
struct iommu_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
if (!obj || !pdata)
|
||||
return;
|
||||
|
||||
arch_iommu->disable(obj);
|
||||
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
|
||||
if (pdata->assert_reset)
|
||||
pdata->assert_reset(pdev, pdata->reset_name);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -272,7 +303,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
|
|||
if (!obj || !obj->nr_tlb_entries || !e)
|
||||
return -EINVAL;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
|
||||
iotlb_lock_get(obj, &l);
|
||||
if (l.base == obj->nr_tlb_entries) {
|
||||
|
@ -302,7 +333,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
|
|||
|
||||
cr = iotlb_alloc_cr(obj, e);
|
||||
if (IS_ERR(cr)) {
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
return PTR_ERR(cr);
|
||||
}
|
||||
|
||||
|
@ -316,7 +347,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
|
|||
l.vict = l.base;
|
||||
iotlb_lock_set(obj, &l);
|
||||
out:
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -346,7 +377,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
|
|||
int i;
|
||||
struct cr_regs cr;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
|
||||
for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
|
||||
u32 start;
|
||||
|
@ -365,7 +396,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
|
|||
iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
|
||||
}
|
||||
}
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
|
||||
if (i == obj->nr_tlb_entries)
|
||||
dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
|
||||
|
@ -379,7 +410,7 @@ static void flush_iotlb_all(struct omap_iommu *obj)
|
|||
{
|
||||
struct iotlb_lock l;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
|
||||
l.base = 0;
|
||||
l.vict = 0;
|
||||
|
@ -387,7 +418,7 @@ static void flush_iotlb_all(struct omap_iommu *obj)
|
|||
|
||||
iommu_write_reg(obj, 1, MMU_GFLUSH);
|
||||
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE)
|
||||
|
@ -397,11 +428,11 @@ ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes)
|
|||
if (!obj || !buf)
|
||||
return -EINVAL;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
|
||||
bytes = arch_iommu->dump_ctx(obj, buf, bytes);
|
||||
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
@ -415,7 +446,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
|
|||
struct cr_regs tmp;
|
||||
struct cr_regs *p = crs;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
pm_runtime_get_sync(obj->dev);
|
||||
iotlb_lock_get(obj, &saved);
|
||||
|
||||
for_each_iotlb_cr(obj, num, i, tmp) {
|
||||
|
@ -425,7 +456,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num)
|
|||
}
|
||||
|
||||
iotlb_lock_set(obj, &saved);
|
||||
clk_disable(obj->clk);
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
|
||||
return p - crs;
|
||||
}
|
||||
|
@ -789,9 +820,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
|
|||
if (!obj->refcount)
|
||||
return IRQ_NONE;
|
||||
|
||||
clk_enable(obj->clk);
|
||||
errs = iommu_report_fault(obj, &da);
|
||||
clk_disable(obj->clk);
|
||||
if (errs == 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
|
@ -913,17 +942,10 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct iommu_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (pdev->num_resources != 2)
|
||||
return -EINVAL;
|
||||
|
||||
obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
obj->clk = clk_get(&pdev->dev, pdata->clk_name);
|
||||
if (IS_ERR(obj->clk))
|
||||
goto err_clk;
|
||||
|
||||
obj->nr_tlb_entries = pdata->nr_tlb_entries;
|
||||
obj->name = pdata->name;
|
||||
obj->dev = &pdev->dev;
|
||||
|
@ -966,6 +988,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
|
|||
goto err_irq;
|
||||
platform_set_drvdata(pdev, obj);
|
||||
|
||||
pm_runtime_irq_safe(obj->dev);
|
||||
pm_runtime_enable(obj->dev);
|
||||
|
||||
dev_info(&pdev->dev, "%s registered\n", obj->name);
|
||||
return 0;
|
||||
|
||||
|
@ -974,8 +999,6 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
|
|||
err_ioremap:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_mem:
|
||||
clk_put(obj->clk);
|
||||
err_clk:
|
||||
kfree(obj);
|
||||
return err;
|
||||
}
|
||||
|
@ -996,7 +1019,8 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev)
|
|||
release_mem_region(res->start, resource_size(res));
|
||||
iounmap(obj->regbase);
|
||||
|
||||
clk_put(obj->clk);
|
||||
pm_runtime_disable(obj->dev);
|
||||
|
||||
dev_info(&pdev->dev, "%s removed\n", obj->name);
|
||||
kfree(obj);
|
||||
return 0;
|
||||
|
@ -1015,6 +1039,23 @@ static void iopte_cachep_ctor(void *iopte)
|
|||
clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
|
||||
}
|
||||
|
||||
static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa,
|
||||
u32 flags)
|
||||
{
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
e->da = da;
|
||||
e->pa = pa;
|
||||
e->valid = 1;
|
||||
/* FIXME: add OMAP1 support */
|
||||
e->pgsz = flags & MMU_CAM_PGSZ_MASK;
|
||||
e->endian = flags & MMU_RAM_ENDIAN_MASK;
|
||||
e->elsz = flags & MMU_RAM_ELSZ_MASK;
|
||||
e->mixed = flags & MMU_RAM_MIXED_MASK;
|
||||
|
||||
return iopgsz_to_bytes(e->pgsz);
|
||||
}
|
||||
|
||||
static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
||||
phys_addr_t pa, size_t bytes, int prot)
|
||||
{
|
||||
|
|
|
@ -10,8 +10,9 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __MACH_IOMMU_H
|
||||
#define __MACH_IOMMU_H
|
||||
#if defined(CONFIG_ARCH_OMAP1)
|
||||
#error "iommu for this processor not implemented yet"
|
||||
#endif
|
||||
|
||||
struct iotlb_entry {
|
||||
u32 da;
|
||||
|
@ -28,7 +29,6 @@ struct iotlb_entry {
|
|||
struct omap_iommu {
|
||||
const char *name;
|
||||
struct module *owner;
|
||||
struct clk *clk;
|
||||
void __iomem *regbase;
|
||||
struct device *dev;
|
||||
void *isr_priv;
|
||||
|
@ -71,11 +71,6 @@ struct cr_regs {
|
|||
};
|
||||
};
|
||||
|
||||
struct iotlb_lock {
|
||||
short base;
|
||||
short vict;
|
||||
};
|
||||
|
||||
/* architecture specific functions */
|
||||
struct iommu_functions {
|
||||
unsigned long version;
|
||||
|
@ -103,42 +98,6 @@ struct iommu_functions {
|
|||
ssize_t (*dump_ctx)(struct omap_iommu *obj, char *buf, ssize_t len);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct omap_mmu_dev_attr - OMAP mmu device attributes for omap_hwmod
|
||||
* @da_start: device address where the va space starts.
|
||||
* @da_end: device address where the va space ends.
|
||||
* @nr_tlb_entries: number of entries supported by the translation
|
||||
* look-aside buffer (TLB).
|
||||
*/
|
||||
struct omap_mmu_dev_attr {
|
||||
u32 da_start;
|
||||
u32 da_end;
|
||||
int nr_tlb_entries;
|
||||
};
|
||||
|
||||
struct iommu_platform_data {
|
||||
const char *name;
|
||||
const char *clk_name;
|
||||
const int nr_tlb_entries;
|
||||
u32 da_start;
|
||||
u32 da_end;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_arch_data - omap iommu private data
|
||||
* @name: name of the iommu device
|
||||
* @iommu_dev: handle of the iommu device
|
||||
*
|
||||
* This is an omap iommu private data object, which binds an iommu user
|
||||
* to its iommu device. This object should be placed at the iommu user's
|
||||
* dev_archdata so generic IOMMU API can be used without having to
|
||||
* utilize omap-specific plumbing anymore.
|
||||
*/
|
||||
struct omap_iommu_arch_data {
|
||||
const char *name;
|
||||
struct omap_iommu *iommu_dev;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IOMMU_API
|
||||
/**
|
||||
* dev_to_omap_iommu() - retrieves an omap iommu object from a user device
|
||||
|
@ -152,18 +111,57 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* IOMMU errors */
|
||||
#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0)
|
||||
#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1)
|
||||
#define OMAP_IOMMU_ERR_EMU_MISS (1 << 2)
|
||||
#define OMAP_IOMMU_ERR_TBLWALK_FAULT (1 << 3)
|
||||
#define OMAP_IOMMU_ERR_MULTIHIT_FAULT (1 << 4)
|
||||
/*
|
||||
* MMU Register offsets
|
||||
*/
|
||||
#define MMU_REVISION 0x00
|
||||
#define MMU_IRQSTATUS 0x18
|
||||
#define MMU_IRQENABLE 0x1c
|
||||
#define MMU_WALKING_ST 0x40
|
||||
#define MMU_CNTL 0x44
|
||||
#define MMU_FAULT_AD 0x48
|
||||
#define MMU_TTB 0x4c
|
||||
#define MMU_LOCK 0x50
|
||||
#define MMU_LD_TLB 0x54
|
||||
#define MMU_CAM 0x58
|
||||
#define MMU_RAM 0x5c
|
||||
#define MMU_GFLUSH 0x60
|
||||
#define MMU_FLUSH_ENTRY 0x64
|
||||
#define MMU_READ_CAM 0x68
|
||||
#define MMU_READ_RAM 0x6c
|
||||
#define MMU_EMU_FAULT_AD 0x70
|
||||
|
||||
#if defined(CONFIG_ARCH_OMAP1)
|
||||
#error "iommu for this processor not implemented yet"
|
||||
#else
|
||||
#include <plat/iommu2.h>
|
||||
#endif
|
||||
#define MMU_REG_SIZE 256
|
||||
|
||||
/*
|
||||
* MMU Register bit definitions
|
||||
*/
|
||||
#define MMU_CAM_VATAG_SHIFT 12
|
||||
#define MMU_CAM_VATAG_MASK \
|
||||
((~0UL >> MMU_CAM_VATAG_SHIFT) << MMU_CAM_VATAG_SHIFT)
|
||||
#define MMU_CAM_P (1 << 3)
|
||||
#define MMU_CAM_V (1 << 2)
|
||||
#define MMU_CAM_PGSZ_MASK 3
|
||||
#define MMU_CAM_PGSZ_1M (0 << 0)
|
||||
#define MMU_CAM_PGSZ_64K (1 << 0)
|
||||
#define MMU_CAM_PGSZ_4K (2 << 0)
|
||||
#define MMU_CAM_PGSZ_16M (3 << 0)
|
||||
|
||||
#define MMU_RAM_PADDR_SHIFT 12
|
||||
#define MMU_RAM_PADDR_MASK \
|
||||
((~0UL >> MMU_RAM_PADDR_SHIFT) << MMU_RAM_PADDR_SHIFT)
|
||||
|
||||
#define MMU_RAM_ENDIAN_MASK (1 << MMU_RAM_ENDIAN_SHIFT)
|
||||
#define MMU_RAM_ENDIAN_BIG (1 << MMU_RAM_ENDIAN_SHIFT)
|
||||
|
||||
#define MMU_RAM_ELSZ_MASK (3 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_16 (1 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_32 (2 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_ELSZ_NONE (3 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define MMU_RAM_MIXED_SHIFT 6
|
||||
#define MMU_RAM_MIXED_MASK (1 << MMU_RAM_MIXED_SHIFT)
|
||||
#define MMU_RAM_MIXED MMU_RAM_MIXED_MASK
|
||||
|
||||
/*
|
||||
* utilities for super page(16MB, 1MB, 64KB and 4KB)
|
||||
|
@ -199,23 +197,29 @@ extern void omap_iotlb_cr_to_e(struct cr_regs *cr, struct iotlb_entry *e);
|
|||
extern int
|
||||
omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e);
|
||||
|
||||
extern int omap_iommu_set_isr(const char *name,
|
||||
int (*isr)(struct omap_iommu *obj, u32 da, u32 iommu_errs,
|
||||
void *priv),
|
||||
void *isr_priv);
|
||||
|
||||
extern void omap_iommu_save_ctx(struct device *dev);
|
||||
extern void omap_iommu_restore_ctx(struct device *dev);
|
||||
|
||||
extern int omap_install_iommu_arch(const struct iommu_functions *ops);
|
||||
extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
|
||||
|
||||
extern int omap_foreach_iommu_device(void *data,
|
||||
int (*fn)(struct device *, void *));
|
||||
|
||||
extern int omap_install_iommu_arch(const struct iommu_functions *ops);
|
||||
extern void omap_uninstall_iommu_arch(const struct iommu_functions *ops);
|
||||
|
||||
extern ssize_t
|
||||
omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len);
|
||||
extern size_t
|
||||
omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t len);
|
||||
|
||||
#endif /* __MACH_IOMMU_H */
|
||||
/*
|
||||
* register accessors
|
||||
*/
|
||||
static inline u32 iommu_read_reg(struct omap_iommu *obj, size_t offs)
|
||||
{
|
||||
return __raw_readl(obj->regbase + offs);
|
||||
}
|
||||
|
||||
static inline void iommu_write_reg(struct omap_iommu *obj, u32 val, size_t offs)
|
||||
{
|
||||
__raw_writel(val, obj->regbase + offs);
|
||||
}
|
|
@ -13,31 +13,21 @@
|
|||
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include "omap-iommu.h"
|
||||
|
||||
/*
|
||||
* omap2 architecture specific register bit definitions
|
||||
*/
|
||||
#define IOMMU_ARCH_VERSION 0x00000011
|
||||
|
||||
/* SYSCONF */
|
||||
#define MMU_SYS_IDLE_SHIFT 3
|
||||
#define MMU_SYS_IDLE_FORCE (0 << MMU_SYS_IDLE_SHIFT)
|
||||
#define MMU_SYS_IDLE_NONE (1 << MMU_SYS_IDLE_SHIFT)
|
||||
#define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT)
|
||||
#define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT)
|
||||
|
||||
#define MMU_SYS_SOFTRESET (1 << 1)
|
||||
#define MMU_SYS_AUTOIDLE 1
|
||||
|
||||
/* SYSSTATUS */
|
||||
#define MMU_SYS_RESETDONE 1
|
||||
|
||||
/* IRQSTATUS & IRQENABLE */
|
||||
#define MMU_IRQ_MULTIHITFAULT (1 << 4)
|
||||
#define MMU_IRQ_TABLEWALKFAULT (1 << 3)
|
||||
|
@ -65,6 +55,12 @@
|
|||
((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 : \
|
||||
((pgsz) == MMU_CAM_PGSZ_4K) ? 0xfffff000 : 0)
|
||||
|
||||
/* IOMMU errors */
|
||||
#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0)
|
||||
#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1)
|
||||
#define OMAP_IOMMU_ERR_EMU_MISS (1 << 2)
|
||||
#define OMAP_IOMMU_ERR_TBLWALK_FAULT (1 << 3)
|
||||
#define OMAP_IOMMU_ERR_MULTIHIT_FAULT (1 << 4)
|
||||
|
||||
static void __iommu_set_twl(struct omap_iommu *obj, bool on)
|
||||
{
|
||||
|
@ -88,7 +84,6 @@ static void __iommu_set_twl(struct omap_iommu *obj, bool on)
|
|||
static int omap2_iommu_enable(struct omap_iommu *obj)
|
||||
{
|
||||
u32 l, pa;
|
||||
unsigned long timeout;
|
||||
|
||||
if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K))
|
||||
return -EINVAL;
|
||||
|
@ -97,29 +92,10 @@ static int omap2_iommu_enable(struct omap_iommu *obj)
|
|||
if (!IS_ALIGNED(pa, SZ_16K))
|
||||
return -EINVAL;
|
||||
|
||||
iommu_write_reg(obj, MMU_SYS_SOFTRESET, MMU_SYSCONFIG);
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(20);
|
||||
do {
|
||||
l = iommu_read_reg(obj, MMU_SYSSTATUS);
|
||||
if (l & MMU_SYS_RESETDONE)
|
||||
break;
|
||||
} while (!time_after(jiffies, timeout));
|
||||
|
||||
if (!(l & MMU_SYS_RESETDONE)) {
|
||||
dev_err(obj->dev, "can't take mmu out of reset\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
l = iommu_read_reg(obj, MMU_REVISION);
|
||||
dev_info(obj->dev, "%s: version %d.%d\n", obj->name,
|
||||
(l >> 4) & 0xf, l & 0xf);
|
||||
|
||||
l = iommu_read_reg(obj, MMU_SYSCONFIG);
|
||||
l &= ~MMU_SYS_IDLE_MASK;
|
||||
l |= (MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE);
|
||||
iommu_write_reg(obj, l, MMU_SYSCONFIG);
|
||||
|
||||
iommu_write_reg(obj, pa, MMU_TTB);
|
||||
|
||||
__iommu_set_twl(obj, true);
|
||||
|
@ -133,7 +109,6 @@ static void omap2_iommu_disable(struct omap_iommu *obj)
|
|||
|
||||
l &= ~MMU_CNTL_MASK;
|
||||
iommu_write_reg(obj, l, MMU_CNTL);
|
||||
iommu_write_reg(obj, MMU_SYS_IDLE_FORCE, MMU_SYSCONFIG);
|
||||
|
||||
dev_dbg(obj->dev, "%s is shutting down\n", obj->name);
|
||||
}
|
||||
|
@ -262,8 +237,6 @@ omap2_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len)
|
|||
char *p = buf;
|
||||
|
||||
pr_reg(REVISION);
|
||||
pr_reg(SYSCONFIG);
|
||||
pr_reg(SYSSTATUS);
|
||||
pr_reg(IRQSTATUS);
|
||||
pr_reg(IRQENABLE);
|
||||
pr_reg(WALKING_ST);
|
|
@ -10,9 +10,6 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __PLAT_OMAP_IOMMU_H
|
||||
#define __PLAT_OMAP_IOMMU_H
|
||||
|
||||
/*
|
||||
* "L2 table" address mask and size definitions.
|
||||
*/
|
||||
|
@ -97,24 +94,5 @@ static inline phys_addr_t omap_iommu_translate(u32 d, u32 va, u32 mask)
|
|||
#define iopte_index(da) (((da) >> IOPTE_SHIFT) & (PTRS_PER_IOPTE - 1))
|
||||
#define iopte_offset(iopgd, da) (iopgd_page_vaddr(iopgd) + iopte_index(da))
|
||||
|
||||
static inline u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa,
|
||||
u32 flags)
|
||||
{
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
e->da = da;
|
||||
e->pa = pa;
|
||||
e->valid = 1;
|
||||
/* FIXME: add OMAP1 support */
|
||||
e->pgsz = flags & MMU_CAM_PGSZ_MASK;
|
||||
e->endian = flags & MMU_RAM_ENDIAN_MASK;
|
||||
e->elsz = flags & MMU_RAM_ELSZ_MASK;
|
||||
e->mixed = flags & MMU_RAM_MIXED_MASK;
|
||||
|
||||
return iopgsz_to_bytes(e->pgsz);
|
||||
}
|
||||
|
||||
#define to_iommu(dev) \
|
||||
(struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
|
||||
|
||||
#endif /* __PLAT_OMAP_IOMMU_H */
|
|
@ -17,14 +17,58 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include <plat/iommu.h>
|
||||
#include <plat/iovmm.h>
|
||||
#include "omap-iopgtable.h"
|
||||
#include "omap-iommu.h"
|
||||
|
||||
#include <plat/iopgtable.h>
|
||||
/*
|
||||
* IOVMF_FLAGS: attribute for iommu virtual memory area(iovma)
|
||||
*
|
||||
* lower 16 bit is used for h/w and upper 16 bit is for s/w.
|
||||
*/
|
||||
#define IOVMF_SW_SHIFT 16
|
||||
|
||||
/*
|
||||
* iovma: h/w flags derived from cam and ram attribute
|
||||
*/
|
||||
#define IOVMF_CAM_MASK (~((1 << 10) - 1))
|
||||
#define IOVMF_RAM_MASK (~IOVMF_CAM_MASK)
|
||||
|
||||
#define IOVMF_PGSZ_MASK (3 << 0)
|
||||
#define IOVMF_PGSZ_1M MMU_CAM_PGSZ_1M
|
||||
#define IOVMF_PGSZ_64K MMU_CAM_PGSZ_64K
|
||||
#define IOVMF_PGSZ_4K MMU_CAM_PGSZ_4K
|
||||
#define IOVMF_PGSZ_16M MMU_CAM_PGSZ_16M
|
||||
|
||||
#define IOVMF_ENDIAN_MASK (1 << 9)
|
||||
#define IOVMF_ENDIAN_BIG MMU_RAM_ENDIAN_BIG
|
||||
|
||||
#define IOVMF_ELSZ_MASK (3 << 7)
|
||||
#define IOVMF_ELSZ_16 MMU_RAM_ELSZ_16
|
||||
#define IOVMF_ELSZ_32 MMU_RAM_ELSZ_32
|
||||
#define IOVMF_ELSZ_NONE MMU_RAM_ELSZ_NONE
|
||||
|
||||
#define IOVMF_MIXED_MASK (1 << 6)
|
||||
#define IOVMF_MIXED MMU_RAM_MIXED
|
||||
|
||||
/*
|
||||
* iovma: s/w flags, used for mapping and umapping internally.
|
||||
*/
|
||||
#define IOVMF_MMIO (1 << IOVMF_SW_SHIFT)
|
||||
#define IOVMF_ALLOC (2 << IOVMF_SW_SHIFT)
|
||||
#define IOVMF_ALLOC_MASK (3 << IOVMF_SW_SHIFT)
|
||||
|
||||
/* "superpages" is supported just with physically linear pages */
|
||||
#define IOVMF_DISCONT (1 << (2 + IOVMF_SW_SHIFT))
|
||||
#define IOVMF_LINEAR (2 << (2 + IOVMF_SW_SHIFT))
|
||||
#define IOVMF_LINEAR_MASK (3 << (2 + IOVMF_SW_SHIFT))
|
||||
|
||||
#define IOVMF_DA_FIXED (1 << (4 + IOVMF_SW_SHIFT))
|
||||
|
||||
static struct kmem_cache *iovm_area_cachep;
|
||||
|
||||
|
|
|
@ -398,6 +398,7 @@ static int tegra_gart_probe(struct platform_device *pdev)
|
|||
do_gart_setup(gart, NULL);
|
||||
|
||||
gart_handle = gart;
|
||||
bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
|
@ -450,7 +451,6 @@ static struct platform_driver tegra_gart_driver = {
|
|||
|
||||
static int __devinit tegra_gart_init(void)
|
||||
{
|
||||
bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
|
||||
return platform_driver_register(&tegra_gart_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -696,10 +696,8 @@ static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova)
|
|||
*pte = _PTE_VACANT(iova);
|
||||
FLUSH_CPU_DCACHE(pte, page, sizeof(*pte));
|
||||
flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0);
|
||||
if (!--(*count)) {
|
||||
if (!--(*count))
|
||||
free_ptbl(as, iova);
|
||||
smmu_flush_regs(as->smmu, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova,
|
||||
|
@ -1234,6 +1232,7 @@ static int tegra_smmu_probe(struct platform_device *pdev)
|
|||
|
||||
smmu_debugfs_create(smmu);
|
||||
smmu_handle = smmu;
|
||||
bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1278,7 +1277,6 @@ static struct platform_driver tegra_smmu_driver = {
|
|||
|
||||
static int __devinit tegra_smmu_init(void)
|
||||
{
|
||||
bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
|
||||
return platform_driver_register(&tegra_smmu_driver);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -31,11 +31,9 @@
|
|||
#include <media/v4l2-device.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <plat/iommu.h>
|
||||
#include <plat/iovmm.h>
|
||||
|
||||
#include "ispstat.h"
|
||||
#include "ispccdc.h"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <media/v4l2-event.h>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/omap-iommu.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -34,8 +35,6 @@
|
|||
#include <linux/vmalloc.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <plat/iommu.h>
|
||||
#include <plat/iovmm.h>
|
||||
#include <plat/omap-pm.h>
|
||||
|
||||
#include "ispvideo.h"
|
||||
|
|
|
@ -39,6 +39,8 @@ extern void debug_dma_map_page(struct device *dev, struct page *page,
|
|||
int direction, dma_addr_t dma_addr,
|
||||
bool map_single);
|
||||
|
||||
extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
|
||||
|
||||
extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction, bool map_single);
|
||||
|
||||
|
@ -105,6 +107,11 @@ static inline void debug_dma_map_page(struct device *dev, struct page *page,
|
|||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_mapping_error(struct device *dev,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction,
|
||||
bool map_single)
|
||||
|
|
52
include/linux/omap-iommu.h
Normal file
52
include/linux/omap-iommu.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* omap iommu: simple virtual address space management
|
||||
*
|
||||
* Copyright (C) 2008-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _INTEL_IOMMU_H_
|
||||
#define _INTEL_IOMMU_H_
|
||||
|
||||
struct iovm_struct {
|
||||
struct omap_iommu *iommu; /* iommu object which this belongs to */
|
||||
u32 da_start; /* area definition */
|
||||
u32 da_end;
|
||||
u32 flags; /* IOVMF_: see below */
|
||||
struct list_head list; /* linked in ascending order */
|
||||
const struct sg_table *sgt; /* keep 'page' <-> 'da' mapping */
|
||||
void *va; /* mpu side mapped address */
|
||||
};
|
||||
|
||||
#define MMU_RAM_ENDIAN_SHIFT 9
|
||||
#define MMU_RAM_ENDIAN_LITTLE (0 << MMU_RAM_ENDIAN_SHIFT)
|
||||
#define MMU_RAM_ELSZ_8 (0 << MMU_RAM_ELSZ_SHIFT)
|
||||
#define IOVMF_ENDIAN_LITTLE MMU_RAM_ENDIAN_LITTLE
|
||||
#define MMU_RAM_ELSZ_SHIFT 7
|
||||
#define IOVMF_ELSZ_8 MMU_RAM_ELSZ_8
|
||||
|
||||
struct iommu_domain;
|
||||
|
||||
extern struct iovm_struct *omap_find_iovm_area(struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmap(struct iommu_domain *domain, struct device *dev, u32 da,
|
||||
const struct sg_table *sgt, u32 flags);
|
||||
extern struct sg_table *omap_iommu_vunmap(struct iommu_domain *domain,
|
||||
struct device *dev, u32 da);
|
||||
extern u32
|
||||
omap_iommu_vmalloc(struct iommu_domain *domain, struct device *dev,
|
||||
u32 da, size_t bytes, u32 flags);
|
||||
extern void
|
||||
omap_iommu_vfree(struct iommu_domain *domain, struct device *dev,
|
||||
const u32 da);
|
||||
extern void *omap_da_to_va(struct device *dev, u32 da);
|
||||
|
||||
extern void omap_iommu_save_ctx(struct device *dev);
|
||||
extern void omap_iommu_restore_ctx(struct device *dev);
|
||||
|
||||
#endif
|
54
include/linux/platform_data/iommu-omap.h
Normal file
54
include/linux/platform_data/iommu-omap.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* omap iommu: main structures
|
||||
*
|
||||
* Copyright (C) 2008-2009 Nokia Corporation
|
||||
*
|
||||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define MMU_REG_SIZE 256
|
||||
|
||||
/**
|
||||
* struct iommu_arch_data - omap iommu private data
|
||||
* @name: name of the iommu device
|
||||
* @iommu_dev: handle of the iommu device
|
||||
*
|
||||
* This is an omap iommu private data object, which binds an iommu user
|
||||
* to its iommu device. This object should be placed at the iommu user's
|
||||
* dev_archdata so generic IOMMU API can be used without having to
|
||||
* utilize omap-specific plumbing anymore.
|
||||
*/
|
||||
struct omap_iommu_arch_data {
|
||||
const char *name;
|
||||
struct omap_iommu *iommu_dev;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct omap_mmu_dev_attr - OMAP mmu device attributes for omap_hwmod
|
||||
* @da_start: device address where the va space starts.
|
||||
* @da_end: device address where the va space ends.
|
||||
* @nr_tlb_entries: number of entries supported by the translation
|
||||
* look-aside buffer (TLB).
|
||||
*/
|
||||
struct omap_mmu_dev_attr {
|
||||
u32 da_start;
|
||||
u32 da_end;
|
||||
int nr_tlb_entries;
|
||||
};
|
||||
|
||||
struct iommu_platform_data {
|
||||
const char *name;
|
||||
const char *reset_name;
|
||||
int nr_tlb_entries;
|
||||
u32 da_start;
|
||||
u32 da_end;
|
||||
|
||||
int (*assert_reset)(struct platform_device *pdev, const char *name);
|
||||
int (*deassert_reset)(struct platform_device *pdev, const char *name);
|
||||
};
|
|
@ -45,6 +45,12 @@ enum {
|
|||
dma_debug_coherent,
|
||||
};
|
||||
|
||||
enum map_err_types {
|
||||
MAP_ERR_CHECK_NOT_APPLICABLE,
|
||||
MAP_ERR_NOT_CHECKED,
|
||||
MAP_ERR_CHECKED,
|
||||
};
|
||||
|
||||
#define DMA_DEBUG_STACKTRACE_ENTRIES 5
|
||||
|
||||
struct dma_debug_entry {
|
||||
|
@ -57,6 +63,7 @@ struct dma_debug_entry {
|
|||
int direction;
|
||||
int sg_call_ents;
|
||||
int sg_mapped_ents;
|
||||
enum map_err_types map_err_type;
|
||||
#ifdef CONFIG_STACKTRACE
|
||||
struct stack_trace stacktrace;
|
||||
unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES];
|
||||
|
@ -114,6 +121,12 @@ static struct device_driver *current_driver __read_mostly;
|
|||
|
||||
static DEFINE_RWLOCK(driver_name_lock);
|
||||
|
||||
static const char *const maperr2str[] = {
|
||||
[MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable",
|
||||
[MAP_ERR_NOT_CHECKED] = "dma map error not checked",
|
||||
[MAP_ERR_CHECKED] = "dma map error checked",
|
||||
};
|
||||
|
||||
static const char *type2name[4] = { "single", "page",
|
||||
"scather-gather", "coherent" };
|
||||
|
||||
|
@ -376,11 +389,12 @@ void debug_dma_dump_mappings(struct device *dev)
|
|||
list_for_each_entry(entry, &bucket->list, list) {
|
||||
if (!dev || dev == entry->dev) {
|
||||
dev_info(entry->dev,
|
||||
"%s idx %d P=%Lx D=%Lx L=%Lx %s\n",
|
||||
"%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n",
|
||||
type2name[entry->type], idx,
|
||||
(unsigned long long)entry->paddr,
|
||||
entry->dev_addr, entry->size,
|
||||
dir2name[entry->direction]);
|
||||
dir2name[entry->direction],
|
||||
maperr2str[entry->map_err_type]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -844,16 +858,16 @@ static void check_unmap(struct dma_debug_entry *ref)
|
|||
struct hash_bucket *bucket;
|
||||
unsigned long flags;
|
||||
|
||||
if (dma_mapping_error(ref->dev, ref->dev_addr)) {
|
||||
err_printk(ref->dev, NULL, "DMA-API: device driver tries "
|
||||
"to free an invalid DMA memory address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bucket = get_hash_bucket(ref, &flags);
|
||||
entry = bucket_find_exact(bucket, ref);
|
||||
|
||||
if (!entry) {
|
||||
if (dma_mapping_error(ref->dev, ref->dev_addr)) {
|
||||
err_printk(ref->dev, NULL,
|
||||
"DMA-API: device driver tries "
|
||||
"to free an invalid DMA memory address\n");
|
||||
return;
|
||||
}
|
||||
err_printk(ref->dev, NULL, "DMA-API: device driver tries "
|
||||
"to free DMA memory it has not allocated "
|
||||
"[device address=0x%016llx] [size=%llu bytes]\n",
|
||||
|
@ -910,6 +924,15 @@ static void check_unmap(struct dma_debug_entry *ref)
|
|||
dir2name[ref->direction]);
|
||||
}
|
||||
|
||||
if (entry->map_err_type == MAP_ERR_NOT_CHECKED) {
|
||||
err_printk(ref->dev, entry,
|
||||
"DMA-API: device driver failed to check map error"
|
||||
"[device address=0x%016llx] [size=%llu bytes] "
|
||||
"[mapped as %s]",
|
||||
ref->dev_addr, ref->size,
|
||||
type2name[entry->type]);
|
||||
}
|
||||
|
||||
hash_bucket_del(entry);
|
||||
dma_entry_free(entry);
|
||||
|
||||
|
@ -1017,7 +1040,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
|||
if (unlikely(global_disable))
|
||||
return;
|
||||
|
||||
if (unlikely(dma_mapping_error(dev, dma_addr)))
|
||||
if (dma_mapping_error(dev, dma_addr))
|
||||
return;
|
||||
|
||||
entry = dma_entry_alloc();
|
||||
|
@ -1030,6 +1053,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
|||
entry->dev_addr = dma_addr;
|
||||
entry->size = size;
|
||||
entry->direction = direction;
|
||||
entry->map_err_type = MAP_ERR_NOT_CHECKED;
|
||||
|
||||
if (map_single)
|
||||
entry->type = dma_debug_single;
|
||||
|
@ -1045,6 +1069,30 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
|||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_page);
|
||||
|
||||
void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
struct dma_debug_entry ref;
|
||||
struct dma_debug_entry *entry;
|
||||
struct hash_bucket *bucket;
|
||||
unsigned long flags;
|
||||
|
||||
if (unlikely(global_disable))
|
||||
return;
|
||||
|
||||
ref.dev = dev;
|
||||
ref.dev_addr = dma_addr;
|
||||
bucket = get_hash_bucket(&ref, &flags);
|
||||
entry = bucket_find_exact(bucket, &ref);
|
||||
|
||||
if (!entry)
|
||||
goto out;
|
||||
|
||||
entry->map_err_type = MAP_ERR_CHECKED;
|
||||
out:
|
||||
put_hash_bucket(bucket, &flags);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_mapping_error);
|
||||
|
||||
void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction, bool map_single)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue