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> 124e0ee78fSHiroshi Doyu #include <linux/of.h> 13a5bf3cfcSThierry Reding #include <linux/of_address.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 203e36c15fSRobin Murphy #include "iommu-priv.h" 213e36c15fSRobin 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; 26386dce27SWill Deacon int ret; 272a0c5754SRobin Murphy 283f7c3209SRobin Murphy if (!of_device_is_available(iommu_spec->np)) 295b4ea8b0SJason Gunthorpe return -ENODEV; 302a0c5754SRobin Murphy 313f7c3209SRobin Murphy ret = iommu_fwspec_init(dev, of_fwnode_handle(iommu_spec->np)); 323f7c3209SRobin Murphy if (ret == -EPROBE_DEFER) 333f7c3209SRobin Murphy return driver_deferred_probe_check_state(dev); 34386dce27SWill Deacon if (ret) 35386dce27SWill Deacon return ret; 362a0c5754SRobin Murphy 373e36c15fSRobin Murphy ops = iommu_ops_from_fwnode(&iommu_spec->np->fwnode); 383f7c3209SRobin Murphy if (!ops->of_xlate || !try_module_get(ops->owner)) 39386dce27SWill Deacon return -ENODEV; 40386dce27SWill Deacon 41386dce27SWill Deacon ret = ops->of_xlate(dev, iommu_spec); 42386dce27SWill Deacon module_put(ops->owner); 43386dce27SWill Deacon return ret; 442a0c5754SRobin Murphy } 452a0c5754SRobin Murphy 46a081bd4aSLorenzo Pieralisi static int of_iommu_configure_dev_id(struct device_node *master_np, 47a081bd4aSLorenzo Pieralisi struct device *dev, 48a081bd4aSLorenzo Pieralisi const u32 *id) 49a081bd4aSLorenzo Pieralisi { 50a081bd4aSLorenzo Pieralisi struct of_phandle_args iommu_spec = { .args_count = 1 }; 51a081bd4aSLorenzo Pieralisi int err; 52a081bd4aSLorenzo Pieralisi 53a081bd4aSLorenzo Pieralisi err = of_map_id(master_np, *id, "iommu-map", 54a081bd4aSLorenzo Pieralisi "iommu-map-mask", &iommu_spec.np, 55a081bd4aSLorenzo Pieralisi iommu_spec.args); 56a081bd4aSLorenzo Pieralisi if (err) 575b4ea8b0SJason Gunthorpe return err; 58a081bd4aSLorenzo Pieralisi 59a081bd4aSLorenzo Pieralisi err = of_iommu_xlate(dev, &iommu_spec); 60a081bd4aSLorenzo Pieralisi of_node_put(iommu_spec.np); 61a081bd4aSLorenzo Pieralisi return err; 62a081bd4aSLorenzo Pieralisi } 63a081bd4aSLorenzo Pieralisi 64a081bd4aSLorenzo Pieralisi static int of_iommu_configure_dev(struct device_node *master_np, 65a081bd4aSLorenzo Pieralisi struct device *dev) 66a081bd4aSLorenzo Pieralisi { 67a081bd4aSLorenzo Pieralisi struct of_phandle_args iommu_spec; 685b4ea8b0SJason Gunthorpe int err = -ENODEV, idx = 0; 69a081bd4aSLorenzo Pieralisi 70a081bd4aSLorenzo Pieralisi while (!of_parse_phandle_with_args(master_np, "iommus", 71a081bd4aSLorenzo Pieralisi "#iommu-cells", 72a081bd4aSLorenzo Pieralisi idx, &iommu_spec)) { 73a081bd4aSLorenzo Pieralisi err = of_iommu_xlate(dev, &iommu_spec); 74a081bd4aSLorenzo Pieralisi of_node_put(iommu_spec.np); 75a081bd4aSLorenzo Pieralisi idx++; 76a081bd4aSLorenzo Pieralisi if (err) 77a081bd4aSLorenzo Pieralisi break; 78a081bd4aSLorenzo Pieralisi } 79a081bd4aSLorenzo Pieralisi 80a081bd4aSLorenzo Pieralisi return err; 81a081bd4aSLorenzo Pieralisi } 82a081bd4aSLorenzo Pieralisi 83d87beb74SRobin Murphy struct of_pci_iommu_alias_info { 84d87beb74SRobin Murphy struct device *dev; 85d87beb74SRobin Murphy struct device_node *np; 86d87beb74SRobin Murphy }; 87b996444cSRobin Murphy 88d87beb74SRobin Murphy static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) 89b996444cSRobin Murphy { 90d87beb74SRobin Murphy struct of_pci_iommu_alias_info *info = data; 91a081bd4aSLorenzo Pieralisi u32 input_id = alias; 92b996444cSRobin Murphy 93a081bd4aSLorenzo Pieralisi return of_iommu_configure_dev_id(info->np, info->dev, &input_id); 942a0c5754SRobin Murphy } 957eba1d51SWill Deacon 96a081bd4aSLorenzo Pieralisi static int of_iommu_configure_device(struct device_node *master_np, 97a081bd4aSLorenzo Pieralisi struct device *dev, const u32 *id) 98fa0656b4SNipun Gupta { 99a081bd4aSLorenzo Pieralisi return (id) ? of_iommu_configure_dev_id(master_np, dev, id) : 100a081bd4aSLorenzo Pieralisi of_iommu_configure_dev(master_np, dev); 101fa0656b4SNipun Gupta } 102fa0656b4SNipun Gupta 103*86e02a88SJean-Philippe Brucker static void of_pci_check_device_ats(struct device *dev, struct device_node *np) 104*86e02a88SJean-Philippe Brucker { 105*86e02a88SJean-Philippe Brucker struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 106*86e02a88SJean-Philippe Brucker 107*86e02a88SJean-Philippe Brucker if (fwspec && of_property_read_bool(np, "ats-supported")) 108*86e02a88SJean-Philippe Brucker fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS; 109*86e02a88SJean-Philippe Brucker } 110*86e02a88SJean-Philippe Brucker 1116ff6e184SJason Gunthorpe /* 1126ff6e184SJason Gunthorpe * Returns: 1136ff6e184SJason Gunthorpe * 0 on success, an iommu was configured 1146ff6e184SJason Gunthorpe * -ENODEV if the device does not have any IOMMU 1156ff6e184SJason Gunthorpe * -EPROBEDEFER if probing should be tried again 1166ff6e184SJason Gunthorpe * -errno fatal errors 1176ff6e184SJason Gunthorpe */ 1186ff6e184SJason Gunthorpe int of_iommu_configure(struct device *dev, struct device_node *master_np, 119a081bd4aSLorenzo Pieralisi const u32 *id) 1202a0c5754SRobin Murphy { 1215b4ea8b0SJason Gunthorpe int err; 1222a0c5754SRobin Murphy 1232a0c5754SRobin Murphy if (!master_np) 1246ff6e184SJason Gunthorpe return -ENODEV; 1252a0c5754SRobin Murphy 126a2e7e59aSRobin Murphy /* Serialise to make dev->iommu stable under our potential fwspec */ 127a2e7e59aSRobin Murphy mutex_lock(&iommu_probe_device_lock); 1285f937bc4SRobin Murphy if (dev_iommu_fwspec_get(dev)) { 129a2e7e59aSRobin Murphy mutex_unlock(&iommu_probe_device_lock); 1306ff6e184SJason Gunthorpe return 0; 131a2e7e59aSRobin Murphy } 132d7b05582SRobin Murphy 133d87beb74SRobin Murphy /* 134d87beb74SRobin Murphy * We don't currently walk up the tree looking for a parent IOMMU. 135d87beb74SRobin Murphy * See the `Notes:' section of 136d87beb74SRobin Murphy * Documentation/devicetree/bindings/iommu/iommu.txt 137d87beb74SRobin Murphy */ 138d87beb74SRobin Murphy if (dev_is_pci(dev)) { 139d87beb74SRobin Murphy struct of_pci_iommu_alias_info info = { 140d87beb74SRobin Murphy .dev = dev, 141d87beb74SRobin Murphy .np = master_np, 142d87beb74SRobin Murphy }; 143d87beb74SRobin Murphy 1446bf6c247SWill Deacon pci_request_acs(); 145d87beb74SRobin Murphy err = pci_for_each_dma_alias(to_pci_dev(dev), 146d87beb74SRobin Murphy of_pci_iommu_init, &info); 147*86e02a88SJean-Philippe Brucker of_pci_check_device_ats(dev, master_np); 148d87beb74SRobin Murphy } else { 149a081bd4aSLorenzo Pieralisi err = of_iommu_configure_device(master_np, dev, id); 15089535821SJean-Philippe Brucker } 1515f937bc4SRobin Murphy 1525f937bc4SRobin Murphy if (err) 1535f937bc4SRobin Murphy iommu_fwspec_free(dev); 154a2e7e59aSRobin Murphy mutex_unlock(&iommu_probe_device_lock); 155a2e7e59aSRobin Murphy 1565f937bc4SRobin Murphy if (!err && dev->bus) 157641fb0efSJoerg Roedel err = iommu_probe_device(dev); 1582a0c5754SRobin Murphy 1595f937bc4SRobin Murphy if (err && err != -EPROBE_DEFER) 1605f937bc4SRobin Murphy dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); 1615f937bc4SRobin Murphy 1626ff6e184SJason Gunthorpe return err; 1637eba1d51SWill Deacon } 164a5bf3cfcSThierry Reding 1654762315dSRandy Dunlap static enum iommu_resv_type __maybe_unused 1664762315dSRandy Dunlap iommu_resv_region_get_type(struct device *dev, 1674762315dSRandy Dunlap struct resource *phys, 168a5bf3cfcSThierry Reding phys_addr_t start, size_t length) 169a5bf3cfcSThierry Reding { 170a5bf3cfcSThierry Reding phys_addr_t end = start + length - 1; 171a5bf3cfcSThierry Reding 172a5bf3cfcSThierry Reding /* 173a5bf3cfcSThierry Reding * IOMMU regions without an associated physical region cannot be 174a5bf3cfcSThierry Reding * mapped and are simply reservations. 175a5bf3cfcSThierry Reding */ 176a5bf3cfcSThierry Reding if (phys->start >= phys->end) 177a5bf3cfcSThierry Reding return IOMMU_RESV_RESERVED; 178a5bf3cfcSThierry Reding 179a5bf3cfcSThierry Reding /* may be IOMMU_RESV_DIRECT_RELAXABLE for certain cases */ 180a5bf3cfcSThierry Reding if (start == phys->start && end == phys->end) 181a5bf3cfcSThierry Reding return IOMMU_RESV_DIRECT; 182a5bf3cfcSThierry Reding 183c2183b3dSDaniel Mentz dev_warn(dev, "treating non-direct mapping [%pr] -> [%pap-%pap] as reservation\n", phys, 184a5bf3cfcSThierry Reding &start, &end); 185a5bf3cfcSThierry Reding return IOMMU_RESV_RESERVED; 186a5bf3cfcSThierry Reding } 187a5bf3cfcSThierry Reding 188a5bf3cfcSThierry Reding /** 189a5bf3cfcSThierry Reding * of_iommu_get_resv_regions - reserved region driver helper for device tree 190a5bf3cfcSThierry Reding * @dev: device for which to get reserved regions 191a5bf3cfcSThierry Reding * @list: reserved region list 192a5bf3cfcSThierry Reding * 193a5bf3cfcSThierry Reding * IOMMU drivers can use this to implement their .get_resv_regions() callback 194a5bf3cfcSThierry Reding * for memory regions attached to a device tree node. See the reserved-memory 195a5bf3cfcSThierry Reding * device tree bindings on how to use these: 196a5bf3cfcSThierry Reding * 197a5bf3cfcSThierry Reding * Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt 198a5bf3cfcSThierry Reding */ 199a5bf3cfcSThierry Reding void of_iommu_get_resv_regions(struct device *dev, struct list_head *list) 200a5bf3cfcSThierry Reding { 201a5bf3cfcSThierry Reding #if IS_ENABLED(CONFIG_OF_ADDRESS) 202a5bf3cfcSThierry Reding struct of_phandle_iterator it; 203a5bf3cfcSThierry Reding int err; 204a5bf3cfcSThierry Reding 205a5bf3cfcSThierry Reding of_for_each_phandle(&it, err, dev->of_node, "memory-region", NULL, 0) { 206a5bf3cfcSThierry Reding const __be32 *maps, *end; 207a5bf3cfcSThierry Reding struct resource phys; 208a5bf3cfcSThierry Reding int size; 209a5bf3cfcSThierry Reding 210a5bf3cfcSThierry Reding memset(&phys, 0, sizeof(phys)); 211a5bf3cfcSThierry Reding 212a5bf3cfcSThierry Reding /* 213a5bf3cfcSThierry Reding * The "reg" property is optional and can be omitted by reserved-memory regions 214a5bf3cfcSThierry Reding * that represent reservations in the IOVA space, which are regions that should 215a5bf3cfcSThierry Reding * not be mapped. 216a5bf3cfcSThierry Reding */ 217a5bf3cfcSThierry Reding if (of_find_property(it.node, "reg", NULL)) { 218a5bf3cfcSThierry Reding err = of_address_to_resource(it.node, 0, &phys); 219a5bf3cfcSThierry Reding if (err < 0) { 220a5bf3cfcSThierry Reding dev_err(dev, "failed to parse memory region %pOF: %d\n", 221a5bf3cfcSThierry Reding it.node, err); 222a5bf3cfcSThierry Reding continue; 223a5bf3cfcSThierry Reding } 224a5bf3cfcSThierry Reding } 225a5bf3cfcSThierry Reding 226a5bf3cfcSThierry Reding maps = of_get_property(it.node, "iommu-addresses", &size); 227a5bf3cfcSThierry Reding if (!maps) 228a5bf3cfcSThierry Reding continue; 229a5bf3cfcSThierry Reding 230a5bf3cfcSThierry Reding end = maps + size / sizeof(__be32); 231a5bf3cfcSThierry Reding 232a5bf3cfcSThierry Reding while (maps < end) { 233a5bf3cfcSThierry Reding struct device_node *np; 234a5bf3cfcSThierry Reding u32 phandle; 235a5bf3cfcSThierry Reding 236a5bf3cfcSThierry Reding phandle = be32_to_cpup(maps++); 237a5bf3cfcSThierry Reding np = of_find_node_by_phandle(phandle); 238a5bf3cfcSThierry Reding 239a5bf3cfcSThierry Reding if (np == dev->of_node) { 240a5bf3cfcSThierry Reding int prot = IOMMU_READ | IOMMU_WRITE; 241a5bf3cfcSThierry Reding struct iommu_resv_region *region; 242a5bf3cfcSThierry Reding enum iommu_resv_type type; 243a5bf3cfcSThierry Reding phys_addr_t iova; 244a5bf3cfcSThierry Reding size_t length; 245a5bf3cfcSThierry Reding 246f1aad9dfSLaurentiu Tudor if (of_dma_is_coherent(dev->of_node)) 247f1aad9dfSLaurentiu Tudor prot |= IOMMU_CACHE; 248f1aad9dfSLaurentiu Tudor 249a5bf3cfcSThierry Reding maps = of_translate_dma_region(np, maps, &iova, &length); 250bb57f670SAshish Mhetre if (length == 0) { 251bb57f670SAshish Mhetre dev_warn(dev, "Cannot reserve IOVA region of 0 size\n"); 252bb57f670SAshish Mhetre continue; 253bb57f670SAshish Mhetre } 254a5bf3cfcSThierry Reding type = iommu_resv_region_get_type(dev, &phys, iova, length); 255a5bf3cfcSThierry Reding 256a5bf3cfcSThierry Reding region = iommu_alloc_resv_region(iova, length, prot, type, 257a5bf3cfcSThierry Reding GFP_KERNEL); 258a5bf3cfcSThierry Reding if (region) 259a5bf3cfcSThierry Reding list_add_tail(®ion->list, list); 260a5bf3cfcSThierry Reding } 261a5bf3cfcSThierry Reding } 262a5bf3cfcSThierry Reding } 263a5bf3cfcSThierry Reding #endif 264a5bf3cfcSThierry Reding } 265a5bf3cfcSThierry Reding EXPORT_SYMBOL(of_iommu_get_resv_regions); 266