1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 24e0ee78fSHiroshi Doyu /* 34e0ee78fSHiroshi Doyu * OF helpers for IOMMU 44e0ee78fSHiroshi Doyu * 54e0ee78fSHiroshi Doyu * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 64e0ee78fSHiroshi Doyu */ 74e0ee78fSHiroshi Doyu 84e0ee78fSHiroshi Doyu #include <linux/export.h> 97eba1d51SWill Deacon #include <linux/iommu.h> 104e0ee78fSHiroshi Doyu #include <linux/limits.h> 11386dce27SWill Deacon #include <linux/module.h> 12bbd8810dSKrzysztof Wilczynski #include <linux/msi.h> 134e0ee78fSHiroshi Doyu #include <linux/of.h> 14cbff5634SBrian Norris #include <linux/of_iommu.h> 15b996444cSRobin Murphy #include <linux/of_pci.h> 16386dce27SWill Deacon #include <linux/pci.h> 17a42a7a1fSRobin Murphy #include <linux/slab.h> 18fa0656b4SNipun Gupta #include <linux/fsl/mc.h> 194e0ee78fSHiroshi Doyu 20da4b0275SRobin Murphy #define NO_IOMMU 1 21da4b0275SRobin Murphy 22da4b0275SRobin Murphy static int of_iommu_xlate(struct device *dev, 23da4b0275SRobin Murphy struct of_phandle_args *iommu_spec) 242a0c5754SRobin Murphy { 252a0c5754SRobin Murphy const struct iommu_ops *ops; 262a0c5754SRobin Murphy struct fwnode_handle *fwnode = &iommu_spec->np->fwnode; 27386dce27SWill Deacon int ret; 282a0c5754SRobin Murphy 292a0c5754SRobin Murphy ops = iommu_ops_from_fwnode(fwnode); 30d7b05582SRobin Murphy if ((ops && !ops->of_xlate) || 31ac6bbf0cSRob Herring !of_device_is_available(iommu_spec->np)) 32da4b0275SRobin Murphy return NO_IOMMU; 332a0c5754SRobin Murphy 34386dce27SWill Deacon ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); 35386dce27SWill Deacon if (ret) 36386dce27SWill Deacon return ret; 37d7b05582SRobin Murphy /* 38d7b05582SRobin Murphy * The otherwise-empty fwspec handily serves to indicate the specific 39d7b05582SRobin Murphy * IOMMU device we're waiting for, which will be useful if we ever get 40d7b05582SRobin Murphy * a proper probe-ordering dependency mechanism in future. 41d7b05582SRobin Murphy */ 42d7b05582SRobin Murphy if (!ops) 43*a4f12490SSaravana Kannan return driver_deferred_probe_check_state(dev); 442a0c5754SRobin Murphy 45386dce27SWill Deacon if (!try_module_get(ops->owner)) 46386dce27SWill Deacon return -ENODEV; 47386dce27SWill Deacon 48386dce27SWill Deacon ret = ops->of_xlate(dev, iommu_spec); 49386dce27SWill Deacon module_put(ops->owner); 50386dce27SWill Deacon return ret; 512a0c5754SRobin Murphy } 522a0c5754SRobin Murphy 53a081bd4aSLorenzo Pieralisi static int of_iommu_configure_dev_id(struct device_node *master_np, 54a081bd4aSLorenzo Pieralisi struct device *dev, 55a081bd4aSLorenzo Pieralisi const u32 *id) 56a081bd4aSLorenzo Pieralisi { 57a081bd4aSLorenzo Pieralisi struct of_phandle_args iommu_spec = { .args_count = 1 }; 58a081bd4aSLorenzo Pieralisi int err; 59a081bd4aSLorenzo Pieralisi 60a081bd4aSLorenzo Pieralisi err = of_map_id(master_np, *id, "iommu-map", 61a081bd4aSLorenzo Pieralisi "iommu-map-mask", &iommu_spec.np, 62a081bd4aSLorenzo Pieralisi iommu_spec.args); 63a081bd4aSLorenzo Pieralisi if (err) 64a081bd4aSLorenzo Pieralisi return err == -ENODEV ? NO_IOMMU : err; 65a081bd4aSLorenzo Pieralisi 66a081bd4aSLorenzo Pieralisi err = of_iommu_xlate(dev, &iommu_spec); 67a081bd4aSLorenzo Pieralisi of_node_put(iommu_spec.np); 68a081bd4aSLorenzo Pieralisi return err; 69a081bd4aSLorenzo Pieralisi } 70a081bd4aSLorenzo Pieralisi 71a081bd4aSLorenzo Pieralisi static int of_iommu_configure_dev(struct device_node *master_np, 72a081bd4aSLorenzo Pieralisi struct device *dev) 73a081bd4aSLorenzo Pieralisi { 74a081bd4aSLorenzo Pieralisi struct of_phandle_args iommu_spec; 75a081bd4aSLorenzo Pieralisi int err = NO_IOMMU, idx = 0; 76a081bd4aSLorenzo Pieralisi 77a081bd4aSLorenzo Pieralisi while (!of_parse_phandle_with_args(master_np, "iommus", 78a081bd4aSLorenzo Pieralisi "#iommu-cells", 79a081bd4aSLorenzo Pieralisi idx, &iommu_spec)) { 80a081bd4aSLorenzo Pieralisi err = of_iommu_xlate(dev, &iommu_spec); 81a081bd4aSLorenzo Pieralisi of_node_put(iommu_spec.np); 82a081bd4aSLorenzo Pieralisi idx++; 83a081bd4aSLorenzo Pieralisi if (err) 84a081bd4aSLorenzo Pieralisi break; 85a081bd4aSLorenzo Pieralisi } 86a081bd4aSLorenzo Pieralisi 87a081bd4aSLorenzo Pieralisi return err; 88a081bd4aSLorenzo Pieralisi } 89a081bd4aSLorenzo Pieralisi 90d87beb74SRobin Murphy struct of_pci_iommu_alias_info { 91d87beb74SRobin Murphy struct device *dev; 92d87beb74SRobin Murphy struct device_node *np; 93d87beb74SRobin Murphy }; 94b996444cSRobin Murphy 95d87beb74SRobin Murphy static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 96b996444cSRobin Murphy { 97d87beb74SRobin Murphy struct of_pci_iommu_alias_info *info = data; 98a081bd4aSLorenzo Pieralisi u32 input_id = alias; 99b996444cSRobin Murphy 100a081bd4aSLorenzo Pieralisi return of_iommu_configure_dev_id(info->np, info->dev, &input_id); 1012a0c5754SRobin Murphy } 1027eba1d51SWill Deacon 103a081bd4aSLorenzo Pieralisi static int of_iommu_configure_device(struct device_node *master_np, 104a081bd4aSLorenzo Pieralisi struct device *dev, const u32 *id) 105fa0656b4SNipun Gupta { 106a081bd4aSLorenzo Pieralisi return (id) ? of_iommu_configure_dev_id(master_np, dev, id) : 107a081bd4aSLorenzo Pieralisi of_iommu_configure_dev(master_np, dev); 108fa0656b4SNipun Gupta } 109fa0656b4SNipun Gupta 1102a0c5754SRobin Murphy const struct iommu_ops *of_iommu_configure(struct device *dev, 111a081bd4aSLorenzo Pieralisi struct device_node *master_np, 112a081bd4aSLorenzo Pieralisi const u32 *id) 1132a0c5754SRobin Murphy { 114d87beb74SRobin Murphy const struct iommu_ops *ops = NULL; 1155c7e6bd7SJoerg Roedel struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 116da4b0275SRobin Murphy int err = NO_IOMMU; 1172a0c5754SRobin Murphy 1182a0c5754SRobin Murphy if (!master_np) 1197eba1d51SWill Deacon return NULL; 1202a0c5754SRobin Murphy 121d7b05582SRobin Murphy if (fwspec) { 122d7b05582SRobin Murphy if (fwspec->ops) 123d7b05582SRobin Murphy return fwspec->ops; 124d7b05582SRobin Murphy 125d7b05582SRobin Murphy /* In the deferred case, start again from scratch */ 126d7b05582SRobin Murphy iommu_fwspec_free(dev); 127d7b05582SRobin Murphy } 128d7b05582SRobin Murphy 129d87beb74SRobin Murphy /* 130d87beb74SRobin Murphy * We don't currently walk up the tree looking for a parent IOMMU. 131d87beb74SRobin Murphy * See the `Notes:' section of 132d87beb74SRobin Murphy * Documentation/devicetree/bindings/iommu/iommu.txt 133d87beb74SRobin Murphy */ 134d87beb74SRobin Murphy if (dev_is_pci(dev)) { 135d87beb74SRobin Murphy struct of_pci_iommu_alias_info info = { 136d87beb74SRobin Murphy .dev = dev, 137d87beb74SRobin Murphy .np = master_np, 138d87beb74SRobin Murphy }; 139d87beb74SRobin Murphy 1406bf6c247SWill Deacon pci_request_acs(); 141d87beb74SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 142d87beb74SRobin Murphy of_pci_iommu_init, &info); 143d87beb74SRobin Murphy } else { 144a081bd4aSLorenzo Pieralisi err = of_iommu_configure_device(master_np, dev, id); 14589535821SJean-Philippe Brucker } 1465c7e6bd7SJoerg Roedel 147da4b0275SRobin Murphy /* 148da4b0275SRobin Murphy * Two success conditions can be represented by non-negative err here: 149da4b0275SRobin Murphy * >0 : there is no IOMMU, or one was unavailable for non-fatal reasons 150da4b0275SRobin Murphy * 0 : we found an IOMMU, and dev->fwspec is initialised appropriately 151da4b0275SRobin Murphy * <0 : any actual error 152da4b0275SRobin Murphy */ 1535c7e6bd7SJoerg Roedel if (!err) { 1545c7e6bd7SJoerg Roedel /* The fwspec pointer changed, read it again */ 1555c7e6bd7SJoerg Roedel fwspec = dev_iommu_fwspec_get(dev); 1565c7e6bd7SJoerg Roedel ops = fwspec->ops; 1575c7e6bd7SJoerg Roedel } 158d7b05582SRobin Murphy /* 159d7b05582SRobin Murphy * If we have reason to believe the IOMMU driver missed the initial 160641fb0efSJoerg Roedel * probe for dev, replay it to get things in order. 161d7b05582SRobin Murphy */ 162e8e683aeSRobin Murphy if (!err && dev->bus && !device_iommu_mapped(dev)) 163641fb0efSJoerg Roedel err = iommu_probe_device(dev); 1642a0c5754SRobin Murphy 165a37b19a3SSricharan R /* Ignore all other errors apart from EPROBE_DEFER */ 166da4b0275SRobin Murphy if (err == -EPROBE_DEFER) { 167da4b0275SRobin Murphy ops = ERR_PTR(err); 168da4b0275SRobin Murphy } else if (err < 0) { 169da4b0275SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 170a37b19a3SSricharan R ops = NULL; 171a37b19a3SSricharan R } 172a37b19a3SSricharan R 1737b07cbefSLaurent Pinchart return ops; 1747eba1d51SWill Deacon } 175