diff options
-rw-r--r-- | drivers/iommu/fsl_pamu.c | 54 | ||||
-rw-r--r-- | drivers/iommu/fsl_pamu.h | 1 | ||||
-rw-r--r-- | drivers/iommu/fsl_pamu_domain.c | 47 |
3 files changed, 81 insertions, 21 deletions
diff --git a/drivers/iommu/fsl_pamu.c b/drivers/iommu/fsl_pamu.c index 84a5de7..42b903f 100644 --- a/drivers/iommu/fsl_pamu.c +++ b/drivers/iommu/fsl_pamu.c @@ -250,6 +250,40 @@ static unsigned long pamu_get_fspi_and_allocate(u32 subwin_cnt) return (spaace_addr - (unsigned long)spaact) / (sizeof(struct paace)); } +/* + * Defaul PPAACE settings for an LIODN. + */ +static void setup_default_ppaace(struct paace *ppaace) +{ + pamu_init_ppaace(ppaace); + /* window size is 2^(WSE+1) bytes */ + set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, 35); + ppaace->wbah = 0; + set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0); + set_bf(ppaace->impl_attr, PAACE_IA_ATM, + PAACE_ATM_NO_XLATE); + set_bf(ppaace->addr_bitfields, PAACE_AF_AP, + 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); + mb(); + pamu_enable_liodn(liodn); +} + /* Release the subwindows reserved for a particular LIODN */ void pamu_free_subwins(int liodn) { @@ -854,15 +888,7 @@ static void __init enable_remaining_liodns(void) for (liodn = 0; liodn < PAACE_NUMBER_ENTRIES; liodn++) { ppaace = pamu_get_ppaace(liodn); if (!get_bf(ppaace->addr_bitfields, PAACE_AF_V)) { - pamu_init_ppaace(ppaace); - /* window size is 2^(WSE+1) bytes */ - set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, 35); - ppaace->wbah = 0; - set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0); - set_bf(ppaace->impl_attr, PAACE_IA_ATM, - PAACE_ATM_NO_XLATE); - set_bf(ppaace->addr_bitfields, PAACE_AF_AP, - PAACE_AP_PERMS_ALL); + setup_default_ppaace(ppaace); mb(); pamu_enable_liodn(liodn); } @@ -888,15 +914,7 @@ static void __init setup_liodns(void) continue; } ppaace = pamu_get_ppaace(liodn); - pamu_init_ppaace(ppaace); - /* window size is 2^(WSE+1) bytes */ - set_bf(ppaace->addr_bitfields, PPAACE_AF_WSE, 35); - ppaace->wbah = 0; - set_bf(ppaace->addr_bitfields, PPAACE_AF_WBAL, 0); - set_bf(ppaace->impl_attr, PAACE_IA_ATM, - PAACE_ATM_NO_XLATE); - set_bf(ppaace->addr_bitfields, PAACE_AF_AP, - PAACE_AP_PERMS_ALL); + setup_default_ppaace(ppaace); if (of_device_is_compatible(node, "fsl,qman-portal")) setup_dpaa_paace(ppaace, QMAN_PORTAL_PAACE); if (of_device_is_compatible(node, "fsl,qman")) diff --git a/drivers/iommu/fsl_pamu.h b/drivers/iommu/fsl_pamu.h index fd9dd6d..119ae04 100644 --- a/drivers/iommu/fsl_pamu.h +++ b/drivers/iommu/fsl_pamu.h @@ -407,5 +407,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 c18b76f..c9eb0f9 100644 --- a/drivers/iommu/fsl_pamu_domain.c +++ b/drivers/iommu/fsl_pamu_domain.c @@ -338,15 +338,56 @@ 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; + pdev = to_pci_dev(info->dev); + if (pci_is_enabled(pdev)) + pci_disable_device(pdev); + } +#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) { + int enable_dma_window = 0; + list_del(&info->link); spin_lock(&iommu_lock); - 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(&iommu_lock); spin_lock(&device_domain_lock); + disable_device_dma(info, enable_dma_window); info->dev->archdata.iommu_domain = NULL; kmem_cache_free(iommu_devinfo_cache, info); spin_unlock(&device_domain_lock); |