Merge tag 'iommu-updates-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/joro...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Dec 2012 18:07:25 +0000 (10:07 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 20 Dec 2012 18:07:25 +0000 (10:07 -0800)
Pull IOMMU updates from Joerg Roedel:
 "A few new features this merge-window.  The most important one is
  probably, that dma-debug now warns if a dma-handle is not checked with
  dma_mapping_error by the device driver.  This requires minor changes
  to some architectures which make use of dma-debug.  Most of these
  changes have the respective Acks by the Arch-Maintainers.

  Besides that there are updates to the AMD IOMMU driver for refactor
  the IOMMU-Groups support and to make sure it does not trigger a
  hardware erratum.

  The OMAP changes (for which I pulled in a branch from Tony Lindgren's
  tree) have a conflict in linux-next with the arm-soc tree.  The
  conflict is in the file arch/arm/mach-omap2/clock44xx_data.c which is
  deleted in the arm-soc tree.  It is safe to delete the file too so
  solve the conflict.  Similar changes are done in the arm-soc tree in
  the common clock framework migration.  A missing hunk from the patch
  in the IOMMU tree will be submitted as a seperate patch when the
  merge-window is closed."

* tag 'iommu-updates-v3.8' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (29 commits)
  ARM: dma-mapping: support debug_dma_mapping_error
  ARM: OMAP4: hwmod data: ipu and dsp to use parent clocks instead of leaf clocks
  iommu/omap: Adapt to runtime pm
  iommu/omap: Migrate to hwmod framework
  iommu/omap: Keep mmu enabled when requested
  iommu/omap: Remove redundant clock handling on ISR
  iommu/amd: Remove obsolete comment
  iommu/amd: Don't use 512GB pages
  iommu/tegra: smmu: Move bus_set_iommu after probe for multi arch
  iommu/tegra: gart: Move bus_set_iommu after probe for multi arch
  iommu/tegra: smmu: Remove unnecessary PTC/TLB flush all
  tile: dma_debug: add debug_dma_mapping_error support
  sh: dma_debug: add debug_dma_mapping_error support
  powerpc: dma_debug: add debug_dma_mapping_error support
  mips: dma_debug: add debug_dma_mapping_error support
  microblaze: dma-mapping: support debug_dma_mapping_error
  ia64: dma_debug: add debug_dma_mapping_error support
  c6x: dma_debug: add debug_dma_mapping_error support
  ARM64: dma_debug: add debug_dma_mapping_error support
  intel-iommu: Prevent devices with RMRRs from being placed into SI Domain
  ...

27 files changed:
Documentation/DMA-API-HOWTO.txt
Documentation/DMA-API.txt
arch/arm/include/asm/dma-mapping.h
arch/arm/mach-omap2/devices.c
arch/arm/mach-omap2/omap-iommu.c
arch/arm/mach-omap2/omap_hwmod_44xx_data.c
arch/arm64/include/asm/dma-mapping.h
arch/c6x/include/asm/dma-mapping.h
arch/ia64/include/asm/dma-mapping.h
arch/microblaze/include/asm/dma-mapping.h
arch/mips/include/asm/dma-mapping.h
arch/powerpc/include/asm/dma-mapping.h
arch/sh/include/asm/dma-mapping.h
arch/sparc/include/asm/dma-mapping.h
arch/tile/include/asm/dma-mapping.h
arch/x86/include/asm/dma-mapping.h
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_types.h
drivers/iommu/intel-iommu.c
drivers/iommu/omap-iommu.c
drivers/iommu/omap-iommu.h
drivers/iommu/omap-iommu2.c
drivers/iommu/tegra-gart.c
drivers/iommu/tegra-smmu.c
include/linux/dma-debug.h
include/linux/platform_data/iommu-omap.h
lib/dma-debug.c

index a0b6250..4a4fb29 100644 (file)
@@ -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
index 66bd97a..78a6c56 100644 (file)
@@ -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.
+
index 67d0632..5b579b9 100644 (file)
@@ -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;
 }
 
index 4abb8b5..5e304d0 100644 (file)
@@ -226,7 +226,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)
index a6a4ff8..7642fc4 100644 (file)
 
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/slab.h>
 
 #include <linux/platform_data/iommu-omap.h>
+#include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
 
-#include "soc.h"
-#include "common.h"
-
-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;
-
-#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
-
-#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
-
-static struct platform_device **omap_iommu_pdev;
-
-static int __init omap_iommu_init(void)
+static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused)
 {
-       int i, err;
-       struct resource res[] = {
-               { .flags = IORESOURCE_MEM },
-               { .flags = IORESOURCE_IRQ },
-       };
+       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;
+
+       pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return -ENOMEM;
+
+       pdata->name = oh->name;
+       pdata->nr_tlb_entries = a->nr_tlb_entries;
+       pdata->da_start = a->da_start;
+       pdata->da_end = a->da_end;
+
+       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;
+       }
 
-       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;
+       pdev = omap_device_build("omap-iommu", i, oh, pdata, sizeof(*pdata),
+                               NULL, 0, 0);
 
-       for (i = 0; i < num_iommu_devices; i++) {
-               struct platform_device *pdev;
-               const struct iommu_device *d = &devices[i];
+       kfree(pdata);
 
-               pdev = platform_device_alloc("omap-iommu", i);
-               if (!pdev) {
-                       err = -ENOMEM;
-                       goto err_out;
-               }
+       if (IS_ERR(pdev)) {
+               pr_err("%s: device build err: %ld\n", __func__, PTR_ERR(pdev));
+               return PTR_ERR(pdev);
+       }
 
-               res[0].start = d->base;
-               res[0].end = d->base + MMU_REG_SIZE - 1;
-               res[1].start = res[1].end = d->irq;
+       i++;
 
-               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;
+static int __init omap_iommu_init(void)
+{
+       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);
 
index 272b017..f9fab94 100644 (file)
@@ -653,7 +653,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,
@@ -1679,7 +1679,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,
index 538f4b4..9947768 100644 (file)
@@ -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);
 }
 
index 03579fd..3c69406 100644 (file)
@@ -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;
 }
 
index 4f5e814..cf3ab7e 100644 (file)
@@ -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);
 }
 
index 01d2282..46460f1 100644 (file)
@@ -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);
 
index be39a12..006b43e 100644 (file)
@@ -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);
 }
 
index 7816087..e27e9ad 100644 (file)
@@ -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);
 
index 8bd965e..b437f2c 100644 (file)
@@ -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);
 
index 8493fd3..05fe53f 100644 (file)
@@ -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);
 }
 
index 4b6247d..f2ff191 100644 (file)
@@ -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);
 }
 
index f7b4c79..808dae6 100644 (file)
@@ -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);
 
index 55074cb..c1c74e0 100644 (file)
  * 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,41 +269,23 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
        *from = to;
 }
 
-#define REQ_ACS_FLAGS  (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
-
-static int iommu_init_device(struct device *dev)
+static struct pci_bus *find_hosted_bus(struct pci_bus *bus)
 {
-       struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
-       struct iommu_dev_data *dev_data;
-       struct iommu_group *group;
-       u16 alias;
-       int ret;
-
-       if (dev->archdata.iommu)
-               return 0;
-
-       dev_data = find_dev_data(get_device_id(dev));
-       if (!dev_data)
-               return -ENOMEM;
-
-       alias = amd_iommu_alias_table[dev_data->devid];
-       if (alias != dev_data->devid) {
-               struct iommu_dev_data *alias_data;
+       while (!bus->self) {
+               if (!pci_is_root_bus(bus))
+                       bus = bus->parent;
+               else
+                       return ERR_PTR(-ENODEV);
+       }
 
-               alias_data = find_dev_data(alias);
-               if (alias_data == NULL) {
-                       pr_err("AMD-Vi: Warning: Unhandled device %s\n",
-                                       dev_name(dev));
-                       free_dev_data(dev_data);
-                       return -ENOTSUPP;
-               }
-               dev_data->alias_data = alias_data;
+       return bus;
+}
 
-               dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
-       }
+#define REQ_ACS_FLAGS  (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF)
 
-       if (dma_pdev == NULL)
-               dma_pdev = pci_dev_get(pdev);
+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));
@@ -330,14 +307,9 @@ static int iommu_init_device(struct device *dev)
         * 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;
-               }
+               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;
@@ -345,19 +317,137 @@ static int iommu_init_device(struct device *dev)
                swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
        }
 
-root_bus:
-       group = iommu_group_get(&dma_pdev->dev);
-       pci_dev_put(dma_pdev);
+       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 *pdev = to_pci_dev(dev);
+       struct iommu_dev_data *dev_data;
+       u16 alias;
+       int ret;
+
+       if (dev->archdata.iommu)
+               return 0;
+
+       dev_data = find_dev_data(get_device_id(dev));
+       if (!dev_data)
+               return -ENOMEM;
+
+       alias = amd_iommu_alias_table[dev_data->devid];
+       if (alias != dev_data->devid) {
+               struct iommu_dev_data *alias_data;
+
+               alias_data = find_dev_data(alias);
+               if (alias_data == NULL) {
+                       pr_err("AMD-Vi: Warning: Unhandled device %s\n",
+                                       dev_name(dev));
+                       free_dev_data(dev_data);
+                       return -ENOTSUPP;
+               }
+               dev_data->alias_data = alias_data;
+       }
 
+       ret = init_iommu_group(dev);
        if (ret)
                return ret;
 
index c9aa3d0..e38ab43 100644 (file)
@@ -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 */
index 9476c1b..c2c07a4 100644 (file)
@@ -2327,8 +2327,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;
 
index badc17c..18108c1 100644 (file)
 #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>
 
@@ -143,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);
 }
 
 /*
@@ -290,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) {
@@ -320,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);
        }
 
@@ -334,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;
 }
 
@@ -364,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;
@@ -383,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);
@@ -397,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;
@@ -405,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)
@@ -415,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;
 }
@@ -433,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) {
@@ -443,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;
 }
@@ -807,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;
 
@@ -931,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;
@@ -984,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;
 
@@ -992,8 +999,6 @@ err_irq:
 err_ioremap:
        release_mem_region(res->start, resource_size(res));
 err_mem:
-       clk_put(obj->clk);
-err_clk:
        kfree(obj);
        return err;
 }
@@ -1014,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;
index 2b5f3c0..1200842 100644 (file)
@@ -29,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;
@@ -116,8 +115,6 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev)
  * 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
index c020202..d745094 100644 (file)
  */
 #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)
@@ -97,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;
@@ -106,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);
@@ -142,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);
 }
@@ -271,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);
index c16e8fc..4c9db62 100644 (file)
@@ -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);
 }
 
index 4252d74..25c1210 100644 (file)
@@ -694,10 +694,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,
@@ -1232,6 +1230,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;
 }
 
@@ -1276,7 +1275,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);
 }
 
index 171ad8a..fc0e34c 100644 (file)
@@ -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)
index c677b9f..5b429c4 100644 (file)
@@ -10,6 +10,8 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/platform_device.h>
+
 #define MMU_REG_SIZE           256
 
 /**
@@ -42,8 +44,11 @@ struct omap_mmu_dev_attr {
 
 struct iommu_platform_data {
        const char *name;
-       const char *clk_name;
-       const int nr_tlb_entries;
+       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);
 };
index d84beb9..5e396ac 100644 (file)
@@ -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)
 {