mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-11 07:39:47 +00:00
IOMMU Updates for Linux v3.16
The changes include: * A new IOMMU driver for ARM Renesas SOCs * Updates and fixes for the ARM Exynos driver to bring it closer to a usable state again * Convert the AMD IOMMUv2 driver to use the mmu_notifier->release call-back instead of the task_exit notifier * Random other fixes and minor improvements to a number of other IOMMU drivers -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTkI/xAAoJECvwRC2XARrjgPgQANk/62drPe+Am7YIftYWgaMM e2i5PruVtDfHYo/KhV3lqPr3/GWDKuD6zrrymEYNLoq1T2GH2XtjRvJZZrzXvApO jEOj9Lf35JQgWnjh1IhFBq2N8baX9UFhNLBUkqBT4+CFQrrqARXk1pZG56VtYjUg PRn3HCHatEyN/o24tLTpXymWGjH6Z1jJQ8LLFL1/woU4nZRVHSA6HATIx1Ytv+D3 MQTy+r7M+tphT2moeJiiWo2wX98XZH/lM7+4kuc94+7CHiJjnc9rvG+Tt/cp82cL kSf7EYnW7rEnN1Tr1unFgOkdX8GhIK1Pkm1QiJ5mfIsXdFeRVj66NBpuQhaAXfPU XhISkbl5K6YqjxLCpbId8KSbonxFfk9sO+LILrjWj6x/YsWpns7LP1OPUbbwJnXh ncsn/goeCFU5M1JO9AHm2XbrDdumTUceAtgotVRQwo6GDkAd7ReVb+6jj1MND7L7 hol8UbHZKvf41msGILqIDsVTotQvzd1fQg7hl9DLcM+mRyhJo7dgTlq8GcMcYc40 3v+aDFaD1BOtgQ2VMdCiaJ2UqJNDlhC8827wEwqvIlnPXStOOgdErPNKUvr16jYV qAZsehdFIQWylve528CtR05bG/VuzaldMo4xixktC0wj3zc2gxH1BqNmZU1zluES qNOsm/wjtaqi1we+DLFu =1DJL -----END PGP SIGNATURE----- Merge tag 'iommu-updates-v3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu into next Pull IOMMU updates from Joerg Roedel: "The changes include: - a new IOMMU driver for ARM Renesas SOCs - updates and fixes for the ARM Exynos driver to bring it closer to a usable state again - convert the AMD IOMMUv2 driver to use the mmu_notifier->release call-back instead of the task_exit notifier - random other fixes and minor improvements to a number of other IOMMU drivers" * tag 'iommu-updates-v3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (54 commits) iommu/msm: Use devm_ioremap_resource to simplify code iommu/amd: Fix recently introduced compile warnings arm/ipmmu-vmsa: Fix compile error iommu/exynos: Fix checkpatch warning iommu/exynos: Fix trivial typo iommu/exynos: Remove invalid symbol dependency iommu: fsl_pamu.c: Fix for possible null pointer dereference iommu/amd: Remove duplicate checking code iommu/amd: Handle parallel invalidate_range_start/end calls correctly iommu/amd: Remove IOMMUv2 pasid_state_list iommu/amd: Implement mmu_notifier_release call-back iommu/amd: Convert IOMMUv2 state_table into state_list iommu/amd: Don't access IOMMUv2 state_table directly iommu/ipmmu-vmsa: Support clearing mappings iommu/ipmmu-vmsa: Remove stage 2 PTE bits definitions iommu/ipmmu-vmsa: Support 2MB mappings iommu/ipmmu-vmsa: Rewrite page table management iommu/ipmmu-vmsa: PMD is never folded, PUD always is iommu/ipmmu-vmsa: Set the PTE contiguous hint bit when possible iommu/ipmmu-vmsa: Define driver-specific page directory sizes ...
This commit is contained in:
commit
2732ea9e85
70
Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt
Normal file
70
Documentation/devicetree/bindings/iommu/samsung,sysmmu.txt
Normal file
@ -0,0 +1,70 @@
|
||||
Samsung Exynos IOMMU H/W, System MMU (System Memory Management Unit)
|
||||
|
||||
Samsung's Exynos architecture contains System MMUs that enables scattered
|
||||
physical memory chunks visible as a contiguous region to DMA-capable peripheral
|
||||
devices like MFC, FIMC, FIMD, GScaler, FIMC-IS and so forth.
|
||||
|
||||
System MMU is an IOMMU and supports identical translation table format to
|
||||
ARMv7 translation tables with minimum set of page properties including access
|
||||
permissions, shareability and security protection. In addition, System MMU has
|
||||
another capabilities like L2 TLB or block-fetch buffers to minimize translation
|
||||
latency.
|
||||
|
||||
System MMUs are in many to one relation with peripheral devices, i.e. single
|
||||
peripheral device might have multiple System MMUs (usually one for each bus
|
||||
master), but one System MMU can handle transactions from only one peripheral
|
||||
device. The relation between a System MMU and the peripheral device needs to be
|
||||
defined in device node of the peripheral device.
|
||||
|
||||
MFC in all Exynos SoCs and FIMD, M2M Scalers and G2D in Exynos5420 has 2 System
|
||||
MMUs.
|
||||
* MFC has one System MMU on its left and right bus.
|
||||
* FIMD in Exynos5420 has one System MMU for window 0 and 4, the other system MMU
|
||||
for window 1, 2 and 3.
|
||||
* M2M Scalers and G2D in Exynos5420 has one System MMU on the read channel and
|
||||
the other System MMU on the write channel.
|
||||
The drivers must consider how to handle those System MMUs. One of the idea is
|
||||
to implement child devices or sub-devices which are the client devices of the
|
||||
System MMU.
|
||||
|
||||
Note:
|
||||
The current DT binding for the Exynos System MMU is incomplete.
|
||||
The following properties can be removed or changed, if found incompatible with
|
||||
the "Generic IOMMU Binding" support for attaching devices to the IOMMU.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "samsung,exynos-sysmmu"
|
||||
- reg: A tuple of base address and size of System MMU registers.
|
||||
- interrupt-parent: The phandle of the interrupt controller of System MMU
|
||||
- interrupts: An interrupt specifier for interrupt signal of System MMU,
|
||||
according to the format defined by a particular interrupt
|
||||
controller.
|
||||
- clock-names: Should be "sysmmu" if the System MMU is needed to gate its clock.
|
||||
Optional "master" if the clock to the System MMU is gated by
|
||||
another gate clock other than "sysmmu".
|
||||
Exynos4 SoCs, there needs no "master" clock.
|
||||
Exynos5 SoCs, some System MMUs must have "master" clocks.
|
||||
- clocks: Required if the System MMU is needed to gate its clock.
|
||||
- samsung,power-domain: Required if the System MMU is needed to gate its power.
|
||||
Please refer to the following document:
|
||||
Documentation/devicetree/bindings/arm/exynos/power_domain.txt
|
||||
|
||||
Examples:
|
||||
gsc_0: gsc@13e00000 {
|
||||
compatible = "samsung,exynos5-gsc";
|
||||
reg = <0x13e00000 0x1000>;
|
||||
interrupts = <0 85 0>;
|
||||
samsung,power-domain = <&pd_gsc>;
|
||||
clocks = <&clock CLK_GSCL0>;
|
||||
clock-names = "gscl";
|
||||
};
|
||||
|
||||
sysmmu_gsc0: sysmmu@13E80000 {
|
||||
compatible = "samsung,exynos-sysmmu";
|
||||
reg = <0x13E80000 0x1000>;
|
||||
interrupt-parent = <&combiner>;
|
||||
interrupts = <2 0>;
|
||||
clock-names = "sysmmu", "master";
|
||||
clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>;
|
||||
samsung,power-domain = <&pd_gsc>;
|
||||
};
|
@ -178,13 +178,13 @@ config TEGRA_IOMMU_SMMU
|
||||
|
||||
config EXYNOS_IOMMU
|
||||
bool "Exynos IOMMU Support"
|
||||
depends on ARCH_EXYNOS && EXYNOS_DEV_SYSMMU
|
||||
depends on ARCH_EXYNOS
|
||||
select IOMMU_API
|
||||
help
|
||||
Support for the IOMMU(System MMU) of Samsung Exynos application
|
||||
processor family. This enables H/W multimedia accellerators to see
|
||||
non-linear physical memory chunks as a linear memory in their
|
||||
address spaces
|
||||
Support for the IOMMU (System MMU) of Samsung Exynos application
|
||||
processor family. This enables H/W multimedia accelerators to see
|
||||
non-linear physical memory chunks as linear memory in their
|
||||
address space.
|
||||
|
||||
If unsure, say N here.
|
||||
|
||||
@ -193,9 +193,9 @@ config EXYNOS_IOMMU_DEBUG
|
||||
depends on EXYNOS_IOMMU
|
||||
help
|
||||
Select this to see the detailed log message that shows what
|
||||
happens in the IOMMU driver
|
||||
happens in the IOMMU driver.
|
||||
|
||||
Say N unless you need kernel log message for IOMMU debugging
|
||||
Say N unless you need kernel log message for IOMMU debugging.
|
||||
|
||||
config SHMOBILE_IPMMU
|
||||
bool
|
||||
@ -272,6 +272,18 @@ config SHMOBILE_IOMMU_L1SIZE
|
||||
default 256 if SHMOBILE_IOMMU_ADDRSIZE_64MB
|
||||
default 128 if SHMOBILE_IOMMU_ADDRSIZE_32MB
|
||||
|
||||
config IPMMU_VMSA
|
||||
bool "Renesas VMSA-compatible IPMMU"
|
||||
depends on ARM_LPAE
|
||||
depends on ARCH_SHMOBILE || COMPILE_TEST
|
||||
select IOMMU_API
|
||||
select ARM_DMA_USE_IOMMU
|
||||
help
|
||||
Support for the Renesas VMSA-compatible IPMMU Renesas found in the
|
||||
R-Mobile APE6 and R-Car H2/M2 SoCs.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config SPAPR_TCE_IOMMU
|
||||
bool "sPAPR TCE IOMMU Support"
|
||||
depends on PPC_POWERNV || PPC_PSERIES
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
|
||||
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
|
||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||
obj-$(CONFIG_INTEL_IOMMU) += iova.o intel-iommu.o
|
||||
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
||||
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
|
||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu2.o
|
||||
|
@ -3499,8 +3499,6 @@ int __init amd_iommu_init_passthrough(void)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct pci_dev *dev = NULL;
|
||||
struct amd_iommu *iommu;
|
||||
u16 devid;
|
||||
int ret;
|
||||
|
||||
ret = alloc_passthrough_domain();
|
||||
@ -3514,12 +3512,6 @@ int __init amd_iommu_init_passthrough(void)
|
||||
dev_data = get_dev_data(&dev->dev);
|
||||
dev_data->passthrough = true;
|
||||
|
||||
devid = get_device_id(&dev->dev);
|
||||
|
||||
iommu = amd_iommu_rlookup_table[devid];
|
||||
if (!iommu)
|
||||
continue;
|
||||
|
||||
attach_device(&dev->dev, pt_domain);
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ struct pri_queue {
|
||||
struct pasid_state {
|
||||
struct list_head list; /* For global state-list */
|
||||
atomic_t count; /* Reference count */
|
||||
atomic_t mmu_notifier_count; /* Counting nested mmu_notifier
|
||||
calls */
|
||||
struct task_struct *task; /* Task bound to this PASID */
|
||||
struct mm_struct *mm; /* mm_struct for the faults */
|
||||
struct mmu_notifier mn; /* mmu_otifier handle */
|
||||
@ -56,6 +58,8 @@ struct pasid_state {
|
||||
};
|
||||
|
||||
struct device_state {
|
||||
struct list_head list;
|
||||
u16 devid;
|
||||
atomic_t count;
|
||||
struct pci_dev *pdev;
|
||||
struct pasid_state **states;
|
||||
@ -81,13 +85,9 @@ struct fault {
|
||||
u16 flags;
|
||||
};
|
||||
|
||||
static struct device_state **state_table;
|
||||
static LIST_HEAD(state_list);
|
||||
static spinlock_t state_lock;
|
||||
|
||||
/* List and lock for all pasid_states */
|
||||
static LIST_HEAD(pasid_state_list);
|
||||
static DEFINE_SPINLOCK(ps_lock);
|
||||
|
||||
static struct workqueue_struct *iommu_wq;
|
||||
|
||||
/*
|
||||
@ -99,7 +99,6 @@ static u64 *empty_page_table;
|
||||
|
||||
static void free_pasid_states(struct device_state *dev_state);
|
||||
static void unbind_pasid(struct device_state *dev_state, int pasid);
|
||||
static int task_exit(struct notifier_block *nb, unsigned long e, void *data);
|
||||
|
||||
static u16 device_id(struct pci_dev *pdev)
|
||||
{
|
||||
@ -111,13 +110,25 @@ static u16 device_id(struct pci_dev *pdev)
|
||||
return devid;
|
||||
}
|
||||
|
||||
static struct device_state *__get_device_state(u16 devid)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
|
||||
list_for_each_entry(dev_state, &state_list, list) {
|
||||
if (dev_state->devid == devid)
|
||||
return dev_state;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct device_state *get_device_state(u16 devid)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
dev_state = state_table[devid];
|
||||
dev_state = __get_device_state(devid);
|
||||
if (dev_state != NULL)
|
||||
atomic_inc(&dev_state->count);
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
@ -158,29 +169,6 @@ static void put_device_state_wait(struct device_state *dev_state)
|
||||
free_device_state(dev_state);
|
||||
}
|
||||
|
||||
static struct notifier_block profile_nb = {
|
||||
.notifier_call = task_exit,
|
||||
};
|
||||
|
||||
static void link_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
spin_lock(&ps_lock);
|
||||
list_add_tail(&pasid_state->list, &pasid_state_list);
|
||||
spin_unlock(&ps_lock);
|
||||
}
|
||||
|
||||
static void __unlink_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
list_del(&pasid_state->list);
|
||||
}
|
||||
|
||||
static void unlink_pasid_state(struct pasid_state *pasid_state)
|
||||
{
|
||||
spin_lock(&ps_lock);
|
||||
__unlink_pasid_state(pasid_state);
|
||||
spin_unlock(&ps_lock);
|
||||
}
|
||||
|
||||
/* Must be called under dev_state->lock */
|
||||
static struct pasid_state **__get_pasid_state_ptr(struct device_state *dev_state,
|
||||
int pasid, bool alloc)
|
||||
@ -337,7 +325,6 @@ static void unbind_pasid(struct device_state *dev_state, int pasid)
|
||||
if (pasid_state == NULL)
|
||||
return;
|
||||
|
||||
unlink_pasid_state(pasid_state);
|
||||
__unbind_pasid(pasid_state);
|
||||
put_pasid_state_wait(pasid_state); /* Reference taken in this function */
|
||||
}
|
||||
@ -379,7 +366,12 @@ static void free_pasid_states(struct device_state *dev_state)
|
||||
continue;
|
||||
|
||||
put_pasid_state(pasid_state);
|
||||
unbind_pasid(dev_state, i);
|
||||
|
||||
/*
|
||||
* This will call the mn_release function and
|
||||
* unbind the PASID
|
||||
*/
|
||||
mmu_notifier_unregister(&pasid_state->mn, pasid_state->mm);
|
||||
}
|
||||
|
||||
if (dev_state->pasid_levels == 2)
|
||||
@ -443,8 +435,11 @@ static void mn_invalidate_range_start(struct mmu_notifier *mn,
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain, pasid_state->pasid,
|
||||
__pa(empty_page_table));
|
||||
if (atomic_add_return(1, &pasid_state->mmu_notifier_count) == 1) {
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain,
|
||||
pasid_state->pasid,
|
||||
__pa(empty_page_table));
|
||||
}
|
||||
}
|
||||
|
||||
static void mn_invalidate_range_end(struct mmu_notifier *mn,
|
||||
@ -457,11 +452,31 @@ static void mn_invalidate_range_end(struct mmu_notifier *mn,
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain, pasid_state->pasid,
|
||||
__pa(pasid_state->mm->pgd));
|
||||
if (atomic_dec_and_test(&pasid_state->mmu_notifier_count)) {
|
||||
amd_iommu_domain_set_gcr3(dev_state->domain,
|
||||
pasid_state->pasid,
|
||||
__pa(pasid_state->mm->pgd));
|
||||
}
|
||||
}
|
||||
|
||||
static void mn_release(struct mmu_notifier *mn, struct mm_struct *mm)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
|
||||
might_sleep();
|
||||
|
||||
pasid_state = mn_to_state(mn);
|
||||
dev_state = pasid_state->device_state;
|
||||
|
||||
if (pasid_state->device_state->inv_ctx_cb)
|
||||
dev_state->inv_ctx_cb(dev_state->pdev, pasid_state->pasid);
|
||||
|
||||
unbind_pasid(dev_state, pasid_state->pasid);
|
||||
}
|
||||
|
||||
static struct mmu_notifier_ops iommu_mn = {
|
||||
.release = mn_release,
|
||||
.clear_flush_young = mn_clear_flush_young,
|
||||
.change_pte = mn_change_pte,
|
||||
.invalidate_page = mn_invalidate_page,
|
||||
@ -606,53 +621,6 @@ static struct notifier_block ppr_nb = {
|
||||
.notifier_call = ppr_notifier,
|
||||
};
|
||||
|
||||
static int task_exit(struct notifier_block *nb, unsigned long e, void *data)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct task_struct *task;
|
||||
|
||||
task = data;
|
||||
|
||||
/*
|
||||
* Using this notifier is a hack - but there is no other choice
|
||||
* at the moment. What I really want is a sleeping notifier that
|
||||
* is called when an MM goes down. But such a notifier doesn't
|
||||
* exist yet. The notifier needs to sleep because it has to make
|
||||
* sure that the device does not use the PASID and the address
|
||||
* space anymore before it is destroyed. This includes waiting
|
||||
* for pending PRI requests to pass the workqueue. The
|
||||
* MMU-Notifiers would be a good fit, but they use RCU and so
|
||||
* they are not allowed to sleep. Lets see how we can solve this
|
||||
* in a more intelligent way in the future.
|
||||
*/
|
||||
again:
|
||||
spin_lock(&ps_lock);
|
||||
list_for_each_entry(pasid_state, &pasid_state_list, list) {
|
||||
struct device_state *dev_state;
|
||||
int pasid;
|
||||
|
||||
if (pasid_state->task != task)
|
||||
continue;
|
||||
|
||||
/* Drop Lock and unbind */
|
||||
spin_unlock(&ps_lock);
|
||||
|
||||
dev_state = pasid_state->device_state;
|
||||
pasid = pasid_state->pasid;
|
||||
|
||||
if (pasid_state->device_state->inv_ctx_cb)
|
||||
dev_state->inv_ctx_cb(dev_state->pdev, pasid);
|
||||
|
||||
unbind_pasid(dev_state, pasid);
|
||||
|
||||
/* Task may be in the list multiple times */
|
||||
goto again;
|
||||
}
|
||||
spin_unlock(&ps_lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
|
||||
struct task_struct *task)
|
||||
{
|
||||
@ -682,6 +650,7 @@ int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
|
||||
goto out;
|
||||
|
||||
atomic_set(&pasid_state->count, 1);
|
||||
atomic_set(&pasid_state->mmu_notifier_count, 0);
|
||||
init_waitqueue_head(&pasid_state->wq);
|
||||
spin_lock_init(&pasid_state->lock);
|
||||
|
||||
@ -705,8 +674,6 @@ int amd_iommu_bind_pasid(struct pci_dev *pdev, int pasid,
|
||||
if (ret)
|
||||
goto out_clear_state;
|
||||
|
||||
link_pasid_state(pasid_state);
|
||||
|
||||
return 0;
|
||||
|
||||
out_clear_state:
|
||||
@ -727,6 +694,7 @@ EXPORT_SYMBOL(amd_iommu_bind_pasid);
|
||||
|
||||
void amd_iommu_unbind_pasid(struct pci_dev *pdev, int pasid)
|
||||
{
|
||||
struct pasid_state *pasid_state;
|
||||
struct device_state *dev_state;
|
||||
u16 devid;
|
||||
|
||||
@ -743,7 +711,17 @@ void amd_iommu_unbind_pasid(struct pci_dev *pdev, int pasid)
|
||||
if (pasid < 0 || pasid >= dev_state->max_pasids)
|
||||
goto out;
|
||||
|
||||
unbind_pasid(dev_state, pasid);
|
||||
pasid_state = get_pasid_state(dev_state, pasid);
|
||||
if (pasid_state == NULL)
|
||||
goto out;
|
||||
/*
|
||||
* Drop reference taken here. We are safe because we still hold
|
||||
* the reference taken in the amd_iommu_bind_pasid function.
|
||||
*/
|
||||
put_pasid_state(pasid_state);
|
||||
|
||||
/* This will call the mn_release function and unbind the PASID */
|
||||
mmu_notifier_unregister(&pasid_state->mn, pasid_state->mm);
|
||||
|
||||
out:
|
||||
put_device_state(dev_state);
|
||||
@ -773,7 +751,8 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
|
||||
|
||||
spin_lock_init(&dev_state->lock);
|
||||
init_waitqueue_head(&dev_state->wq);
|
||||
dev_state->pdev = pdev;
|
||||
dev_state->pdev = pdev;
|
||||
dev_state->devid = devid;
|
||||
|
||||
tmp = pasids;
|
||||
for (dev_state->pasid_levels = 0; (tmp - 1) & ~0x1ff; tmp >>= 9)
|
||||
@ -803,13 +782,13 @@ int amd_iommu_init_device(struct pci_dev *pdev, int pasids)
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
if (state_table[devid] != NULL) {
|
||||
if (__get_device_state(devid) != NULL) {
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
ret = -EBUSY;
|
||||
goto out_free_domain;
|
||||
}
|
||||
|
||||
state_table[devid] = dev_state;
|
||||
list_add_tail(&dev_state->list, &state_list);
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
@ -841,13 +820,13 @@ void amd_iommu_free_device(struct pci_dev *pdev)
|
||||
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
dev_state = state_table[devid];
|
||||
dev_state = __get_device_state(devid);
|
||||
if (dev_state == NULL) {
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
state_table[devid] = NULL;
|
||||
list_del(&dev_state->list);
|
||||
|
||||
spin_unlock_irqrestore(&state_lock, flags);
|
||||
|
||||
@ -874,7 +853,7 @@ int amd_iommu_set_invalid_ppr_cb(struct pci_dev *pdev,
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
ret = -EINVAL;
|
||||
dev_state = state_table[devid];
|
||||
dev_state = __get_device_state(devid);
|
||||
if (dev_state == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
@ -905,7 +884,7 @@ int amd_iommu_set_invalidate_ctx_cb(struct pci_dev *pdev,
|
||||
spin_lock_irqsave(&state_lock, flags);
|
||||
|
||||
ret = -EINVAL;
|
||||
dev_state = state_table[devid];
|
||||
dev_state = __get_device_state(devid);
|
||||
if (dev_state == NULL)
|
||||
goto out_unlock;
|
||||
|
||||
@ -922,7 +901,6 @@ EXPORT_SYMBOL(amd_iommu_set_invalidate_ctx_cb);
|
||||
|
||||
static int __init amd_iommu_v2_init(void)
|
||||
{
|
||||
size_t state_table_size;
|
||||
int ret;
|
||||
|
||||
pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>\n");
|
||||
@ -938,16 +916,10 @@ static int __init amd_iommu_v2_init(void)
|
||||
|
||||
spin_lock_init(&state_lock);
|
||||
|
||||
state_table_size = MAX_DEVICES * sizeof(struct device_state *);
|
||||
state_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
get_order(state_table_size));
|
||||
if (state_table == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = -ENOMEM;
|
||||
iommu_wq = create_workqueue("amd_iommu_v2");
|
||||
if (iommu_wq == NULL)
|
||||
goto out_free;
|
||||
goto out;
|
||||
|
||||
ret = -ENOMEM;
|
||||
empty_page_table = (u64 *)get_zeroed_page(GFP_KERNEL);
|
||||
@ -955,29 +927,24 @@ static int __init amd_iommu_v2_init(void)
|
||||
goto out_destroy_wq;
|
||||
|
||||
amd_iommu_register_ppr_notifier(&ppr_nb);
|
||||
profile_event_register(PROFILE_TASK_EXIT, &profile_nb);
|
||||
|
||||
return 0;
|
||||
|
||||
out_destroy_wq:
|
||||
destroy_workqueue(iommu_wq);
|
||||
|
||||
out_free:
|
||||
free_pages((unsigned long)state_table, get_order(state_table_size));
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit amd_iommu_v2_exit(void)
|
||||
{
|
||||
struct device_state *dev_state;
|
||||
size_t state_table_size;
|
||||
int i;
|
||||
|
||||
if (!amd_iommu_v2_supported())
|
||||
return;
|
||||
|
||||
profile_event_unregister(PROFILE_TASK_EXIT, &profile_nb);
|
||||
amd_iommu_unregister_ppr_notifier(&ppr_nb);
|
||||
|
||||
flush_workqueue(iommu_wq);
|
||||
@ -1000,9 +967,6 @@ static void __exit amd_iommu_v2_exit(void)
|
||||
|
||||
destroy_workqueue(iommu_wq);
|
||||
|
||||
state_table_size = MAX_DEVICES * sizeof(struct device_state *);
|
||||
free_pages((unsigned long)state_table, get_order(state_table_size));
|
||||
|
||||
free_page((unsigned long)empty_page_table);
|
||||
}
|
||||
|
||||
|
@ -1167,7 +1167,7 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
|
||||
for (i = 0; i < master->num_streamids; ++i) {
|
||||
u32 idx, s2cr;
|
||||
idx = master->smrs ? master->smrs[i].idx : master->streamids[i];
|
||||
s2cr = (S2CR_TYPE_TRANS << S2CR_TYPE_SHIFT) |
|
||||
s2cr = S2CR_TYPE_TRANS |
|
||||
(smmu_domain->root_cfg.cbndx << S2CR_CBNDX_SHIFT);
|
||||
writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
|
||||
}
|
||||
@ -1804,7 +1804,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
||||
* allocation (PTRS_PER_PGD).
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
smmu->s1_output_size = min(39UL, size);
|
||||
smmu->s1_output_size = min((unsigned long)VA_BITS, size);
|
||||
#else
|
||||
smmu->s1_output_size = min(32UL, size);
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -592,8 +592,7 @@ found_cpu_node:
|
||||
/* advance to next node in cache hierarchy */
|
||||
node = of_find_node_by_phandle(*prop);
|
||||
if (!node) {
|
||||
pr_debug("Invalid node for cache hierarchy %s\n",
|
||||
node->full_name);
|
||||
pr_debug("Invalid node for cache hierarchy\n");
|
||||
return ~(u32)0;
|
||||
}
|
||||
}
|
||||
|
1255
drivers/iommu/ipmmu-vmsa.c
Normal file
1255
drivers/iommu/ipmmu-vmsa.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -127,13 +127,12 @@ static void msm_iommu_reset(void __iomem *base, int ncb)
|
||||
|
||||
static int msm_iommu_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *r, *r2;
|
||||
struct resource *r;
|
||||
struct clk *iommu_clk;
|
||||
struct clk *iommu_pclk;
|
||||
struct msm_iommu_drvdata *drvdata;
|
||||
struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
|
||||
void __iomem *regs_base;
|
||||
resource_size_t len;
|
||||
int ret, irq, par;
|
||||
|
||||
if (pdev->id == -1) {
|
||||
@ -178,35 +177,16 @@ static int msm_iommu_probe(struct platform_device *pdev)
|
||||
iommu_clk = NULL;
|
||||
|
||||
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "physbase");
|
||||
|
||||
if (!r) {
|
||||
ret = -ENODEV;
|
||||
regs_base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(regs_base)) {
|
||||
ret = PTR_ERR(regs_base);
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
len = resource_size(r);
|
||||
|
||||
r2 = request_mem_region(r->start, len, r->name);
|
||||
if (!r2) {
|
||||
pr_err("Could not request memory region: start=%p, len=%d\n",
|
||||
(void *) r->start, len);
|
||||
ret = -EBUSY;
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
regs_base = ioremap(r2->start, len);
|
||||
|
||||
if (!regs_base) {
|
||||
pr_err("Could not ioremap: start=%p, len=%d\n",
|
||||
(void *) r2->start, len);
|
||||
ret = -EBUSY;
|
||||
goto fail_mem;
|
||||
}
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "secure_irq");
|
||||
if (irq < 0) {
|
||||
ret = -ENODEV;
|
||||
goto fail_io;
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
msm_iommu_reset(regs_base, iommu_dev->ncb);
|
||||
@ -222,14 +202,14 @@ static int msm_iommu_probe(struct platform_device *pdev)
|
||||
if (!par) {
|
||||
pr_err("%s: Invalid PAR value detected\n", iommu_dev->name);
|
||||
ret = -ENODEV;
|
||||
goto fail_io;
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
ret = request_irq(irq, msm_iommu_fault_handler, 0,
|
||||
"msm_iommu_secure_irpt_handler", drvdata);
|
||||
if (ret) {
|
||||
pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
|
||||
goto fail_io;
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
|
||||
@ -250,10 +230,6 @@ static int msm_iommu_probe(struct platform_device *pdev)
|
||||
clk_disable(iommu_pclk);
|
||||
|
||||
return 0;
|
||||
fail_io:
|
||||
iounmap(regs_base);
|
||||
fail_mem:
|
||||
release_mem_region(r->start, len);
|
||||
fail_clk:
|
||||
if (iommu_clk) {
|
||||
clk_disable(iommu_clk);
|
||||
|
@ -34,6 +34,9 @@
|
||||
#include "omap-iopgtable.h"
|
||||
#include "omap-iommu.h"
|
||||
|
||||
#define to_iommu(dev) \
|
||||
((struct omap_iommu *)platform_get_drvdata(to_platform_device(dev)))
|
||||
|
||||
#define for_each_iotlb_cr(obj, n, __i, cr) \
|
||||
for (__i = 0; \
|
||||
(__i < (n)) && (cr = __iotlb_read_cr((obj), __i), true); \
|
||||
@ -391,6 +394,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da)
|
||||
__func__, start, da, bytes);
|
||||
iotlb_load_cr(obj, &cr);
|
||||
iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
pm_runtime_put_sync(obj->dev);
|
||||
@ -1037,19 +1041,18 @@ static void iopte_cachep_ctor(void *iopte)
|
||||
clean_dcache_area(iopte, IOPTE_TABLE_SIZE);
|
||||
}
|
||||
|
||||
static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa,
|
||||
u32 flags)
|
||||
static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz)
|
||||
{
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
e->da = da;
|
||||
e->pa = pa;
|
||||
e->valid = 1;
|
||||
e->valid = MMU_CAM_V;
|
||||
/* 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;
|
||||
e->pgsz = pgsz;
|
||||
e->endian = MMU_RAM_ENDIAN_LITTLE;
|
||||
e->elsz = MMU_RAM_ELSZ_8;
|
||||
e->mixed = 0;
|
||||
|
||||
return iopgsz_to_bytes(e->pgsz);
|
||||
}
|
||||
@ -1062,9 +1065,8 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
||||
struct device *dev = oiommu->dev;
|
||||
struct iotlb_entry e;
|
||||
int omap_pgsz;
|
||||
u32 ret, flags;
|
||||
u32 ret;
|
||||
|
||||
/* we only support mapping a single iommu page for now */
|
||||
omap_pgsz = bytes_to_iopgsz(bytes);
|
||||
if (omap_pgsz < 0) {
|
||||
dev_err(dev, "invalid size to map: %d\n", bytes);
|
||||
@ -1073,9 +1075,7 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
||||
|
||||
dev_dbg(dev, "mapping da 0x%lx to pa 0x%x size 0x%x\n", da, pa, bytes);
|
||||
|
||||
flags = omap_pgsz | prot;
|
||||
|
||||
iotlb_init_entry(&e, da, pa, flags);
|
||||
iotlb_init_entry(&e, da, pa, omap_pgsz);
|
||||
|
||||
ret = omap_iopgtable_store_entry(oiommu, &e);
|
||||
if (ret)
|
||||
@ -1248,12 +1248,6 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_iommu_domain_has_cap(struct iommu_domain *domain,
|
||||
unsigned long cap)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct omap_iommu_arch_data *arch_data;
|
||||
@ -1305,7 +1299,6 @@ static struct iommu_ops omap_iommu_ops = {
|
||||
.map = omap_iommu_map,
|
||||
.unmap = omap_iommu_unmap,
|
||||
.iova_to_phys = omap_iommu_iova_to_phys,
|
||||
.domain_has_cap = omap_iommu_domain_has_cap,
|
||||
.add_device = omap_iommu_add_device,
|
||||
.remove_device = omap_iommu_remove_device,
|
||||
.pgsize_bitmap = OMAP_IOMMU_PGSIZES,
|
||||
|
@ -93,6 +93,3 @@ static inline phys_addr_t omap_iommu_translate(u32 d, u32 va, u32 mask)
|
||||
/* to find an entry in the second-level page table. */
|
||||
#define iopte_index(da) (((da) >> IOPTE_SHIFT) & (PTRS_PER_IOPTE - 1))
|
||||
#define iopte_offset(iopgd, da) (iopgd_page_vaddr(iopgd) + iopte_index(da))
|
||||
|
||||
#define to_iommu(dev) \
|
||||
(platform_get_drvdata(to_platform_device(dev)))
|
||||
|
@ -94,11 +94,6 @@ static int ipmmu_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
struct shmobile_ipmmu_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "cannot get platform resources\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
ipmmu = devm_kzalloc(&pdev->dev, sizeof(*ipmmu), GFP_KERNEL);
|
||||
if (!ipmmu) {
|
||||
dev_err(&pdev->dev, "cannot allocate device data\n");
|
||||
@ -106,19 +101,18 @@ static int ipmmu_probe(struct platform_device *pdev)
|
||||
}
|
||||
spin_lock_init(&ipmmu->flush_lock);
|
||||
ipmmu->dev = &pdev->dev;
|
||||
ipmmu->ipmmu_base = devm_ioremap_nocache(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!ipmmu->ipmmu_base) {
|
||||
dev_err(&pdev->dev, "ioremap_nocache failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ipmmu->ipmmu_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ipmmu->ipmmu_base))
|
||||
return PTR_ERR(ipmmu->ipmmu_base);
|
||||
|
||||
ipmmu->dev_names = pdata->dev_names;
|
||||
ipmmu->num_dev_names = pdata->num_dev_names;
|
||||
platform_set_drvdata(pdev, ipmmu);
|
||||
ipmmu_reg_write(ipmmu, IMCTR1, 0x0); /* disable TLB */
|
||||
ipmmu_reg_write(ipmmu, IMCTR2, 0x0); /* disable PMB */
|
||||
ipmmu_iommu_init(ipmmu);
|
||||
return 0;
|
||||
return ipmmu_iommu_init(ipmmu);
|
||||
}
|
||||
|
||||
static struct platform_driver ipmmu_driver = {
|
||||
|
24
include/linux/platform_data/ipmmu-vmsa.h
Normal file
24
include/linux/platform_data/ipmmu-vmsa.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* IPMMU VMSA Platform Data
|
||||
*
|
||||
* Copyright (C) 2014 Renesas Electronics Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#ifndef __IPMMU_VMSA_H__
|
||||
#define __IPMMU_VMSA_H__
|
||||
|
||||
struct ipmmu_vmsa_master {
|
||||
const char *name;
|
||||
unsigned int utlb;
|
||||
};
|
||||
|
||||
struct ipmmu_vmsa_platform_data {
|
||||
const struct ipmmu_vmsa_master *masters;
|
||||
unsigned int num_masters;
|
||||
};
|
||||
|
||||
#endif /* __IPMMU_VMSA_H__ */
|
Loading…
x
Reference in New Issue
Block a user