IOMMU Updates for Linux v4.14
Slightly more changes than usual this time: - KDump Kernel IOMMU take-over code for AMD IOMMU. The code now tries to preserve the mappings of the kernel so that master aborts for devices are avoided. Master aborts cause some devices to fail in the kdump kernel, so this code makes the dump more likely to succeed when AMD IOMMU is enabled. - Common flush queue implementation for IOVA code users. The code is still optional, but AMD and Intel IOMMU drivers had their own implementation which is now unified. - Finish support for iommu-groups. All drivers implement this feature now so that IOMMU core code can rely on it. - Finish support for 'struct iommu_device' in iommu drivers. All drivers now use the interface. - New functions in the IOMMU-API for explicit IO/TLB flushing. This will help to reduce the number of IO/TLB flushes when IOMMU drivers support this interface. - Support for mt2712 in the Mediatek IOMMU driver - New IOMMU driver for QCOM hardware - System PM support for ARM-SMMU - Shutdown method for ARM-SMMU-v3 - Some constification patches - Various other small improvements and fixes -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJZtCFNAAoJECvwRC2XARrjZnQP/AxC/ezQpq82HbegF4sM/cVE Ep7TeTqodEl75FS/6txe2wU0pwodqk/LB9ajfQZUbE1w8vKsNEqi5qf4ZYHGoxYI 5bWyjJBzKIlwENH5lsBpQNt6XLevrYmRsFy7F0tRYy+qPQq8k+js2i7/XkCL3q7L 3xklF847RRoITaTOhhaROx1pF23dSMEsS2XGuWHcZfjORtep4wcFKzd/2SvlCWCo P2bRU7jBzfJuuGSA80gaiUbDmrULTUfYuZNp7njASzCgsDmagERtvDEpdoXPNNSp u6s4LjU1Dp3fgr6g6cFRO7B6JUbWd619nwo9so/c/wZN54yEngBF9EyeeF3mv2O5 ZbM2mOW3RlZcjxFT/AC8G4cZwwP6MpCEQOdqknoqc6ZQwcDqwN0o9I4+po0wsiAU 89ijZZe9Mx0p9lNpihaBEB1erAUWPo5Obh62zo80W3h6x9WzkGQWM+PyFK2DYoaC 8biEZzcc21sLEHvXQkcEGJSKrihHr9sluOqvxmCw5QAkYIFAeZRoeH7JtZWjVCnr T3XvaG1G1Aw6tS7ErxufdKawREAGki0Rm9i1baiH9sqNj5rllM01Y+PgU6E21Nbg iZp9gJLjfwM4vhYLlovvQK5PRoOBsCkyCpEI4GJqjLeam5p/WN06CFFf0ifQofYr qDPCVDkWHWV8nugFFKE7 =EVh9 -----END PGP SIGNATURE----- Merge tag 'iommu-updates-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu Pull IOMMU updates from Joerg Roedel: "Slightly more changes than usual this time: - KDump Kernel IOMMU take-over code for AMD IOMMU. The code now tries to preserve the mappings of the kernel so that master aborts for devices are avoided. Master aborts cause some devices to fail in the kdump kernel, so this code makes the dump more likely to succeed when AMD IOMMU is enabled. - common flush queue implementation for IOVA code users. The code is still optional, but AMD and Intel IOMMU drivers had their own implementation which is now unified. - finish support for iommu-groups. All drivers implement this feature now so that IOMMU core code can rely on it. - finish support for 'struct iommu_device' in iommu drivers. All drivers now use the interface. - new functions in the IOMMU-API for explicit IO/TLB flushing. This will help to reduce the number of IO/TLB flushes when IOMMU drivers support this interface. - support for mt2712 in the Mediatek IOMMU driver - new IOMMU driver for QCOM hardware - system PM support for ARM-SMMU - shutdown method for ARM-SMMU-v3 - some constification patches - various other small improvements and fixes" * tag 'iommu-updates-v4.14' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (87 commits) iommu/vt-d: Don't be too aggressive when clearing one context entry iommu: Introduce Interface for IOMMU TLB Flushing iommu/s390: Constify iommu_ops iommu/vt-d: Avoid calling virt_to_phys() on null pointer iommu/vt-d: IOMMU Page Request needs to check if address is canonical. arm/tegra: Call bus_set_iommu() after iommu_device_register() iommu/exynos: Constify iommu_ops iommu/ipmmu-vmsa: Make ipmmu_gather_ops const iommu/ipmmu-vmsa: Rereserving a free context before setting up a pagetable iommu/amd: Rename a few flush functions iommu/amd: Check if domain is NULL in get_domain() and return -EBUSY iommu/mediatek: Fix a build warning of BIT(32) in ARM iommu/mediatek: Fix a build fail of m4u_type iommu: qcom: annotate PM functions as __maybe_unused iommu/pamu: Fix PAMU boot crash memory: mtk-smi: Degrade SMI init to module_init iommu/mediatek: Enlarge the validate PA range for 4GB mode iommu/mediatek: Disable iommu clock when system suspend iommu/mediatek: Move pgtable allocation into domain_alloc iommu/mediatek: Merge 2 M4U HWs into one iommu domain ...
This commit is contained in:
commit
4dfc278803
|
@ -0,0 +1,121 @@
|
|||
* QCOM IOMMU v1 Implementation
|
||||
|
||||
Qualcomm "B" family devices which are not compatible with arm-smmu have
|
||||
a similar looking IOMMU but without access to the global register space,
|
||||
and optionally requiring additional configuration to route context irqs
|
||||
to non-secure vs secure interrupt line.
|
||||
|
||||
** Required properties:
|
||||
|
||||
- compatible : Should be one of:
|
||||
|
||||
"qcom,msm8916-iommu"
|
||||
|
||||
Followed by "qcom,msm-iommu-v1".
|
||||
|
||||
- clock-names : Should be a pair of "iface" (required for IOMMUs
|
||||
register group access) and "bus" (required for
|
||||
the IOMMUs underlying bus access).
|
||||
|
||||
- clocks : Phandles for respective clocks described by
|
||||
clock-names.
|
||||
|
||||
- #address-cells : must be 1.
|
||||
|
||||
- #size-cells : must be 1.
|
||||
|
||||
- #iommu-cells : Must be 1. Index identifies the context-bank #.
|
||||
|
||||
- ranges : Base address and size of the iommu context banks.
|
||||
|
||||
- qcom,iommu-secure-id : secure-id.
|
||||
|
||||
- List of sub-nodes, one per translation context bank. Each sub-node
|
||||
has the following required properties:
|
||||
|
||||
- compatible : Should be one of:
|
||||
- "qcom,msm-iommu-v1-ns" : non-secure context bank
|
||||
- "qcom,msm-iommu-v1-sec" : secure context bank
|
||||
- reg : Base address and size of context bank within the iommu
|
||||
- interrupts : The context fault irq.
|
||||
|
||||
** Optional properties:
|
||||
|
||||
- reg : Base address and size of the SMMU local base, should
|
||||
be only specified if the iommu requires configuration
|
||||
for routing of context bank irq's to secure vs non-
|
||||
secure lines. (Ie. if the iommu contains secure
|
||||
context banks)
|
||||
|
||||
|
||||
** Examples:
|
||||
|
||||
apps_iommu: iommu@1e20000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
#iommu-cells = <1>;
|
||||
compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
|
||||
ranges = <0 0x1e20000 0x40000>;
|
||||
reg = <0x1ef0000 0x3000>;
|
||||
clocks = <&gcc GCC_SMMU_CFG_CLK>,
|
||||
<&gcc GCC_APSS_TCU_CLK>;
|
||||
clock-names = "iface", "bus";
|
||||
qcom,iommu-secure-id = <17>;
|
||||
|
||||
// mdp_0:
|
||||
iommu-ctx@4000 {
|
||||
compatible = "qcom,msm-iommu-v1-ns";
|
||||
reg = <0x4000 0x1000>;
|
||||
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
// venus_ns:
|
||||
iommu-ctx@5000 {
|
||||
compatible = "qcom,msm-iommu-v1-sec";
|
||||
reg = <0x5000 0x1000>;
|
||||
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
gpu_iommu: iommu@1f08000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
#iommu-cells = <1>;
|
||||
compatible = "qcom,msm8916-iommu", "qcom,msm-iommu-v1";
|
||||
ranges = <0 0x1f08000 0x10000>;
|
||||
clocks = <&gcc GCC_SMMU_CFG_CLK>,
|
||||
<&gcc GCC_GFX_TCU_CLK>;
|
||||
clock-names = "iface", "bus";
|
||||
qcom,iommu-secure-id = <18>;
|
||||
|
||||
// gfx3d_user:
|
||||
iommu-ctx@1000 {
|
||||
compatible = "qcom,msm-iommu-v1-ns";
|
||||
reg = <0x1000 0x1000>;
|
||||
interrupts = <GIC_SPI 241 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
|
||||
// gfx3d_priv:
|
||||
iommu-ctx@2000 {
|
||||
compatible = "qcom,msm-iommu-v1-ns";
|
||||
reg = <0x2000 0x1000>;
|
||||
interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
venus: video-codec@1d00000 {
|
||||
...
|
||||
iommus = <&apps_iommu 5>;
|
||||
};
|
||||
|
||||
mdp: mdp@1a01000 {
|
||||
...
|
||||
iommus = <&apps_iommu 4>;
|
||||
};
|
||||
|
||||
gpu@01c00000 {
|
||||
...
|
||||
iommus = <&gpu_iommu 1>, <&gpu_iommu 2>;
|
||||
};
|
|
@ -15,6 +15,11 @@ Required properties:
|
|||
to associate with its master device. See:
|
||||
Documentation/devicetree/bindings/iommu/iommu.txt
|
||||
|
||||
Optional properties:
|
||||
- rockchip,disable-mmu-reset : Don't use the mmu reset operation.
|
||||
Some mmu instances may produce unexpected results
|
||||
when the reset operation is used.
|
||||
|
||||
Example:
|
||||
|
||||
vopl_mmu: iommu@ff940300 {
|
||||
|
|
|
@ -15,6 +15,9 @@ Required properties:
|
|||
the register.
|
||||
- "smi" : It's the clock for transfer data and command.
|
||||
|
||||
Required property for mt2701:
|
||||
- mediatek,larb-id :the hardware id of this larb.
|
||||
|
||||
Example:
|
||||
larb1: larb@16010000 {
|
||||
compatible = "mediatek,mt8173-smi-larb";
|
||||
|
@ -25,3 +28,15 @@ Example:
|
|||
<&vdecsys CLK_VDEC_LARB_CKEN>;
|
||||
clock-names = "apb", "smi";
|
||||
};
|
||||
|
||||
Example for mt2701:
|
||||
larb0: larb@14010000 {
|
||||
compatible = "mediatek,mt2701-smi-larb";
|
||||
reg = <0 0x14010000 0 0x1000>;
|
||||
mediatek,smi = <&smi_common>;
|
||||
mediatek,larb-id = <0>;
|
||||
clocks = <&mmsys CLK_MM_SMI_LARB0>,
|
||||
<&mmsys CLK_MM_SMI_LARB0>;
|
||||
clock-names = "apb", "smi";
|
||||
power-domains = <&scpsys MT2701_POWER_DOMAIN_DISP>;
|
||||
};
|
||||
|
|
|
@ -11117,6 +11117,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rkuo/linux-hexagon-kernel.g
|
|||
S: Supported
|
||||
F: arch/hexagon/
|
||||
|
||||
QUALCOMM IOMMU
|
||||
M: Rob Clark <robdclark@gmail.com>
|
||||
L: iommu@lists.linux-foundation.org
|
||||
L: linux-arm-msm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iommu/qcom_iommu.c
|
||||
|
||||
QUALCOMM VENUS VIDEO ACCELERATOR DRIVER
|
||||
M: Stanimir Varbanov <stanimir.varbanov@linaro.org>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <asm-generic/pci.h>
|
||||
#include <asm/pci_clp.h>
|
||||
#include <asm/pci_debug.h>
|
||||
|
@ -122,6 +123,8 @@ struct zpci_dev {
|
|||
unsigned long iommu_pages;
|
||||
unsigned int next_bit;
|
||||
|
||||
struct iommu_device iommu_dev; /* IOMMU core handle */
|
||||
|
||||
char res_name[16];
|
||||
struct zpci_bar_struct bars[PCI_BAR_COUNT];
|
||||
|
||||
|
@ -174,6 +177,10 @@ int clp_enable_fh(struct zpci_dev *, u8);
|
|||
int clp_disable_fh(struct zpci_dev *);
|
||||
int clp_get_state(u32 fid, enum zpci_state *state);
|
||||
|
||||
/* IOMMU Interface */
|
||||
int zpci_init_iommu(struct zpci_dev *zdev);
|
||||
void zpci_destroy_iommu(struct zpci_dev *zdev);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/* Error handling and recovery */
|
||||
void zpci_event_error(void *);
|
||||
|
|
|
@ -772,6 +772,7 @@ void pcibios_remove_bus(struct pci_bus *bus)
|
|||
|
||||
zpci_exit_slot(zdev);
|
||||
zpci_cleanup_bus_resources(zdev);
|
||||
zpci_destroy_iommu(zdev);
|
||||
zpci_free_domain(zdev);
|
||||
|
||||
spin_lock(&zpci_list_lock);
|
||||
|
@ -844,11 +845,15 @@ int zpci_create_device(struct zpci_dev *zdev)
|
|||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = zpci_init_iommu(zdev);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
|
||||
mutex_init(&zdev->lock);
|
||||
if (zdev->state == ZPCI_FN_STATE_CONFIGURED) {
|
||||
rc = zpci_enable_device(zdev);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
goto out_destroy_iommu;
|
||||
}
|
||||
rc = zpci_scan_bus(zdev);
|
||||
if (rc)
|
||||
|
@ -865,6 +870,8 @@ int zpci_create_device(struct zpci_dev *zdev)
|
|||
out_disable:
|
||||
if (zdev->state == ZPCI_FN_STATE_ONLINE)
|
||||
zpci_disable_device(zdev);
|
||||
out_destroy_iommu:
|
||||
zpci_destroy_iommu(zdev);
|
||||
out_free:
|
||||
zpci_free_domain(zdev);
|
||||
out:
|
||||
|
|
|
@ -76,6 +76,8 @@ config IOMMU_DMA
|
|||
|
||||
config FSL_PAMU
|
||||
bool "Freescale IOMMU support"
|
||||
depends on PCI
|
||||
depends on PHYS_64BIT
|
||||
depends on PPC_E500MC || (COMPILE_TEST && PPC)
|
||||
select IOMMU_API
|
||||
select GENERIC_ALLOCATOR
|
||||
|
@ -253,6 +255,7 @@ config TEGRA_IOMMU_SMMU
|
|||
config EXYNOS_IOMMU
|
||||
bool "Exynos IOMMU Support"
|
||||
depends on ARCH_EXYNOS && MMU
|
||||
depends on !CPU_BIG_ENDIAN # revisit driver if we can enable big-endian ptes
|
||||
select IOMMU_API
|
||||
select ARM_DMA_USE_IOMMU
|
||||
help
|
||||
|
@ -367,4 +370,14 @@ config MTK_IOMMU_V1
|
|||
|
||||
if unsure, say N here.
|
||||
|
||||
config QCOM_IOMMU
|
||||
# Note: iommu drivers cannot (yet?) be built as modules
|
||||
bool "Qualcomm IOMMU Support"
|
||||
depends on ARCH_QCOM || COMPILE_TEST
|
||||
select IOMMU_API
|
||||
select IOMMU_IO_PGTABLE_LPAE
|
||||
select ARM_DMA_USE_IOMMU
|
||||
help
|
||||
Support for IOMMU on certain Qualcomm SoCs.
|
||||
|
||||
endif # IOMMU_SUPPORT
|
||||
|
|
|
@ -27,3 +27,4 @@ obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
|
|||
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
|
||||
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
|
||||
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
|
||||
obj-$(CONFIG_QCOM_IOMMU) += qcom_iommu.o
|
||||
|
|
|
@ -102,29 +102,6 @@ int amd_iommu_max_glx_val = -1;
|
|||
|
||||
static const struct dma_map_ops amd_iommu_dma_ops;
|
||||
|
||||
/*
|
||||
* This struct contains device specific data for the IOMMU
|
||||
*/
|
||||
struct iommu_dev_data {
|
||||
struct list_head list; /* For domain->dev_list */
|
||||
struct list_head dev_data_list; /* For global dev_data_list */
|
||||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
u16 devid; /* PCI Device ID */
|
||||
u16 alias; /* Alias Device ID */
|
||||
bool iommu_v2; /* Device can make use of IOMMUv2 */
|
||||
bool passthrough; /* Device is identity mapped */
|
||||
struct {
|
||||
bool enabled;
|
||||
int qdep;
|
||||
} ats; /* ATS state */
|
||||
bool pri_tlp; /* PASID TLB required for
|
||||
PPR completions */
|
||||
u32 errata; /* Bitmap for errata to apply */
|
||||
bool use_vapic; /* Enable device to use vapic mode */
|
||||
|
||||
struct ratelimit_state rs; /* Ratelimit IOPF messages */
|
||||
};
|
||||
|
||||
/*
|
||||
* general struct to manage commands send to an IOMMU
|
||||
*/
|
||||
|
@ -137,20 +114,7 @@ struct kmem_cache *amd_iommu_irq_cache;
|
|||
static void update_domain(struct protection_domain *domain);
|
||||
static int protection_domain_init(struct protection_domain *domain);
|
||||
static void detach_device(struct device *dev);
|
||||
|
||||
#define FLUSH_QUEUE_SIZE 256
|
||||
|
||||
struct flush_queue_entry {
|
||||
unsigned long iova_pfn;
|
||||
unsigned long pages;
|
||||
u64 counter; /* Flush counter when this entry was added to the queue */
|
||||
};
|
||||
|
||||
struct flush_queue {
|
||||
struct flush_queue_entry *entries;
|
||||
unsigned head, tail;
|
||||
spinlock_t lock;
|
||||
};
|
||||
static void iova_domain_flush_tlb(struct iova_domain *iovad);
|
||||
|
||||
/*
|
||||
* Data container for a dma_ops specific protection domain
|
||||
|
@ -161,36 +125,6 @@ struct dma_ops_domain {
|
|||
|
||||
/* IOVA RB-Tree */
|
||||
struct iova_domain iovad;
|
||||
|
||||
struct flush_queue __percpu *flush_queue;
|
||||
|
||||
/*
|
||||
* We need two counter here to be race-free wrt. IOTLB flushing and
|
||||
* adding entries to the flush queue.
|
||||
*
|
||||
* The flush_start_cnt is incremented _before_ the IOTLB flush starts.
|
||||
* New entries added to the flush ring-buffer get their 'counter' value
|
||||
* from here. This way we can make sure that entries added to the queue
|
||||
* (or other per-cpu queues of the same domain) while the TLB is about
|
||||
* to be flushed are not considered to be flushed already.
|
||||
*/
|
||||
atomic64_t flush_start_cnt;
|
||||
|
||||
/*
|
||||
* The flush_finish_cnt is incremented when an IOTLB flush is complete.
|
||||
* This value is always smaller than flush_start_cnt. The queue_add
|
||||
* function frees all IOVAs that have a counter value smaller than
|
||||
* flush_finish_cnt. This makes sure that we only free IOVAs that are
|
||||
* flushed out of the IOTLB of the domain.
|
||||
*/
|
||||
atomic64_t flush_finish_cnt;
|
||||
|
||||
/*
|
||||
* Timer to make sure we don't keep IOVAs around unflushed
|
||||
* for too long
|
||||
*/
|
||||
struct timer_list flush_timer;
|
||||
atomic_t flush_timer_on;
|
||||
};
|
||||
|
||||
static struct iova_domain reserved_iova_ranges;
|
||||
|
@ -371,19 +305,25 @@ static u16 get_alias(struct device *dev)
|
|||
static struct iommu_dev_data *find_dev_data(u16 devid)
|
||||
{
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct amd_iommu *iommu = amd_iommu_rlookup_table[devid];
|
||||
|
||||
dev_data = search_dev_data(devid);
|
||||
|
||||
if (dev_data == NULL)
|
||||
if (dev_data == NULL) {
|
||||
dev_data = alloc_dev_data(devid);
|
||||
|
||||
if (translation_pre_enabled(iommu))
|
||||
dev_data->defer_attach = true;
|
||||
}
|
||||
|
||||
return dev_data;
|
||||
}
|
||||
|
||||
static struct iommu_dev_data *get_dev_data(struct device *dev)
|
||||
struct iommu_dev_data *get_dev_data(struct device *dev)
|
||||
{
|
||||
return dev->archdata.iommu;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dev_data);
|
||||
|
||||
/*
|
||||
* Find or create an IOMMU group for a acpihid device.
|
||||
|
@ -1167,7 +1107,7 @@ static int iommu_flush_dte(struct amd_iommu *iommu, u16 devid)
|
|||
return iommu_queue_command(iommu, &cmd);
|
||||
}
|
||||
|
||||
static void iommu_flush_dte_all(struct amd_iommu *iommu)
|
||||
static void amd_iommu_flush_dte_all(struct amd_iommu *iommu)
|
||||
{
|
||||
u32 devid;
|
||||
|
||||
|
@ -1181,7 +1121,7 @@ static void iommu_flush_dte_all(struct amd_iommu *iommu)
|
|||
* This function uses heavy locking and may disable irqs for some time. But
|
||||
* this is no issue because it is only called during resume.
|
||||
*/
|
||||
static void iommu_flush_tlb_all(struct amd_iommu *iommu)
|
||||
static void amd_iommu_flush_tlb_all(struct amd_iommu *iommu)
|
||||
{
|
||||
u32 dom_id;
|
||||
|
||||
|
@ -1195,7 +1135,7 @@ static void iommu_flush_tlb_all(struct amd_iommu *iommu)
|
|||
iommu_completion_wait(iommu);
|
||||
}
|
||||
|
||||
static void iommu_flush_all(struct amd_iommu *iommu)
|
||||
static void amd_iommu_flush_all(struct amd_iommu *iommu)
|
||||
{
|
||||
struct iommu_cmd cmd;
|
||||
|
||||
|
@ -1214,7 +1154,7 @@ static void iommu_flush_irt(struct amd_iommu *iommu, u16 devid)
|
|||
iommu_queue_command(iommu, &cmd);
|
||||
}
|
||||
|
||||
static void iommu_flush_irt_all(struct amd_iommu *iommu)
|
||||
static void amd_iommu_flush_irt_all(struct amd_iommu *iommu)
|
||||
{
|
||||
u32 devid;
|
||||
|
||||
|
@ -1227,11 +1167,11 @@ static void iommu_flush_irt_all(struct amd_iommu *iommu)
|
|||
void iommu_flush_all_caches(struct amd_iommu *iommu)
|
||||
{
|
||||
if (iommu_feature(iommu, FEATURE_IA)) {
|
||||
iommu_flush_all(iommu);
|
||||
amd_iommu_flush_all(iommu);
|
||||
} else {
|
||||
iommu_flush_dte_all(iommu);
|
||||
iommu_flush_irt_all(iommu);
|
||||
iommu_flush_tlb_all(iommu);
|
||||
amd_iommu_flush_dte_all(iommu);
|
||||
amd_iommu_flush_irt_all(iommu);
|
||||
amd_iommu_flush_tlb_all(iommu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1539,9 +1479,9 @@ static int iommu_map_page(struct protection_domain *dom,
|
|||
|
||||
if (count > 1) {
|
||||
__pte = PAGE_SIZE_PTE(__sme_set(phys_addr), page_size);
|
||||
__pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_P | IOMMU_PTE_FC;
|
||||
__pte |= PM_LEVEL_ENC(7) | IOMMU_PTE_PR | IOMMU_PTE_FC;
|
||||
} else
|
||||
__pte = __sme_set(phys_addr) | IOMMU_PTE_P | IOMMU_PTE_FC;
|
||||
__pte = __sme_set(phys_addr) | IOMMU_PTE_PR | IOMMU_PTE_FC;
|
||||
|
||||
if (prot & IOMMU_PROT_IR)
|
||||
__pte |= IOMMU_PTE_IR;
|
||||
|
@ -1790,178 +1730,19 @@ static void free_gcr3_table(struct protection_domain *domain)
|
|||
free_page((unsigned long)domain->gcr3_tbl);
|
||||
}
|
||||
|
||||
static void dma_ops_domain_free_flush_queue(struct dma_ops_domain *dom)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct flush_queue *queue;
|
||||
|
||||
queue = per_cpu_ptr(dom->flush_queue, cpu);
|
||||
kfree(queue->entries);
|
||||
}
|
||||
|
||||
free_percpu(dom->flush_queue);
|
||||
|
||||
dom->flush_queue = NULL;
|
||||
}
|
||||
|
||||
static int dma_ops_domain_alloc_flush_queue(struct dma_ops_domain *dom)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
atomic64_set(&dom->flush_start_cnt, 0);
|
||||
atomic64_set(&dom->flush_finish_cnt, 0);
|
||||
|
||||
dom->flush_queue = alloc_percpu(struct flush_queue);
|
||||
if (!dom->flush_queue)
|
||||
return -ENOMEM;
|
||||
|
||||
/* First make sure everything is cleared */
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct flush_queue *queue;
|
||||
|
||||
queue = per_cpu_ptr(dom->flush_queue, cpu);
|
||||
queue->head = 0;
|
||||
queue->tail = 0;
|
||||
queue->entries = NULL;
|
||||
}
|
||||
|
||||
/* Now start doing the allocation */
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct flush_queue *queue;
|
||||
|
||||
queue = per_cpu_ptr(dom->flush_queue, cpu);
|
||||
queue->entries = kzalloc(FLUSH_QUEUE_SIZE * sizeof(*queue->entries),
|
||||
GFP_KERNEL);
|
||||
if (!queue->entries) {
|
||||
dma_ops_domain_free_flush_queue(dom);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&queue->lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dma_ops_domain_flush_tlb(struct dma_ops_domain *dom)
|
||||
{
|
||||
atomic64_inc(&dom->flush_start_cnt);
|
||||
domain_flush_tlb(&dom->domain);
|
||||
domain_flush_complete(&dom->domain);
|
||||
atomic64_inc(&dom->flush_finish_cnt);
|
||||
}
|
||||
|
||||
static inline bool queue_ring_full(struct flush_queue *queue)
|
||||
static void iova_domain_flush_tlb(struct iova_domain *iovad)
|
||||
{
|
||||
assert_spin_locked(&queue->lock);
|
||||
struct dma_ops_domain *dom;
|
||||
|
||||
return (((queue->tail + 1) % FLUSH_QUEUE_SIZE) == queue->head);
|
||||
}
|
||||
|
||||
#define queue_ring_for_each(i, q) \
|
||||
for (i = (q)->head; i != (q)->tail; i = (i + 1) % FLUSH_QUEUE_SIZE)
|
||||
|
||||
static inline unsigned queue_ring_add(struct flush_queue *queue)
|
||||
{
|
||||
unsigned idx = queue->tail;
|
||||
|
||||
assert_spin_locked(&queue->lock);
|
||||
queue->tail = (idx + 1) % FLUSH_QUEUE_SIZE;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static inline void queue_ring_remove_head(struct flush_queue *queue)
|
||||
{
|
||||
assert_spin_locked(&queue->lock);
|
||||
queue->head = (queue->head + 1) % FLUSH_QUEUE_SIZE;
|
||||
}
|
||||
|
||||
static void queue_ring_free_flushed(struct dma_ops_domain *dom,
|
||||
struct flush_queue *queue)
|
||||
{
|
||||
u64 counter = atomic64_read(&dom->flush_finish_cnt);
|
||||
int idx;
|
||||
|
||||
queue_ring_for_each(idx, queue) {
|
||||
/*
|
||||
* This assumes that counter values in the ring-buffer are
|
||||
* monotonously rising.
|
||||
*/
|
||||
if (queue->entries[idx].counter >= counter)
|
||||
break;
|
||||
|
||||
free_iova_fast(&dom->iovad,
|
||||
queue->entries[idx].iova_pfn,
|
||||
queue->entries[idx].pages);
|
||||
|
||||
queue_ring_remove_head(queue);
|
||||
}
|
||||
}
|
||||
|
||||
static void queue_add(struct dma_ops_domain *dom,
|
||||
unsigned long address, unsigned long pages)
|
||||
{
|
||||
struct flush_queue *queue;
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
|
||||
pages = __roundup_pow_of_two(pages);
|
||||
address >>= PAGE_SHIFT;
|
||||
|
||||
queue = get_cpu_ptr(dom->flush_queue);
|
||||
spin_lock_irqsave(&queue->lock, flags);
|
||||
|
||||
/*
|
||||
* First remove the enries from the ring-buffer that are already
|
||||
* flushed to make the below queue_ring_full() check less likely
|
||||
*/
|
||||
queue_ring_free_flushed(dom, queue);
|
||||
|
||||
/*
|
||||
* When ring-queue is full, flush the entries from the IOTLB so
|
||||
* that we can free all entries with queue_ring_free_flushed()
|
||||
* below.
|
||||
*/
|
||||
if (queue_ring_full(queue)) {
|
||||
dma_ops_domain_flush_tlb(dom);
|
||||
queue_ring_free_flushed(dom, queue);
|
||||
}
|
||||
|
||||
idx = queue_ring_add(queue);
|
||||
|
||||
queue->entries[idx].iova_pfn = address;
|
||||
queue->entries[idx].pages = pages;
|
||||
queue->entries[idx].counter = atomic64_read(&dom->flush_start_cnt);
|
||||
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
|
||||
if (atomic_cmpxchg(&dom->flush_timer_on, 0, 1) == 0)
|
||||
mod_timer(&dom->flush_timer, jiffies + msecs_to_jiffies(10));
|
||||
|
||||
put_cpu_ptr(dom->flush_queue);
|
||||
}
|
||||
|
||||
static void queue_flush_timeout(unsigned long data)
|
||||
{
|
||||
struct dma_ops_domain *dom = (struct dma_ops_domain *)data;
|
||||
int cpu;
|
||||
|
||||
atomic_set(&dom->flush_timer_on, 0);
|
||||
dom = container_of(iovad, struct dma_ops_domain, iovad);
|
||||
|
||||
dma_ops_domain_flush_tlb(dom);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct flush_queue *queue;
|
||||
unsigned long flags;
|
||||
|
||||
queue = per_cpu_ptr(dom->flush_queue, cpu);
|
||||
spin_lock_irqsave(&queue->lock, flags);
|
||||
queue_ring_free_flushed(dom, queue);
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1975,11 +1756,6 @@ static void dma_ops_domain_free(struct dma_ops_domain *dom)
|
|||
|
||||
del_domain_from_list(&dom->domain);
|
||||
|
||||
if (timer_pending(&dom->flush_timer))
|
||||
del_timer(&dom->flush_timer);
|
||||
|
||||
dma_ops_domain_free_flush_queue(dom);
|
||||
|
||||
put_iova_domain(&dom->iovad);
|
||||
|
||||
free_pagetable(&dom->domain);
|
||||
|
@ -2015,16 +1791,11 @@ static struct dma_ops_domain *dma_ops_domain_alloc(void)
|
|||
init_iova_domain(&dma_dom->iovad, PAGE_SIZE,
|
||||
IOVA_START_PFN, DMA_32BIT_PFN);
|
||||
|
||||
/* Initialize reserved ranges */
|
||||
copy_reserved_iova(&reserved_iova_ranges, &dma_dom->iovad);
|
||||
|
||||
if (dma_ops_domain_alloc_flush_queue(dma_dom))
|
||||
if (init_iova_flush_queue(&dma_dom->iovad, iova_domain_flush_tlb, NULL))
|
||||
goto free_dma_dom;
|
||||
|
||||
setup_timer(&dma_dom->flush_timer, queue_flush_timeout,
|
||||
(unsigned long)dma_dom);
|
||||
|
||||
atomic_set(&dma_dom->flush_timer_on, 0);
|
||||
/* Initialize reserved ranges */
|
||||
copy_reserved_iova(&reserved_iova_ranges, &dma_dom->iovad);
|
||||
|
||||
add_domain_to_list(&dma_dom->domain);
|
||||
|
||||
|
@ -2055,7 +1826,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
|
|||
|
||||
pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK)
|
||||
<< DEV_ENTRY_MODE_SHIFT;
|
||||
pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV;
|
||||
pte_root |= DTE_FLAG_IR | DTE_FLAG_IW | DTE_FLAG_V | DTE_FLAG_TV;
|
||||
|
||||
flags = amd_iommu_dev_table[devid].data[1];
|
||||
|
||||
|
@ -2088,8 +1859,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
|
|||
flags |= tmp;
|
||||
}
|
||||
|
||||
|
||||
flags &= ~(DTE_FLAG_SA | 0xffffULL);
|
||||
flags &= ~DEV_DOMID_MASK;
|
||||
flags |= domain->id;
|
||||
|
||||
amd_iommu_dev_table[devid].data[1] = flags;
|
||||
|
@ -2099,7 +1869,7 @@ static void set_dte_entry(u16 devid, struct protection_domain *domain, bool ats)
|
|||
static void clear_dte_entry(u16 devid)
|
||||
{
|
||||
/* remove entry from the device table seen by the hardware */
|
||||
amd_iommu_dev_table[devid].data[0] = IOMMU_PTE_P | IOMMU_PTE_TV;
|
||||
amd_iommu_dev_table[devid].data[0] = DTE_FLAG_V | DTE_FLAG_TV;
|
||||
amd_iommu_dev_table[devid].data[1] &= DTE_FLAG_MASK;
|
||||
|
||||
amd_iommu_apply_erratum_63(devid);
|
||||
|
@ -2480,11 +2250,21 @@ static struct iommu_group *amd_iommu_device_group(struct device *dev)
|
|||
static struct protection_domain *get_domain(struct device *dev)
|
||||
{
|
||||
struct protection_domain *domain;
|
||||
struct iommu_domain *io_domain;
|
||||
|
||||
if (!check_device(dev))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
domain = get_dev_data(dev)->domain;
|
||||
if (domain == NULL && get_dev_data(dev)->defer_attach) {
|
||||
get_dev_data(dev)->defer_attach = false;
|
||||
io_domain = iommu_get_domain_for_dev(dev);
|
||||
domain = to_pdomain(io_domain);
|
||||
attach_device(dev, domain);
|
||||
}
|
||||
if (domain == NULL)
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (!dma_ops_domain(domain))
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
|
@ -2530,6 +2310,7 @@ static int dir2prot(enum dma_data_direction direction)
|
|||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function contains common code for mapping of a physically
|
||||
* contiguous memory region into DMA address space. It is used by all
|
||||
|
@ -2621,7 +2402,8 @@ static void __unmap_single(struct dma_ops_domain *dma_dom,
|
|||
domain_flush_tlb(&dma_dom->domain);
|
||||
domain_flush_complete(&dma_dom->domain);
|
||||
} else {
|
||||
queue_add(dma_dom, dma_addr, pages);
|
||||
pages = __roundup_pow_of_two(pages);
|
||||
queue_iova(&dma_dom->iovad, dma_addr >> PAGE_SHIFT, pages, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3375,6 +3157,13 @@ static void amd_iommu_apply_resv_region(struct device *dev,
|
|||
WARN_ON_ONCE(reserve_iova(&dma_dom->iovad, start, end) == NULL);
|
||||
}
|
||||
|
||||
static bool amd_iommu_is_attach_deferred(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct iommu_dev_data *dev_data = dev->archdata.iommu;
|
||||
return dev_data->defer_attach;
|
||||
}
|
||||
|
||||
const struct iommu_ops amd_iommu_ops = {
|
||||
.capable = amd_iommu_capable,
|
||||
.domain_alloc = amd_iommu_domain_alloc,
|
||||
|
@ -3391,6 +3180,7 @@ const struct iommu_ops amd_iommu_ops = {
|
|||
.get_resv_regions = amd_iommu_get_resv_regions,
|
||||
.put_resv_regions = amd_iommu_put_resv_regions,
|
||||
.apply_resv_region = amd_iommu_apply_resv_region,
|
||||
.is_attach_deferred = amd_iommu_is_attach_deferred,
|
||||
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
|
||||
};
|
||||
|
||||
|
@ -3779,11 +3569,6 @@ EXPORT_SYMBOL(amd_iommu_device_info);
|
|||
|
||||
static struct irq_chip amd_ir_chip;
|
||||
|
||||
#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
|
||||
#define DTE_IRQ_REMAP_INTCTL (2ULL << 60)
|
||||
#define DTE_IRQ_TABLE_LEN (8ULL << 1)
|
||||
#define DTE_IRQ_REMAP_ENABLE 1ULL
|
||||
|
||||
static void set_dte_irq_entry(u16 devid, struct irq_remap_table *table)
|
||||
{
|
||||
u64 dte;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/mem_encrypt.h>
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/iommu.h>
|
||||
|
@ -39,6 +38,7 @@
|
|||
#include <asm/io_apic.h>
|
||||
#include <asm/irq_remapping.h>
|
||||
|
||||
#include <linux/crash_dump.h>
|
||||
#include "amd_iommu_proto.h"
|
||||
#include "amd_iommu_types.h"
|
||||
#include "irq_remapping.h"
|
||||
|
@ -197,6 +197,11 @@ spinlock_t amd_iommu_pd_lock;
|
|||
* page table root pointer.
|
||||
*/
|
||||
struct dev_table_entry *amd_iommu_dev_table;
|
||||
/*
|
||||
* Pointer to a device table which the content of old device table
|
||||
* will be copied to. It's only be used in kdump kernel.
|
||||
*/
|
||||
static struct dev_table_entry *old_dev_tbl_cpy;
|
||||
|
||||
/*
|
||||
* The alias table is a driver specific data structure which contains the
|
||||
|
@ -210,6 +215,7 @@ u16 *amd_iommu_alias_table;
|
|||
* for a specific device. It is also indexed by the PCI device id.
|
||||
*/
|
||||
struct amd_iommu **amd_iommu_rlookup_table;
|
||||
EXPORT_SYMBOL(amd_iommu_rlookup_table);
|
||||
|
||||
/*
|
||||
* This table is used to find the irq remapping table for a given device id
|
||||
|
@ -259,6 +265,28 @@ static int amd_iommu_enable_interrupts(void);
|
|||
static int __init iommu_go_to_state(enum iommu_init_state state);
|
||||
static void init_device_table_dma(void);
|
||||
|
||||
static bool amd_iommu_pre_enabled = true;
|
||||
|
||||
bool translation_pre_enabled(struct amd_iommu *iommu)
|
||||
{
|
||||
return (iommu->flags & AMD_IOMMU_FLAG_TRANS_PRE_ENABLED);
|
||||
}
|
||||
EXPORT_SYMBOL(translation_pre_enabled);
|
||||
|
||||
static void clear_translation_pre_enabled(struct amd_iommu *iommu)
|
||||
{
|
||||
iommu->flags &= ~AMD_IOMMU_FLAG_TRANS_PRE_ENABLED;
|
||||
}
|
||||
|
||||
static void init_translation_status(struct amd_iommu *iommu)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(iommu->mmio_base + MMIO_CONTROL_OFFSET);
|
||||
if (ctrl & (1<<CONTROL_IOMMU_EN))
|
||||
iommu->flags |= AMD_IOMMU_FLAG_TRANS_PRE_ENABLED;
|
||||
}
|
||||
|
||||
static inline void update_last_devid(u16 devid)
|
||||
{
|
||||
if (devid > amd_iommu_last_bdf)
|
||||
|
@ -616,6 +644,14 @@ static void iommu_enable_command_buffer(struct amd_iommu *iommu)
|
|||
amd_iommu_reset_cmd_buffer(iommu);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function disables the command buffer
|
||||
*/
|
||||
static void iommu_disable_command_buffer(struct amd_iommu *iommu)
|
||||
{
|
||||
iommu_feature_disable(iommu, CONTROL_CMDBUF_EN);
|
||||
}
|
||||
|
||||
static void __init free_command_buffer(struct amd_iommu *iommu)
|
||||
{
|
||||
free_pages((unsigned long)iommu->cmd_buf, get_order(CMD_BUFFER_SIZE));
|
||||
|
@ -648,6 +684,14 @@ static void iommu_enable_event_buffer(struct amd_iommu *iommu)
|
|||
iommu_feature_enable(iommu, CONTROL_EVT_LOG_EN);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function disables the event log buffer
|
||||
*/
|
||||
static void iommu_disable_event_buffer(struct amd_iommu *iommu)
|
||||
{
|
||||
iommu_feature_disable(iommu, CONTROL_EVT_LOG_EN);
|
||||
}
|
||||
|
||||
static void __init free_event_buffer(struct amd_iommu *iommu)
|
||||
{
|
||||
free_pages((unsigned long)iommu->evt_buf, get_order(EVT_BUFFER_SIZE));
|
||||
|
@ -809,6 +853,96 @@ static int get_dev_entry_bit(u16 devid, u8 bit)
|
|||
}
|
||||
|
||||
|
||||
static bool copy_device_table(void)
|
||||
{
|
||||
u64 int_ctl, int_tab_len, entry = 0, last_entry = 0;
|
||||
struct dev_table_entry *old_devtb = NULL;
|
||||
u32 lo, hi, devid, old_devtb_size;
|
||||
phys_addr_t old_devtb_phys;
|
||||
struct amd_iommu *iommu;
|
||||
u16 dom_id, dte_v, irq_v;
|
||||
gfp_t gfp_flag;
|
||||
u64 tmp;
|
||||
|
||||
if (!amd_iommu_pre_enabled)
|
||||
return false;
|
||||
|
||||
pr_warn("Translation is already enabled - trying to copy translation structures\n");
|
||||
for_each_iommu(iommu) {
|
||||
/* All IOMMUs should use the same device table with the same size */
|
||||
lo = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET);
|
||||
hi = readl(iommu->mmio_base + MMIO_DEV_TABLE_OFFSET + 4);
|
||||
entry = (((u64) hi) << 32) + lo;
|
||||
if (last_entry && last_entry != entry) {
|
||||
pr_err("IOMMU:%d should use the same dev table as others!/n",
|
||||
iommu->index);
|
||||
return false;
|
||||
}
|
||||
last_entry = entry;
|
||||
|
||||
old_devtb_size = ((entry & ~PAGE_MASK) + 1) << 12;
|
||||
if (old_devtb_size != dev_table_size) {
|
||||
pr_err("The device table size of IOMMU:%d is not expected!/n",
|
||||
iommu->index);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
old_devtb_phys = entry & PAGE_MASK;
|
||||
if (old_devtb_phys >= 0x100000000ULL) {
|
||||
pr_err("The address of old device table is above 4G, not trustworthy!/n");
|
||||
return false;
|
||||
}
|
||||
old_devtb = memremap(old_devtb_phys, dev_table_size, MEMREMAP_WB);
|
||||
if (!old_devtb)
|
||||
return false;
|
||||
|
||||
gfp_flag = GFP_KERNEL | __GFP_ZERO | GFP_DMA32;
|
||||
old_dev_tbl_cpy = (void *)__get_free_pages(gfp_flag,
|
||||
get_order(dev_table_size));
|
||||
if (old_dev_tbl_cpy == NULL) {
|
||||
pr_err("Failed to allocate memory for copying old device table!/n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) {
|
||||
old_dev_tbl_cpy[devid] = old_devtb[devid];
|
||||
dom_id = old_devtb[devid].data[1] & DEV_DOMID_MASK;
|
||||
dte_v = old_devtb[devid].data[0] & DTE_FLAG_V;
|
||||
|
||||
if (dte_v && dom_id) {
|
||||
old_dev_tbl_cpy[devid].data[0] = old_devtb[devid].data[0];
|
||||
old_dev_tbl_cpy[devid].data[1] = old_devtb[devid].data[1];
|
||||
__set_bit(dom_id, amd_iommu_pd_alloc_bitmap);
|
||||
/* If gcr3 table existed, mask it out */
|
||||
if (old_devtb[devid].data[0] & DTE_FLAG_GV) {
|
||||
tmp = DTE_GCR3_VAL_B(~0ULL) << DTE_GCR3_SHIFT_B;
|
||||
tmp |= DTE_GCR3_VAL_C(~0ULL) << DTE_GCR3_SHIFT_C;
|
||||
old_dev_tbl_cpy[devid].data[1] &= ~tmp;
|
||||
tmp = DTE_GCR3_VAL_A(~0ULL) << DTE_GCR3_SHIFT_A;
|
||||
tmp |= DTE_FLAG_GV;
|
||||
old_dev_tbl_cpy[devid].data[0] &= ~tmp;
|
||||
}
|
||||
}
|
||||
|
||||
irq_v = old_devtb[devid].data[2] & DTE_IRQ_REMAP_ENABLE;
|
||||
int_ctl = old_devtb[devid].data[2] & DTE_IRQ_REMAP_INTCTL_MASK;
|
||||
int_tab_len = old_devtb[devid].data[2] & DTE_IRQ_TABLE_LEN_MASK;
|
||||
if (irq_v && (int_ctl || int_tab_len)) {
|
||||
if ((int_ctl != DTE_IRQ_REMAP_INTCTL) ||
|
||||
(int_tab_len != DTE_IRQ_TABLE_LEN)) {
|
||||
pr_err("Wrong old irq remapping flag: %#x\n", devid);
|
||||
return false;
|
||||
}
|
||||
|
||||
old_dev_tbl_cpy[devid].data[2] = old_devtb[devid].data[2];
|
||||
}
|
||||
}
|
||||
memunmap(old_devtb);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void amd_iommu_apply_erratum_63(u16 devid)
|
||||
{
|
||||
int sysmgt;
|
||||
|
@ -1400,6 +1534,16 @@ static int __init init_iommu_one(struct amd_iommu *iommu, struct ivhd_header *h)
|
|||
|
||||
iommu->int_enabled = false;
|
||||
|
||||
init_translation_status(iommu);
|
||||
if (translation_pre_enabled(iommu) && !is_kdump_kernel()) {
|
||||
iommu_disable(iommu);
|
||||
clear_translation_pre_enabled(iommu);
|
||||
pr_warn("Translation was enabled for IOMMU:%d but we are not in kdump mode\n",
|
||||
iommu->index);
|
||||
}
|
||||
if (amd_iommu_pre_enabled)
|
||||
amd_iommu_pre_enabled = translation_pre_enabled(iommu);
|
||||
|
||||
ret = init_iommu_from_acpi(iommu, h);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1893,8 +2037,7 @@ static int __init init_memory_definitions(struct acpi_table_header *table)
|
|||
}
|
||||
|
||||
/*
|
||||
* Init the device table to not allow DMA access for devices and
|
||||
* suppress all page faults
|
||||
* Init the device table to not allow DMA access for devices
|
||||
*/
|
||||
static void init_device_table_dma(void)
|
||||
{
|
||||
|
@ -1903,14 +2046,6 @@ static void init_device_table_dma(void)
|
|||
for (devid = 0; devid <= amd_iommu_last_bdf; ++devid) {
|
||||
set_dev_entry_bit(devid, DEV_ENTRY_VALID);
|
||||
set_dev_entry_bit(devid, DEV_ENTRY_TRANSLATION);
|
||||
/*
|
||||
* In kdump kernels in-flight DMA from the old kernel might
|
||||
* cause IO_PAGE_FAULTs. There are no reports that a kdump
|
||||
* actually failed because of that, so just disable fault
|
||||
* reporting in the hardware to get rid of the messages
|
||||
*/
|
||||
if (is_kdump_kernel())
|
||||
set_dev_entry_bit(devid, DEV_ENTRY_NO_PAGE_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2023,15 +2158,8 @@ static void iommu_enable_ga(struct amd_iommu *iommu)
|
|||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* This function finally enables all IOMMUs found in the system after
|
||||
* they have been initialized
|
||||
*/
|
||||
static void early_enable_iommus(void)
|
||||
static void early_enable_iommu(struct amd_iommu *iommu)
|
||||
{
|
||||
struct amd_iommu *iommu;
|
||||
|
||||
for_each_iommu(iommu) {
|
||||
iommu_disable(iommu);
|
||||
iommu_init_flags(iommu);
|
||||
iommu_set_device_table(iommu);
|
||||
|
@ -2041,6 +2169,51 @@ static void early_enable_iommus(void)
|
|||
iommu_enable_ga(iommu);
|
||||
iommu_enable(iommu);
|
||||
iommu_flush_all_caches(iommu);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function finally enables all IOMMUs found in the system after
|
||||
* they have been initialized.
|
||||
*
|
||||
* Or if in kdump kernel and IOMMUs are all pre-enabled, try to copy
|
||||
* the old content of device table entries. Not this case or copy failed,
|
||||
* just continue as normal kernel does.
|
||||
*/
|
||||
static void early_enable_iommus(void)
|
||||
{
|
||||
struct amd_iommu *iommu;
|
||||
|
||||
|
||||
if (!copy_device_table()) {
|
||||
/*
|
||||
* If come here because of failure in copying device table from old
|
||||
* kernel with all IOMMUs enabled, print error message and try to
|
||||
* free allocated old_dev_tbl_cpy.
|
||||
*/
|
||||
if (amd_iommu_pre_enabled)
|
||||
pr_err("Failed to copy DEV table from previous kernel.\n");
|
||||
if (old_dev_tbl_cpy != NULL)
|
||||
free_pages((unsigned long)old_dev_tbl_cpy,
|
||||
get_order(dev_table_size));
|
||||
|
||||
for_each_iommu(iommu) {
|
||||
clear_translation_pre_enabled(iommu);
|
||||
early_enable_iommu(iommu);
|
||||
}
|
||||
} else {
|
||||
pr_info("Copied DEV table from previous kernel.\n");
|
||||
free_pages((unsigned long)amd_iommu_dev_table,
|
||||
get_order(dev_table_size));
|
||||
amd_iommu_dev_table = old_dev_tbl_cpy;
|
||||
for_each_iommu(iommu) {
|
||||
iommu_disable_command_buffer(iommu);
|
||||
iommu_disable_event_buffer(iommu);
|
||||
iommu_enable_command_buffer(iommu);
|
||||
iommu_enable_event_buffer(iommu);
|
||||
iommu_enable_ga(iommu);
|
||||
iommu_set_device_table(iommu);
|
||||
iommu_flush_all_caches(iommu);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IRQ_REMAP
|
||||
|
@ -2276,7 +2449,8 @@ static int __init early_amd_iommu_init(void)
|
|||
|
||||
/* Device table - directly used by all IOMMUs */
|
||||
ret = -ENOMEM;
|
||||
amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
|
||||
amd_iommu_dev_table = (void *)__get_free_pages(
|
||||
GFP_KERNEL | __GFP_ZERO | GFP_DMA32,
|
||||
get_order(dev_table_size));
|
||||
if (amd_iommu_dev_table == NULL)
|
||||
goto out;
|
||||
|
@ -2326,6 +2500,7 @@ static int __init early_amd_iommu_init(void)
|
|||
goto out;
|
||||
|
||||
/* Disable any previously enabled IOMMUs */
|
||||
if (!is_kdump_kernel() || amd_iommu_disabled)
|
||||
disable_iommus();
|
||||
|
||||
if (amd_iommu_irq_remap)
|
||||
|
|
|
@ -97,4 +97,6 @@ static inline void *iommu_phys_to_virt(unsigned long paddr)
|
|||
return phys_to_virt(__sme_clr(paddr));
|
||||
}
|
||||
|
||||
extern bool translation_pre_enabled(struct amd_iommu *iommu);
|
||||
extern struct iommu_dev_data *get_dev_data(struct device *dev);
|
||||
#endif /* _ASM_X86_AMD_IOMMU_PROTO_H */
|
||||
|
|
|
@ -250,6 +250,14 @@
|
|||
|
||||
#define GA_GUEST_NR 0x1
|
||||
|
||||
/* Bit value definition for dte irq remapping fields*/
|
||||
#define DTE_IRQ_PHYS_ADDR_MASK (((1ULL << 45)-1) << 6)
|
||||
#define DTE_IRQ_REMAP_INTCTL_MASK (0x3ULL << 60)
|
||||
#define DTE_IRQ_TABLE_LEN_MASK (0xfULL << 1)
|
||||
#define DTE_IRQ_REMAP_INTCTL (2ULL << 60)
|
||||
#define DTE_IRQ_TABLE_LEN (8ULL << 1)
|
||||
#define DTE_IRQ_REMAP_ENABLE 1ULL
|
||||
|
||||
#define PAGE_MODE_NONE 0x00
|
||||
#define PAGE_MODE_1_LEVEL 0x01
|
||||
#define PAGE_MODE_2_LEVEL 0x02
|
||||
|
@ -265,7 +273,7 @@
|
|||
#define PM_LEVEL_INDEX(x, a) (((a) >> PM_LEVEL_SHIFT((x))) & 0x1ffULL)
|
||||
#define PM_LEVEL_ENC(x) (((x) << 9) & 0xe00ULL)
|
||||
#define PM_LEVEL_PDE(x, a) ((a) | PM_LEVEL_ENC((x)) | \
|
||||
IOMMU_PTE_P | IOMMU_PTE_IR | IOMMU_PTE_IW)
|
||||
IOMMU_PTE_PR | IOMMU_PTE_IR | IOMMU_PTE_IW)
|
||||
#define PM_PTE_LEVEL(pte) (((pte) >> 9) & 0x7ULL)
|
||||
|
||||
#define PM_MAP_4k 0
|
||||
|
@ -314,19 +322,29 @@
|
|||
#define PTE_LEVEL_PAGE_SIZE(level) \
|
||||
(1ULL << (12 + (9 * (level))))
|
||||
|
||||
#define IOMMU_PTE_P (1ULL << 0)
|
||||
#define IOMMU_PTE_TV (1ULL << 1)
|
||||
/*
|
||||
* Bit value definition for I/O PTE fields
|
||||
*/
|
||||
#define IOMMU_PTE_PR (1ULL << 0)
|
||||
#define IOMMU_PTE_U (1ULL << 59)
|
||||
#define IOMMU_PTE_FC (1ULL << 60)
|
||||
#define IOMMU_PTE_IR (1ULL << 61)
|
||||
#define IOMMU_PTE_IW (1ULL << 62)
|
||||
|
||||
/*
|
||||
* Bit value definition for DTE fields
|
||||
*/
|
||||
#define DTE_FLAG_V (1ULL << 0)
|
||||
#define DTE_FLAG_TV (1ULL << 1)
|
||||
#define DTE_FLAG_IR (1ULL << 61)
|
||||
#define DTE_FLAG_IW (1ULL << 62)
|
||||
|
||||
#define DTE_FLAG_IOTLB (1ULL << 32)
|
||||
#define DTE_FLAG_SA (1ULL << 34)
|
||||
#define DTE_FLAG_GV (1ULL << 55)
|
||||
#define DTE_FLAG_MASK (0x3ffULL << 32)
|
||||
#define DTE_GLX_SHIFT (56)
|
||||
#define DTE_GLX_MASK (3)
|
||||
#define DEV_DOMID_MASK 0xffffULL
|
||||
|
||||
#define DTE_GCR3_VAL_A(x) (((x) >> 12) & 0x00007ULL)
|
||||
#define DTE_GCR3_VAL_B(x) (((x) >> 15) & 0x0ffffULL)
|
||||
|
@ -343,7 +361,7 @@
|
|||
#define GCR3_VALID 0x01ULL
|
||||
|
||||
#define IOMMU_PAGE_MASK (((1ULL << 52) - 1) & ~0xfffULL)
|
||||
#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_P)
|
||||
#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_PR)
|
||||
#define IOMMU_PTE_PAGE(pte) (iommu_phys_to_virt((pte) & IOMMU_PAGE_MASK))
|
||||
#define IOMMU_PTE_MODE(pte) (((pte) >> 9) & 0x07)
|
||||
|
||||
|
@ -435,6 +453,8 @@ struct iommu_domain;
|
|||
struct irq_domain;
|
||||
struct amd_irte_ops;
|
||||
|
||||
#define AMD_IOMMU_FLAG_TRANS_PRE_ENABLED (1 << 0)
|
||||
|
||||
/*
|
||||
* This structure contains generic data for IOMMU protection domains
|
||||
* independent of their use.
|
||||
|
@ -569,6 +589,7 @@ struct amd_iommu {
|
|||
struct amd_irte_ops *irte_ops;
|
||||
#endif
|
||||
|
||||
u32 flags;
|
||||
volatile u64 __aligned(8) cmd_sem;
|
||||
};
|
||||
|
||||
|
@ -599,6 +620,30 @@ struct devid_map {
|
|||
bool cmd_line;
|
||||
};
|
||||
|
||||
/*
|
||||
* This struct contains device specific data for the IOMMU
|
||||
*/
|
||||
struct iommu_dev_data {
|
||||
struct list_head list; /* For domain->dev_list */
|
||||
struct list_head dev_data_list; /* For global dev_data_list */
|
||||
struct protection_domain *domain; /* Domain the device is bound to */
|
||||
u16 devid; /* PCI Device ID */
|
||||
u16 alias; /* Alias Device ID */
|
||||
bool iommu_v2; /* Device can make use of IOMMUv2 */
|
||||
bool passthrough; /* Device is identity mapped */
|
||||
struct {
|
||||
bool enabled;
|
||||
int qdep;
|
||||
} ats; /* ATS state */
|
||||
bool pri_tlp; /* PASID TLB required for
|
||||
PPR completions */
|
||||
u32 errata; /* Bitmap for errata to apply */
|
||||
bool use_vapic; /* Enable device to use vapic mode */
|
||||
bool defer_attach;
|
||||
|
||||
struct ratelimit_state rs; /* Ratelimit IOPF messages */
|
||||
};
|
||||
|
||||
/* Map HPET and IOAPIC ids to the devid used by the IOMMU */
|
||||
extern struct list_head ioapic_map;
|
||||
extern struct list_head hpet_map;
|
||||
|
|
|
@ -554,14 +554,30 @@ static int ppr_notifier(struct notifier_block *nb, unsigned long e, void *data)
|
|||
unsigned long flags;
|
||||
struct fault *fault;
|
||||
bool finish;
|
||||
u16 tag;
|
||||
u16 tag, devid;
|
||||
int ret;
|
||||
struct iommu_dev_data *dev_data;
|
||||
struct pci_dev *pdev = NULL;
|
||||
|
||||
iommu_fault = data;
|
||||
tag = iommu_fault->tag & 0x1ff;
|
||||
finish = (iommu_fault->tag >> 9) & 1;
|
||||
|
||||
devid = iommu_fault->device_id;
|
||||
pdev = pci_get_bus_and_slot(PCI_BUS_NUM(devid), devid & 0xff);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
dev_data = get_dev_data(&pdev->dev);
|
||||
|
||||
/* In kdump kernel pci dev is not initialized yet -> send INVALID */
|
||||
ret = NOTIFY_DONE;
|
||||
if (translation_pre_enabled(amd_iommu_rlookup_table[devid])
|
||||
&& dev_data->defer_attach) {
|
||||
amd_iommu_complete_ppr(pdev, iommu_fault->pasid,
|
||||
PPR_INVALID, tag);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_state = get_device_state(iommu_fault->device_id);
|
||||
if (dev_state == NULL)
|
||||
goto out;
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* IOMMU API for ARM architected SMMU implementations.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) 2013 ARM Limited
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#ifndef _ARM_SMMU_REGS_H
|
||||
#define _ARM_SMMU_REGS_H
|
||||
|
||||
/* Configuration registers */
|
||||
#define ARM_SMMU_GR0_sCR0 0x0
|
||||
#define sCR0_CLIENTPD (1 << 0)
|
||||
#define sCR0_GFRE (1 << 1)
|
||||
#define sCR0_GFIE (1 << 2)
|
||||
#define sCR0_EXIDENABLE (1 << 3)
|
||||
#define sCR0_GCFGFRE (1 << 4)
|
||||
#define sCR0_GCFGFIE (1 << 5)
|
||||
#define sCR0_USFCFG (1 << 10)
|
||||
#define sCR0_VMIDPNE (1 << 11)
|
||||
#define sCR0_PTM (1 << 12)
|
||||
#define sCR0_FB (1 << 13)
|
||||
#define sCR0_VMID16EN (1 << 31)
|
||||
#define sCR0_BSU_SHIFT 14
|
||||
#define sCR0_BSU_MASK 0x3
|
||||
|
||||
/* Auxiliary Configuration register */
|
||||
#define ARM_SMMU_GR0_sACR 0x10
|
||||
|
||||
/* Identification registers */
|
||||
#define ARM_SMMU_GR0_ID0 0x20
|
||||
#define ARM_SMMU_GR0_ID1 0x24
|
||||
#define ARM_SMMU_GR0_ID2 0x28
|
||||
#define ARM_SMMU_GR0_ID3 0x2c
|
||||
#define ARM_SMMU_GR0_ID4 0x30
|
||||
#define ARM_SMMU_GR0_ID5 0x34
|
||||
#define ARM_SMMU_GR0_ID6 0x38
|
||||
#define ARM_SMMU_GR0_ID7 0x3c
|
||||
#define ARM_SMMU_GR0_sGFSR 0x48
|
||||
#define ARM_SMMU_GR0_sGFSYNR0 0x50
|
||||
#define ARM_SMMU_GR0_sGFSYNR1 0x54
|
||||
#define ARM_SMMU_GR0_sGFSYNR2 0x58
|
||||
|
||||
#define ID0_S1TS (1 << 30)
|
||||
#define ID0_S2TS (1 << 29)
|
||||
#define ID0_NTS (1 << 28)
|
||||
#define ID0_SMS (1 << 27)
|
||||
#define ID0_ATOSNS (1 << 26)
|
||||
#define ID0_PTFS_NO_AARCH32 (1 << 25)
|
||||
#define ID0_PTFS_NO_AARCH32S (1 << 24)
|
||||
#define ID0_CTTW (1 << 14)
|
||||
#define ID0_NUMIRPT_SHIFT 16
|
||||
#define ID0_NUMIRPT_MASK 0xff
|
||||
#define ID0_NUMSIDB_SHIFT 9
|
||||
#define ID0_NUMSIDB_MASK 0xf
|
||||
#define ID0_EXIDS (1 << 8)
|
||||
#define ID0_NUMSMRG_SHIFT 0
|
||||
#define ID0_NUMSMRG_MASK 0xff
|
||||
|
||||
#define ID1_PAGESIZE (1 << 31)
|
||||
#define ID1_NUMPAGENDXB_SHIFT 28
|
||||
#define ID1_NUMPAGENDXB_MASK 7
|
||||
#define ID1_NUMS2CB_SHIFT 16
|
||||
#define ID1_NUMS2CB_MASK 0xff
|
||||
#define ID1_NUMCB_SHIFT 0
|
||||
#define ID1_NUMCB_MASK 0xff
|
||||
|
||||
#define ID2_OAS_SHIFT 4
|
||||
#define ID2_OAS_MASK 0xf
|
||||
#define ID2_IAS_SHIFT 0
|
||||
#define ID2_IAS_MASK 0xf
|
||||
#define ID2_UBS_SHIFT 8
|
||||
#define ID2_UBS_MASK 0xf
|
||||
#define ID2_PTFS_4K (1 << 12)
|
||||
#define ID2_PTFS_16K (1 << 13)
|
||||
#define ID2_PTFS_64K (1 << 14)
|
||||
#define ID2_VMID16 (1 << 15)
|
||||
|
||||
#define ID7_MAJOR_SHIFT 4
|
||||
#define ID7_MAJOR_MASK 0xf
|
||||
|
||||
/* Global TLB invalidation */
|
||||
#define ARM_SMMU_GR0_TLBIVMID 0x64
|
||||
#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
|
||||
#define ARM_SMMU_GR0_TLBIALLH 0x6c
|
||||
#define ARM_SMMU_GR0_sTLBGSYNC 0x70
|
||||
#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
|
||||
#define sTLBGSTATUS_GSACTIVE (1 << 0)
|
||||
|
||||
/* Stream mapping registers */
|
||||
#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
|
||||
#define SMR_VALID (1 << 31)
|
||||
#define SMR_MASK_SHIFT 16
|
||||
#define SMR_ID_SHIFT 0
|
||||
|
||||
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
|
||||
#define S2CR_CBNDX_SHIFT 0
|
||||
#define S2CR_CBNDX_MASK 0xff
|
||||
#define S2CR_EXIDVALID (1 << 10)
|
||||
#define S2CR_TYPE_SHIFT 16
|
||||
#define S2CR_TYPE_MASK 0x3
|
||||
enum arm_smmu_s2cr_type {
|
||||
S2CR_TYPE_TRANS,
|
||||
S2CR_TYPE_BYPASS,
|
||||
S2CR_TYPE_FAULT,
|
||||
};
|
||||
|
||||
#define S2CR_PRIVCFG_SHIFT 24
|
||||
#define S2CR_PRIVCFG_MASK 0x3
|
||||
enum arm_smmu_s2cr_privcfg {
|
||||
S2CR_PRIVCFG_DEFAULT,
|
||||
S2CR_PRIVCFG_DIPAN,
|
||||
S2CR_PRIVCFG_UNPRIV,
|
||||
S2CR_PRIVCFG_PRIV,
|
||||
};
|
||||
|
||||
/* Context bank attribute registers */
|
||||
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
|
||||
#define CBAR_VMID_SHIFT 0
|
||||
#define CBAR_VMID_MASK 0xff
|
||||
#define CBAR_S1_BPSHCFG_SHIFT 8
|
||||
#define CBAR_S1_BPSHCFG_MASK 3
|
||||
#define CBAR_S1_BPSHCFG_NSH 3
|
||||
#define CBAR_S1_MEMATTR_SHIFT 12
|
||||
#define CBAR_S1_MEMATTR_MASK 0xf
|
||||
#define CBAR_S1_MEMATTR_WB 0xf
|
||||
#define CBAR_TYPE_SHIFT 16
|
||||
#define CBAR_TYPE_MASK 0x3
|
||||
#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_IRPTNDX_SHIFT 24
|
||||
#define CBAR_IRPTNDX_MASK 0xff
|
||||
|
||||
#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
|
||||
#define CBA2R_RW64_32BIT (0 << 0)
|
||||
#define CBA2R_RW64_64BIT (1 << 0)
|
||||
#define CBA2R_VMID_SHIFT 16
|
||||
#define CBA2R_VMID_MASK 0xffff
|
||||
|
||||
#define ARM_SMMU_CB_SCTLR 0x0
|
||||
#define ARM_SMMU_CB_ACTLR 0x4
|
||||
#define ARM_SMMU_CB_RESUME 0x8
|
||||
#define ARM_SMMU_CB_TTBCR2 0x10
|
||||
#define ARM_SMMU_CB_TTBR0 0x20
|
||||
#define ARM_SMMU_CB_TTBR1 0x28
|
||||
#define ARM_SMMU_CB_TTBCR 0x30
|
||||
#define ARM_SMMU_CB_CONTEXTIDR 0x34
|
||||
#define ARM_SMMU_CB_S1_MAIR0 0x38
|
||||
#define ARM_SMMU_CB_S1_MAIR1 0x3c
|
||||
#define ARM_SMMU_CB_PAR 0x50
|
||||
#define ARM_SMMU_CB_FSR 0x58
|
||||
#define ARM_SMMU_CB_FAR 0x60
|
||||
#define ARM_SMMU_CB_FSYNR0 0x68
|
||||
#define ARM_SMMU_CB_S1_TLBIVA 0x600
|
||||
#define ARM_SMMU_CB_S1_TLBIASID 0x610
|
||||
#define ARM_SMMU_CB_S1_TLBIVAL 0x620
|
||||
#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
|
||||
#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
|
||||
#define ARM_SMMU_CB_TLBSYNC 0x7f0
|
||||
#define ARM_SMMU_CB_TLBSTATUS 0x7f4
|
||||
#define ARM_SMMU_CB_ATS1PR 0x800
|
||||
#define ARM_SMMU_CB_ATSR 0x8f0
|
||||
|
||||
#define SCTLR_S1_ASIDPNE (1 << 12)
|
||||
#define SCTLR_CFCFG (1 << 7)
|
||||
#define SCTLR_CFIE (1 << 6)
|
||||
#define SCTLR_CFRE (1 << 5)
|
||||
#define SCTLR_E (1 << 4)
|
||||
#define SCTLR_AFE (1 << 2)
|
||||
#define SCTLR_TRE (1 << 1)
|
||||
#define SCTLR_M (1 << 0)
|
||||
|
||||
#define CB_PAR_F (1 << 0)
|
||||
|
||||
#define ATSR_ACTIVE (1 << 0)
|
||||
|
||||
#define RESUME_RETRY (0 << 0)
|
||||
#define RESUME_TERMINATE (1 << 0)
|
||||
|
||||
#define TTBCR2_SEP_SHIFT 15
|
||||
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
|
||||
#define TTBCR2_AS (1 << 4)
|
||||
|
||||
#define TTBRn_ASID_SHIFT 48
|
||||
|
||||
#define FSR_MULTI (1 << 31)
|
||||
#define FSR_SS (1 << 30)
|
||||
#define FSR_UUT (1 << 8)
|
||||
#define FSR_ASF (1 << 7)
|
||||
#define FSR_TLBLKF (1 << 6)
|
||||
#define FSR_TLBMCF (1 << 5)
|
||||
#define FSR_EF (1 << 4)
|
||||
#define FSR_PF (1 << 3)
|
||||
#define FSR_AFF (1 << 2)
|
||||
#define FSR_TF (1 << 1)
|
||||
|
||||
#define FSR_IGN (FSR_AFF | FSR_ASF | \
|
||||
FSR_TLBMCF | FSR_TLBLKF)
|
||||
#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
|
||||
FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
|
||||
|
||||
#define FSYNR0_WNR (1 << 4)
|
||||
|
||||
#endif /* _ARM_SMMU_REGS_H */
|
|
@ -2852,9 +2852,15 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
|
|||
struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
|
||||
|
||||
arm_smmu_device_disable(smmu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void arm_smmu_device_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
arm_smmu_device_remove(pdev);
|
||||
}
|
||||
|
||||
static const struct of_device_id arm_smmu_of_match[] = {
|
||||
{ .compatible = "arm,smmu-v3", },
|
||||
{ },
|
||||
|
@ -2868,6 +2874,7 @@ static struct platform_driver arm_smmu_driver = {
|
|||
},
|
||||
.probe = arm_smmu_device_probe,
|
||||
.remove = arm_smmu_device_remove,
|
||||
.shutdown = arm_smmu_device_shutdown,
|
||||
};
|
||||
module_platform_driver(arm_smmu_driver);
|
||||
|
||||
|
|
|
@ -54,6 +54,15 @@
|
|||
#include <linux/amba/bus.h>
|
||||
|
||||
#include "io-pgtable.h"
|
||||
#include "arm-smmu-regs.h"
|
||||
|
||||
#define ARM_MMU500_ACTLR_CPRE (1 << 1)
|
||||
|
||||
#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
|
||||
#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
|
||||
|
||||
#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
|
||||
#define TLB_SPIN_COUNT 10
|
||||
|
||||
/* Maximum number of context banks per SMMU */
|
||||
#define ARM_SMMU_MAX_CBS 128
|
||||
|
@ -83,211 +92,9 @@
|
|||
#define smmu_write_atomic_lq writel_relaxed
|
||||
#endif
|
||||
|
||||
/* Configuration registers */
|
||||
#define ARM_SMMU_GR0_sCR0 0x0
|
||||
#define sCR0_CLIENTPD (1 << 0)
|
||||
#define sCR0_GFRE (1 << 1)
|
||||
#define sCR0_GFIE (1 << 2)
|
||||
#define sCR0_EXIDENABLE (1 << 3)
|
||||
#define sCR0_GCFGFRE (1 << 4)
|
||||
#define sCR0_GCFGFIE (1 << 5)
|
||||
#define sCR0_USFCFG (1 << 10)
|
||||
#define sCR0_VMIDPNE (1 << 11)
|
||||
#define sCR0_PTM (1 << 12)
|
||||
#define sCR0_FB (1 << 13)
|
||||
#define sCR0_VMID16EN (1 << 31)
|
||||
#define sCR0_BSU_SHIFT 14
|
||||
#define sCR0_BSU_MASK 0x3
|
||||
|
||||
/* Auxiliary Configuration register */
|
||||
#define ARM_SMMU_GR0_sACR 0x10
|
||||
|
||||
/* Identification registers */
|
||||
#define ARM_SMMU_GR0_ID0 0x20
|
||||
#define ARM_SMMU_GR0_ID1 0x24
|
||||
#define ARM_SMMU_GR0_ID2 0x28
|
||||
#define ARM_SMMU_GR0_ID3 0x2c
|
||||
#define ARM_SMMU_GR0_ID4 0x30
|
||||
#define ARM_SMMU_GR0_ID5 0x34
|
||||
#define ARM_SMMU_GR0_ID6 0x38
|
||||
#define ARM_SMMU_GR0_ID7 0x3c
|
||||
#define ARM_SMMU_GR0_sGFSR 0x48
|
||||
#define ARM_SMMU_GR0_sGFSYNR0 0x50
|
||||
#define ARM_SMMU_GR0_sGFSYNR1 0x54
|
||||
#define ARM_SMMU_GR0_sGFSYNR2 0x58
|
||||
|
||||
#define ID0_S1TS (1 << 30)
|
||||
#define ID0_S2TS (1 << 29)
|
||||
#define ID0_NTS (1 << 28)
|
||||
#define ID0_SMS (1 << 27)
|
||||
#define ID0_ATOSNS (1 << 26)
|
||||
#define ID0_PTFS_NO_AARCH32 (1 << 25)
|
||||
#define ID0_PTFS_NO_AARCH32S (1 << 24)
|
||||
#define ID0_CTTW (1 << 14)
|
||||
#define ID0_NUMIRPT_SHIFT 16
|
||||
#define ID0_NUMIRPT_MASK 0xff
|
||||
#define ID0_NUMSIDB_SHIFT 9
|
||||
#define ID0_NUMSIDB_MASK 0xf
|
||||
#define ID0_EXIDS (1 << 8)
|
||||
#define ID0_NUMSMRG_SHIFT 0
|
||||
#define ID0_NUMSMRG_MASK 0xff
|
||||
|
||||
#define ID1_PAGESIZE (1 << 31)
|
||||
#define ID1_NUMPAGENDXB_SHIFT 28
|
||||
#define ID1_NUMPAGENDXB_MASK 7
|
||||
#define ID1_NUMS2CB_SHIFT 16
|
||||
#define ID1_NUMS2CB_MASK 0xff
|
||||
#define ID1_NUMCB_SHIFT 0
|
||||
#define ID1_NUMCB_MASK 0xff
|
||||
|
||||
#define ID2_OAS_SHIFT 4
|
||||
#define ID2_OAS_MASK 0xf
|
||||
#define ID2_IAS_SHIFT 0
|
||||
#define ID2_IAS_MASK 0xf
|
||||
#define ID2_UBS_SHIFT 8
|
||||
#define ID2_UBS_MASK 0xf
|
||||
#define ID2_PTFS_4K (1 << 12)
|
||||
#define ID2_PTFS_16K (1 << 13)
|
||||
#define ID2_PTFS_64K (1 << 14)
|
||||
#define ID2_VMID16 (1 << 15)
|
||||
|
||||
#define ID7_MAJOR_SHIFT 4
|
||||
#define ID7_MAJOR_MASK 0xf
|
||||
|
||||
/* Global TLB invalidation */
|
||||
#define ARM_SMMU_GR0_TLBIVMID 0x64
|
||||
#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
|
||||
#define ARM_SMMU_GR0_TLBIALLH 0x6c
|
||||
#define ARM_SMMU_GR0_sTLBGSYNC 0x70
|
||||
#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
|
||||
#define sTLBGSTATUS_GSACTIVE (1 << 0)
|
||||
#define TLB_LOOP_TIMEOUT 1000000 /* 1s! */
|
||||
#define TLB_SPIN_COUNT 10
|
||||
|
||||
/* Stream mapping registers */
|
||||
#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
|
||||
#define SMR_VALID (1 << 31)
|
||||
#define SMR_MASK_SHIFT 16
|
||||
#define SMR_ID_SHIFT 0
|
||||
|
||||
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
|
||||
#define S2CR_CBNDX_SHIFT 0
|
||||
#define S2CR_CBNDX_MASK 0xff
|
||||
#define S2CR_EXIDVALID (1 << 10)
|
||||
#define S2CR_TYPE_SHIFT 16
|
||||
#define S2CR_TYPE_MASK 0x3
|
||||
enum arm_smmu_s2cr_type {
|
||||
S2CR_TYPE_TRANS,
|
||||
S2CR_TYPE_BYPASS,
|
||||
S2CR_TYPE_FAULT,
|
||||
};
|
||||
|
||||
#define S2CR_PRIVCFG_SHIFT 24
|
||||
#define S2CR_PRIVCFG_MASK 0x3
|
||||
enum arm_smmu_s2cr_privcfg {
|
||||
S2CR_PRIVCFG_DEFAULT,
|
||||
S2CR_PRIVCFG_DIPAN,
|
||||
S2CR_PRIVCFG_UNPRIV,
|
||||
S2CR_PRIVCFG_PRIV,
|
||||
};
|
||||
|
||||
/* Context bank attribute registers */
|
||||
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
|
||||
#define CBAR_VMID_SHIFT 0
|
||||
#define CBAR_VMID_MASK 0xff
|
||||
#define CBAR_S1_BPSHCFG_SHIFT 8
|
||||
#define CBAR_S1_BPSHCFG_MASK 3
|
||||
#define CBAR_S1_BPSHCFG_NSH 3
|
||||
#define CBAR_S1_MEMATTR_SHIFT 12
|
||||
#define CBAR_S1_MEMATTR_MASK 0xf
|
||||
#define CBAR_S1_MEMATTR_WB 0xf
|
||||
#define CBAR_TYPE_SHIFT 16
|
||||
#define CBAR_TYPE_MASK 0x3
|
||||
#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
|
||||
#define CBAR_IRPTNDX_SHIFT 24
|
||||
#define CBAR_IRPTNDX_MASK 0xff
|
||||
|
||||
#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
|
||||
#define CBA2R_RW64_32BIT (0 << 0)
|
||||
#define CBA2R_RW64_64BIT (1 << 0)
|
||||
#define CBA2R_VMID_SHIFT 16
|
||||
#define CBA2R_VMID_MASK 0xffff
|
||||
|
||||
/* Translation context bank */
|
||||
#define ARM_SMMU_CB(smmu, n) ((smmu)->cb_base + ((n) << (smmu)->pgshift))
|
||||
|
||||
#define ARM_SMMU_CB_SCTLR 0x0
|
||||
#define ARM_SMMU_CB_ACTLR 0x4
|
||||
#define ARM_SMMU_CB_RESUME 0x8
|
||||
#define ARM_SMMU_CB_TTBCR2 0x10
|
||||
#define ARM_SMMU_CB_TTBR0 0x20
|
||||
#define ARM_SMMU_CB_TTBR1 0x28
|
||||
#define ARM_SMMU_CB_TTBCR 0x30
|
||||
#define ARM_SMMU_CB_CONTEXTIDR 0x34
|
||||
#define ARM_SMMU_CB_S1_MAIR0 0x38
|
||||
#define ARM_SMMU_CB_S1_MAIR1 0x3c
|
||||
#define ARM_SMMU_CB_PAR 0x50
|
||||
#define ARM_SMMU_CB_FSR 0x58
|
||||
#define ARM_SMMU_CB_FAR 0x60
|
||||
#define ARM_SMMU_CB_FSYNR0 0x68
|
||||
#define ARM_SMMU_CB_S1_TLBIVA 0x600
|
||||
#define ARM_SMMU_CB_S1_TLBIASID 0x610
|
||||
#define ARM_SMMU_CB_S1_TLBIVAL 0x620
|
||||
#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
|
||||
#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
|
||||
#define ARM_SMMU_CB_TLBSYNC 0x7f0
|
||||
#define ARM_SMMU_CB_TLBSTATUS 0x7f4
|
||||
#define ARM_SMMU_CB_ATS1PR 0x800
|
||||
#define ARM_SMMU_CB_ATSR 0x8f0
|
||||
|
||||
#define SCTLR_S1_ASIDPNE (1 << 12)
|
||||
#define SCTLR_CFCFG (1 << 7)
|
||||
#define SCTLR_CFIE (1 << 6)
|
||||
#define SCTLR_CFRE (1 << 5)
|
||||
#define SCTLR_E (1 << 4)
|
||||
#define SCTLR_AFE (1 << 2)
|
||||
#define SCTLR_TRE (1 << 1)
|
||||
#define SCTLR_M (1 << 0)
|
||||
|
||||
#define ARM_MMU500_ACTLR_CPRE (1 << 1)
|
||||
|
||||
#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
|
||||
#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
|
||||
|
||||
#define CB_PAR_F (1 << 0)
|
||||
|
||||
#define ATSR_ACTIVE (1 << 0)
|
||||
|
||||
#define RESUME_RETRY (0 << 0)
|
||||
#define RESUME_TERMINATE (1 << 0)
|
||||
|
||||
#define TTBCR2_SEP_SHIFT 15
|
||||
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
|
||||
#define TTBCR2_AS (1 << 4)
|
||||
|
||||
#define TTBRn_ASID_SHIFT 48
|
||||
|
||||
#define FSR_MULTI (1 << 31)
|
||||
#define FSR_SS (1 << 30)
|
||||
#define FSR_UUT (1 << 8)
|
||||
#define FSR_ASF (1 << 7)
|
||||
#define FSR_TLBLKF (1 << 6)
|
||||
#define FSR_TLBMCF (1 << 5)
|
||||
#define FSR_EF (1 << 4)
|
||||
#define FSR_PF (1 << 3)
|
||||
#define FSR_AFF (1 << 2)
|
||||
#define FSR_TF (1 << 1)
|
||||
|
||||
#define FSR_IGN (FSR_AFF | FSR_ASF | \
|
||||
FSR_TLBMCF | FSR_TLBLKF)
|
||||
#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
|
||||
FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
|
||||
|
||||
#define FSYNR0_WNR (1 << 4)
|
||||
|
||||
#define MSI_IOVA_BASE 0x8000000
|
||||
#define MSI_IOVA_LENGTH 0x100000
|
||||
|
||||
|
@ -338,6 +145,13 @@ struct arm_smmu_smr {
|
|||
bool valid;
|
||||
};
|
||||
|
||||
struct arm_smmu_cb {
|
||||
u64 ttbr[2];
|
||||
u32 tcr[2];
|
||||
u32 mair[2];
|
||||
struct arm_smmu_cfg *cfg;
|
||||
};
|
||||
|
||||
struct arm_smmu_master_cfg {
|
||||
struct arm_smmu_device *smmu;
|
||||
s16 smendx[];
|
||||
|
@ -380,6 +194,7 @@ struct arm_smmu_device {
|
|||
u32 num_context_banks;
|
||||
u32 num_s2_context_banks;
|
||||
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
|
||||
struct arm_smmu_cb *cbs;
|
||||
atomic_t irptndx;
|
||||
|
||||
u32 num_mapping_groups;
|
||||
|
@ -776,17 +591,74 @@ static irqreturn_t arm_smmu_global_fault(int irq, void *dev)
|
|||
static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
||||
struct io_pgtable_cfg *pgtbl_cfg)
|
||||
{
|
||||
u32 reg, reg2;
|
||||
u64 reg64;
|
||||
bool stage1;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
|
||||
bool stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
|
||||
|
||||
cb->cfg = cfg;
|
||||
|
||||
/* TTBCR */
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
cb->tcr[0] = pgtbl_cfg->arm_v7s_cfg.tcr;
|
||||
} else {
|
||||
cb->tcr[0] = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
|
||||
cb->tcr[1] = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
|
||||
cb->tcr[1] |= TTBCR2_SEP_UPSTREAM;
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
|
||||
cb->tcr[1] |= TTBCR2_AS;
|
||||
}
|
||||
} else {
|
||||
cb->tcr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
|
||||
}
|
||||
|
||||
/* TTBRs */
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
cb->ttbr[0] = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
|
||||
cb->ttbr[1] = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
|
||||
} else {
|
||||
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
|
||||
cb->ttbr[0] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
|
||||
cb->ttbr[1] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
|
||||
cb->ttbr[1] |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
|
||||
}
|
||||
} else {
|
||||
cb->ttbr[0] = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
|
||||
}
|
||||
|
||||
/* MAIRs (stage-1 only) */
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
cb->mair[0] = pgtbl_cfg->arm_v7s_cfg.prrr;
|
||||
cb->mair[1] = pgtbl_cfg->arm_v7s_cfg.nmrr;
|
||||
} else {
|
||||
cb->mair[0] = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
|
||||
cb->mair[1] = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_smmu_write_context_bank(struct arm_smmu_device *smmu, int idx)
|
||||
{
|
||||
u32 reg;
|
||||
bool stage1;
|
||||
struct arm_smmu_cb *cb = &smmu->cbs[idx];
|
||||
struct arm_smmu_cfg *cfg = cb->cfg;
|
||||
void __iomem *cb_base, *gr1_base;
|
||||
|
||||
cb_base = ARM_SMMU_CB(smmu, idx);
|
||||
|
||||
/* Unassigned context banks only need disabling */
|
||||
if (!cfg) {
|
||||
writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
|
||||
return;
|
||||
}
|
||||
|
||||
gr1_base = ARM_SMMU_GR1(smmu);
|
||||
stage1 = cfg->cbar != CBAR_TYPE_S2_TRANS;
|
||||
cb_base = ARM_SMMU_CB(smmu, cfg->cbndx);
|
||||
|
||||
/* CBA2R */
|
||||
if (smmu->version > ARM_SMMU_V1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
|
||||
reg = CBA2R_RW64_64BIT;
|
||||
|
@ -796,7 +668,7 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
|||
if (smmu->features & ARM_SMMU_FEAT_VMID16)
|
||||
reg |= cfg->vmid << CBA2R_VMID_SHIFT;
|
||||
|
||||
writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(cfg->cbndx));
|
||||
writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBA2R(idx));
|
||||
}
|
||||
|
||||
/* CBAR */
|
||||
|
@ -815,72 +687,41 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
|
|||
/* 8-bit VMIDs live in CBAR */
|
||||
reg |= cfg->vmid << CBAR_VMID_SHIFT;
|
||||
}
|
||||
writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(cfg->cbndx));
|
||||
writel_relaxed(reg, gr1_base + ARM_SMMU_GR1_CBAR(idx));
|
||||
|
||||
/*
|
||||
* TTBCR
|
||||
* We must write this before the TTBRs, since it determines the
|
||||
* access behaviour of some fields (in particular, ASID[15:8]).
|
||||
*/
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
reg = pgtbl_cfg->arm_v7s_cfg.tcr;
|
||||
reg2 = 0;
|
||||
} else {
|
||||
reg = pgtbl_cfg->arm_lpae_s1_cfg.tcr;
|
||||
reg2 = pgtbl_cfg->arm_lpae_s1_cfg.tcr >> 32;
|
||||
reg2 |= TTBCR2_SEP_UPSTREAM;
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
|
||||
reg2 |= TTBCR2_AS;
|
||||
}
|
||||
if (smmu->version > ARM_SMMU_V1)
|
||||
writel_relaxed(reg2, cb_base + ARM_SMMU_CB_TTBCR2);
|
||||
} else {
|
||||
reg = pgtbl_cfg->arm_lpae_s2_cfg.vtcr;
|
||||
}
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBCR);
|
||||
if (stage1 && smmu->version > ARM_SMMU_V1)
|
||||
writel_relaxed(cb->tcr[1], cb_base + ARM_SMMU_CB_TTBCR2);
|
||||
writel_relaxed(cb->tcr[0], cb_base + ARM_SMMU_CB_TTBCR);
|
||||
|
||||
/* TTBRs */
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
reg = pgtbl_cfg->arm_v7s_cfg.ttbr[0];
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR0);
|
||||
reg = pgtbl_cfg->arm_v7s_cfg.ttbr[1];
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_TTBR1);
|
||||
writel_relaxed(cfg->asid, cb_base + ARM_SMMU_CB_CONTEXTIDR);
|
||||
writel_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
|
||||
writel_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
|
||||
} else {
|
||||
reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[0];
|
||||
reg64 |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
|
||||
writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
|
||||
reg64 = pgtbl_cfg->arm_lpae_s1_cfg.ttbr[1];
|
||||
reg64 |= (u64)cfg->asid << TTBRn_ASID_SHIFT;
|
||||
writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR1);
|
||||
}
|
||||
} else {
|
||||
reg64 = pgtbl_cfg->arm_lpae_s2_cfg.vttbr;
|
||||
writeq_relaxed(reg64, cb_base + ARM_SMMU_CB_TTBR0);
|
||||
writeq_relaxed(cb->ttbr[0], cb_base + ARM_SMMU_CB_TTBR0);
|
||||
if (stage1)
|
||||
writeq_relaxed(cb->ttbr[1], cb_base + ARM_SMMU_CB_TTBR1);
|
||||
}
|
||||
|
||||
/* MAIRs (stage-1 only) */
|
||||
if (stage1) {
|
||||
if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH32_S) {
|
||||
reg = pgtbl_cfg->arm_v7s_cfg.prrr;
|
||||
reg2 = pgtbl_cfg->arm_v7s_cfg.nmrr;
|
||||
} else {
|
||||
reg = pgtbl_cfg->arm_lpae_s1_cfg.mair[0];
|
||||
reg2 = pgtbl_cfg->arm_lpae_s1_cfg.mair[1];
|
||||
}
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_S1_MAIR0);
|
||||
writel_relaxed(reg2, cb_base + ARM_SMMU_CB_S1_MAIR1);
|
||||
writel_relaxed(cb->mair[0], cb_base + ARM_SMMU_CB_S1_MAIR0);
|
||||
writel_relaxed(cb->mair[1], cb_base + ARM_SMMU_CB_S1_MAIR1);
|
||||
}
|
||||
|
||||
/* SCTLR */
|
||||
reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE | SCTLR_M;
|
||||
if (stage1)
|
||||
reg |= SCTLR_S1_ASIDPNE;
|
||||
#ifdef __BIG_ENDIAN
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
|
||||
reg |= SCTLR_E;
|
||||
#endif
|
||||
|
||||
writel_relaxed(reg, cb_base + ARM_SMMU_CB_SCTLR);
|
||||
}
|
||||
|
||||
|
@ -1043,6 +884,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
|
|||
|
||||
/* Initialise the context bank with our page table cfg */
|
||||
arm_smmu_init_context_bank(smmu_domain, &pgtbl_cfg);
|
||||
arm_smmu_write_context_bank(smmu, cfg->cbndx);
|
||||
|
||||
/*
|
||||
* Request context fault interrupt. Do this last to avoid the
|
||||
|
@ -1075,7 +917,6 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
|||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
struct arm_smmu_device *smmu = smmu_domain->smmu;
|
||||
struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
|
||||
void __iomem *cb_base;
|
||||
int irq;
|
||||
|
||||
if (!smmu || domain->type == IOMMU_DOMAIN_IDENTITY)
|
||||
|
@ -1085,8 +926,8 @@ static void arm_smmu_destroy_domain_context(struct iommu_domain *domain)
|
|||
* Disable the context bank and free the page tables before freeing
|
||||
* it.
|
||||
*/
|
||||
cb_base = ARM_SMMU_CB(smmu, cfg->cbndx);
|
||||
writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
|
||||
smmu->cbs[cfg->cbndx].cfg = NULL;
|
||||
arm_smmu_write_context_bank(smmu, cfg->cbndx);
|
||||
|
||||
if (cfg->irptndx != INVALID_IRPTNDX) {
|
||||
irq = smmu->irqs[smmu->num_global_irqs + cfg->irptndx];
|
||||
|
@ -1736,7 +1577,6 @@ static struct iommu_ops arm_smmu_ops = {
|
|||
static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
{
|
||||
void __iomem *gr0_base = ARM_SMMU_GR0(smmu);
|
||||
void __iomem *cb_base;
|
||||
int i;
|
||||
u32 reg, major;
|
||||
|
||||
|
@ -1772,8 +1612,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
|||
|
||||
/* Make sure all context banks are disabled and clear CB_FSR */
|
||||
for (i = 0; i < smmu->num_context_banks; ++i) {
|
||||
cb_base = ARM_SMMU_CB(smmu, i);
|
||||
writel_relaxed(0, cb_base + ARM_SMMU_CB_SCTLR);
|
||||
void __iomem *cb_base = ARM_SMMU_CB(smmu, i);
|
||||
|
||||
arm_smmu_write_context_bank(smmu, i);
|
||||
writel_relaxed(FSR_FAULT, cb_base + ARM_SMMU_CB_FSR);
|
||||
/*
|
||||
* Disable MMU-500's not-particularly-beneficial next-page
|
||||
|
@ -1979,6 +1820,10 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
|
|||
smmu->cavium_id_base -= smmu->num_context_banks;
|
||||
dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n");
|
||||
}
|
||||
smmu->cbs = devm_kcalloc(smmu->dev, smmu->num_context_banks,
|
||||
sizeof(*smmu->cbs), GFP_KERNEL);
|
||||
if (!smmu->cbs)
|
||||
return -ENOMEM;
|
||||
|
||||
/* ID2 */
|
||||
id = readl_relaxed(gr0_base + ARM_SMMU_GR0_ID2);
|
||||
|
@ -2336,13 +2181,30 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void arm_smmu_device_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
arm_smmu_device_remove(pdev);
|
||||
}
|
||||
|
||||
static int __maybe_unused arm_smmu_pm_resume(struct device *dev)
|
||||
{
|
||||
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
|
||||
|
||||
arm_smmu_device_reset(smmu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(arm_smmu_pm_ops, NULL, arm_smmu_pm_resume);
|
||||
|
||||
static struct platform_driver arm_smmu_driver = {
|
||||
.driver = {
|
||||
.name = "arm-smmu",
|
||||
.of_match_table = of_match_ptr(arm_smmu_of_match),
|
||||
.pm = &arm_smmu_pm_ops,
|
||||
},
|
||||
.probe = arm_smmu_device_probe,
|
||||
.remove = arm_smmu_device_remove,
|
||||
.shutdown = arm_smmu_device_shutdown,
|
||||
};
|
||||
module_platform_driver(arm_smmu_driver);
|
||||
|
||||
|
|
|
@ -1343,7 +1343,7 @@ void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 qdep,
|
|||
|
||||
if (mask) {
|
||||
BUG_ON(addr & ((1 << (VTD_PAGE_SHIFT + mask)) - 1));
|
||||
addr |= (1 << (VTD_PAGE_SHIFT + mask - 1)) - 1;
|
||||
addr |= (1ULL << (VTD_PAGE_SHIFT + mask - 1)) - 1;
|
||||
desc.high = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE;
|
||||
} else
|
||||
desc.high = QI_DEV_IOTLB_ADDR(addr);
|
||||
|
|
|
@ -54,10 +54,6 @@ typedef u32 sysmmu_pte_t;
|
|||
#define lv2ent_small(pent) ((*(pent) & 2) == 2)
|
||||
#define lv2ent_large(pent) ((*(pent) & 3) == 1)
|
||||
|
||||
#ifdef CONFIG_BIG_ENDIAN
|
||||
#warning "revisit driver if we can enable big-endian ptes"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* v1.x - v3.x SYSMMU supports 32bit physical and 32bit virtual address spaces
|
||||
* v5.0 introduced support for 36bit physical address space by shifting
|
||||
|
@ -569,7 +565,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,
|
|||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
}
|
||||
|
||||
static struct iommu_ops exynos_iommu_ops;
|
||||
static const struct iommu_ops exynos_iommu_ops;
|
||||
|
||||
static int __init exynos_sysmmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -659,6 +655,13 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* use the first registered sysmmu device for performing
|
||||
* dma mapping operations on iommu page tables (cpu cache flush)
|
||||
*/
|
||||
if (!dma_dev)
|
||||
dma_dev = &pdev->dev;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
@ -1323,7 +1326,7 @@ static int exynos_iommu_of_xlate(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct iommu_ops exynos_iommu_ops = {
|
||||
static const struct iommu_ops exynos_iommu_ops = {
|
||||
.domain_alloc = exynos_iommu_domain_alloc,
|
||||
.domain_free = exynos_iommu_domain_free,
|
||||
.attach_dev = exynos_iommu_attach_device,
|
||||
|
@ -1339,8 +1342,6 @@ static struct iommu_ops exynos_iommu_ops = {
|
|||
.of_xlate = exynos_iommu_of_xlate,
|
||||
};
|
||||
|
||||
static bool init_done;
|
||||
|
||||
static int __init exynos_iommu_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1373,8 +1374,6 @@ static int __init exynos_iommu_init(void)
|
|||
goto err_set_iommu;
|
||||
}
|
||||
|
||||
init_done = true;
|
||||
|
||||
return 0;
|
||||
err_set_iommu:
|
||||
kmem_cache_free(lv2table_kmem_cache, zero_lv2_table);
|
||||
|
@ -1384,27 +1383,6 @@ static int __init exynos_iommu_init(void)
|
|||
kmem_cache_destroy(lv2table_kmem_cache);
|
||||
return ret;
|
||||
}
|
||||
core_initcall(exynos_iommu_init);
|
||||
|
||||
static int __init exynos_iommu_of_setup(struct device_node *np)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (!init_done)
|
||||
exynos_iommu_init();
|
||||
|
||||
pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
|
||||
if (!pdev)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* use the first registered sysmmu device for performing
|
||||
* dma mapping operations on iommu page tables (cpu cache flush)
|
||||
*/
|
||||
if (!dma_dev)
|
||||
dma_dev = &pdev->dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu",
|
||||
exynos_iommu_of_setup);
|
||||
IOMMU_OF_DECLARE(exynos_iommu_of, "samsung,exynos-sysmmu", NULL);
|
||||
|
|
|
@ -42,6 +42,8 @@ struct pamu_isr_data {
|
|||
static struct paace *ppaact;
|
||||
static struct paace *spaact;
|
||||
|
||||
static bool probed; /* Has PAMU been probed? */
|
||||
|
||||
/*
|
||||
* Table for matching compatible strings, for device tree
|
||||
* guts node, for QorIQ SOCs.
|
||||
|
@ -530,8 +532,8 @@ u32 get_stash_id(u32 stash_dest_hint, u32 vcpu)
|
|||
if (node) {
|
||||
prop = of_get_property(node, "cache-stash-id", NULL);
|
||||
if (!prop) {
|
||||
pr_debug("missing cache-stash-id at %s\n",
|
||||
node->full_name);
|
||||
pr_debug("missing cache-stash-id at %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
return ~(u32)0;
|
||||
}
|
||||
|
@ -557,8 +559,8 @@ u32 get_stash_id(u32 stash_dest_hint, u32 vcpu)
|
|||
if (stash_dest_hint == cache_level) {
|
||||
prop = of_get_property(node, "cache-stash-id", NULL);
|
||||
if (!prop) {
|
||||
pr_debug("missing cache-stash-id at %s\n",
|
||||
node->full_name);
|
||||
pr_debug("missing cache-stash-id at %pOF\n",
|
||||
node);
|
||||
of_node_put(node);
|
||||
return ~(u32)0;
|
||||
}
|
||||
|
@ -568,8 +570,7 @@ u32 get_stash_id(u32 stash_dest_hint, u32 vcpu)
|
|||
|
||||
prop = of_get_property(node, "next-level-cache", NULL);
|
||||
if (!prop) {
|
||||
pr_debug("can't find next-level-cache at %s\n",
|
||||
node->full_name);
|
||||
pr_debug("can't find next-level-cache at %pOF\n", node);
|
||||
of_node_put(node);
|
||||
return ~(u32)0; /* can't traverse any further */
|
||||
}
|
||||
|
@ -1033,6 +1034,9 @@ static int fsl_pamu_probe(struct platform_device *pdev)
|
|||
* NOTE : All PAMUs share the same LIODN tables.
|
||||
*/
|
||||
|
||||
if (WARN_ON(probed))
|
||||
return -EBUSY;
|
||||
|
||||
pamu_regs = of_iomap(dev->of_node, 0);
|
||||
if (!pamu_regs) {
|
||||
dev_err(dev, "ioremap of PAMU node failed\n");
|
||||
|
@ -1063,8 +1067,7 @@ static int fsl_pamu_probe(struct platform_device *pdev)
|
|||
|
||||
guts_node = of_find_matching_node(NULL, guts_device_ids);
|
||||
if (!guts_node) {
|
||||
dev_err(dev, "could not find GUTS node %s\n",
|
||||
dev->of_node->full_name);
|
||||
dev_err(dev, "could not find GUTS node %pOF\n", dev->of_node);
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
@ -1172,6 +1175,8 @@ static int fsl_pamu_probe(struct platform_device *pdev)
|
|||
|
||||
setup_liodns();
|
||||
|
||||
probed = true;
|
||||
|
||||
return 0;
|
||||
|
||||
error_genpool:
|
||||
|
@ -1246,8 +1251,7 @@ static __init int fsl_pamu_init(void)
|
|||
|
||||
pdev = platform_device_alloc("fsl-of-pamu", 0);
|
||||
if (!pdev) {
|
||||
pr_err("could not allocate device %s\n",
|
||||
np->full_name);
|
||||
pr_err("could not allocate device %pOF\n", np);
|
||||
ret = -ENOMEM;
|
||||
goto error_device_alloc;
|
||||
}
|
||||
|
@ -1259,8 +1263,7 @@ static __init int fsl_pamu_init(void)
|
|||
|
||||
ret = platform_device_add(pdev);
|
||||
if (ret) {
|
||||
pr_err("could not add device %s (err=%i)\n",
|
||||
np->full_name, ret);
|
||||
pr_err("could not add device %pOF (err=%i)\n", np, ret);
|
||||
goto error_device_add;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ static struct kmem_cache *fsl_pamu_domain_cache;
|
|||
static struct kmem_cache *iommu_devinfo_cache;
|
||||
static DEFINE_SPINLOCK(device_domain_lock);
|
||||
|
||||
struct iommu_device pamu_iommu; /* IOMMU core code handle */
|
||||
|
||||
static struct fsl_dma_domain *to_fsl_dma_domain(struct iommu_domain *dom)
|
||||
{
|
||||
return container_of(dom, struct fsl_dma_domain, iommu_domain);
|
||||
|
@ -619,8 +621,8 @@ static int handle_attach_device(struct fsl_dma_domain *dma_domain,
|
|||
for (i = 0; i < num; i++) {
|
||||
/* Ensure that LIODN value is valid */
|
||||
if (liodn[i] >= PAACE_NUMBER_ENTRIES) {
|
||||
pr_debug("Invalid liodn %d, attach device failed for %s\n",
|
||||
liodn[i], dev->of_node->full_name);
|
||||
pr_debug("Invalid liodn %d, attach device failed for %pOF\n",
|
||||
liodn[i], dev->of_node);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
@ -684,8 +686,7 @@ static int fsl_pamu_attach_device(struct iommu_domain *domain,
|
|||
liodn_cnt = len / sizeof(u32);
|
||||
ret = handle_attach_device(dma_domain, dev, liodn, liodn_cnt);
|
||||
} else {
|
||||
pr_debug("missing fsl,liodn property at %s\n",
|
||||
dev->of_node->full_name);
|
||||
pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -720,8 +721,7 @@ static void fsl_pamu_detach_device(struct iommu_domain *domain,
|
|||
if (prop)
|
||||
detach_device(dev, dma_domain);
|
||||
else
|
||||
pr_debug("missing fsl,liodn property at %s\n",
|
||||
dev->of_node->full_name);
|
||||
pr_debug("missing fsl,liodn property at %pOF\n", dev->of_node);
|
||||
}
|
||||
|
||||
static int configure_domain_geometry(struct iommu_domain *domain, void *data)
|
||||
|
@ -983,11 +983,14 @@ static int fsl_pamu_add_device(struct device *dev)
|
|||
|
||||
iommu_group_put(group);
|
||||
|
||||
iommu_device_link(&pamu_iommu, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fsl_pamu_remove_device(struct device *dev)
|
||||
{
|
||||
iommu_device_unlink(&pamu_iommu, dev);
|
||||
iommu_group_remove_device(dev);
|
||||
}
|
||||
|
||||
|
@ -1073,6 +1076,19 @@ int __init pamu_domain_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iommu_device_sysfs_add(&pamu_iommu, NULL, NULL, "iommu0");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iommu_device_set_ops(&pamu_iommu, &fsl_pamu_ops);
|
||||
|
||||
ret = iommu_device_register(&pamu_iommu);
|
||||
if (ret) {
|
||||
iommu_device_sysfs_remove(&pamu_iommu);
|
||||
pr_err("Can't register iommu device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bus_set_iommu(&platform_bus_type, &fsl_pamu_ops);
|
||||
bus_set_iommu(&pci_bus_type, &fsl_pamu_ops);
|
||||
|
||||
|
|
|
@ -458,31 +458,6 @@ static LIST_HEAD(dmar_rmrr_units);
|
|||
#define for_each_rmrr_units(rmrr) \
|
||||
list_for_each_entry(rmrr, &dmar_rmrr_units, list)
|
||||
|
||||
static void flush_unmaps_timeout(unsigned long data);
|
||||
|
||||
struct deferred_flush_entry {
|
||||
unsigned long iova_pfn;
|
||||
unsigned long nrpages;
|
||||
struct dmar_domain *domain;
|
||||
struct page *freelist;
|
||||
};
|
||||
|
||||
#define HIGH_WATER_MARK 250
|
||||
struct deferred_flush_table {
|
||||
int next;
|
||||
struct deferred_flush_entry entries[HIGH_WATER_MARK];
|
||||
};
|
||||
|
||||
struct deferred_flush_data {
|
||||
spinlock_t lock;
|
||||
int timer_on;
|
||||
struct timer_list timer;
|
||||
long size;
|
||||
struct deferred_flush_table *tables;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct deferred_flush_data, deferred_flush);
|
||||
|
||||
/* bitmap for indexing intel_iommus */
|
||||
static int g_num_of_iommus;
|
||||
|
||||
|
@ -981,20 +956,6 @@ static int device_context_mapped(struct intel_iommu *iommu, u8 bus, u8 devfn)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void clear_context_table(struct intel_iommu *iommu, u8 bus, u8 devfn)
|
||||
{
|
||||
struct context_entry *context;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
context = iommu_context_addr(iommu, bus, devfn, 0);
|
||||
if (context) {
|
||||
context_clear_entry(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
}
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
}
|
||||
|
||||
static void free_context_table(struct intel_iommu *iommu)
|
||||
{
|
||||
int i;
|
||||
|
@ -1144,8 +1105,9 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
|
|||
}
|
||||
|
||||
static void dma_pte_free_level(struct dmar_domain *domain, int level,
|
||||
struct dma_pte *pte, unsigned long pfn,
|
||||
unsigned long start_pfn, unsigned long last_pfn)
|
||||
int retain_level, struct dma_pte *pte,
|
||||
unsigned long pfn, unsigned long start_pfn,
|
||||
unsigned long last_pfn)
|
||||
{
|
||||
pfn = max(start_pfn, pfn);
|
||||
pte = &pte[pfn_level_offset(pfn, level)];
|
||||
|
@ -1160,12 +1122,17 @@ static void dma_pte_free_level(struct dmar_domain *domain, int level,
|
|||
level_pfn = pfn & level_mask(level);
|
||||
level_pte = phys_to_virt(dma_pte_addr(pte));
|
||||
|
||||
if (level > 2)
|
||||
dma_pte_free_level(domain, level - 1, level_pte,
|
||||
level_pfn, start_pfn, last_pfn);
|
||||
if (level > 2) {
|
||||
dma_pte_free_level(domain, level - 1, retain_level,
|
||||
level_pte, level_pfn, start_pfn,
|
||||
last_pfn);
|
||||
}
|
||||
|
||||
/* If range covers entire pagetable, free it */
|
||||
if (!(start_pfn > level_pfn ||
|
||||
/*
|
||||
* Free the page table if we're below the level we want to
|
||||
* retain and the range covers the entire table.
|
||||
*/
|
||||
if (level < retain_level && !(start_pfn > level_pfn ||
|
||||
last_pfn < level_pfn + level_size(level) - 1)) {
|
||||
dma_clear_pte(pte);
|
||||
domain_flush_cache(domain, pte, sizeof(*pte));
|
||||
|
@ -1176,10 +1143,14 @@ static void dma_pte_free_level(struct dmar_domain *domain, int level,
|
|||
} while (!first_pte_in_page(++pte) && pfn <= last_pfn);
|
||||
}
|
||||
|
||||
/* clear last level (leaf) ptes and free page table pages. */
|
||||
/*
|
||||
* clear last level (leaf) ptes and free page table pages below the
|
||||
* level we wish to keep intact.
|
||||
*/
|
||||
static void dma_pte_free_pagetable(struct dmar_domain *domain,
|
||||
unsigned long start_pfn,
|
||||
unsigned long last_pfn)
|
||||
unsigned long last_pfn,
|
||||
int retain_level)
|
||||
{
|
||||
BUG_ON(!domain_pfn_supported(domain, start_pfn));
|
||||
BUG_ON(!domain_pfn_supported(domain, last_pfn));
|
||||
|
@ -1188,7 +1159,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
|
|||
dma_pte_clear_range(domain, start_pfn, last_pfn);
|
||||
|
||||
/* We don't need lock here; nobody else touches the iova range */
|
||||
dma_pte_free_level(domain, agaw_to_level(domain->agaw),
|
||||
dma_pte_free_level(domain, agaw_to_level(domain->agaw), retain_level,
|
||||
domain->pgd, 0, start_pfn, last_pfn);
|
||||
|
||||
/* free pgd */
|
||||
|
@ -1316,6 +1287,13 @@ static void dma_free_pagelist(struct page *freelist)
|
|||
}
|
||||
}
|
||||
|
||||
static void iova_entry_free(unsigned long data)
|
||||
{
|
||||
struct page *freelist = (struct page *)data;
|
||||
|
||||
dma_free_pagelist(freelist);
|
||||
}
|
||||
|
||||
/* iommu handling */
|
||||
static int iommu_alloc_root_entry(struct intel_iommu *iommu)
|
||||
{
|
||||
|
@ -1629,6 +1607,25 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu,
|
|||
addr, mask);
|
||||
}
|
||||
|
||||
static void iommu_flush_iova(struct iova_domain *iovad)
|
||||
{
|
||||
struct dmar_domain *domain;
|
||||
int idx;
|
||||
|
||||
domain = container_of(iovad, struct dmar_domain, iovad);
|
||||
|
||||
for_each_domain_iommu(idx, domain) {
|
||||
struct intel_iommu *iommu = g_iommus[idx];
|
||||
u16 did = domain->iommu_did[iommu->seq_id];
|
||||
|
||||
iommu->flush.flush_iotlb(iommu, did, 0, 0, DMA_TLB_DSI_FLUSH);
|
||||
|
||||
if (!cap_caching_mode(iommu->cap))
|
||||
iommu_flush_dev_iotlb(get_iommu_domain(iommu, did),
|
||||
0, MAX_AGAW_PFN_WIDTH);
|
||||
}
|
||||
}
|
||||
|
||||
static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu)
|
||||
{
|
||||
u32 pmen;
|
||||
|
@ -1939,9 +1936,16 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu,
|
|||
{
|
||||
int adjust_width, agaw;
|
||||
unsigned long sagaw;
|
||||
int err;
|
||||
|
||||
init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN,
|
||||
DMA_32BIT_PFN);
|
||||
|
||||
err = init_iova_flush_queue(&domain->iovad,
|
||||
iommu_flush_iova, iova_entry_free);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
domain_reserve_special_ranges(domain);
|
||||
|
||||
/* calculate AGAW */
|
||||
|
@ -1993,14 +1997,6 @@ static void domain_exit(struct dmar_domain *domain)
|
|||
if (!domain)
|
||||
return;
|
||||
|
||||
/* Flush any lazy unmaps that may reference this domain */
|
||||
if (!intel_iommu_strict) {
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
flush_unmaps_timeout(cpu);
|
||||
}
|
||||
|
||||
/* Remove associated devices and clear attached or cached domains */
|
||||
rcu_read_lock();
|
||||
domain_remove_dev_info(domain);
|
||||
|
@ -2284,8 +2280,11 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn,
|
|||
/*
|
||||
* Ensure that old small page tables are
|
||||
* removed to make room for superpage(s).
|
||||
* We're adding new large pages, so make sure
|
||||
* we don't remove their parent tables.
|
||||
*/
|
||||
dma_pte_free_pagetable(domain, iov_pfn, end_pfn);
|
||||
dma_pte_free_pagetable(domain, iov_pfn, end_pfn,
|
||||
largepage_lvl + 1);
|
||||
} else {
|
||||
pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE;
|
||||
}
|
||||
|
@ -2358,13 +2357,33 @@ static inline int domain_pfn_mapping(struct dmar_domain *domain, unsigned long i
|
|||
|
||||
static void domain_context_clear_one(struct intel_iommu *iommu, u8 bus, u8 devfn)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct context_entry *context;
|
||||
u16 did_old;
|
||||
|
||||
if (!iommu)
|
||||
return;
|
||||
|
||||
clear_context_table(iommu, bus, devfn);
|
||||
iommu->flush.flush_context(iommu, 0, 0, 0,
|
||||
DMA_CCMD_GLOBAL_INVL);
|
||||
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
|
||||
spin_lock_irqsave(&iommu->lock, flags);
|
||||
context = iommu_context_addr(iommu, bus, devfn, 0);
|
||||
if (!context) {
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
return;
|
||||
}
|
||||
did_old = context_domain_id(context);
|
||||
context_clear_entry(context);
|
||||
__iommu_flush_cache(iommu, context, sizeof(*context));
|
||||
spin_unlock_irqrestore(&iommu->lock, flags);
|
||||
iommu->flush.flush_context(iommu,
|
||||
did_old,
|
||||
(((u16)bus) << 8) | devfn,
|
||||
DMA_CCMD_MASK_NOBIT,
|
||||
DMA_CCMD_DEVICE_INVL);
|
||||
iommu->flush.flush_iotlb(iommu,
|
||||
did_old,
|
||||
0,
|
||||
0,
|
||||
DMA_TLB_DSI_FLUSH);
|
||||
}
|
||||
|
||||
static inline void unlink_domain_info(struct device_domain_info *info)
|
||||
|
@ -3213,7 +3232,7 @@ static int __init init_dmars(void)
|
|||
bool copied_tables = false;
|
||||
struct device *dev;
|
||||
struct intel_iommu *iommu;
|
||||
int i, ret, cpu;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* for each drhd
|
||||
|
@ -3246,22 +3265,6 @@ static int __init init_dmars(void)
|
|||
goto error;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct deferred_flush_data *dfd = per_cpu_ptr(&deferred_flush,
|
||||
cpu);
|
||||
|
||||
dfd->tables = kzalloc(g_num_of_iommus *
|
||||
sizeof(struct deferred_flush_table),
|
||||
GFP_KERNEL);
|
||||
if (!dfd->tables) {
|
||||
ret = -ENOMEM;
|
||||
goto free_g_iommus;
|
||||
}
|
||||
|
||||
spin_lock_init(&dfd->lock);
|
||||
setup_timer(&dfd->timer, flush_unmaps_timeout, cpu);
|
||||
}
|
||||
|
||||
for_each_active_iommu(iommu, drhd) {
|
||||
g_iommus[iommu->seq_id] = iommu;
|
||||
|
||||
|
@ -3444,10 +3447,9 @@ static int __init init_dmars(void)
|
|||
disable_dmar_iommu(iommu);
|
||||
free_dmar_iommu(iommu);
|
||||
}
|
||||
free_g_iommus:
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(per_cpu_ptr(&deferred_flush, cpu)->tables);
|
||||
|
||||
kfree(g_iommus);
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
@ -3652,110 +3654,6 @@ static dma_addr_t intel_map_page(struct device *dev, struct page *page,
|
|||
dir, *dev->dma_mask);
|
||||
}
|
||||
|
||||
static void flush_unmaps(struct deferred_flush_data *flush_data)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
flush_data->timer_on = 0;
|
||||
|
||||
/* just flush them all */
|
||||
for (i = 0; i < g_num_of_iommus; i++) {
|
||||
struct intel_iommu *iommu = g_iommus[i];
|
||||
struct deferred_flush_table *flush_table =
|
||||
&flush_data->tables[i];
|
||||
if (!iommu)
|
||||
continue;
|
||||
|
||||
if (!flush_table->next)
|
||||
continue;
|
||||
|
||||
/* In caching mode, global flushes turn emulation expensive */
|
||||
if (!cap_caching_mode(iommu->cap))
|
||||
iommu->flush.flush_iotlb(iommu, 0, 0, 0,
|
||||
DMA_TLB_GLOBAL_FLUSH);
|
||||
for (j = 0; j < flush_table->next; j++) {
|
||||
unsigned long mask;
|
||||
struct deferred_flush_entry *entry =
|
||||
&flush_table->entries[j];
|
||||
unsigned long iova_pfn = entry->iova_pfn;
|
||||
unsigned long nrpages = entry->nrpages;
|
||||
struct dmar_domain *domain = entry->domain;
|
||||
struct page *freelist = entry->freelist;
|
||||
|
||||
/* On real hardware multiple invalidations are expensive */
|
||||
if (cap_caching_mode(iommu->cap))
|
||||
iommu_flush_iotlb_psi(iommu, domain,
|
||||
mm_to_dma_pfn(iova_pfn),
|
||||
nrpages, !freelist, 0);
|
||||
else {
|
||||
mask = ilog2(nrpages);
|
||||
iommu_flush_dev_iotlb(domain,
|
||||
(uint64_t)iova_pfn << PAGE_SHIFT, mask);
|
||||
}
|
||||
free_iova_fast(&domain->iovad, iova_pfn, nrpages);
|
||||
if (freelist)
|
||||
dma_free_pagelist(freelist);
|
||||
}
|
||||
flush_table->next = 0;
|
||||
}
|
||||
|
||||
flush_data->size = 0;
|
||||
}
|
||||
|
||||
static void flush_unmaps_timeout(unsigned long cpuid)
|
||||
{
|
||||
struct deferred_flush_data *flush_data = per_cpu_ptr(&deferred_flush, cpuid);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&flush_data->lock, flags);
|
||||
flush_unmaps(flush_data);
|
||||
spin_unlock_irqrestore(&flush_data->lock, flags);
|
||||
}
|
||||
|
||||
static void add_unmap(struct dmar_domain *dom, unsigned long iova_pfn,
|
||||
unsigned long nrpages, struct page *freelist)
|
||||
{
|
||||
unsigned long flags;
|
||||
int entry_id, iommu_id;
|
||||
struct intel_iommu *iommu;
|
||||
struct deferred_flush_entry *entry;
|
||||
struct deferred_flush_data *flush_data;
|
||||
|
||||
flush_data = raw_cpu_ptr(&deferred_flush);
|
||||
|
||||
/* Flush all CPUs' entries to avoid deferring too much. If
|
||||
* this becomes a bottleneck, can just flush us, and rely on
|
||||
* flush timer for the rest.
|
||||
*/
|
||||
if (flush_data->size == HIGH_WATER_MARK) {
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
flush_unmaps_timeout(cpu);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&flush_data->lock, flags);
|
||||
|
||||
iommu = domain_get_iommu(dom);
|
||||
iommu_id = iommu->seq_id;
|
||||
|
||||
entry_id = flush_data->tables[iommu_id].next;
|
||||
++(flush_data->tables[iommu_id].next);
|
||||
|
||||
entry = &flush_data->tables[iommu_id].entries[entry_id];
|
||||
entry->domain = dom;
|
||||
entry->iova_pfn = iova_pfn;
|
||||
entry->nrpages = nrpages;
|
||||
entry->freelist = freelist;
|
||||
|
||||
if (!flush_data->timer_on) {
|
||||
mod_timer(&flush_data->timer, jiffies + msecs_to_jiffies(10));
|
||||
flush_data->timer_on = 1;
|
||||
}
|
||||
flush_data->size++;
|
||||
spin_unlock_irqrestore(&flush_data->lock, flags);
|
||||
}
|
||||
|
||||
static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
|
||||
{
|
||||
struct dmar_domain *domain;
|
||||
|
@ -3791,7 +3689,8 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
|
|||
free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(nrpages));
|
||||
dma_free_pagelist(freelist);
|
||||
} else {
|
||||
add_unmap(domain, iova_pfn, nrpages, freelist);
|
||||
queue_iova(&domain->iovad, iova_pfn, nrpages,
|
||||
(unsigned long)freelist);
|
||||
/*
|
||||
* queue up the release of the unmap to save the 1/6th of the
|
||||
* cpu used up by the iotlb flush operation...
|
||||
|
@ -3945,7 +3844,8 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele
|
|||
ret = domain_sg_mapping(domain, start_vpfn, sglist, size, prot);
|
||||
if (unlikely(ret)) {
|
||||
dma_pte_free_pagetable(domain, start_vpfn,
|
||||
start_vpfn + size - 1);
|
||||
start_vpfn + size - 1,
|
||||
agaw_to_level(domain->agaw) + 1);
|
||||
free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(size));
|
||||
return 0;
|
||||
}
|
||||
|
@ -4728,7 +4628,6 @@ static void free_all_cpu_cached_iovas(unsigned int cpu)
|
|||
static int intel_iommu_cpu_dead(unsigned int cpu)
|
||||
{
|
||||
free_all_cpu_cached_iovas(cpu);
|
||||
flush_unmaps_timeout(cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -5350,6 +5249,7 @@ int intel_iommu_enable_pasid(struct intel_iommu *iommu, struct intel_svm_dev *sd
|
|||
sdev->sid = PCI_DEVID(info->bus, info->devfn);
|
||||
|
||||
if (!(ctx_lo & CONTEXT_PASIDE)) {
|
||||
if (iommu->pasid_state_table)
|
||||
context[1].hi = (u64)virt_to_phys(iommu->pasid_state_table);
|
||||
context[1].lo = (u64)virt_to_phys(iommu->pasid_table) |
|
||||
intel_iommu_get_pts(iommu);
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/pci-ats.h>
|
||||
#include <linux/dmar.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
static irqreturn_t prq_event_thread(int irq, void *d);
|
||||
|
||||
|
@ -546,6 +547,14 @@ static bool access_error(struct vm_area_struct *vma, struct page_req_dsc *req)
|
|||
return (requested & ~vma->vm_flags) != 0;
|
||||
}
|
||||
|
||||
static bool is_canonical_address(u64 addr)
|
||||
{
|
||||
int shift = 64 - (__VIRTUAL_MASK_SHIFT + 1);
|
||||
long saddr = (long) addr;
|
||||
|
||||
return (((saddr << shift) >> shift) == saddr);
|
||||
}
|
||||
|
||||
static irqreturn_t prq_event_thread(int irq, void *d)
|
||||
{
|
||||
struct intel_iommu *iommu = d;
|
||||
|
@ -603,6 +612,11 @@ static irqreturn_t prq_event_thread(int irq, void *d)
|
|||
/* If the mm is already defunct, don't handle faults. */
|
||||
if (!mmget_not_zero(svm->mm))
|
||||
goto bad_req;
|
||||
|
||||
/* If address is not canonical, return invalid response */
|
||||
if (!is_canonical_address(address))
|
||||
goto bad_req;
|
||||
|
||||
down_read(&svm->mm->mmap_sem);
|
||||
vma = find_extend_vma(svm->mm, address);
|
||||
if (!vma || address < vma->vm_start)
|
||||
|
|
|
@ -527,6 +527,8 @@ static int iommu_group_create_direct_mappings(struct iommu_group *group,
|
|||
|
||||
}
|
||||
|
||||
iommu_flush_tlb_all(domain);
|
||||
|
||||
out:
|
||||
iommu_put_resv_regions(dev, &mappings);
|
||||
|
||||
|
@ -1005,11 +1007,10 @@ struct iommu_group *iommu_group_get_for_dev(struct device *dev)
|
|||
if (group)
|
||||
return group;
|
||||
|
||||
group = ERR_PTR(-EINVAL);
|
||||
if (!ops)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (ops && ops->device_group)
|
||||
group = ops->device_group(dev);
|
||||
|
||||
if (WARN_ON_ONCE(group == NULL))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
|
@ -1283,6 +1284,10 @@ static int __iommu_attach_device(struct iommu_domain *domain,
|
|||
struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
if ((domain->ops->is_attach_deferred != NULL) &&
|
||||
domain->ops->is_attach_deferred(domain, dev))
|
||||
return 0;
|
||||
|
||||
if (unlikely(domain->ops->attach_dev == NULL))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -1298,12 +1303,8 @@ int iommu_attach_device(struct iommu_domain *domain, struct device *dev)
|
|||
int ret;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
/* FIXME: Remove this when groups a mandatory for iommu drivers */
|
||||
if (group == NULL)
|
||||
return __iommu_attach_device(domain, dev);
|
||||
|
||||
/*
|
||||
* We have a group - lock it to make sure the device-count doesn't
|
||||
* Lock the group to make sure the device-count doesn't
|
||||
* change while we are attaching
|
||||
*/
|
||||
mutex_lock(&group->mutex);
|
||||
|
@ -1324,6 +1325,10 @@ EXPORT_SYMBOL_GPL(iommu_attach_device);
|
|||
static void __iommu_detach_device(struct iommu_domain *domain,
|
||||
struct device *dev)
|
||||
{
|
||||
if ((domain->ops->is_attach_deferred != NULL) &&
|
||||
domain->ops->is_attach_deferred(domain, dev))
|
||||
return;
|
||||
|
||||
if (unlikely(domain->ops->detach_dev == NULL))
|
||||
return;
|
||||
|
||||
|
@ -1336,9 +1341,6 @@ void iommu_detach_device(struct iommu_domain *domain, struct device *dev)
|
|||
struct iommu_group *group;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
/* FIXME: Remove this when groups a mandatory for iommu drivers */
|
||||
if (group == NULL)
|
||||
return __iommu_detach_device(domain, dev);
|
||||
|
||||
mutex_lock(&group->mutex);
|
||||
if (iommu_group_device_count(group) != 1) {
|
||||
|
@ -1360,8 +1362,7 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
|
|||
struct iommu_group *group;
|
||||
|
||||
group = iommu_group_get(dev);
|
||||
/* FIXME: Remove this when groups a mandatory for iommu drivers */
|
||||
if (group == NULL)
|
||||
if (!group)
|
||||
return NULL;
|
||||
|
||||
domain = group->domain;
|
||||
|
@ -1556,13 +1557,16 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_map);
|
||||
|
||||
size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
||||
static size_t __iommu_unmap(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size,
|
||||
bool sync)
|
||||
{
|
||||
const struct iommu_ops *ops = domain->ops;
|
||||
size_t unmapped_page, unmapped = 0;
|
||||
unsigned int min_pagesz;
|
||||
unsigned long orig_iova = iova;
|
||||
unsigned int min_pagesz;
|
||||
|
||||
if (unlikely(domain->ops->unmap == NULL ||
|
||||
if (unlikely(ops->unmap == NULL ||
|
||||
domain->pgsize_bitmap == 0UL))
|
||||
return -ENODEV;
|
||||
|
||||
|
@ -1592,10 +1596,13 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
|||
while (unmapped < size) {
|
||||
size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
|
||||
|
||||
unmapped_page = domain->ops->unmap(domain, iova, pgsize);
|
||||
unmapped_page = ops->unmap(domain, iova, pgsize);
|
||||
if (!unmapped_page)
|
||||
break;
|
||||
|
||||
if (sync && ops->iotlb_range_add)
|
||||
ops->iotlb_range_add(domain, iova, pgsize);
|
||||
|
||||
pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
|
||||
iova, unmapped_page);
|
||||
|
||||
|
@ -1603,11 +1610,27 @@ size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova, size_t size)
|
|||
unmapped += unmapped_page;
|
||||
}
|
||||
|
||||
if (sync && ops->iotlb_sync)
|
||||
ops->iotlb_sync(domain);
|
||||
|
||||
trace_unmap(orig_iova, size, unmapped);
|
||||
return unmapped;
|
||||
}
|
||||
|
||||
size_t iommu_unmap(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
return __iommu_unmap(domain, iova, size, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_unmap);
|
||||
|
||||
size_t iommu_unmap_fast(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
return __iommu_unmap(domain, iova, size, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iommu_unmap_fast);
|
||||
|
||||
size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot)
|
||||
{
|
||||
|
|
|
@ -32,6 +32,8 @@ static unsigned long iova_rcache_get(struct iova_domain *iovad,
|
|||
unsigned long limit_pfn);
|
||||
static void init_iova_rcaches(struct iova_domain *iovad);
|
||||
static void free_iova_rcaches(struct iova_domain *iovad);
|
||||
static void fq_destroy_all_entries(struct iova_domain *iovad);
|
||||
static void fq_flush_timeout(unsigned long data);
|
||||
|
||||
void
|
||||
init_iova_domain(struct iova_domain *iovad, unsigned long granule,
|
||||
|
@ -50,10 +52,61 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule,
|
|||
iovad->granule = granule;
|
||||
iovad->start_pfn = start_pfn;
|
||||
iovad->dma_32bit_pfn = pfn_32bit + 1;
|
||||
iovad->flush_cb = NULL;
|
||||
iovad->fq = NULL;
|
||||
init_iova_rcaches(iovad);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(init_iova_domain);
|
||||
|
||||
static void free_iova_flush_queue(struct iova_domain *iovad)
|
||||
{
|
||||
if (!iovad->fq)
|
||||
return;
|
||||
|
||||
if (timer_pending(&iovad->fq_timer))
|
||||
del_timer(&iovad->fq_timer);
|
||||
|
||||
fq_destroy_all_entries(iovad);
|
||||
|
||||
free_percpu(iovad->fq);
|
||||
|
||||
iovad->fq = NULL;
|
||||
iovad->flush_cb = NULL;
|
||||
iovad->entry_dtor = NULL;
|
||||
}
|
||||
|
||||
int init_iova_flush_queue(struct iova_domain *iovad,
|
||||
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
atomic64_set(&iovad->fq_flush_start_cnt, 0);
|
||||
atomic64_set(&iovad->fq_flush_finish_cnt, 0);
|
||||
|
||||
iovad->fq = alloc_percpu(struct iova_fq);
|
||||
if (!iovad->fq)
|
||||
return -ENOMEM;
|
||||
|
||||
iovad->flush_cb = flush_cb;
|
||||
iovad->entry_dtor = entry_dtor;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct iova_fq *fq;
|
||||
|
||||
fq = per_cpu_ptr(iovad->fq, cpu);
|
||||
fq->head = 0;
|
||||
fq->tail = 0;
|
||||
|
||||
spin_lock_init(&fq->lock);
|
||||
}
|
||||
|
||||
setup_timer(&iovad->fq_timer, fq_flush_timeout, (unsigned long)iovad);
|
||||
atomic_set(&iovad->fq_timer_on, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(init_iova_flush_queue);
|
||||
|
||||
static struct rb_node *
|
||||
__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
|
||||
{
|
||||
|
@ -423,6 +476,135 @@ free_iova_fast(struct iova_domain *iovad, unsigned long pfn, unsigned long size)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(free_iova_fast);
|
||||
|
||||
#define fq_ring_for_each(i, fq) \
|
||||
for ((i) = (fq)->head; (i) != (fq)->tail; (i) = ((i) + 1) % IOVA_FQ_SIZE)
|
||||
|
||||
static inline bool fq_full(struct iova_fq *fq)
|
||||
{
|
||||
assert_spin_locked(&fq->lock);
|
||||
return (((fq->tail + 1) % IOVA_FQ_SIZE) == fq->head);
|
||||
}
|
||||
|
||||
static inline unsigned fq_ring_add(struct iova_fq *fq)
|
||||
{
|
||||
unsigned idx = fq->tail;
|
||||
|
||||
assert_spin_locked(&fq->lock);
|
||||
|
||||
fq->tail = (idx + 1) % IOVA_FQ_SIZE;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void fq_ring_free(struct iova_domain *iovad, struct iova_fq *fq)
|
||||
{
|
||||
u64 counter = atomic64_read(&iovad->fq_flush_finish_cnt);
|
||||
unsigned idx;
|
||||
|
||||
assert_spin_locked(&fq->lock);
|
||||
|
||||
fq_ring_for_each(idx, fq) {
|
||||
|
||||
if (fq->entries[idx].counter >= counter)
|
||||
break;
|
||||
|
||||
if (iovad->entry_dtor)
|
||||
iovad->entry_dtor(fq->entries[idx].data);
|
||||
|
||||
free_iova_fast(iovad,
|
||||
fq->entries[idx].iova_pfn,
|
||||
fq->entries[idx].pages);
|
||||
|
||||
fq->head = (fq->head + 1) % IOVA_FQ_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
static void iova_domain_flush(struct iova_domain *iovad)
|
||||
{
|
||||
atomic64_inc(&iovad->fq_flush_start_cnt);
|
||||
iovad->flush_cb(iovad);
|
||||
atomic64_inc(&iovad->fq_flush_finish_cnt);
|
||||
}
|
||||
|
||||
static void fq_destroy_all_entries(struct iova_domain *iovad)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
/*
|
||||
* This code runs when the iova_domain is being detroyed, so don't
|
||||
* bother to free iovas, just call the entry_dtor on all remaining
|
||||
* entries.
|
||||
*/
|
||||
if (!iovad->entry_dtor)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct iova_fq *fq = per_cpu_ptr(iovad->fq, cpu);
|
||||
int idx;
|
||||
|
||||
fq_ring_for_each(idx, fq)
|
||||
iovad->entry_dtor(fq->entries[idx].data);
|
||||
}
|
||||
}
|
||||
|
||||
static void fq_flush_timeout(unsigned long data)
|
||||
{
|
||||
struct iova_domain *iovad = (struct iova_domain *)data;
|
||||
int cpu;
|
||||
|
||||
atomic_set(&iovad->fq_timer_on, 0);
|
||||
iova_domain_flush(iovad);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
unsigned long flags;
|
||||
struct iova_fq *fq;
|
||||
|
||||
fq = per_cpu_ptr(iovad->fq, cpu);
|
||||
spin_lock_irqsave(&fq->lock, flags);
|
||||
fq_ring_free(iovad, fq);
|
||||
spin_unlock_irqrestore(&fq->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
void queue_iova(struct iova_domain *iovad,
|
||||
unsigned long pfn, unsigned long pages,
|
||||
unsigned long data)
|
||||
{
|
||||
struct iova_fq *fq = get_cpu_ptr(iovad->fq);
|
||||
unsigned long flags;
|
||||
unsigned idx;
|
||||
|
||||
spin_lock_irqsave(&fq->lock, flags);
|
||||
|
||||
/*
|
||||
* First remove all entries from the flush queue that have already been
|
||||
* flushed out on another CPU. This makes the fq_full() check below less
|
||||
* likely to be true.
|
||||
*/
|
||||
fq_ring_free(iovad, fq);
|
||||
|
||||
if (fq_full(fq)) {
|
||||
iova_domain_flush(iovad);
|
||||
fq_ring_free(iovad, fq);
|
||||
}
|
||||
|
||||
idx = fq_ring_add(fq);
|
||||
|
||||
fq->entries[idx].iova_pfn = pfn;
|
||||
fq->entries[idx].pages = pages;
|
||||
fq->entries[idx].data = data;
|
||||
fq->entries[idx].counter = atomic64_read(&iovad->fq_flush_start_cnt);
|
||||
|
||||
spin_unlock_irqrestore(&fq->lock, flags);
|
||||
|
||||
if (atomic_cmpxchg(&iovad->fq_timer_on, 0, 1) == 0)
|
||||
mod_timer(&iovad->fq_timer,
|
||||
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
|
||||
|
||||
put_cpu_ptr(iovad->fq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(queue_iova);
|
||||
|
||||
/**
|
||||
* put_iova_domain - destroys the iova doamin
|
||||
* @iovad: - iova domain in question.
|
||||
|
@ -433,6 +615,7 @@ void put_iova_domain(struct iova_domain *iovad)
|
|||
struct rb_node *node;
|
||||
unsigned long flags;
|
||||
|
||||
free_iova_flush_queue(iovad);
|
||||
free_iova_rcaches(iovad);
|
||||
spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
|
||||
node = rb_first(&iovad->rbroot);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/iommu.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -35,7 +36,7 @@
|
|||
struct ipmmu_vmsa_device {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct list_head list;
|
||||
struct iommu_device iommu;
|
||||
|
||||
unsigned int num_utlbs;
|
||||
spinlock_t lock; /* Protects ctx and domains[] */
|
||||
|
@ -58,36 +59,18 @@ struct ipmmu_vmsa_domain {
|
|||
|
||||
struct ipmmu_vmsa_iommu_priv {
|
||||
struct ipmmu_vmsa_device *mmu;
|
||||
unsigned int *utlbs;
|
||||
unsigned int num_utlbs;
|
||||
struct device *dev;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(ipmmu_devices_lock);
|
||||
static LIST_HEAD(ipmmu_devices);
|
||||
|
||||
static struct ipmmu_vmsa_domain *to_vmsa_domain(struct iommu_domain *dom)
|
||||
{
|
||||
return container_of(dom, struct ipmmu_vmsa_domain, io_domain);
|
||||
}
|
||||
|
||||
|
||||
static struct ipmmu_vmsa_iommu_priv *to_priv(struct device *dev)
|
||||
{
|
||||
#if defined(CONFIG_ARM)
|
||||
return dev->archdata.iommu;
|
||||
#else
|
||||
return dev->iommu_fwspec->iommu_priv;
|
||||
#endif
|
||||
}
|
||||
static void set_priv(struct device *dev, struct ipmmu_vmsa_iommu_priv *p)
|
||||
{
|
||||
#if defined(CONFIG_ARM)
|
||||
dev->archdata.iommu = p;
|
||||
#else
|
||||
dev->iommu_fwspec->iommu_priv = p;
|
||||
#endif
|
||||
return dev->iommu_fwspec ? dev->iommu_fwspec->iommu_priv : NULL;
|
||||
}
|
||||
|
||||
#define TLB_LOOP_TIMEOUT 100 /* 100us */
|
||||
|
@ -312,7 +295,7 @@ static void ipmmu_tlb_add_flush(unsigned long iova, size_t size,
|
|||
/* The hardware doesn't support selective TLB flush. */
|
||||
}
|
||||
|
||||
static struct iommu_gather_ops ipmmu_gather_ops = {
|
||||
static const struct iommu_gather_ops ipmmu_gather_ops = {
|
||||
.tlb_flush_all = ipmmu_tlb_flush_all,
|
||||
.tlb_add_flush = ipmmu_tlb_add_flush,
|
||||
.tlb_sync = ipmmu_tlb_flush_all,
|
||||
|
@ -341,6 +324,19 @@ static int ipmmu_domain_allocate_context(struct ipmmu_vmsa_device *mmu,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void ipmmu_domain_free_context(struct ipmmu_vmsa_device *mmu,
|
||||
unsigned int context_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mmu->lock, flags);
|
||||
|
||||
clear_bit(context_id, mmu->ctx);
|
||||
mmu->domains[context_id] = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&mmu->lock, flags);
|
||||
}
|
||||
|
||||
static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
|
||||
{
|
||||
u64 ttbr;
|
||||
|
@ -370,22 +366,22 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
|
|||
*/
|
||||
domain->cfg.iommu_dev = domain->mmu->dev;
|
||||
|
||||
domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg,
|
||||
domain);
|
||||
if (!domain->iop)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Find an unused context.
|
||||
*/
|
||||
ret = ipmmu_domain_allocate_context(domain->mmu, domain);
|
||||
if (ret == IPMMU_CTX_MAX) {
|
||||
free_io_pgtable_ops(domain->iop);
|
||||
if (ret == IPMMU_CTX_MAX)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
domain->context_id = ret;
|
||||
|
||||
domain->iop = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &domain->cfg,
|
||||
domain);
|
||||
if (!domain->iop) {
|
||||
ipmmu_domain_free_context(domain->mmu, domain->context_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* TTBR0 */
|
||||
ttbr = domain->cfg.arm_lpae_s1_cfg.ttbr[0];
|
||||
ipmmu_ctx_write(domain, IMTTLBR0, ttbr);
|
||||
|
@ -426,19 +422,6 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ipmmu_domain_free_context(struct ipmmu_vmsa_device *mmu,
|
||||
unsigned int context_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mmu->lock, flags);
|
||||
|
||||
clear_bit(context_id, mmu->ctx);
|
||||
mmu->domains[context_id] = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&mmu->lock, flags);
|
||||
}
|
||||
|
||||
static void ipmmu_domain_destroy_context(struct ipmmu_vmsa_domain *domain)
|
||||
{
|
||||
/*
|
||||
|
@ -562,13 +545,14 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
|
|||
struct device *dev)
|
||||
{
|
||||
struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev);
|
||||
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
|
||||
struct ipmmu_vmsa_device *mmu = priv->mmu;
|
||||
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
int ret = 0;
|
||||
|
||||
if (!mmu) {
|
||||
if (!priv || !priv->mmu) {
|
||||
dev_err(dev, "Cannot attach to IPMMU\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
@ -595,8 +579,8 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < priv->num_utlbs; ++i)
|
||||
ipmmu_utlb_enable(domain, priv->utlbs[i]);
|
||||
for (i = 0; i < fwspec->num_ids; ++i)
|
||||
ipmmu_utlb_enable(domain, fwspec->ids[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -604,12 +588,12 @@ static int ipmmu_attach_device(struct iommu_domain *io_domain,
|
|||
static void ipmmu_detach_device(struct iommu_domain *io_domain,
|
||||
struct device *dev)
|
||||
{
|
||||
struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev);
|
||||
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
|
||||
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < priv->num_utlbs; ++i)
|
||||
ipmmu_utlb_disable(domain, priv->utlbs[i]);
|
||||
for (i = 0; i < fwspec->num_ids; ++i)
|
||||
ipmmu_utlb_disable(domain, fwspec->ids[i]);
|
||||
|
||||
/*
|
||||
* TODO: Optimize by disabling the context when no device is attached.
|
||||
|
@ -645,92 +629,36 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
|
|||
return domain->iop->iova_to_phys(domain->iop, iova);
|
||||
}
|
||||
|
||||
static int ipmmu_find_utlbs(struct ipmmu_vmsa_device *mmu, struct device *dev,
|
||||
unsigned int *utlbs, unsigned int num_utlbs)
|
||||
static int ipmmu_init_platform_device(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
unsigned int i;
|
||||
struct platform_device *ipmmu_pdev;
|
||||
struct ipmmu_vmsa_iommu_priv *priv;
|
||||
|
||||
for (i = 0; i < num_utlbs; ++i) {
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
ipmmu_pdev = of_find_device_by_node(args->np);
|
||||
if (!ipmmu_pdev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_parse_phandle_with_args(dev->of_node, "iommus",
|
||||
"#iommu-cells", i, &args);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
of_node_put(args.np);
|
||||
|
||||
if (args.np != mmu->dev->of_node || args.args_count != 1)
|
||||
return -EINVAL;
|
||||
|
||||
utlbs[i] = args.args[0];
|
||||
}
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->mmu = platform_get_drvdata(ipmmu_pdev);
|
||||
priv->dev = dev;
|
||||
dev->iommu_fwspec->iommu_priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipmmu_init_platform_device(struct device *dev)
|
||||
static int ipmmu_of_xlate(struct device *dev,
|
||||
struct of_phandle_args *spec)
|
||||
{
|
||||
struct ipmmu_vmsa_iommu_priv *priv;
|
||||
struct ipmmu_vmsa_device *mmu;
|
||||
unsigned int *utlbs;
|
||||
unsigned int i;
|
||||
int num_utlbs;
|
||||
int ret = -ENODEV;
|
||||
iommu_fwspec_add_ids(dev, spec->args, 1);
|
||||
|
||||
/* Find the master corresponding to the device. */
|
||||
|
||||
num_utlbs = of_count_phandle_with_args(dev->of_node, "iommus",
|
||||
"#iommu-cells");
|
||||
if (num_utlbs < 0)
|
||||
return -ENODEV;
|
||||
|
||||
utlbs = kcalloc(num_utlbs, sizeof(*utlbs), GFP_KERNEL);
|
||||
if (!utlbs)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock(&ipmmu_devices_lock);
|
||||
|
||||
list_for_each_entry(mmu, &ipmmu_devices, list) {
|
||||
ret = ipmmu_find_utlbs(mmu, dev, utlbs, num_utlbs);
|
||||
if (!ret) {
|
||||
/*
|
||||
* TODO Take a reference to the MMU to protect
|
||||
* against device removal.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&ipmmu_devices_lock);
|
||||
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
for (i = 0; i < num_utlbs; ++i) {
|
||||
if (utlbs[i] >= mmu->num_utlbs) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
priv->mmu = mmu;
|
||||
priv->utlbs = utlbs;
|
||||
priv->num_utlbs = num_utlbs;
|
||||
priv->dev = dev;
|
||||
set_priv(dev, priv);
|
||||
/* Initialize once - xlate() will call multiple times */
|
||||
if (to_priv(dev))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
kfree(utlbs);
|
||||
return ret;
|
||||
return ipmmu_init_platform_device(dev, spec);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA)
|
||||
|
@ -749,11 +677,11 @@ static int ipmmu_add_device(struct device *dev)
|
|||
struct iommu_group *group;
|
||||
int ret;
|
||||
|
||||
if (to_priv(dev)) {
|
||||
dev_warn(dev, "IOMMU driver already assigned to device %s\n",
|
||||
dev_name(dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* Only let through devices that have been verified in xlate()
|
||||
*/
|
||||
if (!to_priv(dev))
|
||||
return -ENODEV;
|
||||
|
||||
/* Create a device group and add the device to it. */
|
||||
group = iommu_group_alloc();
|
||||
|
@ -772,10 +700,6 @@ static int ipmmu_add_device(struct device *dev)
|
|||
goto error;
|
||||
}
|
||||
|
||||
ret = ipmmu_init_platform_device(dev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Create the ARM mapping, used by the ARM DMA mapping core to allocate
|
||||
* VAs. This will allocate a corresponding IOMMU domain.
|
||||
|
@ -816,24 +740,13 @@ static int ipmmu_add_device(struct device *dev)
|
|||
if (!IS_ERR_OR_NULL(group))
|
||||
iommu_group_remove_device(dev);
|
||||
|
||||
kfree(to_priv(dev)->utlbs);
|
||||
kfree(to_priv(dev));
|
||||
set_priv(dev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ipmmu_remove_device(struct device *dev)
|
||||
{
|
||||
struct ipmmu_vmsa_iommu_priv *priv = to_priv(dev);
|
||||
|
||||
arm_iommu_detach_device(dev);
|
||||
iommu_group_remove_device(dev);
|
||||
|
||||
kfree(priv->utlbs);
|
||||
kfree(priv);
|
||||
|
||||
set_priv(dev, NULL);
|
||||
}
|
||||
|
||||
static const struct iommu_ops ipmmu_ops = {
|
||||
|
@ -848,6 +761,7 @@ static const struct iommu_ops ipmmu_ops = {
|
|||
.add_device = ipmmu_add_device,
|
||||
.remove_device = ipmmu_remove_device,
|
||||
.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
|
||||
.of_xlate = ipmmu_of_xlate,
|
||||
};
|
||||
|
||||
#endif /* !CONFIG_ARM && CONFIG_IOMMU_DMA */
|
||||
|
@ -890,14 +804,12 @@ static void ipmmu_domain_free_dma(struct iommu_domain *io_domain)
|
|||
|
||||
static int ipmmu_add_device_dma(struct device *dev)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
|
||||
struct iommu_group *group;
|
||||
|
||||
/*
|
||||
* Only let through devices that have been verified in xlate()
|
||||
* We may get called with dev->iommu_fwspec set to NULL.
|
||||
*/
|
||||
if (!fwspec || !fwspec->iommu_priv)
|
||||
if (!to_priv(dev))
|
||||
return -ENODEV;
|
||||
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
|
@ -957,19 +869,6 @@ static struct iommu_group *ipmmu_find_group_dma(struct device *dev)
|
|||
return group;
|
||||
}
|
||||
|
||||
static int ipmmu_of_xlate_dma(struct device *dev,
|
||||
struct of_phandle_args *spec)
|
||||
{
|
||||
/* If the IPMMU device is disabled in DT then return error
|
||||
* to make sure the of_iommu code does not install ops
|
||||
* even though the iommu device is disabled
|
||||
*/
|
||||
if (!of_device_is_available(spec->np))
|
||||
return -ENODEV;
|
||||
|
||||
return ipmmu_init_platform_device(dev);
|
||||
}
|
||||
|
||||
static const struct iommu_ops ipmmu_ops = {
|
||||
.domain_alloc = ipmmu_domain_alloc_dma,
|
||||
.domain_free = ipmmu_domain_free_dma,
|
||||
|
@ -983,7 +882,7 @@ static const struct iommu_ops ipmmu_ops = {
|
|||
.remove_device = ipmmu_remove_device_dma,
|
||||
.device_group = ipmmu_find_group_dma,
|
||||
.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K,
|
||||
.of_xlate = ipmmu_of_xlate_dma,
|
||||
.of_xlate = ipmmu_of_xlate,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_IOMMU_DMA */
|
||||
|
@ -1054,16 +953,24 @@ static int ipmmu_probe(struct platform_device *pdev)
|
|||
|
||||
ipmmu_device_reset(mmu);
|
||||
|
||||
ret = iommu_device_sysfs_add(&mmu->iommu, &pdev->dev, NULL,
|
||||
dev_name(&pdev->dev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
iommu_device_set_ops(&mmu->iommu, &ipmmu_ops);
|
||||
iommu_device_set_fwnode(&mmu->iommu, &pdev->dev.of_node->fwnode);
|
||||
|
||||
ret = iommu_device_register(&mmu->iommu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We can't create the ARM mapping here as it requires the bus to have
|
||||
* an IOMMU, which only happens when bus_set_iommu() is called in
|
||||
* ipmmu_init() after the probe function returns.
|
||||
*/
|
||||
|
||||
spin_lock(&ipmmu_devices_lock);
|
||||
list_add(&mmu->list, &ipmmu_devices);
|
||||
spin_unlock(&ipmmu_devices_lock);
|
||||
|
||||
platform_set_drvdata(pdev, mmu);
|
||||
|
||||
return 0;
|
||||
|
@ -1073,9 +980,8 @@ static int ipmmu_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct ipmmu_vmsa_device *mmu = platform_get_drvdata(pdev);
|
||||
|
||||
spin_lock(&ipmmu_devices_lock);
|
||||
list_del(&mmu->list);
|
||||
spin_unlock(&ipmmu_devices_lock);
|
||||
iommu_device_sysfs_remove(&mmu->iommu);
|
||||
iommu_device_unregister(&mmu->iommu);
|
||||
|
||||
#if defined(CONFIG_ARM) && !defined(CONFIG_IOMMU_DMA)
|
||||
arm_iommu_release_mapping(mmu->mapping);
|
||||
|
|
|
@ -393,6 +393,7 @@ static struct msm_iommu_dev *find_iommu_for_dev(struct device *dev)
|
|||
static int msm_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct msm_iommu_dev *iommu;
|
||||
struct iommu_group *group;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
|
@ -406,7 +407,16 @@ static int msm_iommu_add_device(struct device *dev)
|
|||
|
||||
spin_unlock_irqrestore(&msm_iommu_lock, flags);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_iommu_remove_device(struct device *dev)
|
||||
|
@ -421,6 +431,8 @@ static void msm_iommu_remove_device(struct device *dev)
|
|||
iommu_device_unlink(&iommu->iommu, dev);
|
||||
|
||||
spin_unlock_irqrestore(&msm_iommu_lock, flags);
|
||||
|
||||
iommu_group_remove_device(dev);
|
||||
}
|
||||
|
||||
static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
|
@ -700,6 +712,7 @@ static struct iommu_ops msm_iommu_ops = {
|
|||
.iova_to_phys = msm_iommu_iova_to_phys,
|
||||
.add_device = msm_iommu_add_device,
|
||||
.remove_device = msm_iommu_remove_device,
|
||||
.device_group = generic_device_group,
|
||||
.pgsize_bitmap = MSM_IOMMU_PGSIZES,
|
||||
.of_xlate = qcom_iommu_of_xlate,
|
||||
};
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/barrier.h>
|
||||
#include <dt-bindings/memory/mt8173-larb-port.h>
|
||||
#include <soc/mediatek/smi.h>
|
||||
|
||||
#include "mtk_iommu.h"
|
||||
|
@ -54,10 +53,16 @@
|
|||
|
||||
#define REG_MMU_CTRL_REG 0x110
|
||||
#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4)
|
||||
#define F_MMU_TF_PROTECT_SEL(prot) (((prot) & 0x3) << 5)
|
||||
#define F_MMU_TF_PROTECT_SEL_SHIFT(data) \
|
||||
((data)->m4u_plat == M4U_MT2712 ? 4 : 5)
|
||||
/* It's named by F_MMU_TF_PROT_SEL in mt2712. */
|
||||
#define F_MMU_TF_PROTECT_SEL(prot, data) \
|
||||
(((prot) & 0x3) << F_MMU_TF_PROTECT_SEL_SHIFT(data))
|
||||
|
||||
#define REG_MMU_IVRP_PADDR 0x114
|
||||
#define F_MMU_IVRP_PA_SET(pa, ext) (((pa) >> 1) | ((!!(ext)) << 31))
|
||||
#define REG_MMU_VLD_PA_RNG 0x118
|
||||
#define F_MMU_VLD_PA_RNG(EA, SA) (((EA) << 8) | (SA))
|
||||
|
||||
#define REG_MMU_INT_CONTROL0 0x120
|
||||
#define F_L2_MULIT_HIT_EN BIT(0)
|
||||
|
@ -82,7 +87,6 @@
|
|||
#define REG_MMU_FAULT_ST1 0x134
|
||||
|
||||
#define REG_MMU_FAULT_VA 0x13c
|
||||
#define F_MMU_FAULT_VA_MSK 0xfffff000
|
||||
#define F_MMU_FAULT_VA_WRITE_BIT BIT(1)
|
||||
#define F_MMU_FAULT_VA_LAYER_BIT BIT(0)
|
||||
|
||||
|
@ -93,6 +97,13 @@
|
|||
|
||||
#define MTK_PROTECT_PA_ALIGN 128
|
||||
|
||||
/*
|
||||
* Get the local arbiter ID and the portid within the larb arbiter
|
||||
* from mtk_m4u_id which is defined by MTK_M4U_ID.
|
||||
*/
|
||||
#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0xf)
|
||||
#define MTK_M4U_TO_PORT(id) ((id) & 0x1f)
|
||||
|
||||
struct mtk_iommu_domain {
|
||||
spinlock_t pgtlock; /* lock for page table */
|
||||
|
||||
|
@ -104,6 +115,27 @@ struct mtk_iommu_domain {
|
|||
|
||||
static struct iommu_ops mtk_iommu_ops;
|
||||
|
||||
static LIST_HEAD(m4ulist); /* List all the M4U HWs */
|
||||
|
||||
#define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list)
|
||||
|
||||
/*
|
||||
* There may be 1 or 2 M4U HWs, But we always expect they are in the same domain
|
||||
* for the performance.
|
||||
*
|
||||
* Here always return the mtk_iommu_data of the first probed M4U where the
|
||||
* iommu domain information is recorded.
|
||||
*/
|
||||
static struct mtk_iommu_data *mtk_iommu_get_m4u_data(void)
|
||||
{
|
||||
struct mtk_iommu_data *data;
|
||||
|
||||
for_each_m4u(data)
|
||||
return data;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
|
||||
{
|
||||
return container_of(dom, struct mtk_iommu_domain, domain);
|
||||
|
@ -113,9 +145,12 @@ static void mtk_iommu_tlb_flush_all(void *cookie)
|
|||
{
|
||||
struct mtk_iommu_data *data = cookie;
|
||||
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
|
||||
for_each_m4u(data) {
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
|
||||
data->base + REG_MMU_INV_SEL);
|
||||
writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
|
||||
wmb(); /* Make sure the tlb flush all done */
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,
|
||||
|
@ -124,12 +159,17 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,
|
|||
{
|
||||
struct mtk_iommu_data *data = cookie;
|
||||
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
|
||||
for_each_m4u(data) {
|
||||
writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
|
||||
data->base + REG_MMU_INV_SEL);
|
||||
|
||||
writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A);
|
||||
writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A);
|
||||
writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
|
||||
writel_relaxed(iova + size - 1,
|
||||
data->base + REG_MMU_INVLD_END_A);
|
||||
writel_relaxed(F_MMU_INV_RANGE,
|
||||
data->base + REG_MMU_INVALIDATE);
|
||||
data->tlb_flush_active = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_iommu_tlb_sync(void *cookie)
|
||||
|
@ -138,12 +178,13 @@ static void mtk_iommu_tlb_sync(void *cookie)
|
|||
int ret;
|
||||
u32 tmp;
|
||||
|
||||
for_each_m4u(data) {
|
||||
/* Avoid timing out if there's nothing to wait for */
|
||||
if (!data->tlb_flush_active)
|
||||
return;
|
||||
|
||||
ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp,
|
||||
tmp != 0, 10, 100000);
|
||||
ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
|
||||
tmp, tmp != 0, 10, 100000);
|
||||
if (ret) {
|
||||
dev_warn(data->dev,
|
||||
"Partial TLB flush timed out, falling back to full flush\n");
|
||||
|
@ -152,6 +193,7 @@ static void mtk_iommu_tlb_sync(void *cookie)
|
|||
/* Clear the CPE status */
|
||||
writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
|
||||
data->tlb_flush_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iommu_gather_ops mtk_iommu_gather_ops = {
|
||||
|
@ -173,7 +215,6 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
|||
fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
|
||||
layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
|
||||
write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
|
||||
fault_iova &= F_MMU_FAULT_VA_MSK;
|
||||
fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
|
||||
regval = readl_relaxed(data->base + REG_MMU_INT_ID);
|
||||
fault_larb = F_MMU0_INT_ID_LARB_ID(regval);
|
||||
|
@ -221,9 +262,9 @@ static void mtk_iommu_config(struct mtk_iommu_data *data,
|
|||
}
|
||||
}
|
||||
|
||||
static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
|
||||
static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom)
|
||||
{
|
||||
struct mtk_iommu_domain *dom = data->m4u_dom;
|
||||
struct mtk_iommu_data *data = mtk_iommu_get_m4u_data();
|
||||
|
||||
spin_lock_init(&dom->pgtlock);
|
||||
|
||||
|
@ -249,9 +290,6 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
|
|||
|
||||
/* Update our support page sizes bitmap */
|
||||
dom->domain.pgsize_bitmap = dom->cfg.pgsize_bitmap;
|
||||
|
||||
writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
|
||||
data->base + REG_MMU_PT_BASE_ADDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -266,20 +304,30 @@ static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type)
|
|||
if (!dom)
|
||||
return NULL;
|
||||
|
||||
if (iommu_get_dma_cookie(&dom->domain)) {
|
||||
kfree(dom);
|
||||
return NULL;
|
||||
}
|
||||
if (iommu_get_dma_cookie(&dom->domain))
|
||||
goto free_dom;
|
||||
|
||||
if (mtk_iommu_domain_finalise(dom))
|
||||
goto put_dma_cookie;
|
||||
|
||||
dom->domain.geometry.aperture_start = 0;
|
||||
dom->domain.geometry.aperture_end = DMA_BIT_MASK(32);
|
||||
dom->domain.geometry.force_aperture = true;
|
||||
|
||||
return &dom->domain;
|
||||
|
||||
put_dma_cookie:
|
||||
iommu_put_dma_cookie(&dom->domain);
|
||||
free_dom:
|
||||
kfree(dom);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void mtk_iommu_domain_free(struct iommu_domain *domain)
|
||||
{
|
||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||
|
||||
free_io_pgtable_ops(dom->iop);
|
||||
iommu_put_dma_cookie(domain);
|
||||
kfree(to_mtk_domain(domain));
|
||||
}
|
||||
|
@ -289,22 +337,15 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain,
|
|||
{
|
||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||
struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv;
|
||||
int ret;
|
||||
|
||||
if (!data)
|
||||
return -ENODEV;
|
||||
|
||||
/* Update the pgtable base address register of the M4U HW */
|
||||
if (!data->m4u_dom) {
|
||||
data->m4u_dom = dom;
|
||||
ret = mtk_iommu_domain_finalise(data);
|
||||
if (ret) {
|
||||
data->m4u_dom = NULL;
|
||||
return ret;
|
||||
}
|
||||
} else if (data->m4u_dom != dom) {
|
||||
/* All the client devices should be in the same m4u domain */
|
||||
dev_err(dev, "try to attach into the error iommu domain\n");
|
||||
return -EPERM;
|
||||
writel(dom->cfg.arm_v7s_cfg.ttbr[0],
|
||||
data->base + REG_MMU_PT_BASE_ADDR);
|
||||
}
|
||||
|
||||
mtk_iommu_config(data, dev, true);
|
||||
|
@ -354,6 +395,7 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||
dma_addr_t iova)
|
||||
{
|
||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||
struct mtk_iommu_data *data = mtk_iommu_get_m4u_data();
|
||||
unsigned long flags;
|
||||
phys_addr_t pa;
|
||||
|
||||
|
@ -361,6 +403,9 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||
pa = dom->iop->iova_to_phys(dom->iop, iova);
|
||||
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
||||
|
||||
if (data->enable_4GB)
|
||||
pa |= BIT_ULL(32);
|
||||
|
||||
return pa;
|
||||
}
|
||||
|
||||
|
@ -399,7 +444,7 @@ static void mtk_iommu_remove_device(struct device *dev)
|
|||
|
||||
static struct iommu_group *mtk_iommu_device_group(struct device *dev)
|
||||
{
|
||||
struct mtk_iommu_data *data = dev->iommu_fwspec->iommu_priv;
|
||||
struct mtk_iommu_data *data = mtk_iommu_get_m4u_data();
|
||||
|
||||
if (!data)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
@ -464,8 +509,9 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
|
||||
F_MMU_TF_PROTECT_SEL(2);
|
||||
regval = F_MMU_TF_PROTECT_SEL(2, data);
|
||||
if (data->m4u_plat == M4U_MT8173)
|
||||
regval |= F_MMU_PREFETCH_RT_REPLACE_MOD;
|
||||
writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
|
||||
|
||||
regval = F_L2_MULIT_HIT_EN |
|
||||
|
@ -487,8 +533,18 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||
|
||||
writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB),
|
||||
data->base + REG_MMU_IVRP_PADDR);
|
||||
|
||||
if (data->enable_4GB && data->m4u_plat != M4U_MT8173) {
|
||||
/*
|
||||
* If 4GB mode is enabled, the validate PA range is from
|
||||
* 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30].
|
||||
*/
|
||||
regval = F_MMU_VLD_PA_RNG(7, 4);
|
||||
writel_relaxed(regval, data->base + REG_MMU_VLD_PA_RNG);
|
||||
}
|
||||
writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
|
||||
|
||||
/* It's MISC control register whose default value is ok except mt8173.*/
|
||||
if (data->m4u_plat == M4U_MT8173)
|
||||
writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
|
||||
|
||||
if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
|
||||
|
@ -521,6 +577,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
data->dev = dev;
|
||||
data->m4u_plat = (enum mtk_iommu_plat)of_device_get_match_data(dev);
|
||||
|
||||
/* Protect memory. HW will access here while translation fault.*/
|
||||
protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL);
|
||||
|
@ -529,7 +586,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
|
||||
|
||||
/* Whether the current dram is over 4GB */
|
||||
data->enable_4GB = !!(max_pfn > (0xffffffffUL >> PAGE_SHIFT));
|
||||
data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT));
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->base = devm_ioremap_resource(dev, res);
|
||||
|
@ -554,6 +611,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < larb_nr; i++) {
|
||||
struct device_node *larbnode;
|
||||
struct platform_device *plarbdev;
|
||||
u32 id;
|
||||
|
||||
larbnode = of_parse_phandle(dev->of_node, "mediatek,larbs", i);
|
||||
if (!larbnode)
|
||||
|
@ -562,17 +620,14 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
if (!of_device_is_available(larbnode))
|
||||
continue;
|
||||
|
||||
ret = of_property_read_u32(larbnode, "mediatek,larb-id", &id);
|
||||
if (ret)/* The id is consecutive if there is no this property */
|
||||
id = i;
|
||||
|
||||
plarbdev = of_find_device_by_node(larbnode);
|
||||
if (!plarbdev) {
|
||||
plarbdev = of_platform_device_create(
|
||||
larbnode, NULL,
|
||||
platform_bus_type.dev_root);
|
||||
if (!plarbdev) {
|
||||
of_node_put(larbnode);
|
||||
if (!plarbdev)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
}
|
||||
data->smi_imu.larb_imu[i].dev = &plarbdev->dev;
|
||||
data->smi_imu.larb_imu[id].dev = &plarbdev->dev;
|
||||
|
||||
component_match_add_release(dev, &match, release_of,
|
||||
compare_of, larbnode);
|
||||
|
@ -596,6 +651,8 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_add_tail(&data->list, &m4ulist);
|
||||
|
||||
if (!iommu_present(&platform_bus_type))
|
||||
bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
|
||||
|
||||
|
@ -612,7 +669,6 @@ static int mtk_iommu_remove(struct platform_device *pdev)
|
|||
if (iommu_present(&platform_bus_type))
|
||||
bus_set_iommu(&platform_bus_type, NULL);
|
||||
|
||||
free_io_pgtable_ops(data->m4u_dom->iop);
|
||||
clk_disable_unprepare(data->bclk);
|
||||
devm_free_irq(&pdev->dev, data->irq, data);
|
||||
component_master_del(&pdev->dev, &mtk_iommu_com_ops);
|
||||
|
@ -631,6 +687,7 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev)
|
|||
reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
|
||||
reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
|
||||
reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
|
||||
clk_disable_unprepare(data->bclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -639,9 +696,13 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
|
|||
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
||||
struct mtk_iommu_suspend_reg *reg = &data->reg;
|
||||
void __iomem *base = data->base;
|
||||
int ret;
|
||||
|
||||
writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
|
||||
base + REG_MMU_PT_BASE_ADDR);
|
||||
ret = clk_prepare_enable(data->bclk);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret);
|
||||
return ret;
|
||||
}
|
||||
writel_relaxed(reg->standard_axi_mode,
|
||||
base + REG_MMU_STANDARD_AXI_MODE);
|
||||
writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
|
||||
|
@ -650,15 +711,19 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
|
|||
writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
|
||||
writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base, data->enable_4GB),
|
||||
base + REG_MMU_IVRP_PADDR);
|
||||
if (data->m4u_dom)
|
||||
writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
|
||||
base + REG_MMU_PT_BASE_ADDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops mtk_iommu_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
|
||||
static const struct dev_pm_ops mtk_iommu_pm_ops = {
|
||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_iommu_of_ids[] = {
|
||||
{ .compatible = "mediatek,mt8173-m4u", },
|
||||
{ .compatible = "mediatek,mt2712-m4u", .data = (void *)M4U_MT2712},
|
||||
{ .compatible = "mediatek,mt8173-m4u", .data = (void *)M4U_MT8173},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -667,27 +732,20 @@ static struct platform_driver mtk_iommu_driver = {
|
|||
.remove = mtk_iommu_remove,
|
||||
.driver = {
|
||||
.name = "mtk-iommu",
|
||||
.of_match_table = mtk_iommu_of_ids,
|
||||
.of_match_table = of_match_ptr(mtk_iommu_of_ids),
|
||||
.pm = &mtk_iommu_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
static int mtk_iommu_init_fn(struct device_node *np)
|
||||
static int __init mtk_iommu_init(void)
|
||||
{
|
||||
int ret;
|
||||
struct platform_device *pdev;
|
||||
|
||||
pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
|
||||
if (!pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = platform_driver_register(&mtk_iommu_driver);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed to register driver\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
if (ret != 0)
|
||||
pr_err("Failed to register MTK IOMMU driver\n");
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
IOMMU_OF_DECLARE(mtkm4u, "mediatek,mt8173-m4u", mtk_iommu_init_fn);
|
||||
subsys_initcall(mtk_iommu_init)
|
||||
|
|
|
@ -34,6 +34,12 @@ struct mtk_iommu_suspend_reg {
|
|||
u32 int_main_control;
|
||||
};
|
||||
|
||||
enum mtk_iommu_plat {
|
||||
M4U_MT2701,
|
||||
M4U_MT2712,
|
||||
M4U_MT8173,
|
||||
};
|
||||
|
||||
struct mtk_iommu_domain;
|
||||
|
||||
struct mtk_iommu_data {
|
||||
|
@ -50,6 +56,9 @@ struct mtk_iommu_data {
|
|||
bool tlb_flush_active;
|
||||
|
||||
struct iommu_device iommu;
|
||||
enum mtk_iommu_plat m4u_plat;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static inline int compare_of(struct device *dev, void *data)
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include <linux/of_pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define NO_IOMMU 1
|
||||
|
||||
static const struct of_device_id __iommu_of_table_sentinel
|
||||
__used __section(__iommu_of_table_end);
|
||||
|
||||
|
@ -109,8 +111,8 @@ static bool of_iommu_driver_present(struct device_node *np)
|
|||
return of_match_node(&__iommu_of_table, np);
|
||||
}
|
||||
|
||||
static const struct iommu_ops
|
||||
*of_iommu_xlate(struct device *dev, struct of_phandle_args *iommu_spec)
|
||||
static int of_iommu_xlate(struct device *dev,
|
||||
struct of_phandle_args *iommu_spec)
|
||||
{
|
||||
const struct iommu_ops *ops;
|
||||
struct fwnode_handle *fwnode = &iommu_spec->np->fwnode;
|
||||
|
@ -120,95 +122,53 @@ static const struct iommu_ops
|
|||
if ((ops && !ops->of_xlate) ||
|
||||
!of_device_is_available(iommu_spec->np) ||
|
||||
(!ops && !of_iommu_driver_present(iommu_spec->np)))
|
||||
return NULL;
|
||||
return NO_IOMMU;
|
||||
|
||||
err = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
/*
|
||||
* The otherwise-empty fwspec handily serves to indicate the specific
|
||||
* IOMMU device we're waiting for, which will be useful if we ever get
|
||||
* a proper probe-ordering dependency mechanism in future.
|
||||
*/
|
||||
if (!ops)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
err = ops->of_xlate(dev, iommu_spec);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return ops;
|
||||
return ops->of_xlate(dev, iommu_spec);
|
||||
}
|
||||
|
||||
static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
|
||||
{
|
||||
struct of_phandle_args *iommu_spec = data;
|
||||
struct of_pci_iommu_alias_info {
|
||||
struct device *dev;
|
||||
struct device_node *np;
|
||||
};
|
||||
|
||||
iommu_spec->args[0] = alias;
|
||||
return iommu_spec->np == pdev->bus->dev.of_node;
|
||||
}
|
||||
|
||||
static const struct iommu_ops
|
||||
*of_pci_iommu_init(struct pci_dev *pdev, struct device_node *bridge_np)
|
||||
static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
|
||||
{
|
||||
const struct iommu_ops *ops;
|
||||
struct of_phandle_args iommu_spec;
|
||||
struct of_pci_iommu_alias_info *info = data;
|
||||
struct of_phandle_args iommu_spec = { .args_count = 1 };
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Start by tracing the RID alias down the PCI topology as
|
||||
* far as the host bridge whose OF node we have...
|
||||
* (we're not even attempting to handle multi-alias devices yet)
|
||||
*/
|
||||
iommu_spec.args_count = 1;
|
||||
iommu_spec.np = bridge_np;
|
||||
pci_for_each_dma_alias(pdev, __get_pci_rid, &iommu_spec);
|
||||
/*
|
||||
* ...then find out what that becomes once it escapes the PCI
|
||||
* bus into the system beyond, and which IOMMU it ends up at.
|
||||
*/
|
||||
iommu_spec.np = NULL;
|
||||
err = of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map",
|
||||
err = of_pci_map_rid(info->np, alias, "iommu-map",
|
||||
"iommu-map-mask", &iommu_spec.np,
|
||||
iommu_spec.args);
|
||||
if (err)
|
||||
return err == -ENODEV ? NULL : ERR_PTR(err);
|
||||
|
||||
ops = of_iommu_xlate(&pdev->dev, &iommu_spec);
|
||||
return err == -ENODEV ? NO_IOMMU : err;
|
||||
|
||||
err = of_iommu_xlate(info->dev, &iommu_spec);
|
||||
of_node_put(iommu_spec.np);
|
||||
return ops;
|
||||
}
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
static const struct iommu_ops
|
||||
*of_platform_iommu_init(struct device *dev, struct device_node *np)
|
||||
{
|
||||
struct of_phandle_args iommu_spec;
|
||||
const struct iommu_ops *ops = NULL;
|
||||
int idx = 0;
|
||||
|
||||
/*
|
||||
* We don't currently walk up the tree looking for a parent IOMMU.
|
||||
* See the `Notes:' section of
|
||||
* Documentation/devicetree/bindings/iommu/iommu.txt
|
||||
*/
|
||||
while (!of_parse_phandle_with_args(np, "iommus", "#iommu-cells",
|
||||
idx, &iommu_spec)) {
|
||||
ops = of_iommu_xlate(dev, &iommu_spec);
|
||||
of_node_put(iommu_spec.np);
|
||||
idx++;
|
||||
if (IS_ERR_OR_NULL(ops))
|
||||
break;
|
||||
}
|
||||
|
||||
return ops;
|
||||
return info->np == pdev->bus->dev.of_node;
|
||||
}
|
||||
|
||||
const struct iommu_ops *of_iommu_configure(struct device *dev,
|
||||
struct device_node *master_np)
|
||||
{
|
||||
const struct iommu_ops *ops;
|
||||
const struct iommu_ops *ops = NULL;
|
||||
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
|
||||
int err = NO_IOMMU;
|
||||
|
||||
if (!master_np)
|
||||
return NULL;
|
||||
|
@ -221,25 +181,54 @@ const struct iommu_ops *of_iommu_configure(struct device *dev,
|
|||
iommu_fwspec_free(dev);
|
||||
}
|
||||
|
||||
if (dev_is_pci(dev))
|
||||
ops = of_pci_iommu_init(to_pci_dev(dev), master_np);
|
||||
else
|
||||
ops = of_platform_iommu_init(dev, master_np);
|
||||
/*
|
||||
* We don't currently walk up the tree looking for a parent IOMMU.
|
||||
* See the `Notes:' section of
|
||||
* Documentation/devicetree/bindings/iommu/iommu.txt
|
||||
*/
|
||||
if (dev_is_pci(dev)) {
|
||||
struct of_pci_iommu_alias_info info = {
|
||||
.dev = dev,
|
||||
.np = master_np,
|
||||
};
|
||||
|
||||
err = pci_for_each_dma_alias(to_pci_dev(dev),
|
||||
of_pci_iommu_init, &info);
|
||||
} else {
|
||||
struct of_phandle_args iommu_spec;
|
||||
int idx = 0;
|
||||
|
||||
while (!of_parse_phandle_with_args(master_np, "iommus",
|
||||
"#iommu-cells",
|
||||
idx, &iommu_spec)) {
|
||||
err = of_iommu_xlate(dev, &iommu_spec);
|
||||
of_node_put(iommu_spec.np);
|
||||
idx++;
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Two success conditions can be represented by non-negative err here:
|
||||
* >0 : there is no IOMMU, or one was unavailable for non-fatal reasons
|
||||
* 0 : we found an IOMMU, and dev->fwspec is initialised appropriately
|
||||
* <0 : any actual error
|
||||
*/
|
||||
if (!err)
|
||||
ops = dev->iommu_fwspec->ops;
|
||||
/*
|
||||
* If we have reason to believe the IOMMU driver missed the initial
|
||||
* add_device callback for dev, replay it to get things in order.
|
||||
*/
|
||||
if (!IS_ERR_OR_NULL(ops) && ops->add_device &&
|
||||
dev->bus && !dev->iommu_group) {
|
||||
int err = ops->add_device(dev);
|
||||
|
||||
if (err)
|
||||
ops = ERR_PTR(err);
|
||||
}
|
||||
if (ops && ops->add_device && dev->bus && !dev->iommu_group)
|
||||
err = ops->add_device(dev);
|
||||
|
||||
/* Ignore all other errors apart from EPROBE_DEFER */
|
||||
if (IS_ERR(ops) && (PTR_ERR(ops) != -EPROBE_DEFER)) {
|
||||
dev_dbg(dev, "Adding to IOMMU failed: %ld\n", PTR_ERR(ops));
|
||||
if (err == -EPROBE_DEFER) {
|
||||
ops = ERR_PTR(err);
|
||||
} else if (err < 0) {
|
||||
dev_dbg(dev, "Adding to IOMMU failed: %d\n", err);
|
||||
ops = NULL;
|
||||
}
|
||||
|
||||
|
@ -255,8 +244,7 @@ static int __init of_iommu_init(void)
|
|||
const of_iommu_init_fn init_fn = match->data;
|
||||
|
||||
if (init_fn && init_fn(np))
|
||||
pr_err("Failed to initialise IOMMU %s\n",
|
||||
of_node_full_name(np));
|
||||
pr_err("Failed to initialise IOMMU %pOF\n", np);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -29,8 +30,6 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <linux/platform_data/iommu-omap.h>
|
||||
|
||||
#include "omap-iopgtable.h"
|
||||
|
@ -454,36 +453,35 @@ static void flush_iotlb_all(struct omap_iommu *obj)
|
|||
/*
|
||||
* H/W pagetable operations
|
||||
*/
|
||||
static void flush_iopgd_range(u32 *first, u32 *last)
|
||||
static void flush_iopte_range(struct device *dev, dma_addr_t dma,
|
||||
unsigned long offset, int num_entries)
|
||||
{
|
||||
/* FIXME: L2 cache should be taken care of if it exists */
|
||||
do {
|
||||
asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pgd"
|
||||
: : "r" (first));
|
||||
first += L1_CACHE_BYTES / sizeof(*first);
|
||||
} while (first <= last);
|
||||
size_t size = num_entries * sizeof(u32);
|
||||
|
||||
dma_sync_single_range_for_device(dev, dma, offset, size, DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
static void flush_iopte_range(u32 *first, u32 *last)
|
||||
static void iopte_free(struct omap_iommu *obj, u32 *iopte, bool dma_valid)
|
||||
{
|
||||
/* FIXME: L2 cache should be taken care of if it exists */
|
||||
do {
|
||||
asm("mcr p15, 0, %0, c7, c10, 1 @ flush_pte"
|
||||
: : "r" (first));
|
||||
first += L1_CACHE_BYTES / sizeof(*first);
|
||||
} while (first <= last);
|
||||
}
|
||||
dma_addr_t pt_dma;
|
||||
|
||||
static void iopte_free(u32 *iopte)
|
||||
{
|
||||
/* Note: freed iopte's must be clean ready for re-use */
|
||||
if (iopte)
|
||||
if (iopte) {
|
||||
if (dma_valid) {
|
||||
pt_dma = virt_to_phys(iopte);
|
||||
dma_unmap_single(obj->dev, pt_dma, IOPTE_TABLE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
kmem_cache_free(iopte_cachep, iopte);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
|
||||
static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd,
|
||||
dma_addr_t *pt_dma, u32 da)
|
||||
{
|
||||
u32 *iopte;
|
||||
unsigned long offset = iopgd_index(da) * sizeof(da);
|
||||
|
||||
/* a table has already existed */
|
||||
if (*iopgd)
|
||||
|
@ -500,18 +498,38 @@ static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
|
|||
if (!iopte)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
*iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
|
||||
flush_iopgd_range(iopgd, iopgd);
|
||||
*pt_dma = dma_map_single(obj->dev, iopte, IOPTE_TABLE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(obj->dev, *pt_dma)) {
|
||||
dev_err(obj->dev, "DMA map error for L2 table\n");
|
||||
iopte_free(obj, iopte, false);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/*
|
||||
* we rely on dma address and the physical address to be
|
||||
* the same for mapping the L2 table
|
||||
*/
|
||||
if (WARN_ON(*pt_dma != virt_to_phys(iopte))) {
|
||||
dev_err(obj->dev, "DMA translation error for L2 table\n");
|
||||
dma_unmap_single(obj->dev, *pt_dma, IOPTE_TABLE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
iopte_free(obj, iopte, false);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
*iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
|
||||
|
||||
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
|
||||
dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
|
||||
} else {
|
||||
/* We raced, free the reduniovant table */
|
||||
iopte_free(iopte);
|
||||
iopte_free(obj, iopte, false);
|
||||
}
|
||||
|
||||
pte_ready:
|
||||
iopte = iopte_offset(iopgd, da);
|
||||
|
||||
*pt_dma = virt_to_phys(iopte);
|
||||
dev_vdbg(obj->dev,
|
||||
"%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
|
||||
__func__, da, iopgd, *iopgd, iopte, *iopte);
|
||||
|
@ -522,6 +540,7 @@ static u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd, u32 da)
|
|||
static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
||||
{
|
||||
u32 *iopgd = iopgd_offset(obj, da);
|
||||
unsigned long offset = iopgd_index(da) * sizeof(da);
|
||||
|
||||
if ((da | pa) & ~IOSECTION_MASK) {
|
||||
dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
|
||||
|
@ -530,13 +549,14 @@ static int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
|||
}
|
||||
|
||||
*iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
|
||||
flush_iopgd_range(iopgd, iopgd);
|
||||
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
||||
{
|
||||
u32 *iopgd = iopgd_offset(obj, da);
|
||||
unsigned long offset = iopgd_index(da) * sizeof(da);
|
||||
int i;
|
||||
|
||||
if ((da | pa) & ~IOSUPER_MASK) {
|
||||
|
@ -547,20 +567,22 @@ static int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
|||
|
||||
for (i = 0; i < 16; i++)
|
||||
*(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
|
||||
flush_iopgd_range(iopgd, iopgd + 15);
|
||||
flush_iopte_range(obj->dev, obj->pd_dma, offset, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
||||
{
|
||||
u32 *iopgd = iopgd_offset(obj, da);
|
||||
u32 *iopte = iopte_alloc(obj, iopgd, da);
|
||||
dma_addr_t pt_dma;
|
||||
u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
|
||||
unsigned long offset = iopte_index(da) * sizeof(da);
|
||||
|
||||
if (IS_ERR(iopte))
|
||||
return PTR_ERR(iopte);
|
||||
|
||||
*iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
|
||||
flush_iopte_range(iopte, iopte);
|
||||
flush_iopte_range(obj->dev, pt_dma, offset, 1);
|
||||
|
||||
dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
|
||||
__func__, da, pa, iopte, *iopte);
|
||||
|
@ -571,7 +593,9 @@ static int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
|||
static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
||||
{
|
||||
u32 *iopgd = iopgd_offset(obj, da);
|
||||
u32 *iopte = iopte_alloc(obj, iopgd, da);
|
||||
dma_addr_t pt_dma;
|
||||
u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
|
||||
unsigned long offset = iopte_index(da) * sizeof(da);
|
||||
int i;
|
||||
|
||||
if ((da | pa) & ~IOLARGE_MASK) {
|
||||
|
@ -585,7 +609,7 @@ static int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
|
|||
|
||||
for (i = 0; i < 16; i++)
|
||||
*(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
|
||||
flush_iopte_range(iopte, iopte + 15);
|
||||
flush_iopte_range(obj->dev, pt_dma, offset, 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -674,6 +698,9 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
|
|||
size_t bytes;
|
||||
u32 *iopgd = iopgd_offset(obj, da);
|
||||
int nent = 1;
|
||||
dma_addr_t pt_dma;
|
||||
unsigned long pd_offset = iopgd_index(da) * sizeof(da);
|
||||
unsigned long pt_offset = iopte_index(da) * sizeof(da);
|
||||
|
||||
if (!*iopgd)
|
||||
return 0;
|
||||
|
@ -690,7 +717,8 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
|
|||
}
|
||||
bytes *= nent;
|
||||
memset(iopte, 0, nent * sizeof(*iopte));
|
||||
flush_iopte_range(iopte, iopte + (nent - 1) * sizeof(*iopte));
|
||||
pt_dma = virt_to_phys(iopte);
|
||||
flush_iopte_range(obj->dev, pt_dma, pt_offset, nent);
|
||||
|
||||
/*
|
||||
* do table walk to check if this table is necessary or not
|
||||
|
@ -700,7 +728,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
|
|||
if (iopte[i])
|
||||
goto out;
|
||||
|
||||
iopte_free(iopte);
|
||||
iopte_free(obj, iopte, true);
|
||||
nent = 1; /* for the next L1 entry */
|
||||
} else {
|
||||
bytes = IOPGD_SIZE;
|
||||
|
@ -712,7 +740,7 @@ static size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
|
|||
bytes *= nent;
|
||||
}
|
||||
memset(iopgd, 0, nent * sizeof(*iopgd));
|
||||
flush_iopgd_range(iopgd, iopgd + (nent - 1) * sizeof(*iopgd));
|
||||
flush_iopte_range(obj->dev, obj->pd_dma, pd_offset, nent);
|
||||
out:
|
||||
return bytes;
|
||||
}
|
||||
|
@ -738,6 +766,7 @@ static size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
|
|||
|
||||
static void iopgtable_clear_entry_all(struct omap_iommu *obj)
|
||||
{
|
||||
unsigned long offset;
|
||||
int i;
|
||||
|
||||
spin_lock(&obj->page_table_lock);
|
||||
|
@ -748,15 +777,16 @@ static void iopgtable_clear_entry_all(struct omap_iommu *obj)
|
|||
|
||||
da = i << IOPGD_SHIFT;
|
||||
iopgd = iopgd_offset(obj, da);
|
||||
offset = iopgd_index(da) * sizeof(da);
|
||||
|
||||
if (!*iopgd)
|
||||
continue;
|
||||
|
||||
if (iopgd_is_table(*iopgd))
|
||||
iopte_free(iopte_offset(iopgd, 0));
|
||||
iopte_free(obj, iopte_offset(iopgd, 0), true);
|
||||
|
||||
*iopgd = 0;
|
||||
flush_iopgd_range(iopgd, iopgd);
|
||||
flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
|
||||
}
|
||||
|
||||
flush_iotlb_all(obj);
|
||||
|
@ -786,7 +816,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data)
|
|||
if (!report_iommu_fault(domain, obj->dev, da, 0))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
iommu_disable(obj);
|
||||
iommu_write_reg(obj, 0, MMU_IRQENABLE);
|
||||
|
||||
iopgd = iopgd_offset(obj, da);
|
||||
|
||||
|
@ -815,10 +845,18 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd)
|
|||
|
||||
spin_lock(&obj->iommu_lock);
|
||||
|
||||
obj->pd_dma = dma_map_single(obj->dev, iopgd, IOPGD_TABLE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(obj->dev, obj->pd_dma)) {
|
||||
dev_err(obj->dev, "DMA map error for L1 table\n");
|
||||
err = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
obj->iopgd = iopgd;
|
||||
err = iommu_enable(obj);
|
||||
if (err)
|
||||
goto err_enable;
|
||||
goto out_err;
|
||||
flush_iotlb_all(obj);
|
||||
|
||||
spin_unlock(&obj->iommu_lock);
|
||||
|
@ -827,7 +865,7 @@ static int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd)
|
|||
|
||||
return 0;
|
||||
|
||||
err_enable:
|
||||
out_err:
|
||||
spin_unlock(&obj->iommu_lock);
|
||||
|
||||
return err;
|
||||
|
@ -844,7 +882,10 @@ static void omap_iommu_detach(struct omap_iommu *obj)
|
|||
|
||||
spin_lock(&obj->iommu_lock);
|
||||
|
||||
dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
iommu_disable(obj);
|
||||
obj->pd_dma = 0;
|
||||
obj->iopgd = NULL;
|
||||
|
||||
spin_unlock(&obj->iommu_lock);
|
||||
|
@ -1008,11 +1049,6 @@ static struct platform_driver omap_iommu_driver = {
|
|||
},
|
||||
};
|
||||
|
||||
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, int pgsz)
|
||||
{
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
@ -1159,7 +1195,6 @@ static struct iommu_domain *omap_iommu_domain_alloc(unsigned type)
|
|||
if (WARN_ON(!IS_ALIGNED((long)omap_domain->pgtable, IOPGD_TABLE_SIZE)))
|
||||
goto fail_align;
|
||||
|
||||
clean_dcache_area(omap_domain->pgtable, IOPGD_TABLE_SIZE);
|
||||
spin_lock_init(&omap_domain->lock);
|
||||
|
||||
omap_domain->domain.geometry.aperture_start = 0;
|
||||
|
@ -1347,7 +1382,7 @@ static int __init omap_iommu_init(void)
|
|||
of_node_put(np);
|
||||
|
||||
p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
|
||||
iopte_cachep_ctor);
|
||||
NULL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
iopte_cachep = p;
|
||||
|
|
|
@ -61,6 +61,7 @@ struct omap_iommu {
|
|||
*/
|
||||
u32 *iopgd;
|
||||
spinlock_t page_table_lock; /* protect iopgd */
|
||||
dma_addr_t pd_dma;
|
||||
|
||||
int nr_tlb_entries;
|
||||
|
||||
|
|
|
@ -0,0 +1,930 @@
|
|||
/*
|
||||
* IOMMU API for QCOM secure IOMMUs. Somewhat based on arm-smmu.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Copyright (C) 2013 ARM Limited
|
||||
* Copyright (C) 2017 Red Hat
|
||||
*/
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-iommu.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kconfig.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_iommu.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include "io-pgtable.h"
|
||||
#include "arm-smmu-regs.h"
|
||||
|
||||
#define SMMU_INTR_SEL_NS 0x2000
|
||||
|
||||
struct qcom_iommu_ctx;
|
||||
|
||||
struct qcom_iommu_dev {
|
||||
/* IOMMU core code handle */
|
||||
struct iommu_device iommu;
|
||||
struct device *dev;
|
||||
struct clk *iface_clk;
|
||||
struct clk *bus_clk;
|
||||
void __iomem *local_base;
|
||||
u32 sec_id;
|
||||
u8 num_ctxs;
|
||||
struct qcom_iommu_ctx *ctxs[0]; /* indexed by asid-1 */
|
||||
};
|
||||
|
||||
struct qcom_iommu_ctx {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
bool secure_init;
|
||||
u8 asid; /* asid and ctx bank # are 1:1 */
|
||||
};
|
||||
|
||||
struct qcom_iommu_domain {
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
spinlock_t pgtbl_lock;
|
||||
struct mutex init_mutex; /* Protects iommu pointer */
|
||||
struct iommu_domain domain;
|
||||
struct qcom_iommu_dev *iommu;
|
||||
};
|
||||
|
||||
static struct qcom_iommu_domain *to_qcom_iommu_domain(struct iommu_domain *dom)
|
||||
{
|
||||
return container_of(dom, struct qcom_iommu_domain, domain);
|
||||
}
|
||||
|
||||
static const struct iommu_ops qcom_iommu_ops;
|
||||
|
||||
static struct qcom_iommu_dev * to_iommu(struct iommu_fwspec *fwspec)
|
||||
{
|
||||
if (!fwspec || fwspec->ops != &qcom_iommu_ops)
|
||||
return NULL;
|
||||
return fwspec->iommu_priv;
|
||||
}
|
||||
|
||||
static struct qcom_iommu_ctx * to_ctx(struct iommu_fwspec *fwspec, unsigned asid)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec);
|
||||
if (!qcom_iommu)
|
||||
return NULL;
|
||||
return qcom_iommu->ctxs[asid - 1];
|
||||
}
|
||||
|
||||
static inline void
|
||||
iommu_writel(struct qcom_iommu_ctx *ctx, unsigned reg, u32 val)
|
||||
{
|
||||
writel_relaxed(val, ctx->base + reg);
|
||||
}
|
||||
|
||||
static inline void
|
||||
iommu_writeq(struct qcom_iommu_ctx *ctx, unsigned reg, u64 val)
|
||||
{
|
||||
writeq_relaxed(val, ctx->base + reg);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
iommu_readl(struct qcom_iommu_ctx *ctx, unsigned reg)
|
||||
{
|
||||
return readl_relaxed(ctx->base + reg);
|
||||
}
|
||||
|
||||
static inline u64
|
||||
iommu_readq(struct qcom_iommu_ctx *ctx, unsigned reg)
|
||||
{
|
||||
return readq_relaxed(ctx->base + reg);
|
||||
}
|
||||
|
||||
static void qcom_iommu_tlb_sync(void *cookie)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = cookie;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < fwspec->num_ids; i++) {
|
||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||
unsigned int val, ret;
|
||||
|
||||
iommu_writel(ctx, ARM_SMMU_CB_TLBSYNC, 0);
|
||||
|
||||
ret = readl_poll_timeout(ctx->base + ARM_SMMU_CB_TLBSTATUS, val,
|
||||
(val & 0x1) == 0, 0, 5000000);
|
||||
if (ret)
|
||||
dev_err(ctx->dev, "timeout waiting for TLB SYNC\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void qcom_iommu_tlb_inv_context(void *cookie)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = cookie;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < fwspec->num_ids; i++) {
|
||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||
iommu_writel(ctx, ARM_SMMU_CB_S1_TLBIASID, ctx->asid);
|
||||
}
|
||||
|
||||
qcom_iommu_tlb_sync(cookie);
|
||||
}
|
||||
|
||||
static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size,
|
||||
size_t granule, bool leaf, void *cookie)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = cookie;
|
||||
unsigned i, reg;
|
||||
|
||||
reg = leaf ? ARM_SMMU_CB_S1_TLBIVAL : ARM_SMMU_CB_S1_TLBIVA;
|
||||
|
||||
for (i = 0; i < fwspec->num_ids; i++) {
|
||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||
size_t s = size;
|
||||
|
||||
iova &= ~12UL;
|
||||
iova |= ctx->asid;
|
||||
do {
|
||||
iommu_writel(ctx, reg, iova);
|
||||
iova += granule;
|
||||
} while (s -= granule);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iommu_gather_ops qcom_gather_ops = {
|
||||
.tlb_flush_all = qcom_iommu_tlb_inv_context,
|
||||
.tlb_add_flush = qcom_iommu_tlb_inv_range_nosync,
|
||||
.tlb_sync = qcom_iommu_tlb_sync,
|
||||
};
|
||||
|
||||
static irqreturn_t qcom_iommu_fault(int irq, void *dev)
|
||||
{
|
||||
struct qcom_iommu_ctx *ctx = dev;
|
||||
u32 fsr, fsynr;
|
||||
u64 iova;
|
||||
|
||||
fsr = iommu_readl(ctx, ARM_SMMU_CB_FSR);
|
||||
|
||||
if (!(fsr & FSR_FAULT))
|
||||
return IRQ_NONE;
|
||||
|
||||
fsynr = iommu_readl(ctx, ARM_SMMU_CB_FSYNR0);
|
||||
iova = iommu_readq(ctx, ARM_SMMU_CB_FAR);
|
||||
|
||||
dev_err_ratelimited(ctx->dev,
|
||||
"Unhandled context fault: fsr=0x%x, "
|
||||
"iova=0x%016llx, fsynr=0x%x, cb=%d\n",
|
||||
fsr, iova, fsynr, ctx->asid);
|
||||
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FSR, fsr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qcom_iommu_init_domain(struct iommu_domain *domain,
|
||||
struct qcom_iommu_dev *qcom_iommu,
|
||||
struct iommu_fwspec *fwspec)
|
||||
{
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
struct io_pgtable_ops *pgtbl_ops;
|
||||
struct io_pgtable_cfg pgtbl_cfg;
|
||||
int i, ret = 0;
|
||||
u32 reg;
|
||||
|
||||
mutex_lock(&qcom_domain->init_mutex);
|
||||
if (qcom_domain->iommu)
|
||||
goto out_unlock;
|
||||
|
||||
pgtbl_cfg = (struct io_pgtable_cfg) {
|
||||
.pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap,
|
||||
.ias = 32,
|
||||
.oas = 40,
|
||||
.tlb = &qcom_gather_ops,
|
||||
.iommu_dev = qcom_iommu->dev,
|
||||
};
|
||||
|
||||
qcom_domain->iommu = qcom_iommu;
|
||||
pgtbl_ops = alloc_io_pgtable_ops(ARM_32_LPAE_S1, &pgtbl_cfg, fwspec);
|
||||
if (!pgtbl_ops) {
|
||||
dev_err(qcom_iommu->dev, "failed to allocate pagetable ops\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_clear_iommu;
|
||||
}
|
||||
|
||||
/* Update the domain's page sizes to reflect the page table format */
|
||||
domain->pgsize_bitmap = pgtbl_cfg.pgsize_bitmap;
|
||||
domain->geometry.aperture_end = (1ULL << pgtbl_cfg.ias) - 1;
|
||||
domain->geometry.force_aperture = true;
|
||||
|
||||
for (i = 0; i < fwspec->num_ids; i++) {
|
||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||
|
||||
if (!ctx->secure_init) {
|
||||
ret = qcom_scm_restore_sec_cfg(qcom_iommu->sec_id, ctx->asid);
|
||||
if (ret) {
|
||||
dev_err(qcom_iommu->dev, "secure init failed: %d\n", ret);
|
||||
goto out_clear_iommu;
|
||||
}
|
||||
ctx->secure_init = true;
|
||||
}
|
||||
|
||||
/* TTBRs */
|
||||
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
|
||||
((u64)ctx->asid << TTBRn_ASID_SHIFT));
|
||||
iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
|
||||
((u64)ctx->asid << TTBRn_ASID_SHIFT));
|
||||
|
||||
/* TTBCR */
|
||||
iommu_writel(ctx, ARM_SMMU_CB_TTBCR2,
|
||||
(pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) |
|
||||
TTBCR2_SEP_UPSTREAM);
|
||||
iommu_writel(ctx, ARM_SMMU_CB_TTBCR,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.tcr);
|
||||
|
||||
/* MAIRs (stage-1 only) */
|
||||
iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR0,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.mair[0]);
|
||||
iommu_writel(ctx, ARM_SMMU_CB_S1_MAIR1,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.mair[1]);
|
||||
|
||||
/* SCTLR */
|
||||
reg = SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE |
|
||||
SCTLR_M | SCTLR_S1_ASIDPNE;
|
||||
|
||||
if (IS_ENABLED(CONFIG_BIG_ENDIAN))
|
||||
reg |= SCTLR_E;
|
||||
|
||||
iommu_writel(ctx, ARM_SMMU_CB_SCTLR, reg);
|
||||
}
|
||||
|
||||
mutex_unlock(&qcom_domain->init_mutex);
|
||||
|
||||
/* Publish page table ops for map/unmap */
|
||||
qcom_domain->pgtbl_ops = pgtbl_ops;
|
||||
|
||||
return 0;
|
||||
|
||||
out_clear_iommu:
|
||||
qcom_domain->iommu = NULL;
|
||||
out_unlock:
|
||||
mutex_unlock(&qcom_domain->init_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct iommu_domain *qcom_iommu_domain_alloc(unsigned type)
|
||||
{
|
||||
struct qcom_iommu_domain *qcom_domain;
|
||||
|
||||
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
|
||||
return NULL;
|
||||
/*
|
||||
* Allocate the domain and initialise some of its data structures.
|
||||
* We can't really do anything meaningful until we've added a
|
||||
* master.
|
||||
*/
|
||||
qcom_domain = kzalloc(sizeof(*qcom_domain), GFP_KERNEL);
|
||||
if (!qcom_domain)
|
||||
return NULL;
|
||||
|
||||
if (type == IOMMU_DOMAIN_DMA &&
|
||||
iommu_get_dma_cookie(&qcom_domain->domain)) {
|
||||
kfree(qcom_domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_init(&qcom_domain->init_mutex);
|
||||
spin_lock_init(&qcom_domain->pgtbl_lock);
|
||||
|
||||
return &qcom_domain->domain;
|
||||
}
|
||||
|
||||
static void qcom_iommu_domain_free(struct iommu_domain *domain)
|
||||
{
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
|
||||
if (WARN_ON(qcom_domain->iommu)) /* forgot to detach? */
|
||||
return;
|
||||
|
||||
iommu_put_dma_cookie(domain);
|
||||
|
||||
/* NOTE: unmap can be called after client device is powered off,
|
||||
* for example, with GPUs or anything involving dma-buf. So we
|
||||
* cannot rely on the device_link. Make sure the IOMMU is on to
|
||||
* avoid unclocked accesses in the TLB inv path:
|
||||
*/
|
||||
pm_runtime_get_sync(qcom_domain->iommu->dev);
|
||||
|
||||
free_io_pgtable_ops(qcom_domain->pgtbl_ops);
|
||||
|
||||
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
||||
|
||||
kfree(qcom_domain);
|
||||
}
|
||||
|
||||
static int qcom_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
int ret;
|
||||
|
||||
if (!qcom_iommu) {
|
||||
dev_err(dev, "cannot attach to IOMMU, is it on the same bus?\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Ensure that the domain is finalized */
|
||||
pm_runtime_get_sync(qcom_iommu->dev);
|
||||
ret = qcom_iommu_init_domain(domain, qcom_iommu, dev->iommu_fwspec);
|
||||
pm_runtime_put_sync(qcom_iommu->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Sanity check the domain. We don't support domains across
|
||||
* different IOMMUs.
|
||||
*/
|
||||
if (qcom_domain->iommu != qcom_iommu) {
|
||||
dev_err(dev, "cannot attach to IOMMU %s while already "
|
||||
"attached to domain on IOMMU %s\n",
|
||||
dev_name(qcom_domain->iommu->dev),
|
||||
dev_name(qcom_iommu->dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *dev)
|
||||
{
|
||||
struct iommu_fwspec *fwspec = dev->iommu_fwspec;
|
||||
struct qcom_iommu_dev *qcom_iommu = to_iommu(fwspec);
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
unsigned i;
|
||||
|
||||
if (!qcom_domain->iommu)
|
||||
return;
|
||||
|
||||
pm_runtime_get_sync(qcom_iommu->dev);
|
||||
for (i = 0; i < fwspec->num_ids; i++) {
|
||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||
|
||||
/* Disable the context bank: */
|
||||
iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
|
||||
}
|
||||
pm_runtime_put_sync(qcom_iommu->dev);
|
||||
|
||||
qcom_domain->iommu = NULL;
|
||||
}
|
||||
|
||||
static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
|
||||
|
||||
if (!ops)
|
||||
return -ENODEV;
|
||||
|
||||
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
||||
ret = ops->map(ops, iova, paddr, size, prot);
|
||||
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size)
|
||||
{
|
||||
size_t ret;
|
||||
unsigned long flags;
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
|
||||
|
||||
if (!ops)
|
||||
return 0;
|
||||
|
||||
/* NOTE: unmap can be called after client device is powered off,
|
||||
* for example, with GPUs or anything involving dma-buf. So we
|
||||
* cannot rely on the device_link. Make sure the IOMMU is on to
|
||||
* avoid unclocked accesses in the TLB inv path:
|
||||
*/
|
||||
pm_runtime_get_sync(qcom_domain->iommu->dev);
|
||||
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
||||
ret = ops->unmap(ops, iova, size);
|
||||
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
||||
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
dma_addr_t iova)
|
||||
{
|
||||
phys_addr_t ret;
|
||||
unsigned long flags;
|
||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||
struct io_pgtable_ops *ops = qcom_domain->pgtbl_ops;
|
||||
|
||||
if (!ops)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
||||
ret = ops->iova_to_phys(ops, iova);
|
||||
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool qcom_iommu_capable(enum iommu_cap cap)
|
||||
{
|
||||
switch (cap) {
|
||||
case IOMMU_CAP_CACHE_COHERENCY:
|
||||
/*
|
||||
* Return true here as the SMMU can always send out coherent
|
||||
* requests.
|
||||
*/
|
||||
return true;
|
||||
case IOMMU_CAP_NOEXEC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int qcom_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
|
||||
struct iommu_group *group;
|
||||
struct device_link *link;
|
||||
|
||||
if (!qcom_iommu)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Establish the link between iommu and master, so that the
|
||||
* iommu gets runtime enabled/disabled as per the master's
|
||||
* needs.
|
||||
*/
|
||||
link = device_link_add(dev, qcom_iommu->dev, DL_FLAG_PM_RUNTIME);
|
||||
if (!link) {
|
||||
dev_err(qcom_iommu->dev, "Unable to create device link between %s and %s\n",
|
||||
dev_name(qcom_iommu->dev), dev_name(dev));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
if (IS_ERR_OR_NULL(group))
|
||||
return PTR_ERR_OR_ZERO(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
iommu_device_link(&qcom_iommu->iommu, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_iommu_remove_device(struct device *dev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = to_iommu(dev->iommu_fwspec);
|
||||
|
||||
if (!qcom_iommu)
|
||||
return;
|
||||
|
||||
iommu_device_unlink(&qcom_iommu->iommu, dev);
|
||||
iommu_group_remove_device(dev);
|
||||
iommu_fwspec_free(dev);
|
||||
}
|
||||
|
||||
static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu;
|
||||
struct platform_device *iommu_pdev;
|
||||
unsigned asid = args->args[0];
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "incorrect number of iommu params found for %s "
|
||||
"(found %d, expected 1)\n",
|
||||
args->np->full_name, args->args_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iommu_pdev = of_find_device_by_node(args->np);
|
||||
if (WARN_ON(!iommu_pdev))
|
||||
return -EINVAL;
|
||||
|
||||
qcom_iommu = platform_get_drvdata(iommu_pdev);
|
||||
|
||||
/* make sure the asid specified in dt is valid, so we don't have
|
||||
* to sanity check this elsewhere, since 'asid - 1' is used to
|
||||
* index into qcom_iommu->ctxs:
|
||||
*/
|
||||
if (WARN_ON(asid < 1) ||
|
||||
WARN_ON(asid > qcom_iommu->num_ctxs))
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev->iommu_fwspec->iommu_priv) {
|
||||
dev->iommu_fwspec->iommu_priv = qcom_iommu;
|
||||
} else {
|
||||
/* make sure devices iommus dt node isn't referring to
|
||||
* multiple different iommu devices. Multiple context
|
||||
* banks are ok, but multiple devices are not:
|
||||
*/
|
||||
if (WARN_ON(qcom_iommu != dev->iommu_fwspec->iommu_priv))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return iommu_fwspec_add_ids(dev, &asid, 1);
|
||||
}
|
||||
|
||||
static const struct iommu_ops qcom_iommu_ops = {
|
||||
.capable = qcom_iommu_capable,
|
||||
.domain_alloc = qcom_iommu_domain_alloc,
|
||||
.domain_free = qcom_iommu_domain_free,
|
||||
.attach_dev = qcom_iommu_attach_dev,
|
||||
.detach_dev = qcom_iommu_detach_dev,
|
||||
.map = qcom_iommu_map,
|
||||
.unmap = qcom_iommu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.iova_to_phys = qcom_iommu_iova_to_phys,
|
||||
.add_device = qcom_iommu_add_device,
|
||||
.remove_device = qcom_iommu_remove_device,
|
||||
.device_group = generic_device_group,
|
||||
.of_xlate = qcom_iommu_of_xlate,
|
||||
.pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
|
||||
};
|
||||
|
||||
static int qcom_iommu_enable_clocks(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(qcom_iommu->iface_clk);
|
||||
if (ret) {
|
||||
dev_err(qcom_iommu->dev, "Couldn't enable iface_clk\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(qcom_iommu->bus_clk);
|
||||
if (ret) {
|
||||
dev_err(qcom_iommu->dev, "Couldn't enable bus_clk\n");
|
||||
clk_disable_unprepare(qcom_iommu->iface_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_iommu_disable_clocks(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
clk_disable_unprepare(qcom_iommu->bus_clk);
|
||||
clk_disable_unprepare(qcom_iommu->iface_clk);
|
||||
}
|
||||
|
||||
static int qcom_iommu_sec_ptbl_init(struct device *dev)
|
||||
{
|
||||
size_t psize = 0;
|
||||
unsigned int spare = 0;
|
||||
void *cpu_addr;
|
||||
dma_addr_t paddr;
|
||||
unsigned long attrs;
|
||||
static bool allocated = false;
|
||||
int ret;
|
||||
|
||||
if (allocated)
|
||||
return 0;
|
||||
|
||||
ret = qcom_scm_iommu_secure_ptbl_size(spare, &psize);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get iommu secure pgtable size (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(dev, "iommu sec: pgtable size: %zu\n", psize);
|
||||
|
||||
attrs = DMA_ATTR_NO_KERNEL_MAPPING;
|
||||
|
||||
cpu_addr = dma_alloc_attrs(dev, psize, &paddr, GFP_KERNEL, attrs);
|
||||
if (!cpu_addr) {
|
||||
dev_err(dev, "failed to allocate %zu bytes for pgtable\n",
|
||||
psize);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = qcom_scm_iommu_secure_ptbl_init(paddr, psize, spare);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to init iommu pgtable (%d)\n", ret);
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
allocated = true;
|
||||
return 0;
|
||||
|
||||
free_mem:
|
||||
dma_free_attrs(dev, psize, cpu_addr, paddr, attrs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_asid(const struct device_node *np)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* read the "reg" property directly to get the relative address
|
||||
* of the context bank, and calculate the asid from that:
|
||||
*/
|
||||
if (of_property_read_u32_index(np, "reg", 0, ®))
|
||||
return -ENODEV;
|
||||
|
||||
return reg / 0x1000; /* context banks are 0x1000 apart */
|
||||
}
|
||||
|
||||
static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_iommu_ctx *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev->parent);
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->dev = dev;
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctx->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(ctx->base))
|
||||
return PTR_ERR(ctx->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "failed to get irq\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* clear IRQs before registering fault handler, just in case the
|
||||
* boot-loader left us a surprise:
|
||||
*/
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
|
||||
|
||||
ret = devm_request_irq(dev, irq,
|
||||
qcom_iommu_fault,
|
||||
IRQF_SHARED,
|
||||
"qcom-iommu-fault",
|
||||
ctx);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ %u\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = get_asid(dev->of_node);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "missing reg property\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->asid = ret;
|
||||
|
||||
dev_dbg(dev, "found asid %u\n", ctx->asid);
|
||||
|
||||
qcom_iommu->ctxs[ctx->asid - 1] = ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_iommu_ctx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(pdev->dev.parent);
|
||||
struct qcom_iommu_ctx *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
qcom_iommu->ctxs[ctx->asid - 1] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ctx_of_match[] = {
|
||||
{ .compatible = "qcom,msm-iommu-v1-ns" },
|
||||
{ .compatible = "qcom,msm-iommu-v1-sec" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static struct platform_driver qcom_iommu_ctx_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-iommu-ctx",
|
||||
.of_match_table = of_match_ptr(ctx_of_match),
|
||||
},
|
||||
.probe = qcom_iommu_ctx_probe,
|
||||
.remove = qcom_iommu_ctx_remove,
|
||||
};
|
||||
|
||||
static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
|
||||
{
|
||||
struct device_node *child;
|
||||
|
||||
for_each_child_of_node(qcom_iommu->dev->of_node, child)
|
||||
if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int qcom_iommu_device_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct qcom_iommu_dev *qcom_iommu;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int ret, sz, max_asid = 0;
|
||||
|
||||
/* find the max asid (which is 1:1 to ctx bank idx), so we know how
|
||||
* many child ctx devices we have:
|
||||
*/
|
||||
for_each_child_of_node(dev->of_node, child)
|
||||
max_asid = max(max_asid, get_asid(child));
|
||||
|
||||
sz = sizeof(*qcom_iommu) + (max_asid * sizeof(qcom_iommu->ctxs[0]));
|
||||
|
||||
qcom_iommu = devm_kzalloc(dev, sz, GFP_KERNEL);
|
||||
if (!qcom_iommu)
|
||||
return -ENOMEM;
|
||||
qcom_iommu->num_ctxs = max_asid;
|
||||
qcom_iommu->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res)
|
||||
qcom_iommu->local_base = devm_ioremap_resource(dev, res);
|
||||
|
||||
qcom_iommu->iface_clk = devm_clk_get(dev, "iface");
|
||||
if (IS_ERR(qcom_iommu->iface_clk)) {
|
||||
dev_err(dev, "failed to get iface clock\n");
|
||||
return PTR_ERR(qcom_iommu->iface_clk);
|
||||
}
|
||||
|
||||
qcom_iommu->bus_clk = devm_clk_get(dev, "bus");
|
||||
if (IS_ERR(qcom_iommu->bus_clk)) {
|
||||
dev_err(dev, "failed to get bus clock\n");
|
||||
return PTR_ERR(qcom_iommu->bus_clk);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "qcom,iommu-secure-id",
|
||||
&qcom_iommu->sec_id)) {
|
||||
dev_err(dev, "missing qcom,iommu-secure-id property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (qcom_iommu_has_secure_context(qcom_iommu)) {
|
||||
ret = qcom_iommu_sec_ptbl_init(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot init secure pg table(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, qcom_iommu);
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* register context bank devices, which are child nodes: */
|
||||
ret = devm_of_platform_populate(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to populate iommu contexts\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iommu_device_sysfs_add(&qcom_iommu->iommu, dev, NULL,
|
||||
dev_name(dev));
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iommu in sysfs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iommu_device_set_ops(&qcom_iommu->iommu, &qcom_iommu_ops);
|
||||
iommu_device_set_fwnode(&qcom_iommu->iommu, dev->fwnode);
|
||||
|
||||
ret = iommu_device_register(&qcom_iommu->iommu);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iommu\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
bus_set_iommu(&platform_bus_type, &qcom_iommu_ops);
|
||||
|
||||
if (qcom_iommu->local_base) {
|
||||
pm_runtime_get_sync(dev);
|
||||
writel_relaxed(0xffffffff, qcom_iommu->local_base + SMMU_INTR_SEL_NS);
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcom_iommu_device_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
|
||||
|
||||
bus_set_iommu(&platform_bus_type, NULL);
|
||||
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iommu_device_sysfs_remove(&qcom_iommu->iommu);
|
||||
iommu_device_unregister(&qcom_iommu->iommu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_iommu_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
|
||||
|
||||
return qcom_iommu_enable_clocks(qcom_iommu);
|
||||
}
|
||||
|
||||
static int __maybe_unused qcom_iommu_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct qcom_iommu_dev *qcom_iommu = platform_get_drvdata(pdev);
|
||||
|
||||
qcom_iommu_disable_clocks(qcom_iommu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops qcom_iommu_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(qcom_iommu_suspend, qcom_iommu_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id qcom_iommu_of_match[] = {
|
||||
{ .compatible = "qcom,msm-iommu-v1" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_iommu_of_match);
|
||||
|
||||
static struct platform_driver qcom_iommu_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-iommu",
|
||||
.of_match_table = of_match_ptr(qcom_iommu_of_match),
|
||||
.pm = &qcom_iommu_pm_ops,
|
||||
},
|
||||
.probe = qcom_iommu_device_probe,
|
||||
.remove = qcom_iommu_device_remove,
|
||||
};
|
||||
|
||||
static int __init qcom_iommu_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&qcom_iommu_ctx_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_driver_register(&qcom_iommu_driver);
|
||||
if (ret)
|
||||
platform_driver_unregister(&qcom_iommu_ctx_driver);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit qcom_iommu_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&qcom_iommu_driver);
|
||||
platform_driver_unregister(&qcom_iommu_ctx_driver);
|
||||
}
|
||||
|
||||
module_init(qcom_iommu_init);
|
||||
module_exit(qcom_iommu_exit);
|
||||
|
||||
IOMMU_OF_DECLARE(qcom_iommu_dev, "qcom,msm-iommu-v1", NULL);
|
||||
|
||||
MODULE_DESCRIPTION("IOMMU API for QCOM IOMMU v1 implementations");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -90,7 +90,9 @@ struct rk_iommu {
|
|||
struct device *dev;
|
||||
void __iomem **bases;
|
||||
int num_mmu;
|
||||
int irq;
|
||||
int *irq;
|
||||
int num_irq;
|
||||
bool reset_disabled;
|
||||
struct iommu_device iommu;
|
||||
struct list_head node; /* entry in rk_iommu_domain.iommus */
|
||||
struct iommu_domain *domain; /* domain to which iommu is attached */
|
||||
|
@ -414,6 +416,9 @@ static int rk_iommu_force_reset(struct rk_iommu *iommu)
|
|||
int ret, i;
|
||||
u32 dte_addr;
|
||||
|
||||
if (iommu->reset_disabled)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY
|
||||
* and verifying that upper 5 nybbles are read back.
|
||||
|
@ -825,10 +830,12 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
|
|||
|
||||
iommu->domain = domain;
|
||||
|
||||
ret = devm_request_irq(iommu->dev, iommu->irq, rk_iommu_irq,
|
||||
for (i = 0; i < iommu->num_irq; i++) {
|
||||
ret = devm_request_irq(iommu->dev, iommu->irq[i], rk_iommu_irq,
|
||||
IRQF_SHARED, dev_name(dev), iommu);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
|
||||
|
@ -878,7 +885,8 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
|
|||
}
|
||||
rk_iommu_disable_stall(iommu);
|
||||
|
||||
devm_free_irq(iommu->dev, iommu->irq, iommu);
|
||||
for (i = 0; i < iommu->num_irq; i++)
|
||||
devm_free_irq(iommu->dev, iommu->irq[i], iommu);
|
||||
|
||||
iommu->domain = NULL;
|
||||
|
||||
|
@ -1008,20 +1016,20 @@ static int rk_iommu_group_set_iommudata(struct iommu_group *group,
|
|||
ret = of_parse_phandle_with_args(np, "iommus", "#iommu-cells", 0,
|
||||
&args);
|
||||
if (ret) {
|
||||
dev_err(dev, "of_parse_phandle_with_args(%s) => %d\n",
|
||||
np->full_name, ret);
|
||||
dev_err(dev, "of_parse_phandle_with_args(%pOF) => %d\n",
|
||||
np, ret);
|
||||
return ret;
|
||||
}
|
||||
if (args.args_count != 0) {
|
||||
dev_err(dev, "incorrect number of iommu params found for %s (found %d, expected 0)\n",
|
||||
args.np->full_name, args.args_count);
|
||||
dev_err(dev, "incorrect number of iommu params found for %pOF (found %d, expected 0)\n",
|
||||
args.np, args.args_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pd = of_find_device_by_node(args.np);
|
||||
of_node_put(args.np);
|
||||
if (!pd) {
|
||||
dev_err(dev, "iommu %s not found\n", args.np->full_name);
|
||||
dev_err(dev, "iommu %pOF not found\n", args.np);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
|
@ -1157,11 +1165,27 @@ static int rk_iommu_probe(struct platform_device *pdev)
|
|||
if (iommu->num_mmu == 0)
|
||||
return PTR_ERR(iommu->bases[0]);
|
||||
|
||||
iommu->irq = platform_get_irq(pdev, 0);
|
||||
if (iommu->irq < 0) {
|
||||
dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq);
|
||||
iommu->num_irq = platform_irq_count(pdev);
|
||||
if (iommu->num_irq < 0)
|
||||
return iommu->num_irq;
|
||||
if (iommu->num_irq == 0)
|
||||
return -ENXIO;
|
||||
|
||||
iommu->irq = devm_kcalloc(dev, iommu->num_irq, sizeof(*iommu->irq),
|
||||
GFP_KERNEL);
|
||||
if (!iommu->irq)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < iommu->num_irq; i++) {
|
||||
iommu->irq[i] = platform_get_irq(pdev, i);
|
||||
if (iommu->irq[i] < 0) {
|
||||
dev_err(dev, "Failed to get IRQ, %d\n", iommu->irq[i]);
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
|
||||
iommu->reset_disabled = device_property_read_bool(dev,
|
||||
"rockchip,disable-mmu-reset");
|
||||
|
||||
err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
|
||||
if (err)
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
#define S390_IOMMU_PGSIZES (~0xFFFUL)
|
||||
|
||||
static const struct iommu_ops s390_iommu_ops;
|
||||
|
||||
struct s390_domain {
|
||||
struct iommu_domain domain;
|
||||
struct list_head devices;
|
||||
|
@ -166,11 +168,13 @@ static void s390_iommu_detach_device(struct iommu_domain *domain,
|
|||
static int s390_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct iommu_group *group = iommu_group_get_for_dev(dev);
|
||||
struct zpci_dev *zdev = to_pci_dev(dev)->sysdata;
|
||||
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
iommu_device_link(&zdev->iommu_dev, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -197,6 +201,7 @@ static void s390_iommu_remove_device(struct device *dev)
|
|||
s390_iommu_detach_device(domain, dev);
|
||||
}
|
||||
|
||||
iommu_device_unlink(&zdev->iommu_dev, dev);
|
||||
iommu_group_remove_device(dev);
|
||||
}
|
||||
|
||||
|
@ -327,7 +332,37 @@ static size_t s390_iommu_unmap(struct iommu_domain *domain,
|
|||
return size;
|
||||
}
|
||||
|
||||
static struct iommu_ops s390_iommu_ops = {
|
||||
int zpci_init_iommu(struct zpci_dev *zdev)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = iommu_device_sysfs_add(&zdev->iommu_dev, NULL, NULL,
|
||||
"s390-iommu.%08x", zdev->fid);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
iommu_device_set_ops(&zdev->iommu_dev, &s390_iommu_ops);
|
||||
|
||||
rc = iommu_device_register(&zdev->iommu_dev);
|
||||
if (rc)
|
||||
goto out_sysfs;
|
||||
|
||||
return 0;
|
||||
|
||||
out_sysfs:
|
||||
iommu_device_sysfs_remove(&zdev->iommu_dev);
|
||||
|
||||
out_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
void zpci_destroy_iommu(struct zpci_dev *zdev)
|
||||
{
|
||||
iommu_device_unregister(&zdev->iommu_dev);
|
||||
iommu_device_sysfs_remove(&zdev->iommu_dev);
|
||||
}
|
||||
|
||||
static const struct iommu_ops s390_iommu_ops = {
|
||||
.capable = s390_iommu_capable,
|
||||
.domain_alloc = s390_domain_alloc,
|
||||
.domain_free = s390_domain_free,
|
||||
|
|
|
@ -61,6 +61,8 @@ struct gart_device {
|
|||
struct list_head client;
|
||||
spinlock_t client_lock; /* for client list */
|
||||
struct device *dev;
|
||||
|
||||
struct iommu_device iommu; /* IOMMU Core handle */
|
||||
};
|
||||
|
||||
struct gart_domain {
|
||||
|
@ -334,12 +336,35 @@ static bool gart_iommu_capable(enum iommu_cap cap)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int gart_iommu_add_device(struct device *dev)
|
||||
{
|
||||
struct iommu_group *group = iommu_group_get_for_dev(dev);
|
||||
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
|
||||
iommu_device_link(&gart_handle->iommu, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gart_iommu_remove_device(struct device *dev)
|
||||
{
|
||||
iommu_group_remove_device(dev);
|
||||
iommu_device_unlink(&gart_handle->iommu, dev);
|
||||
}
|
||||
|
||||
static const struct iommu_ops gart_iommu_ops = {
|
||||
.capable = gart_iommu_capable,
|
||||
.domain_alloc = gart_iommu_domain_alloc,
|
||||
.domain_free = gart_iommu_domain_free,
|
||||
.attach_dev = gart_iommu_attach_dev,
|
||||
.detach_dev = gart_iommu_detach_dev,
|
||||
.add_device = gart_iommu_add_device,
|
||||
.remove_device = gart_iommu_remove_device,
|
||||
.device_group = generic_device_group,
|
||||
.map = gart_iommu_map,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
.unmap = gart_iommu_unmap,
|
||||
|
@ -378,6 +403,7 @@ static int tegra_gart_probe(struct platform_device *pdev)
|
|||
struct resource *res, *res_remap;
|
||||
void __iomem *gart_regs;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
if (gart_handle)
|
||||
return -EIO;
|
||||
|
@ -404,6 +430,22 @@ static int tegra_gart_probe(struct platform_device *pdev)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
ret = iommu_device_sysfs_add(&gart->iommu, &pdev->dev, NULL,
|
||||
dev_name(&pdev->dev));
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IOMMU in sysfs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iommu_device_set_ops(&gart->iommu, &gart_iommu_ops);
|
||||
|
||||
ret = iommu_device_register(&gart->iommu);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register IOMMU\n");
|
||||
iommu_device_sysfs_remove(&gart->iommu);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gart->dev = &pdev->dev;
|
||||
spin_lock_init(&gart->pte_lock);
|
||||
spin_lock_init(&gart->client_lock);
|
||||
|
@ -430,6 +472,9 @@ static int tegra_gart_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct gart_device *gart = platform_get_drvdata(pdev);
|
||||
|
||||
iommu_device_unregister(&gart->iommu);
|
||||
iommu_device_sysfs_remove(&gart->iommu);
|
||||
|
||||
writel(0, gart->regs + GART_CONFIG);
|
||||
if (gart->savedata)
|
||||
vfree(gart->savedata);
|
||||
|
|
|
@ -36,6 +36,8 @@ struct tegra_smmu {
|
|||
struct list_head list;
|
||||
|
||||
struct dentry *debugfs;
|
||||
|
||||
struct iommu_device iommu; /* IOMMU Core code handle */
|
||||
};
|
||||
|
||||
struct tegra_smmu_as {
|
||||
|
@ -704,6 +706,7 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np)
|
|||
static int tegra_smmu_add_device(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct iommu_group *group;
|
||||
struct of_phandle_args args;
|
||||
unsigned int index = 0;
|
||||
|
||||
|
@ -719,18 +722,33 @@ static int tegra_smmu_add_device(struct device *dev)
|
|||
* first match.
|
||||
*/
|
||||
dev->archdata.iommu = smmu;
|
||||
|
||||
iommu_device_link(&smmu->iommu, dev);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
group = iommu_group_get_for_dev(dev);
|
||||
if (IS_ERR(group))
|
||||
return PTR_ERR(group);
|
||||
|
||||
iommu_group_put(group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_smmu_remove_device(struct device *dev)
|
||||
{
|
||||
struct tegra_smmu *smmu = dev->archdata.iommu;
|
||||
|
||||
if (smmu)
|
||||
iommu_device_unlink(&smmu->iommu, dev);
|
||||
|
||||
dev->archdata.iommu = NULL;
|
||||
iommu_group_remove_device(dev);
|
||||
}
|
||||
|
||||
static const struct iommu_ops tegra_smmu_ops = {
|
||||
|
@ -741,6 +759,7 @@ static const struct iommu_ops tegra_smmu_ops = {
|
|||
.detach_dev = tegra_smmu_detach_dev,
|
||||
.add_device = tegra_smmu_add_device,
|
||||
.remove_device = tegra_smmu_remove_device,
|
||||
.device_group = generic_device_group,
|
||||
.map = tegra_smmu_map,
|
||||
.unmap = tegra_smmu_unmap,
|
||||
.map_sg = default_iommu_map_sg,
|
||||
|
@ -930,10 +949,25 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
|
|||
|
||||
tegra_smmu_ahb_enable();
|
||||
|
||||
err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops);
|
||||
if (err < 0)
|
||||
err = iommu_device_sysfs_add(&smmu->iommu, dev, NULL, dev_name(dev));
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
iommu_device_set_ops(&smmu->iommu, &tegra_smmu_ops);
|
||||
|
||||
err = iommu_device_register(&smmu->iommu);
|
||||
if (err) {
|
||||
iommu_device_sysfs_remove(&smmu->iommu);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
err = bus_set_iommu(&platform_bus_type, &tegra_smmu_ops);
|
||||
if (err < 0) {
|
||||
iommu_device_unregister(&smmu->iommu);
|
||||
iommu_device_sysfs_remove(&smmu->iommu);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
tegra_smmu_debugfs_init(smmu);
|
||||
|
||||
|
@ -942,6 +976,9 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev,
|
|||
|
||||
void tegra_smmu_remove(struct tegra_smmu *smmu)
|
||||
{
|
||||
iommu_device_unregister(&smmu->iommu);
|
||||
iommu_device_sysfs_remove(&smmu->iommu);
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
tegra_smmu_debugfs_exit(smmu);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -23,7 +24,10 @@
|
|||
#include <soc/mediatek/smi.h>
|
||||
#include <dt-bindings/memory/mt2701-larb-port.h>
|
||||
|
||||
/* mt8173 */
|
||||
#define SMI_LARB_MMU_EN 0xf00
|
||||
|
||||
/* mt2701 */
|
||||
#define REG_SMI_SECUR_CON_BASE 0x5c0
|
||||
|
||||
/* every register control 8 port, register offset 0x4 */
|
||||
|
@ -41,7 +45,12 @@
|
|||
/* mt2701 domain should be set to 3 */
|
||||
#define SMI_SECUR_CON_VAL_DOMAIN(id) (0x3 << ((((id) & 0x7) << 2) + 1))
|
||||
|
||||
/* mt2712 */
|
||||
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
|
||||
#define F_MMU_EN BIT(0)
|
||||
|
||||
struct mtk_smi_larb_gen {
|
||||
bool need_larbid;
|
||||
int port_in_larb[MTK_LARB_NR_MAX + 1];
|
||||
void (*config_port)(struct device *);
|
||||
};
|
||||
|
@ -148,6 +157,15 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
|
|||
struct mtk_smi_iommu *smi_iommu = data;
|
||||
unsigned int i;
|
||||
|
||||
if (larb->larb_gen->need_larbid) {
|
||||
larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no larbid property, Loop to find the corresponding
|
||||
* iommu information.
|
||||
*/
|
||||
for (i = 0; i < smi_iommu->larb_nr; i++) {
|
||||
if (dev == smi_iommu->larb_imu[i].dev) {
|
||||
/* The 'mmu' may be updated in iommu-attach/detach. */
|
||||
|
@ -158,14 +176,33 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void mtk_smi_larb_config_port(struct device *dev)
|
||||
static void mtk_smi_larb_config_port_mt2712(struct device *dev)
|
||||
{
|
||||
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* larb 8/9 is the bdpsys larb, the iommu_en is enabled defaultly.
|
||||
* Don't need to set it again.
|
||||
*/
|
||||
if (larb->larbid == 8 || larb->larbid == 9)
|
||||
return;
|
||||
|
||||
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
|
||||
reg = readl_relaxed(larb->base + SMI_LARB_NONSEC_CON(i));
|
||||
reg |= F_MMU_EN;
|
||||
writel(reg, larb->base + SMI_LARB_NONSEC_CON(i));
|
||||
}
|
||||
}
|
||||
|
||||
static void mtk_smi_larb_config_port_mt8173(struct device *dev)
|
||||
{
|
||||
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||
|
||||
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
|
||||
}
|
||||
|
||||
|
||||
static void mtk_smi_larb_config_port_gen1(struct device *dev)
|
||||
{
|
||||
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||
|
@ -210,10 +247,11 @@ static const struct component_ops mtk_smi_larb_component_ops = {
|
|||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
|
||||
/* mt8173 do not need the port in larb */
|
||||
.config_port = mtk_smi_larb_config_port,
|
||||
.config_port = mtk_smi_larb_config_port_mt8173,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
|
||||
.need_larbid = true,
|
||||
.port_in_larb = {
|
||||
LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
|
||||
LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
|
||||
|
@ -221,6 +259,11 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
|
|||
.config_port = mtk_smi_larb_config_port_gen1,
|
||||
};
|
||||
|
||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
|
||||
.need_larbid = true,
|
||||
.config_port = mtk_smi_larb_config_port_mt2712,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
||||
{
|
||||
.compatible = "mediatek,mt8173-smi-larb",
|
||||
|
@ -230,6 +273,10 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
|||
.compatible = "mediatek,mt2701-smi-larb",
|
||||
.data = &mtk_smi_larb_mt2701
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt2712-smi-larb",
|
||||
.data = &mtk_smi_larb_mt2712
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -240,20 +287,13 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct device_node *smi_node;
|
||||
struct platform_device *smi_pdev;
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
if (!dev->pm_domain)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
of_id = of_match_node(mtk_smi_larb_of_ids, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
int err;
|
||||
|
||||
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
|
||||
if (!larb)
|
||||
return -ENOMEM;
|
||||
|
||||
larb->larb_gen = of_id->data;
|
||||
larb->larb_gen = of_device_get_match_data(dev);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
larb->base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(larb->base))
|
||||
|
@ -268,6 +308,15 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(larb->smi.clk_smi);
|
||||
larb->smi.dev = dev;
|
||||
|
||||
if (larb->larb_gen->need_larbid) {
|
||||
err = of_property_read_u32(dev->of_node, "mediatek,larb-id",
|
||||
&larb->larbid);
|
||||
if (err) {
|
||||
dev_err(dev, "missing larbid property\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
|
||||
if (!smi_node)
|
||||
return -EINVAL;
|
||||
|
@ -275,6 +324,8 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||
smi_pdev = of_find_device_by_node(smi_node);
|
||||
of_node_put(smi_node);
|
||||
if (smi_pdev) {
|
||||
if (!platform_get_drvdata(smi_pdev))
|
||||
return -EPROBE_DEFER;
|
||||
larb->smi_common_dev = &smi_pdev->dev;
|
||||
} else {
|
||||
dev_err(dev, "Failed to get the smi_common device\n");
|
||||
|
@ -311,6 +362,10 @@ static const struct of_device_id mtk_smi_common_of_ids[] = {
|
|||
.compatible = "mediatek,mt2701-smi-common",
|
||||
.data = (void *)MTK_SMI_GEN1
|
||||
},
|
||||
{
|
||||
.compatible = "mediatek,mt2712-smi-common",
|
||||
.data = (void *)MTK_SMI_GEN2
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -319,11 +374,8 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct mtk_smi *common;
|
||||
struct resource *res;
|
||||
const struct of_device_id *of_id;
|
||||
enum mtk_smi_gen smi_gen;
|
||||
|
||||
if (!dev->pm_domain)
|
||||
return -EPROBE_DEFER;
|
||||
int ret;
|
||||
|
||||
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
|
||||
if (!common)
|
||||
|
@ -338,17 +390,13 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(common->clk_smi))
|
||||
return PTR_ERR(common->clk_smi);
|
||||
|
||||
of_id = of_match_node(mtk_smi_common_of_ids, pdev->dev.of_node);
|
||||
if (!of_id)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* for mtk smi gen 1, we need to get the ao(always on) base to config
|
||||
* m4u port, and we need to enable the aync clock for transform the smi
|
||||
* clock into emi clock domain, but for mtk smi gen2, there's no smi ao
|
||||
* base.
|
||||
*/
|
||||
smi_gen = (enum mtk_smi_gen)of_id->data;
|
||||
smi_gen = (enum mtk_smi_gen)of_device_get_match_data(dev);
|
||||
if (smi_gen == MTK_SMI_GEN1) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
common->smi_ao_base = devm_ioremap_resource(dev, res);
|
||||
|
@ -359,7 +407,9 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(common->clk_async))
|
||||
return PTR_ERR(common->clk_async);
|
||||
|
||||
clk_prepare_enable(common->clk_async);
|
||||
ret = clk_prepare_enable(common->clk_async);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
pm_runtime_enable(dev);
|
||||
platform_set_drvdata(pdev, common);
|
||||
|
@ -403,4 +453,4 @@ static int __init mtk_smi_init(void)
|
|||
return ret;
|
||||
}
|
||||
|
||||
subsys_initcall(mtk_smi_init);
|
||||
module_init(mtk_smi_init);
|
||||
|
|
|
@ -15,10 +15,6 @@
|
|||
#define __DTS_IOMMU_PORT_MT8173_H
|
||||
|
||||
#define MTK_M4U_ID(larb, port) (((larb) << 5) | (port))
|
||||
/* Local arbiter ID */
|
||||
#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0x7)
|
||||
/* PortID within the local arbiter */
|
||||
#define MTK_M4U_TO_PORT(id) ((id) & 0x1f)
|
||||
|
||||
#define M4U_LARB0_ID 0
|
||||
#define M4U_LARB1_ID 1
|
||||
|
|
|
@ -167,6 +167,10 @@ struct iommu_resv_region {
|
|||
* @map: map a physically contiguous memory region to an iommu domain
|
||||
* @unmap: unmap a physically contiguous memory region from an iommu domain
|
||||
* @map_sg: map a scatter-gather list of physically contiguous memory chunks
|
||||
* @flush_tlb_all: Synchronously flush all hardware TLBs for this domain
|
||||
* @tlb_range_add: Add a given iova range to the flush queue for this domain
|
||||
* @tlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
|
||||
* queue
|
||||
* to an iommu domain
|
||||
* @iova_to_phys: translate iova to physical address
|
||||
* @add_device: add device to iommu grouping
|
||||
|
@ -199,6 +203,10 @@ struct iommu_ops {
|
|||
size_t size);
|
||||
size_t (*map_sg)(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg, unsigned int nents, int prot);
|
||||
void (*flush_iotlb_all)(struct iommu_domain *domain);
|
||||
void (*iotlb_range_add)(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size);
|
||||
void (*iotlb_sync)(struct iommu_domain *domain);
|
||||
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
|
||||
int (*add_device)(struct device *dev);
|
||||
void (*remove_device)(struct device *dev);
|
||||
|
@ -225,6 +233,7 @@ struct iommu_ops {
|
|||
u32 (*domain_get_windows)(struct iommu_domain *domain);
|
||||
|
||||
int (*of_xlate)(struct device *dev, struct of_phandle_args *args);
|
||||
bool (*is_attach_deferred)(struct iommu_domain *domain, struct device *dev);
|
||||
|
||||
unsigned long pgsize_bitmap;
|
||||
};
|
||||
|
@ -292,6 +301,8 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||
phys_addr_t paddr, size_t size, int prot);
|
||||
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size);
|
||||
extern size_t iommu_unmap_fast(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size);
|
||||
extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||
struct scatterlist *sg,unsigned int nents,
|
||||
int prot);
|
||||
|
@ -348,6 +359,25 @@ extern void iommu_domain_window_disable(struct iommu_domain *domain, u32 wnd_nr)
|
|||
extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
|
||||
unsigned long iova, int flags);
|
||||
|
||||
static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
|
||||
{
|
||||
if (domain->ops->flush_iotlb_all)
|
||||
domain->ops->flush_iotlb_all(domain);
|
||||
}
|
||||
|
||||
static inline void iommu_tlb_range_add(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
if (domain->ops->iotlb_range_add)
|
||||
domain->ops->iotlb_range_add(domain, iova, size);
|
||||
}
|
||||
|
||||
static inline void iommu_tlb_sync(struct iommu_domain *domain)
|
||||
{
|
||||
if (domain->ops->iotlb_sync)
|
||||
domain->ops->iotlb_sync(domain);
|
||||
}
|
||||
|
||||
static inline size_t iommu_map_sg(struct iommu_domain *domain,
|
||||
unsigned long iova, struct scatterlist *sg,
|
||||
unsigned int nents, int prot)
|
||||
|
@ -430,12 +460,18 @@ static inline struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
|
|||
}
|
||||
|
||||
static inline int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
||||
phys_addr_t paddr, int gfp_order, int prot)
|
||||
phys_addr_t paddr, size_t size, int prot)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||
size_t size)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int iommu_unmap_fast(struct iommu_domain *domain, unsigned long iova,
|
||||
int gfp_order)
|
||||
{
|
||||
return -ENODEV;
|
||||
|
@ -448,6 +484,19 @@ static inline size_t iommu_map_sg(struct iommu_domain *domain,
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void iommu_tlb_range_add(struct iommu_domain *domain,
|
||||
unsigned long iova, size_t size)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void iommu_tlb_sync(struct iommu_domain *domain)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int iommu_domain_window_enable(struct iommu_domain *domain,
|
||||
u32 wnd_nr, phys_addr_t paddr,
|
||||
u64 size, int prot)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
/* iova structure */
|
||||
|
@ -36,6 +37,35 @@ struct iova_rcache {
|
|||
struct iova_cpu_rcache __percpu *cpu_rcaches;
|
||||
};
|
||||
|
||||
struct iova_domain;
|
||||
|
||||
/* Call-Back from IOVA code into IOMMU drivers */
|
||||
typedef void (* iova_flush_cb)(struct iova_domain *domain);
|
||||
|
||||
/* Destructor for per-entry data */
|
||||
typedef void (* iova_entry_dtor)(unsigned long data);
|
||||
|
||||
/* Number of entries per Flush Queue */
|
||||
#define IOVA_FQ_SIZE 256
|
||||
|
||||
/* Timeout (in ms) after which entries are flushed from the Flush-Queue */
|
||||
#define IOVA_FQ_TIMEOUT 10
|
||||
|
||||
/* Flush Queue entry for defered flushing */
|
||||
struct iova_fq_entry {
|
||||
unsigned long iova_pfn;
|
||||
unsigned long pages;
|
||||
unsigned long data;
|
||||
u64 counter; /* Flush counter when this entrie was added */
|
||||
};
|
||||
|
||||
/* Per-CPU Flush Queue structure */
|
||||
struct iova_fq {
|
||||
struct iova_fq_entry entries[IOVA_FQ_SIZE];
|
||||
unsigned head, tail;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/* holds all the iova translations for a domain */
|
||||
struct iova_domain {
|
||||
spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */
|
||||
|
@ -45,6 +75,25 @@ struct iova_domain {
|
|||
unsigned long start_pfn; /* Lower limit for this domain */
|
||||
unsigned long dma_32bit_pfn;
|
||||
struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE]; /* IOVA range caches */
|
||||
|
||||
iova_flush_cb flush_cb; /* Call-Back function to flush IOMMU
|
||||
TLBs */
|
||||
|
||||
iova_entry_dtor entry_dtor; /* IOMMU driver specific destructor for
|
||||
iova entry */
|
||||
|
||||
struct iova_fq __percpu *fq; /* Flush Queue */
|
||||
|
||||
atomic64_t fq_flush_start_cnt; /* Number of TLB flushes that
|
||||
have been started */
|
||||
|
||||
atomic64_t fq_flush_finish_cnt; /* Number of TLB flushes that
|
||||
have been finished */
|
||||
|
||||
struct timer_list fq_timer; /* Timer to regularily empty the
|
||||
flush-queues */
|
||||
atomic_t fq_timer_on; /* 1 when timer is active, 0
|
||||
when not */
|
||||
};
|
||||
|
||||
static inline unsigned long iova_size(struct iova *iova)
|
||||
|
@ -95,6 +144,9 @@ struct iova *alloc_iova(struct iova_domain *iovad, unsigned long size,
|
|||
bool size_aligned);
|
||||
void free_iova_fast(struct iova_domain *iovad, unsigned long pfn,
|
||||
unsigned long size);
|
||||
void queue_iova(struct iova_domain *iovad,
|
||||
unsigned long pfn, unsigned long pages,
|
||||
unsigned long data);
|
||||
unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size,
|
||||
unsigned long limit_pfn);
|
||||
struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
|
||||
|
@ -102,6 +154,8 @@ struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo,
|
|||
void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to);
|
||||
void init_iova_domain(struct iova_domain *iovad, unsigned long granule,
|
||||
unsigned long start_pfn, unsigned long pfn_32bit);
|
||||
int init_iova_flush_queue(struct iova_domain *iovad,
|
||||
iova_flush_cb flush_cb, iova_entry_dtor entry_dtor);
|
||||
struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn);
|
||||
void put_iova_domain(struct iova_domain *iovad);
|
||||
struct iova *split_and_remove_iova(struct iova_domain *iovad,
|
||||
|
@ -148,6 +202,12 @@ static inline void free_iova_fast(struct iova_domain *iovad,
|
|||
{
|
||||
}
|
||||
|
||||
static inline void queue_iova(struct iova_domain *iovad,
|
||||
unsigned long pfn, unsigned long pages,
|
||||
unsigned long data)
|
||||
{
|
||||
}
|
||||
|
||||
static inline unsigned long alloc_iova_fast(struct iova_domain *iovad,
|
||||
unsigned long size,
|
||||
unsigned long limit_pfn)
|
||||
|
@ -174,6 +234,13 @@ static inline void init_iova_domain(struct iova_domain *iovad,
|
|||
{
|
||||
}
|
||||
|
||||
static inline int init_iova_flush_queue(struct iova_domain *iovad,
|
||||
iova_flush_cb flush_cb,
|
||||
iova_entry_dtor entry_dtor)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline struct iova *find_iova(struct iova_domain *iovad,
|
||||
unsigned long pfn)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#ifdef CONFIG_MTK_SMI
|
||||
|
||||
#define MTK_LARB_NR_MAX 8
|
||||
#define MTK_LARB_NR_MAX 16
|
||||
|
||||
#define MTK_SMI_MMU_EN(port) BIT(port)
|
||||
|
||||
|
|
Loading…
Reference in New Issue