diff options
-rw-r--r-- | drivers/iommu/fsl_pamu.c | 20 | ||||
-rw-r--r-- | drivers/iommu/fsl_pamu.h | 1 | ||||
-rw-r--r-- | drivers/iommu/fsl_pamu_domain.c | 52 |
3 files changed, 70 insertions, 3 deletions
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c index da707d8..e7b3d45 100644 --- a/drivers/iommu/fsl_pamu.c +++ b/drivers/iommu/fsl_pamu.c @@ -277,6 +277,26 @@ static void setup_default_ppaace(struct paace *ppaace) PAACE_AP_PERMS_ALL); } +/* Reset the PAACE entry to the default state */ +void enable_default_dma_window(int liodn) +{ + struct paace *ppaace; + + ppaace = pamu_get_ppaace(liodn); + if (!ppaace) { + pr_debug("Invalid liodn entry\n"); + return; + } + + memset(ppaace, 0, sizeof(struct paace)); + + setup_default_ppaace(ppaace); + + /* Ensure that all other stores to the ppaace complete first */ + mb(); + pamu_enable_liodn(liodn); +} + /* Release the subwindows reserved for a particular LIODN */ void pamu_free_subwins(int liodn) { diff --git a/drivers/iommu/fsl_pamu.h b/drivers/iommu/fsl_pamu.h index 853cdb7..89940fa 100644 --- a/drivers/iommu/fsl_pamu.h +++ b/drivers/iommu/fsl_pamu.h @@ -412,5 +412,6 @@ void get_ome_index(u32 *omi_index, struct device *dev); int pamu_update_paace_field(int liodn, u32 subwin, int field, u32 value); int pamu_disable_spaace(int liodn, u32 subwin); u32 pamu_get_max_subwin_cnt(void); +void enable_default_dma_window(int liodn); #endif /* __FSL_PAMU_H */ diff --git a/drivers/iommu/fsl_pamu_domain.c b/drivers/iommu/fsl_pamu_domain.c index 844997e..e62048b 100644 --- a/drivers/iommu/fsl_pamu_domain.c +++ b/drivers/iommu/fsl_pamu_domain.c @@ -366,17 +366,63 @@ static inline struct device_domain_info *find_domain(struct device *dev) return dev->archdata.iommu_domain; } +/* Disable device DMA capability and enable default DMA window */ +static void disable_device_dma(struct device_domain_info *info, + int enable_dma_window) +{ +#ifdef CONFIG_PCI + if (info->dev->bus == &pci_bus_type) { + struct pci_dev *pdev = NULL; + u16 pci_command; + + pdev = to_pci_dev(info->dev); + pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + /* disable device bus master capability */ + if (pci_command & PCI_COMMAND_MASTER) { + pci_command &= ~PCI_COMMAND_MASTER; + pci_write_config_word(pdev, PCI_COMMAND, pci_command); + } + } +#endif + + if (enable_dma_window) + enable_default_dma_window(info->liodn); +} + +static int check_for_shared_liodn(struct device_domain_info *info) +{ + struct device_domain_info *tmp; + + /* + * Sanity check, to ensure that this is not a + * shared LIODN. In case of a PCIe controller + * it's possible that all PCIe devices share + * the same LIODN. + */ + list_for_each_entry(tmp, &info->domain->devices, link) { + if (info->liodn == tmp->liodn) + return 1; + } + + return 0; +} + static void remove_device_ref(struct device_domain_info *info, u32 win_cnt) { unsigned long flags; + int enable_dma_window = 0; list_del(&info->link); spin_lock_irqsave(&iommu_lock, flags); - if (win_cnt > 1) - pamu_free_subwins(info->liodn); - pamu_disable_liodn(info->liodn); + if (!check_for_shared_liodn(info)) { + if (win_cnt > 1) + pamu_free_subwins(info->liodn); + pamu_disable_liodn(info->liodn); + enable_dma_window = 1; + } spin_unlock_irqrestore(&iommu_lock, flags); spin_lock_irqsave(&device_domain_lock, flags); + disable_device_dma(info, enable_dma_window); info->dev->archdata.iommu_domain = NULL; kmem_cache_free(iommu_devinfo_cache, info); spin_unlock_irqrestore(&device_domain_lock, flags); |