From eb9c95271eafb62f7024547016ad00636c1425c1 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 8 Oct 2012 22:49:35 -0600 Subject: iommu/amd: Split IOMMU group initialization This needs to be broken apart, start with pulling all the IOMMU group init code into a new function. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 61 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 55074cba20eb..b65b377815ac 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -276,39 +276,32 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) -static int iommu_init_device(struct device *dev) +static int init_iommu_group(struct device *dev) { - struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev); struct iommu_dev_data *dev_data; struct iommu_group *group; - u16 alias; + struct pci_dev *dma_pdev = NULL; int ret; - if (dev->archdata.iommu) + group = iommu_group_get(dev); + if (group) { + iommu_group_put(group); return 0; + } dev_data = find_dev_data(get_device_id(dev)); if (!dev_data) return -ENOMEM; - alias = amd_iommu_alias_table[dev_data->devid]; - if (alias != dev_data->devid) { - struct iommu_dev_data *alias_data; - - alias_data = find_dev_data(alias); - if (alias_data == NULL) { - pr_err("AMD-Vi: Warning: Unhandled device %s\n", - dev_name(dev)); - free_dev_data(dev_data); - return -ENOTSUPP; - } - dev_data->alias_data = alias_data; + if (dev_data->alias_data) { + u16 alias; + alias = amd_iommu_alias_table[dev_data->devid]; dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); } - if (dma_pdev == NULL) - dma_pdev = pci_dev_get(pdev); + if (!dma_pdev) + dma_pdev = pci_dev_get(to_pci_dev(dev)); /* Account for quirked devices */ swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); @@ -358,6 +351,38 @@ root_bus: iommu_group_put(group); + return ret; +} + +static int iommu_init_device(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct iommu_dev_data *dev_data; + u16 alias; + int ret; + + if (dev->archdata.iommu) + return 0; + + dev_data = find_dev_data(get_device_id(dev)); + if (!dev_data) + return -ENOMEM; + + alias = amd_iommu_alias_table[dev_data->devid]; + if (alias != dev_data->devid) { + struct iommu_dev_data *alias_data; + + alias_data = find_dev_data(alias); + if (alias_data == NULL) { + pr_err("AMD-Vi: Warning: Unhandled device %s\n", + dev_name(dev)); + free_dev_data(dev_data); + return -ENOTSUPP; + } + dev_data->alias_data = alias_data; + } + + ret = init_iommu_group(dev); if (ret) return ret; -- cgit From 2851db21b88ff3e80e1b3ab5e23b1a49d6f734bf Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 8 Oct 2012 22:49:41 -0600 Subject: iommu/amd: Split IOMMU Group topology walk Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 58 +++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 25 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index b65b377815ac..6edbd0edcae7 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -276,32 +276,9 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) -static int init_iommu_group(struct device *dev) +static struct pci_dev *get_isolation_root(struct pci_dev *pdev) { - struct iommu_dev_data *dev_data; - struct iommu_group *group; - struct pci_dev *dma_pdev = NULL; - int ret; - - group = iommu_group_get(dev); - if (group) { - iommu_group_put(group); - return 0; - } - - dev_data = find_dev_data(get_device_id(dev)); - if (!dev_data) - return -ENOMEM; - - if (dev_data->alias_data) { - u16 alias; - - alias = amd_iommu_alias_table[dev_data->devid]; - dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); - } - - if (!dma_pdev) - dma_pdev = pci_dev_get(to_pci_dev(dev)); + struct pci_dev *dma_pdev = pdev; /* Account for quirked devices */ swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); @@ -339,6 +316,37 @@ static int init_iommu_group(struct device *dev) } root_bus: + return dma_pdev; +} + +static int init_iommu_group(struct device *dev) +{ + struct iommu_dev_data *dev_data; + struct iommu_group *group; + struct pci_dev *dma_pdev = NULL; + int ret; + + group = iommu_group_get(dev); + if (group) { + iommu_group_put(group); + return 0; + } + + dev_data = find_dev_data(get_device_id(dev)); + if (!dev_data) + return -ENOMEM; + + if (dev_data->alias_data) { + u16 alias; + + alias = amd_iommu_alias_table[dev_data->devid]; + dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); + } + + if (!dma_pdev) + dma_pdev = pci_dev_get(to_pci_dev(dev)); + + dma_pdev = get_isolation_root(dma_pdev); group = iommu_group_get(&dma_pdev->dev); pci_dev_put(dma_pdev); if (!group) { -- cgit From 2bff6a508eb2ba8b341c20db3099e050e5496e3d Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 8 Oct 2012 22:49:48 -0600 Subject: iommu/amd: Split upstream bus device lookup Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 6edbd0edcae7..3a00b5ce73ab 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -274,6 +274,18 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) *from = to; } +static struct pci_bus *find_hosted_bus(struct pci_bus *bus) +{ + while (!bus->self) { + if (!pci_is_root_bus(bus)) + bus = bus->parent; + else + return ERR_PTR(-ENODEV); + } + + return bus; +} + #define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) static struct pci_dev *get_isolation_root(struct pci_dev *pdev) @@ -300,14 +312,9 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev) * Finding the next device may require skipping virtual buses. */ while (!pci_is_root_bus(dma_pdev->bus)) { - struct pci_bus *bus = dma_pdev->bus; - - while (!bus->self) { - if (!pci_is_root_bus(bus)) - bus = bus->parent; - else - goto root_bus; - } + struct pci_bus *bus = find_hosted_bus(dma_pdev->bus); + if (IS_ERR(bus)) + break; if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) break; @@ -315,7 +322,6 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev) swap_pci_ref(&dma_pdev, pci_dev_get(bus->self)); } -root_bus: return dma_pdev; } -- cgit From ce7ac4abf2401dfdcb1ac4c7277dab8ed90c8788 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 8 Oct 2012 22:49:54 -0600 Subject: iommu/amd: Split IOMMU group allocation and attach Add a WARN_ON to make it clear why we don't add dma_pdev->dev to the group we're allocating. Signed-off-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 3a00b5ce73ab..7fa97a5e3eaf 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -325,6 +325,24 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev) return dma_pdev; } +static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) +{ + struct iommu_group *group = iommu_group_get(&pdev->dev); + int ret; + + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); + + WARN_ON(&pdev->dev != dev); + } + + ret = iommu_group_add_device(group, dev); + iommu_group_put(group); + return ret; +} + static int init_iommu_group(struct device *dev) { struct iommu_dev_data *dev_data; @@ -353,18 +371,8 @@ static int init_iommu_group(struct device *dev) dma_pdev = pci_dev_get(to_pci_dev(dev)); dma_pdev = get_isolation_root(dma_pdev); - group = iommu_group_get(&dma_pdev->dev); + ret = use_pdev_iommu_group(dma_pdev, dev); pci_dev_put(dma_pdev); - if (!group) { - group = iommu_group_alloc(); - if (IS_ERR(group)) - return PTR_ERR(group); - } - - ret = iommu_group_add_device(group, dev); - - iommu_group_put(group); - return ret; } -- cgit From 78bfa9f395f6bbab168b83f69b99659818517b02 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 8 Oct 2012 22:50:00 -0600 Subject: iommu/amd: Properly account for virtual aliases in IOMMU groups An alias doesn't always point to a physical device. When this happens we must first verify that the IOMMU group isn't rooted in a device above the alias. In this case the alias is effectively just another quirk for the devices aliased to it. Alternatively, the virtual alias itself may be the root of the IOMMU group. To support this, allow a group to be hosted on the alias dev_data for use by anything that might have the same alias. Signed-off-by: Alex williamson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 61 +++++++++++++++++++++++++++++++++++++---- drivers/iommu/amd_iommu_types.h | 1 + 2 files changed, 57 insertions(+), 5 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 7fa97a5e3eaf..cb63cc5d94d7 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -140,6 +140,9 @@ static void free_dev_data(struct iommu_dev_data *dev_data) list_del(&dev_data->dev_data_list); spin_unlock_irqrestore(&dev_data_list_lock, flags); + if (dev_data->group) + iommu_group_put(dev_data->group); + kfree(dev_data); } @@ -343,11 +346,25 @@ static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) return ret; } +static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, + struct device *dev) +{ + if (!dev_data->group) { + struct iommu_group *group = iommu_group_alloc(); + if (IS_ERR(group)) + return PTR_ERR(group); + + dev_data->group = group; + } + + return iommu_group_add_device(dev_data->group, dev); +} + static int init_iommu_group(struct device *dev) { struct iommu_dev_data *dev_data; struct iommu_group *group; - struct pci_dev *dma_pdev = NULL; + struct pci_dev *dma_pdev; int ret; group = iommu_group_get(dev); @@ -362,18 +379,52 @@ static int init_iommu_group(struct device *dev) if (dev_data->alias_data) { u16 alias; + struct pci_bus *bus; + + if (dev_data->alias_data->group) + goto use_group; + /* + * If the alias device exists, it's effectively just a first + * level quirk for finding the DMA source. + */ alias = amd_iommu_alias_table[dev_data->devid]; dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); - } + if (dma_pdev) { + dma_pdev = get_isolation_root(dma_pdev); + goto use_pdev; + } + + /* + * If the alias is virtual, try to find a parent device + * and test whether the IOMMU group is actualy rooted above + * the alias. Be careful to also test the parent device if + * we think the alias is the root of the group. + */ + bus = pci_find_bus(0, alias >> 8); + if (!bus) + goto use_group; + + bus = find_hosted_bus(bus); + if (IS_ERR(bus) || !bus->self) + goto use_group; - if (!dma_pdev) - dma_pdev = pci_dev_get(to_pci_dev(dev)); + dma_pdev = get_isolation_root(pci_dev_get(bus->self)); + if (dma_pdev != bus->self || (dma_pdev->multifunction && + !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) + goto use_pdev; + + pci_dev_put(dma_pdev); + goto use_group; + } - dma_pdev = get_isolation_root(dma_pdev); + dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); +use_pdev: ret = use_pdev_iommu_group(dma_pdev, dev); pci_dev_put(dma_pdev); return ret; +use_group: + return use_dev_data_iommu_group(dev_data->alias_data, dev); } static int iommu_init_device(struct device *dev) diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index c9aa3d079ff0..e38ab438bb34 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -426,6 +426,7 @@ struct iommu_dev_data { struct iommu_dev_data *alias_data;/* The alias dev_data */ struct protection_domain *domain; /* Domain the device is bound to */ atomic_t bind; /* Domain attach reference count */ + struct iommu_group *group; /* IOMMU group for virtual aliases */ u16 devid; /* PCI Device ID */ bool iommu_v2; /* Device can make use of IOMMUv2 */ bool passthrough; /* Default for device is pt_domain */ -- cgit From ea2447f700cab264019b52e2b417d689e052dcfd Mon Sep 17 00:00:00 2001 From: Tom Mingarelli Date: Tue, 20 Nov 2012 19:43:17 +0000 Subject: intel-iommu: Prevent devices with RMRRs from being placed into SI Domain This patch is to prevent non-USB devices that have RMRRs associated with them from being placed into the SI Domain during init. This fixes the issue where the RMRR info for devices being placed in and out of the SI Domain gets lost. Signed-off-by: Thomas Mingarelli Tested-by: Shuah Khan Reviewed-by: Donald Dutile Reviewed-by: Alex Williamson Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'drivers/iommu') diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index d4a4cd445cab..ca3ee46a8d61 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -2320,8 +2320,39 @@ static int domain_add_dev_info(struct dmar_domain *domain, return 0; } +static bool device_has_rmrr(struct pci_dev *dev) +{ + struct dmar_rmrr_unit *rmrr; + int i; + + for_each_rmrr_units(rmrr) { + for (i = 0; i < rmrr->devices_cnt; i++) { + /* + * Return TRUE if this RMRR contains the device that + * is passed in. + */ + if (rmrr->devices[i] == dev) + return true; + } + } + return false; +} + static int iommu_should_identity_map(struct pci_dev *pdev, int startup) { + + /* + * We want to prevent any device associated with an RMRR from + * getting placed into the SI Domain. This is done because + * problems exist when devices are moved in and out of domains + * and their respective RMRR info is lost. We exempt USB devices + * from this process due to their usage of RMRRs that are known + * to not be needed after BIOS hand-off to OS. + */ + if (device_has_rmrr(pdev) && + (pdev->class >> 8) != PCI_CLASS_SERIAL_USB) + return 0; + if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) return 1; -- cgit From 37683e45623a0ccd34761c71aad618cc129fe35c Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 28 Nov 2012 15:52:53 +0200 Subject: iommu/tegra: smmu: Remove unnecessary PTC/TLB flush all smmu_flush_regs() does TLB/PTC flush all when freeing a second level page table. This isn't necessay at all since each pte entry has been already maintained by address in the above flush_ptc_and_tlb(). Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index c0f7a4266263..48538a6d2198 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -696,10 +696,8 @@ static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) *pte = _PTE_VACANT(iova); FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); - if (!--(*count)) { + if (!--(*count)) free_ptbl(as, iova); - smmu_flush_regs(as->smmu, 0); - } } static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, -- cgit From b7d4bec11199df6ef3267c5249e2676d0531eae5 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 28 Nov 2012 15:52:54 +0200 Subject: iommu/tegra: gart: Move bus_set_iommu after probe for multi arch For a single image to support multiple SoCs(GART/SMMU). Reported-by: Arto Merilainen Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-gart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index c16e8fc8a4bd..4c9db62814ff 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -398,6 +398,7 @@ static int tegra_gart_probe(struct platform_device *pdev) do_gart_setup(gart, NULL); gart_handle = gart; + bus_set_iommu(&platform_bus_type, &gart_iommu_ops); return 0; fail: @@ -450,7 +451,6 @@ static struct platform_driver tegra_gart_driver = { static int __devinit tegra_gart_init(void) { - bus_set_iommu(&platform_bus_type, &gart_iommu_ops); return platform_driver_register(&tegra_gart_driver); } -- cgit From f1bda29c2bfa84c2c022e1f443528e21607bf360 Mon Sep 17 00:00:00 2001 From: Hiroshi Doyu Date: Wed, 28 Nov 2012 15:52:55 +0200 Subject: iommu/tegra: smmu: Move bus_set_iommu after probe for multi arch For a single image to support multiple SoCs(GART/SMMU). Reported-by: Arto Merilainen Signed-off-by: Hiroshi Doyu Signed-off-by: Joerg Roedel --- drivers/iommu/tegra-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 48538a6d2198..843123acbb8d 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -1232,6 +1232,7 @@ static int tegra_smmu_probe(struct platform_device *pdev) smmu_debugfs_create(smmu); smmu_handle = smmu; + bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return 0; } @@ -1276,7 +1277,6 @@ static struct platform_driver tegra_smmu_driver = { static int __devinit tegra_smmu_init(void) { - bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return platform_driver_register(&tegra_smmu_driver); } -- cgit From 954e3dd8308501bb00cae4ed655968282dc65314 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Sun, 2 Dec 2012 15:35:37 +0100 Subject: iommu/amd: Don't use 512GB pages There is a bug in the hardware that will be triggered when this page size is used. Make sure this does not happen. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index cb63cc5d94d7..98d74ab564b6 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -66,8 +66,10 @@ * * If at some point we'd like to utilize the IOMMU core's new behavior, * we could change this to advertise the real page sizes we support. + * + * 512GB Pages are not supported due to a hardware bug */ -#define AMD_IOMMU_PGSIZES (~0xFFFUL) +#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38)) static DEFINE_RWLOCK(amd_iommu_devtable_lock); -- cgit From 310aa95078443c7b7b56c60dbc55b7a11b946edb Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Sun, 2 Dec 2012 15:40:56 +0100 Subject: iommu/amd: Remove obsolete comment The AMD IOMMU driver only uses the page-sizes it gets from IOMMU core and uses the appropriate page-size. So this comment is not necessary. Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 98d74ab564b6..c1c74e030a58 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -57,16 +57,6 @@ * physically contiguous memory regions it is mapping into page sizes * that we support. * - * Traditionally the IOMMU core just handed us the mappings directly, - * after making sure the size is an order of a 4KiB page and that the - * mapping has natural alignment. - * - * To retain this behavior, we currently advertise that we support - * all page sizes that are an order of 4KiB. - * - * If at some point we'd like to utilize the IOMMU core's new behavior, - * we could change this to advertise the real page sizes we support. - * * 512GB Pages are not supported due to a hardware bug */ #define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38)) -- cgit From a33977206cd167cb7541cf9044828552d9cae540 Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 19 Nov 2012 19:05:48 -0600 Subject: iommu/omap: Remove redundant clock handling on ISR For the interrupt to be generated, the mmu clock should be already enabled while translating a virtual address, so, this call to clock handling is just increasing/decreasing the counter. This works now, because its users need the same clock and they indirectly power the mmu, in this interrupt context the handling of clocks inside the ISR doesn't seem to be needed nor helping. Next patch should also correct the dependency on clients to handle iommu clocks. Signed-off-by: Omar Ramirez Luna Tested-by: Ohad Ben-Cohen Signed-off-by: Joerg Roedel --- drivers/iommu/omap-iommu.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index badc17c2bcb4..6b1288cddaef 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -807,9 +807,7 @@ static irqreturn_t iommu_fault_handler(int irq, void *data) if (!obj->refcount) return IRQ_NONE; - clk_enable(obj->clk); errs = iommu_report_fault(obj, &da); - clk_disable(obj->clk); if (errs == 0) return IRQ_HANDLED; -- cgit From 87f8e57327bd8d85fb5b46cad29ac281430cc50d Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 19 Nov 2012 19:05:49 -0600 Subject: iommu/omap: Keep mmu enabled when requested The purpose of the mmu is to handle the memory accesses requested by its users. Typically, the mmu is bundled with the processing unit in a single IP block, which makes them to share the same clock to be functional. Currently, iommu code assumes that its user will be indirectly clocking it, but being a separate mmu driver, it should handle its own clocks, so as long as the mmu is requested it will be powered ON and once detached it will be powered OFF. The remaining clock handling out of iommu_enable and iommu_disable corresponds to paths that can be accessed through debugfs, some of them doesn't work if the module is not enabled first, but in future if the mmu is idled withouth freeing, these are needed to debug. Signed-off-by: Omar Ramirez Luna Tested-by: Ohad Ben-Cohen Signed-off-by: Joerg Roedel --- drivers/iommu/omap-iommu.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index 6b1288cddaef..f8082da6179b 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -154,7 +154,6 @@ static int iommu_enable(struct omap_iommu *obj) err = arch_iommu->enable(obj); - clk_disable(obj->clk); return err; } @@ -163,8 +162,6 @@ static void iommu_disable(struct omap_iommu *obj) if (!obj) return; - clk_enable(obj->clk); - arch_iommu->disable(obj); clk_disable(obj->clk); -- cgit From 72b15b6ae97796c5fac687addde5dbfab872cf94 Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 19 Nov 2012 19:05:50 -0600 Subject: iommu/omap: Migrate to hwmod framework Use hwmod data and device attributes to build and register an omap device for iommu driver. - Update the naming convention in isp module. - Remove unneeded check for number of resources, as this is now handled by omap_device and prevents driver from loading. - Now unused, remove platform device and resource data, handling of sysconfig register for softreset purposes, use default latency structure. - Use hwmod API for reset handling. Signed-off-by: Omar Ramirez Luna Tested-by: Ohad Ben-Cohen Signed-off-by: Joerg Roedel --- drivers/iommu/omap-iommu.c | 23 ++++++++++++++++++----- drivers/iommu/omap-iommu2.c | 19 ------------------- 2 files changed, 18 insertions(+), 24 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index f8082da6179b..af9b4f31f594 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -143,13 +143,23 @@ EXPORT_SYMBOL_GPL(omap_iommu_arch_version); static int iommu_enable(struct omap_iommu *obj) { int err; + struct platform_device *pdev = to_platform_device(obj->dev); + struct iommu_platform_data *pdata = pdev->dev.platform_data; - if (!obj) + if (!obj || !pdata) return -EINVAL; if (!arch_iommu) return -ENODEV; + if (pdata->deassert_reset) { + err = pdata->deassert_reset(pdev, pdata->reset_name); + if (err) { + dev_err(obj->dev, "deassert_reset failed: %d\n", err); + return err; + } + } + clk_enable(obj->clk); err = arch_iommu->enable(obj); @@ -159,12 +169,18 @@ static int iommu_enable(struct omap_iommu *obj) static void iommu_disable(struct omap_iommu *obj) { - if (!obj) + struct platform_device *pdev = to_platform_device(obj->dev); + struct iommu_platform_data *pdata = pdev->dev.platform_data; + + if (!obj || !pdata) return; arch_iommu->disable(obj); clk_disable(obj->clk); + + if (pdata->assert_reset) + pdata->assert_reset(pdev, pdata->reset_name); } /* @@ -926,9 +942,6 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) struct resource *res; struct iommu_platform_data *pdata = pdev->dev.platform_data; - if (pdev->num_resources != 2) - return -EINVAL; - obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL); if (!obj) return -ENOMEM; diff --git a/drivers/iommu/omap-iommu2.c b/drivers/iommu/omap-iommu2.c index c02020292377..4a3a1c7a38c1 100644 --- a/drivers/iommu/omap-iommu2.c +++ b/drivers/iommu/omap-iommu2.c @@ -35,12 +35,8 @@ #define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT) #define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT) -#define MMU_SYS_SOFTRESET (1 << 1) #define MMU_SYS_AUTOIDLE 1 -/* SYSSTATUS */ -#define MMU_SYS_RESETDONE 1 - /* IRQSTATUS & IRQENABLE */ #define MMU_IRQ_MULTIHITFAULT (1 << 4) #define MMU_IRQ_TABLEWALKFAULT (1 << 3) @@ -97,7 +93,6 @@ static void __iommu_set_twl(struct omap_iommu *obj, bool on) static int omap2_iommu_enable(struct omap_iommu *obj) { u32 l, pa; - unsigned long timeout; if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K)) return -EINVAL; @@ -106,20 +101,6 @@ static int omap2_iommu_enable(struct omap_iommu *obj) if (!IS_ALIGNED(pa, SZ_16K)) return -EINVAL; - iommu_write_reg(obj, MMU_SYS_SOFTRESET, MMU_SYSCONFIG); - - timeout = jiffies + msecs_to_jiffies(20); - do { - l = iommu_read_reg(obj, MMU_SYSSTATUS); - if (l & MMU_SYS_RESETDONE) - break; - } while (!time_after(jiffies, timeout)); - - if (!(l & MMU_SYS_RESETDONE)) { - dev_err(obj->dev, "can't take mmu out of reset\n"); - return -ENODEV; - } - l = iommu_read_reg(obj, MMU_REVISION); dev_info(obj->dev, "%s: version %d.%d\n", obj->name, (l >> 4) & 0xf, l & 0xf); -- cgit From ebf7cda0f92effd8169b831fae81e9437dce1fef Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Mon, 19 Nov 2012 19:05:51 -0600 Subject: iommu/omap: Adapt to runtime pm Use runtime PM functionality interfaced with hwmod enable/idle functions, to replace direct clock operations and sysconfig handling. Due to reset sequence, pm_runtime_[get|put]_sync must be used, to avoid possible operations with the module under reset. Because of this and given that the driver uses spin_locks to protect their critical sections, we must use pm_runtime_irq_safe in order for the runtime ops to be happy, otherwise might_sleep_if checks in runtime framework will complain. The remaining pm_runtime out of iommu_enable and iommu_disable corresponds to paths that can be accessed through debugfs, some of them doesn't work if the module is not enabled first, but in future if the mmu is idled withouth freeing, these are needed to debug. Signed-off-by: Omar Ramirez Luna Tested-by: Ohad Ben-Cohen Acked-by: Tony Lindgren Signed-off-by: Joerg Roedel --- drivers/iommu/omap-iommu.c | 40 +++++++++++++++++++--------------------- drivers/iommu/omap-iommu.h | 3 --- drivers/iommu/omap-iommu2.c | 17 ----------------- 3 files changed, 19 insertions(+), 41 deletions(-) (limited to 'drivers/iommu') diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c index af9b4f31f594..18108c1405e2 100644 --- a/drivers/iommu/omap-iommu.c +++ b/drivers/iommu/omap-iommu.c @@ -16,13 +16,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include @@ -160,7 +160,7 @@ static int iommu_enable(struct omap_iommu *obj) } } - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); err = arch_iommu->enable(obj); @@ -177,7 +177,7 @@ static void iommu_disable(struct omap_iommu *obj) arch_iommu->disable(obj); - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); if (pdata->assert_reset) pdata->assert_reset(pdev, pdata->reset_name); @@ -303,7 +303,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) if (!obj || !obj->nr_tlb_entries || !e) return -EINVAL; - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); iotlb_lock_get(obj, &l); if (l.base == obj->nr_tlb_entries) { @@ -333,7 +333,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) cr = iotlb_alloc_cr(obj, e); if (IS_ERR(cr)) { - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); return PTR_ERR(cr); } @@ -347,7 +347,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) l.vict = l.base; iotlb_lock_set(obj, &l); out: - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); return err; } @@ -377,7 +377,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da) int i; struct cr_regs cr; - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) { u32 start; @@ -396,7 +396,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da) iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); } } - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); if (i == obj->nr_tlb_entries) dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da); @@ -410,7 +410,7 @@ static void flush_iotlb_all(struct omap_iommu *obj) { struct iotlb_lock l; - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); l.base = 0; l.vict = 0; @@ -418,7 +418,7 @@ static void flush_iotlb_all(struct omap_iommu *obj) iommu_write_reg(obj, 1, MMU_GFLUSH); - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); } #if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE) @@ -428,11 +428,11 @@ ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes) if (!obj || !buf) return -EINVAL; - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); bytes = arch_iommu->dump_ctx(obj, buf, bytes); - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); return bytes; } @@ -446,7 +446,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num) struct cr_regs tmp; struct cr_regs *p = crs; - clk_enable(obj->clk); + pm_runtime_get_sync(obj->dev); iotlb_lock_get(obj, &saved); for_each_iotlb_cr(obj, num, i, tmp) { @@ -456,7 +456,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num) } iotlb_lock_set(obj, &saved); - clk_disable(obj->clk); + pm_runtime_put_sync(obj->dev); return p - crs; } @@ -946,10 +946,6 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) if (!obj) return -ENOMEM; - obj->clk = clk_get(&pdev->dev, pdata->clk_name); - if (IS_ERR(obj->clk)) - goto err_clk; - obj->nr_tlb_entries = pdata->nr_tlb_entries; obj->name = pdata->name; obj->dev = &pdev->dev; @@ -992,6 +988,9 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) goto err_irq; platform_set_drvdata(pdev, obj); + pm_runtime_irq_safe(obj->dev); + pm_runtime_enable(obj->dev); + dev_info(&pdev->dev, "%s registered\n", obj->name); return 0; @@ -1000,8 +999,6 @@ err_irq: err_ioremap: release_mem_region(res->start, resource_size(res)); err_mem: - clk_put(obj->clk); -err_clk: kfree(obj); return err; } @@ -1022,7 +1019,8 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); iounmap(obj->regbase); - clk_put(obj->clk); + pm_runtime_disable(obj->dev); + dev_info(&pdev->dev, "%s removed\n", obj->name); kfree(obj); return 0; diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h index 2b5f3c04d167..120084206602 100644 --- a/drivers/iommu/omap-iommu.h +++ b/drivers/iommu/omap-iommu.h @@ -29,7 +29,6 @@ struct iotlb_entry { struct omap_iommu { const char *name; struct module *owner; - struct clk *clk; void __iomem *regbase; struct device *dev; void *isr_priv; @@ -116,8 +115,6 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev) * MMU Register offsets */ #define MMU_REVISION 0x00 -#define MMU_SYSCONFIG 0x10 -#define MMU_SYSSTATUS 0x14 #define MMU_IRQSTATUS 0x18 #define MMU_IRQENABLE 0x1c #define MMU_WALKING_ST 0x40 diff --git a/drivers/iommu/omap-iommu2.c b/drivers/iommu/omap-iommu2.c index 4a3a1c7a38c1..d745094a69dd 100644 --- a/drivers/iommu/omap-iommu2.c +++ b/drivers/iommu/omap-iommu2.c @@ -28,15 +28,6 @@ */ #define IOMMU_ARCH_VERSION 0x00000011 -/* SYSCONF */ -#define MMU_SYS_IDLE_SHIFT 3 -#define MMU_SYS_IDLE_FORCE (0 << MMU_SYS_IDLE_SHIFT) -#define MMU_SYS_IDLE_NONE (1 << MMU_SYS_IDLE_SHIFT) -#define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT) -#define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT) - -#define MMU_SYS_AUTOIDLE 1 - /* IRQSTATUS & IRQENABLE */ #define MMU_IRQ_MULTIHITFAULT (1 << 4) #define MMU_IRQ_TABLEWALKFAULT (1 << 3) @@ -105,11 +96,6 @@ static int omap2_iommu_enable(struct omap_iommu *obj) dev_info(obj->dev, "%s: version %d.%d\n", obj->name, (l >> 4) & 0xf, l & 0xf); - l = iommu_read_reg(obj, MMU_SYSCONFIG); - l &= ~MMU_SYS_IDLE_MASK; - l |= (MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE); - iommu_write_reg(obj, l, MMU_SYSCONFIG); - iommu_write_reg(obj, pa, MMU_TTB); __iommu_set_twl(obj, true); @@ -123,7 +109,6 @@ static void omap2_iommu_disable(struct omap_iommu *obj) l &= ~MMU_CNTL_MASK; iommu_write_reg(obj, l, MMU_CNTL); - iommu_write_reg(obj, MMU_SYS_IDLE_FORCE, MMU_SYSCONFIG); dev_dbg(obj->dev, "%s is shutting down\n", obj->name); } @@ -252,8 +237,6 @@ omap2_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len) char *p = buf; pr_reg(REVISION); - pr_reg(SYSCONFIG); - pr_reg(SYSSTATUS); pr_reg(IRQSTATUS); pr_reg(IRQENABLE); pr_reg(WALKING_ST); -- cgit