Merge branches 'iommu/fixes', 'dma-debug', 'x86/amd', 'x86/vt-d', 'arm/tegra' and 'arm/omap' into next

This commit is contained in:
Joerg Roedel 2012-12-16 12:24:09 +01:00
42 changed files with 765 additions and 594 deletions

View File

@ -468,11 +468,46 @@ To map a single region, you do:
size_t size = buffer->len; size_t size = buffer->len;
dma_handle = dma_map_single(dev, addr, size, direction); 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: and to unmap it:
dma_unmap_single(dev, dma_handle, size, direction); 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. 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. from the interrupt which told you that the DMA transfer is done.
@ -489,6 +524,14 @@ Specifically:
size_t size = buffer->len; size_t size = buffer->len;
dma_handle = dma_map_page(dev, page, offset, size, direction); 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. 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: With scatterlists, you map a region gathered from several regions by:
int i, count = dma_map_sg(dev, sglist, nents, direction); int i, count = dma_map_sg(dev, sglist, nents, direction);
@ -578,6 +627,14 @@ to use the dma_sync_*() interfaces.
dma_addr_t mapping; dma_addr_t mapping;
mapping = dma_map_single(cp->dev, buffer, len, DMA_FROM_DEVICE); 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_buf = buffer;
cp->rx_len = len; cp->rx_len = len;
@ -658,6 +715,75 @@ failure can be determined by:
* delay and try again later or * delay and try again later or
* reset driver. * 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 Networking drivers must call dev_kfree_skb to free the socket buffer

View 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 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 boot with 'dma_debug_entries=<your_desired_number>' to overwrite the
architectural default. 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.

View 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) 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; return dma_addr == DMA_ERROR_CODE;
} }

View File

@ -184,8 +184,6 @@ obj-$(CONFIG_HW_PERF_EVENTS) += pmu.o
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o
mailbox_mach-objs := mailbox.o mailbox_mach-objs := mailbox.o
obj-$(CONFIG_OMAP_IOMMU) += iommu2.o
iommu-$(CONFIG_OMAP_IOMMU) := omap-iommu.o iommu-$(CONFIG_OMAP_IOMMU) := omap-iommu.o
obj-y += $(iommu-m) $(iommu-y) obj-y += $(iommu-m) $(iommu-y)

View File

@ -1316,16 +1316,6 @@ static struct clk dmic_fck = {
.clkdm_name = "abe_clkdm", .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 = { static struct clk dss_sys_clk = {
.name = "dss_sys_clk", .name = "dss_sys_clk",
.ops = &clkops_omap2_dflt, .ops = &clkops_omap2_dflt,
@ -1696,16 +1686,6 @@ static struct clk i2c4_fck = {
.recalc = &followparent_recalc, .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 = { static struct clk iss_ctrlclk = {
.name = "iss_ctrlclk", .name = "iss_ctrlclk",
.ops = &clkops_omap2_dflt, .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, "div_ts_ck", &div_ts_ck, CK_446X),
CLK(NULL, "dmic_sync_mux_ck", &dmic_sync_mux_ck, CK_443X), CLK(NULL, "dmic_sync_mux_ck", &dmic_sync_mux_ck, CK_443X),
CLK(NULL, "dmic_fck", &dmic_fck, 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_sys_clk", &dss_sys_clk, CK_443X),
CLK(NULL, "dss_tv_clk", &dss_tv_clk, CK_443X), CLK(NULL, "dss_tv_clk", &dss_tv_clk, CK_443X),
CLK(NULL, "dss_48mhz_clk", &dss_48mhz_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, "i2c2_fck", &i2c2_fck, CK_443X),
CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X), CLK(NULL, "i2c3_fck", &i2c3_fck, CK_443X),
CLK(NULL, "i2c4_fck", &i2c4_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_ctrlclk", &iss_ctrlclk, CK_443X),
CLK(NULL, "iss_fck", &iss_fck, CK_443X), CLK(NULL, "iss_fck", &iss_fck, CK_443X),
CLK(NULL, "iva_fck", &iva_fck, CK_443X), CLK(NULL, "iva_fck", &iva_fck, CK_443X),

View File

@ -127,7 +127,7 @@ static struct platform_device omap2cam_device = {
#if defined(CONFIG_IOMMU_API) #if defined(CONFIG_IOMMU_API)
#include <plat/iommu.h> #include <linux/platform_data/iommu-omap.h>
static struct resource omap3isp_resources[] = { static struct resource omap3isp_resources[] = {
{ {
@ -214,7 +214,7 @@ static struct platform_device omap3isp_device = {
}; };
static struct omap_iommu_arch_data omap3_isp_iommu = { static struct omap_iommu_arch_data omap3_isp_iommu = {
.name = "isp", .name = "mmu_isp",
}; };
int omap3_init_camera(struct isp_platform_data *pdata) int omap3_init_camera(struct isp_platform_data *pdata)

View File

@ -12,153 +12,60 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.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" static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused)
#include "common.h" {
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 { pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
resource_size_t base; if (!pdata)
int irq; return -ENOMEM;
struct iommu_platform_data pdata;
struct resource res[2];
};
static struct iommu_device *devices;
static int num_iommu_devices;
#ifdef CONFIG_ARCH_OMAP3 pdata->name = oh->name;
static struct iommu_device omap3_devices[] = { pdata->nr_tlb_entries = a->nr_tlb_entries;
{ pdata->da_start = a->da_start;
.base = 0x480bd400, pdata->da_end = a->da_end;
.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 if (oh->rst_lines_cnt == 1) {
static struct iommu_device omap4_devices[] = { pdata->reset_name = oh->rst_lines->name;
{ pdata->assert_reset = omap_device_assert_hardreset;
.base = OMAP4_MMU1_BASE, pdata->deassert_reset = omap_device_deassert_hardreset;
.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; 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) static int __init omap_iommu_init(void)
{ {
int i, err; return omap_hwmod_for_each_by_class("mmu", omap_iommu_dev_init, NULL);
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;
} }
/* must be ready before omap3isp is probed */ /* must be ready before omap3isp is probed */
subsys_initcall(omap_iommu_init); subsys_initcall(omap_iommu_init);
static void __exit omap_iommu_exit(void) static void __exit omap_iommu_exit(void)
{ {
int i; /* Do nothing */
for (i = 0; i < num_iommu_devices; i++)
platform_device_unregister(omap_iommu_pdev[i]);
} }
module_exit(omap_iommu_exit); module_exit(omap_iommu_exit);

View File

@ -26,8 +26,8 @@
#include <plat/mmc.h> #include <plat/mmc.h>
#include <linux/platform_data/asoc-ti-mcbsp.h> #include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/iommu-omap.h>
#include <plat/dmtimer.h> #include <plat/dmtimer.h>
#include <plat/iommu.h>
#include "am35xx.h" #include "am35xx.h"

View File

@ -28,10 +28,10 @@
#include <plat/dma.h> #include <plat/dma.h>
#include <linux/platform_data/spi-omap2-mcspi.h> #include <linux/platform_data/spi-omap2-mcspi.h>
#include <linux/platform_data/asoc-ti-mcbsp.h> #include <linux/platform_data/asoc-ti-mcbsp.h>
#include <linux/platform_data/iommu-omap.h>
#include <plat/mmc.h> #include <plat/mmc.h>
#include <plat/dmtimer.h> #include <plat/dmtimer.h>
#include <plat/common.h> #include <plat/common.h>
#include <plat/iommu.h>
#include "omap_hwmod_common_data.h" #include "omap_hwmod_common_data.h"
#include "cm1_44xx.h" #include "cm1_44xx.h"
@ -651,7 +651,7 @@ static struct omap_hwmod omap44xx_dsp_hwmod = {
.mpu_irqs = omap44xx_dsp_irqs, .mpu_irqs = omap44xx_dsp_irqs,
.rst_lines = omap44xx_dsp_resets, .rst_lines = omap44xx_dsp_resets,
.rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_resets), .rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_resets),
.main_clk = "dsp_fck", .main_clk = "dpll_iva_m4x2_ck",
.prcm = { .prcm = {
.omap4 = { .omap4 = {
.clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET, .clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET,
@ -1678,7 +1678,7 @@ static struct omap_hwmod omap44xx_ipu_hwmod = {
.mpu_irqs = omap44xx_ipu_irqs, .mpu_irqs = omap44xx_ipu_irqs,
.rst_lines = omap44xx_ipu_resets, .rst_lines = omap44xx_ipu_resets,
.rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_resets), .rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_resets),
.main_clk = "ipu_fck", .main_clk = "ducati_clk_mux_ck",
.prcm = { .prcm = {
.omap4 = { .omap4 = {
.clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET, .clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET,

View File

@ -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 */

View File

@ -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 */

View 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) static inline int dma_mapping_error(struct device *dev, dma_addr_t dev_addr)
{ {
struct dma_map_ops *ops = get_dma_ops(dev); struct dma_map_ops *ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, dev_addr);
return ops->mapping_error(dev, dev_addr); return ops->mapping_error(dev, dev_addr);
} }

View 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) 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; return dma_addr == ~0;
} }

View 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) static inline int dma_mapping_error(struct device *dev, dma_addr_t daddr)
{ {
struct dma_map_ops *ops = platform_dma_get_ops(dev); struct dma_map_ops *ops = platform_dma_get_ops(dev);
debug_dma_mapping_error(dev, daddr);
return ops->mapping_error(dev, daddr); return ops->mapping_error(dev, daddr);
} }

View 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) static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{ {
struct dma_map_ops *ops = get_dma_ops(dev); struct dma_map_ops *ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, dma_addr);
if (ops->mapping_error) if (ops->mapping_error)
return ops->mapping_error(dev, dma_addr); return ops->mapping_error(dev, dma_addr);

View 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) static inline int dma_mapping_error(struct device *dev, u64 mask)
{ {
struct dma_map_ops *ops = get_dma_ops(dev); struct dma_map_ops *ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, mask);
return ops->mapping_error(dev, mask); return ops->mapping_error(dev, mask);
} }

View 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); struct dma_map_ops *dma_ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, dma_addr);
if (dma_ops->mapping_error) if (dma_ops->mapping_error)
return dma_ops->mapping_error(dev, dma_addr); return dma_ops->mapping_error(dev, dma_addr);

View 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); struct dma_map_ops *ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, dma_addr);
if (ops->mapping_error) if (ops->mapping_error)
return ops->mapping_error(dev, dma_addr); return ops->mapping_error(dev, dma_addr);

View 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) 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); return (dma_addr == DMA_ERROR_CODE);
} }

View File

@ -72,6 +72,7 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
static inline int static inline int
dma_mapping_error(struct device *dev, dma_addr_t dma_addr) 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); return get_dma_ops(dev)->mapping_error(dev, dma_addr);
} }

View 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) static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{ {
struct dma_map_ops *ops = get_dma_ops(dev); struct dma_map_ops *ops = get_dma_ops(dev);
debug_dma_mapping_error(dev, dma_addr);
if (ops->mapping_error) if (ops->mapping_error)
return ops->mapping_error(dev, dma_addr); return ops->mapping_error(dev, dma_addr);

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_DMAR_TABLE) += dmar.o
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.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_IOVMM) += omap-iovmm.o
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o

View File

@ -57,17 +57,9 @@
* physically contiguous memory regions it is mapping into page sizes * physically contiguous memory regions it is mapping into page sizes
* that we support. * that we support.
* *
* Traditionally the IOMMU core just handed us the mappings directly, * 512GB Pages are not supported due to a hardware bug
* 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.
*/ */
#define AMD_IOMMU_PGSIZES (~0xFFFUL) #define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38))
static DEFINE_RWLOCK(amd_iommu_devtable_lock); 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); list_del(&dev_data->dev_data_list);
spin_unlock_irqrestore(&dev_data_list_lock, flags); spin_unlock_irqrestore(&dev_data_list_lock, flags);
if (dev_data->group)
iommu_group_put(dev_data->group);
kfree(dev_data); kfree(dev_data);
} }
@ -274,13 +269,160 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
*from = 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) #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) 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_dev_data *dev_data;
struct iommu_group *group;
u16 alias; u16 alias;
int ret; int ret;
@ -303,61 +445,9 @@ static int iommu_init_device(struct device *dev)
return -ENOTSUPP; return -ENOTSUPP;
} }
dev_data->alias_data = alias_data; dev_data->alias_data = alias_data;
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
} }
if (dma_pdev == NULL) ret = init_iommu_group(dev);
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);
if (ret) if (ret)
return ret; return ret;

View File

@ -426,6 +426,7 @@ struct iommu_dev_data {
struct iommu_dev_data *alias_data;/* The alias dev_data */ struct iommu_dev_data *alias_data;/* The alias dev_data */
struct protection_domain *domain; /* Domain the device is bound to */ struct protection_domain *domain; /* Domain the device is bound to */
atomic_t bind; /* Domain attach reference count */ atomic_t bind; /* Domain attach reference count */
struct iommu_group *group; /* IOMMU group for virtual aliases */
u16 devid; /* PCI Device ID */ u16 devid; /* PCI Device ID */
bool iommu_v2; /* Device can make use of IOMMUv2 */ bool iommu_v2; /* Device can make use of IOMMUv2 */
bool passthrough; /* Default for device is pt_domain */ bool passthrough; /* Default for device is pt_domain */

View File

@ -2320,8 +2320,39 @@ static int domain_add_dev_info(struct dmar_domain *domain,
return 0; 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) 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)) if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev))
return 1; return 1;

View File

@ -18,11 +18,11 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/omap-iommu.h>
#include <linux/platform_data/iommu-omap.h>
#include <plat/iommu.h> #include "omap-iopgtable.h"
#include <plat/iovmm.h> #include "omap-iommu.h"
#include <plat/iopgtable.h>
#define MAXCOLUMN 100 /* for short messages */ #define MAXCOLUMN 100 /* for short messages */

View File

@ -16,17 +16,20 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/clk.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/omap-iommu.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
#include <asm/cacheflush.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) \ #define for_each_iotlb_cr(obj, n, __i, cr) \
for (__i = 0; \ for (__i = 0; \
@ -51,6 +54,21 @@ struct omap_iommu_domain {
spinlock_t lock; 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 */ /* accommodate the difference between omap1 and omap2/3 */
static const struct iommu_functions *arch_iommu; 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) static int iommu_enable(struct omap_iommu *obj)
{ {
int err; 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; return -EINVAL;
if (!arch_iommu) if (!arch_iommu)
return -ENODEV; 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); err = arch_iommu->enable(obj);
clk_disable(obj->clk);
return err; return err;
} }
static void iommu_disable(struct omap_iommu *obj) static void iommu_disable(struct omap_iommu *obj)
{ {
if (!obj) struct platform_device *pdev = to_platform_device(obj->dev);
return; struct iommu_platform_data *pdata = pdev->dev.platform_data;
clk_enable(obj->clk); if (!obj || !pdata)
return;
arch_iommu->disable(obj); 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) if (!obj || !obj->nr_tlb_entries || !e)
return -EINVAL; return -EINVAL;
clk_enable(obj->clk); pm_runtime_get_sync(obj->dev);
iotlb_lock_get(obj, &l); iotlb_lock_get(obj, &l);
if (l.base == obj->nr_tlb_entries) { 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); cr = iotlb_alloc_cr(obj, e);
if (IS_ERR(cr)) { if (IS_ERR(cr)) {
clk_disable(obj->clk); pm_runtime_put_sync(obj->dev);
return PTR_ERR(cr); 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; l.vict = l.base;
iotlb_lock_set(obj, &l); iotlb_lock_set(obj, &l);
out: out:
clk_disable(obj->clk); pm_runtime_put_sync(obj->dev);
return err; return err;
} }
@ -346,7 +377,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
int i; int i;
struct cr_regs cr; 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) { for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
u32 start; 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); iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
} }
} }
clk_disable(obj->clk); pm_runtime_put_sync(obj->dev);
if (i == obj->nr_tlb_entries) if (i == obj->nr_tlb_entries)
dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da); 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; struct iotlb_lock l;
clk_enable(obj->clk); pm_runtime_get_sync(obj->dev);
l.base = 0; l.base = 0;
l.vict = 0; l.vict = 0;
@ -387,7 +418,7 @@ static void flush_iotlb_all(struct omap_iommu *obj)
iommu_write_reg(obj, 1, MMU_GFLUSH); 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) #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) if (!obj || !buf)
return -EINVAL; return -EINVAL;
clk_enable(obj->clk); pm_runtime_get_sync(obj->dev);
bytes = arch_iommu->dump_ctx(obj, buf, bytes); bytes = arch_iommu->dump_ctx(obj, buf, bytes);
clk_disable(obj->clk); pm_runtime_put_sync(obj->dev);
return bytes; 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 tmp;
struct cr_regs *p = crs; struct cr_regs *p = crs;
clk_enable(obj->clk); pm_runtime_get_sync(obj->dev);
iotlb_lock_get(obj, &saved); iotlb_lock_get(obj, &saved);
for_each_iotlb_cr(obj, num, i, tmp) { 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); iotlb_lock_set(obj, &saved);
clk_disable(obj->clk); pm_runtime_put_sync(obj->dev);
return p - crs; return p - crs;
} }
@ -789,9 +820,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
if (!obj->refcount) if (!obj->refcount)
return IRQ_NONE; return IRQ_NONE;
clk_enable(obj->clk);
errs = iommu_report_fault(obj, &da); errs = iommu_report_fault(obj, &da);
clk_disable(obj->clk);
if (errs == 0) if (errs == 0)
return IRQ_HANDLED; return IRQ_HANDLED;
@ -913,17 +942,10 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
struct iommu_platform_data *pdata = pdev->dev.platform_data; 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); obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
if (!obj) if (!obj)
return -ENOMEM; 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->nr_tlb_entries = pdata->nr_tlb_entries;
obj->name = pdata->name; obj->name = pdata->name;
obj->dev = &pdev->dev; obj->dev = &pdev->dev;
@ -966,6 +988,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev)
goto err_irq; goto err_irq;
platform_set_drvdata(pdev, obj); 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); dev_info(&pdev->dev, "%s registered\n", obj->name);
return 0; return 0;
@ -974,8 +999,6 @@ err_irq:
err_ioremap: err_ioremap:
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
err_mem: err_mem:
clk_put(obj->clk);
err_clk:
kfree(obj); kfree(obj);
return err; return err;
} }
@ -996,7 +1019,8 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev)
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
iounmap(obj->regbase); iounmap(obj->regbase);
clk_put(obj->clk); pm_runtime_disable(obj->dev);
dev_info(&pdev->dev, "%s removed\n", obj->name); dev_info(&pdev->dev, "%s removed\n", obj->name);
kfree(obj); kfree(obj);
return 0; return 0;
@ -1015,6 +1039,23 @@ static void iopte_cachep_ctor(void *iopte)
clean_dcache_area(iopte, IOPTE_TABLE_SIZE); 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, static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
phys_addr_t pa, size_t bytes, int prot) phys_addr_t pa, size_t bytes, int prot)
{ {

View File

@ -10,8 +10,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef __MACH_IOMMU_H #if defined(CONFIG_ARCH_OMAP1)
#define __MACH_IOMMU_H #error "iommu for this processor not implemented yet"
#endif
struct iotlb_entry { struct iotlb_entry {
u32 da; u32 da;
@ -28,7 +29,6 @@ struct iotlb_entry {
struct omap_iommu { struct omap_iommu {
const char *name; const char *name;
struct module *owner; struct module *owner;
struct clk *clk;
void __iomem *regbase; void __iomem *regbase;
struct device *dev; struct device *dev;
void *isr_priv; void *isr_priv;
@ -71,11 +71,6 @@ struct cr_regs {
}; };
}; };
struct iotlb_lock {
short base;
short vict;
};
/* architecture specific functions */ /* architecture specific functions */
struct iommu_functions { struct iommu_functions {
unsigned long version; unsigned long version;
@ -103,42 +98,6 @@ struct iommu_functions {
ssize_t (*dump_ctx)(struct omap_iommu *obj, char *buf, ssize_t len); 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 #ifdef CONFIG_IOMMU_API
/** /**
* dev_to_omap_iommu() - retrieves an omap iommu object from a user device * 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 #endif
/* IOMMU errors */ /*
#define OMAP_IOMMU_ERR_TLB_MISS (1 << 0) * MMU Register offsets
#define OMAP_IOMMU_ERR_TRANS_FAULT (1 << 1) */
#define OMAP_IOMMU_ERR_EMU_MISS (1 << 2) #define MMU_REVISION 0x00
#define OMAP_IOMMU_ERR_TBLWALK_FAULT (1 << 3) #define MMU_IRQSTATUS 0x18
#define OMAP_IOMMU_ERR_MULTIHIT_FAULT (1 << 4) #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) #define MMU_REG_SIZE 256
#error "iommu for this processor not implemented yet"
#else /*
#include <plat/iommu2.h> * MMU Register bit definitions
#endif */
#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) * 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 extern int
omap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e); 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_save_ctx(struct device *dev);
extern void omap_iommu_restore_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, extern int omap_foreach_iommu_device(void *data,
int (*fn)(struct device *, void *)); 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 extern ssize_t
omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len); omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len);
extern size_t extern size_t
omap_dump_tlb_entries(struct omap_iommu *obj, char *buf, ssize_t len); 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);
}

View File

@ -13,31 +13,21 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/stringify.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 * omap2 architecture specific register bit definitions
*/ */
#define IOMMU_ARCH_VERSION 0x00000011 #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 */ /* IRQSTATUS & IRQENABLE */
#define MMU_IRQ_MULTIHITFAULT (1 << 4) #define MMU_IRQ_MULTIHITFAULT (1 << 4)
#define MMU_IRQ_TABLEWALKFAULT (1 << 3) #define MMU_IRQ_TABLEWALKFAULT (1 << 3)
@ -65,6 +55,12 @@
((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 : \ ((pgsz) == MMU_CAM_PGSZ_64K) ? 0xffff0000 : \
((pgsz) == MMU_CAM_PGSZ_4K) ? 0xfffff000 : 0) ((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) 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) static int omap2_iommu_enable(struct omap_iommu *obj)
{ {
u32 l, pa; u32 l, pa;
unsigned long timeout;
if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K)) if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K))
return -EINVAL; return -EINVAL;
@ -97,29 +92,10 @@ static int omap2_iommu_enable(struct omap_iommu *obj)
if (!IS_ALIGNED(pa, SZ_16K)) if (!IS_ALIGNED(pa, SZ_16K))
return -EINVAL; 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); l = iommu_read_reg(obj, MMU_REVISION);
dev_info(obj->dev, "%s: version %d.%d\n", obj->name, dev_info(obj->dev, "%s: version %d.%d\n", obj->name,
(l >> 4) & 0xf, l & 0xf); (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_write_reg(obj, pa, MMU_TTB);
__iommu_set_twl(obj, true); __iommu_set_twl(obj, true);
@ -133,7 +109,6 @@ static void omap2_iommu_disable(struct omap_iommu *obj)
l &= ~MMU_CNTL_MASK; l &= ~MMU_CNTL_MASK;
iommu_write_reg(obj, l, MMU_CNTL); 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); 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; char *p = buf;
pr_reg(REVISION); pr_reg(REVISION);
pr_reg(SYSCONFIG);
pr_reg(SYSSTATUS);
pr_reg(IRQSTATUS); pr_reg(IRQSTATUS);
pr_reg(IRQENABLE); pr_reg(IRQENABLE);
pr_reg(WALKING_ST); pr_reg(WALKING_ST);

View File

@ -10,9 +10,6 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#ifndef __PLAT_OMAP_IOMMU_H
#define __PLAT_OMAP_IOMMU_H
/* /*
* "L2 table" address mask and size definitions. * "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_index(da) (((da) >> IOPTE_SHIFT) & (PTRS_PER_IOPTE - 1))
#define iopte_offset(iopgd, da) (iopgd_page_vaddr(iopgd) + iopte_index(da)) #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) \ #define to_iommu(dev) \
(struct omap_iommu *)platform_get_drvdata(to_platform_device(dev)) (struct omap_iommu *)platform_get_drvdata(to_platform_device(dev))
#endif /* __PLAT_OMAP_IOMMU_H */

View File

@ -17,14 +17,58 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/omap-iommu.h>
#include <linux/platform_data/iommu-omap.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
#include <plat/iommu.h> #include "omap-iopgtable.h"
#include <plat/iovmm.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; static struct kmem_cache *iovm_area_cachep;

View File

@ -398,6 +398,7 @@ static int tegra_gart_probe(struct platform_device *pdev)
do_gart_setup(gart, NULL); do_gart_setup(gart, NULL);
gart_handle = gart; gart_handle = gart;
bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
return 0; return 0;
fail: fail:
@ -450,7 +451,6 @@ static struct platform_driver tegra_gart_driver = {
static int __devinit tegra_gart_init(void) static int __devinit tegra_gart_init(void)
{ {
bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
return platform_driver_register(&tegra_gart_driver); return platform_driver_register(&tegra_gart_driver);
} }

View File

@ -696,10 +696,8 @@ static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova)
*pte = _PTE_VACANT(iova); *pte = _PTE_VACANT(iova);
FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); FLUSH_CPU_DCACHE(pte, page, sizeof(*pte));
flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0);
if (!--(*count)) { if (!--(*count))
free_ptbl(as, iova); free_ptbl(as, iova);
smmu_flush_regs(as->smmu, 0);
}
} }
static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, 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_debugfs_create(smmu);
smmu_handle = smmu; smmu_handle = smmu;
bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
return 0; return 0;
} }
@ -1278,7 +1277,6 @@ static struct platform_driver tegra_smmu_driver = {
static int __devinit tegra_smmu_init(void) static int __devinit tegra_smmu_init(void)
{ {
bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
return platform_driver_register(&tegra_smmu_driver); return platform_driver_register(&tegra_smmu_driver);
} }

View File

@ -61,6 +61,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/slab.h> #include <linux/slab.h>

View File

@ -31,11 +31,9 @@
#include <media/v4l2-device.h> #include <media/v4l2-device.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iommu.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/iommu.h>
#include <plat/iommu.h>
#include <plat/iovmm.h>
#include "ispstat.h" #include "ispstat.h"
#include "ispccdc.h" #include "ispccdc.h"

View File

@ -30,6 +30,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/omap-iommu.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <media/v4l2-event.h> #include <media/v4l2-event.h>

View File

@ -26,6 +26,7 @@
*/ */
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/omap-iommu.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>

View File

@ -27,6 +27,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/omap-iommu.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/sched.h> #include <linux/sched.h>
@ -34,8 +35,6 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h> #include <media/v4l2-ioctl.h>
#include <plat/iommu.h>
#include <plat/iovmm.h>
#include <plat/omap-pm.h> #include <plat/omap-pm.h>
#include "ispvideo.h" #include "ispvideo.h"

View File

@ -39,6 +39,8 @@ extern void debug_dma_map_page(struct device *dev, struct page *page,
int direction, dma_addr_t dma_addr, int direction, dma_addr_t dma_addr,
bool map_single); 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, extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
size_t size, int direction, bool map_single); 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, static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
size_t size, int direction, size_t size, int direction,
bool map_single) bool map_single)

View 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

View 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);
};

View File

@ -45,6 +45,12 @@ enum {
dma_debug_coherent, 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 #define DMA_DEBUG_STACKTRACE_ENTRIES 5
struct dma_debug_entry { struct dma_debug_entry {
@ -57,6 +63,7 @@ struct dma_debug_entry {
int direction; int direction;
int sg_call_ents; int sg_call_ents;
int sg_mapped_ents; int sg_mapped_ents;
enum map_err_types map_err_type;
#ifdef CONFIG_STACKTRACE #ifdef CONFIG_STACKTRACE
struct stack_trace stacktrace; struct stack_trace stacktrace;
unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; 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 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", static const char *type2name[4] = { "single", "page",
"scather-gather", "coherent" }; "scather-gather", "coherent" };
@ -376,11 +389,12 @@ void debug_dma_dump_mappings(struct device *dev)
list_for_each_entry(entry, &bucket->list, list) { list_for_each_entry(entry, &bucket->list, list) {
if (!dev || dev == entry->dev) { if (!dev || dev == entry->dev) {
dev_info(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, type2name[entry->type], idx,
(unsigned long long)entry->paddr, (unsigned long long)entry->paddr,
entry->dev_addr, entry->size, 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; struct hash_bucket *bucket;
unsigned long flags; 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); bucket = get_hash_bucket(ref, &flags);
entry = bucket_find_exact(bucket, ref); entry = bucket_find_exact(bucket, ref);
if (!entry) { 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 " err_printk(ref->dev, NULL, "DMA-API: device driver tries "
"to free DMA memory it has not allocated " "to free DMA memory it has not allocated "
"[device address=0x%016llx] [size=%llu bytes]\n", "[device address=0x%016llx] [size=%llu bytes]\n",
@ -910,6 +924,15 @@ static void check_unmap(struct dma_debug_entry *ref)
dir2name[ref->direction]); 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); hash_bucket_del(entry);
dma_entry_free(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)) if (unlikely(global_disable))
return; return;
if (unlikely(dma_mapping_error(dev, dma_addr))) if (dma_mapping_error(dev, dma_addr))
return; return;
entry = dma_entry_alloc(); 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->dev_addr = dma_addr;
entry->size = size; entry->size = size;
entry->direction = direction; entry->direction = direction;
entry->map_err_type = MAP_ERR_NOT_CHECKED;
if (map_single) if (map_single)
entry->type = dma_debug_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); 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, void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
size_t size, int direction, bool map_single) size_t size, int direction, bool map_single)
{ {