]>
Commit | Line | Data |
---|---|---|
a61127c2 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
4e0ee78f HD |
2 | /* |
3 | * OF helpers for IOMMU | |
4 | * | |
5 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
4e0ee78f HD |
6 | */ |
7 | ||
8 | #include <linux/export.h> | |
7eba1d51 | 9 | #include <linux/iommu.h> |
4e0ee78f HD |
10 | #include <linux/limits.h> |
11 | #include <linux/of.h> | |
cbff5634 | 12 | #include <linux/of_iommu.h> |
b996444c | 13 | #include <linux/of_pci.h> |
a42a7a1f | 14 | #include <linux/slab.h> |
fa0656b4 | 15 | #include <linux/fsl/mc.h> |
4e0ee78f | 16 | |
da4b0275 RM |
17 | #define NO_IOMMU 1 |
18 | ||
4e0ee78f HD |
19 | /** |
20 | * of_get_dma_window - Parse *dma-window property and returns 0 if found. | |
21 | * | |
22 | * @dn: device node | |
23 | * @prefix: prefix for property name if any | |
24 | * @index: index to start to parse | |
25 | * @busno: Returns busno if supported. Otherwise pass NULL | |
26 | * @addr: Returns address that DMA starts | |
27 | * @size: Returns the range that DMA can handle | |
28 | * | |
29 | * This supports different formats flexibly. "prefix" can be | |
30 | * configured if any. "busno" and "index" are optionally | |
31 | * specified. Set 0(or NULL) if not used. | |
32 | */ | |
33 | int of_get_dma_window(struct device_node *dn, const char *prefix, int index, | |
34 | unsigned long *busno, dma_addr_t *addr, size_t *size) | |
35 | { | |
36 | const __be32 *dma_window, *end; | |
37 | int bytes, cur_index = 0; | |
38 | char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX]; | |
39 | ||
40 | if (!dn || !addr || !size) | |
41 | return -EINVAL; | |
42 | ||
43 | if (!prefix) | |
44 | prefix = ""; | |
45 | ||
46 | snprintf(propname, sizeof(propname), "%sdma-window", prefix); | |
47 | snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix); | |
48 | snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix); | |
49 | ||
50 | dma_window = of_get_property(dn, propname, &bytes); | |
51 | if (!dma_window) | |
52 | return -ENODEV; | |
53 | end = dma_window + bytes / sizeof(*dma_window); | |
54 | ||
55 | while (dma_window < end) { | |
56 | u32 cells; | |
57 | const void *prop; | |
58 | ||
59 | /* busno is one cell if supported */ | |
60 | if (busno) | |
61 | *busno = be32_to_cpup(dma_window++); | |
62 | ||
63 | prop = of_get_property(dn, addrname, NULL); | |
64 | if (!prop) | |
65 | prop = of_get_property(dn, "#address-cells", NULL); | |
66 | ||
67 | cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn); | |
68 | if (!cells) | |
69 | return -EINVAL; | |
70 | *addr = of_read_number(dma_window, cells); | |
71 | dma_window += cells; | |
72 | ||
73 | prop = of_get_property(dn, sizename, NULL); | |
74 | cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn); | |
75 | if (!cells) | |
76 | return -EINVAL; | |
77 | *size = of_read_number(dma_window, cells); | |
78 | dma_window += cells; | |
79 | ||
80 | if (cur_index++ == index) | |
81 | break; | |
82 | } | |
83 | return 0; | |
84 | } | |
85 | EXPORT_SYMBOL_GPL(of_get_dma_window); | |
1cd076bf | 86 | |
da4b0275 RM |
87 | static int of_iommu_xlate(struct device *dev, |
88 | struct of_phandle_args *iommu_spec) | |
2a0c5754 RM |
89 | { |
90 | const struct iommu_ops *ops; | |
91 | struct fwnode_handle *fwnode = &iommu_spec->np->fwnode; | |
92 | int err; | |
93 | ||
94 | ops = iommu_ops_from_fwnode(fwnode); | |
d7b05582 | 95 | if ((ops && !ops->of_xlate) || |
ac6bbf0c | 96 | !of_device_is_available(iommu_spec->np)) |
da4b0275 | 97 | return NO_IOMMU; |
2a0c5754 RM |
98 | |
99 | err = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); | |
100 | if (err) | |
da4b0275 | 101 | return err; |
d7b05582 RM |
102 | /* |
103 | * The otherwise-empty fwspec handily serves to indicate the specific | |
104 | * IOMMU device we're waiting for, which will be useful if we ever get | |
105 | * a proper probe-ordering dependency mechanism in future. | |
106 | */ | |
107 | if (!ops) | |
78f307be | 108 | return driver_deferred_probe_check_state(dev); |
2a0c5754 | 109 | |
da4b0275 | 110 | return ops->of_xlate(dev, iommu_spec); |
2a0c5754 RM |
111 | } |
112 | ||
d87beb74 RM |
113 | struct of_pci_iommu_alias_info { |
114 | struct device *dev; | |
115 | struct device_node *np; | |
116 | }; | |
b996444c | 117 | |
d87beb74 | 118 | static int of_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data) |
b996444c | 119 | { |
d87beb74 | 120 | struct of_pci_iommu_alias_info *info = data; |
d87beb74 | 121 | struct of_phandle_args iommu_spec = { .args_count = 1 }; |
2a0c5754 | 122 | int err; |
b996444c | 123 | |
2a6db719 NG |
124 | err = of_map_rid(info->np, alias, "iommu-map", "iommu-map-mask", |
125 | &iommu_spec.np, iommu_spec.args); | |
2a0c5754 | 126 | if (err) |
da4b0275 | 127 | return err == -ENODEV ? NO_IOMMU : err; |
b996444c | 128 | |
da4b0275 | 129 | err = of_iommu_xlate(info->dev, &iommu_spec); |
b996444c | 130 | of_node_put(iommu_spec.np); |
c0d05cde | 131 | return err; |
2a0c5754 RM |
132 | } |
133 | ||
fa0656b4 NG |
134 | static int of_fsl_mc_iommu_init(struct fsl_mc_device *mc_dev, |
135 | struct device_node *master_np) | |
136 | { | |
137 | struct of_phandle_args iommu_spec = { .args_count = 1 }; | |
138 | int err; | |
139 | ||
140 | err = of_map_rid(master_np, mc_dev->icid, "iommu-map", | |
141 | "iommu-map-mask", &iommu_spec.np, | |
142 | iommu_spec.args); | |
143 | if (err) | |
144 | return err == -ENODEV ? NO_IOMMU : err; | |
145 | ||
146 | err = of_iommu_xlate(&mc_dev->dev, &iommu_spec); | |
147 | of_node_put(iommu_spec.np); | |
148 | return err; | |
149 | } | |
150 | ||
2a0c5754 RM |
151 | const struct iommu_ops *of_iommu_configure(struct device *dev, |
152 | struct device_node *master_np) | |
153 | { | |
d87beb74 | 154 | const struct iommu_ops *ops = NULL; |
5c7e6bd7 | 155 | struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); |
da4b0275 | 156 | int err = NO_IOMMU; |
2a0c5754 RM |
157 | |
158 | if (!master_np) | |
159 | return NULL; | |
160 | ||
d7b05582 RM |
161 | if (fwspec) { |
162 | if (fwspec->ops) | |
163 | return fwspec->ops; | |
164 | ||
165 | /* In the deferred case, start again from scratch */ | |
166 | iommu_fwspec_free(dev); | |
167 | } | |
168 | ||
d87beb74 RM |
169 | /* |
170 | * We don't currently walk up the tree looking for a parent IOMMU. | |
171 | * See the `Notes:' section of | |
172 | * Documentation/devicetree/bindings/iommu/iommu.txt | |
173 | */ | |
174 | if (dev_is_pci(dev)) { | |
175 | struct of_pci_iommu_alias_info info = { | |
176 | .dev = dev, | |
177 | .np = master_np, | |
178 | }; | |
179 | ||
180 | err = pci_for_each_dma_alias(to_pci_dev(dev), | |
181 | of_pci_iommu_init, &info); | |
fa0656b4 NG |
182 | } else if (dev_is_fsl_mc(dev)) { |
183 | err = of_fsl_mc_iommu_init(to_fsl_mc_device(dev), master_np); | |
d87beb74 RM |
184 | } else { |
185 | struct of_phandle_args iommu_spec; | |
186 | int idx = 0; | |
187 | ||
188 | while (!of_parse_phandle_with_args(master_np, "iommus", | |
189 | "#iommu-cells", | |
190 | idx, &iommu_spec)) { | |
da4b0275 | 191 | err = of_iommu_xlate(dev, &iommu_spec); |
d87beb74 RM |
192 | of_node_put(iommu_spec.np); |
193 | idx++; | |
da4b0275 | 194 | if (err) |
d87beb74 RM |
195 | break; |
196 | } | |
197 | } | |
da4b0275 | 198 | |
5c7e6bd7 | 199 | |
da4b0275 RM |
200 | /* |
201 | * Two success conditions can be represented by non-negative err here: | |
202 | * >0 : there is no IOMMU, or one was unavailable for non-fatal reasons | |
203 | * 0 : we found an IOMMU, and dev->fwspec is initialised appropriately | |
204 | * <0 : any actual error | |
205 | */ | |
5c7e6bd7 JR |
206 | if (!err) { |
207 | /* The fwspec pointer changed, read it again */ | |
208 | fwspec = dev_iommu_fwspec_get(dev); | |
209 | ops = fwspec->ops; | |
210 | } | |
d7b05582 RM |
211 | /* |
212 | * If we have reason to believe the IOMMU driver missed the initial | |
641fb0ef | 213 | * probe for dev, replay it to get things in order. |
d7b05582 | 214 | */ |
e8e683ae | 215 | if (!err && dev->bus && !device_iommu_mapped(dev)) |
641fb0ef | 216 | err = iommu_probe_device(dev); |
7eba1d51 | 217 | |
a37b19a3 | 218 | /* Ignore all other errors apart from EPROBE_DEFER */ |
da4b0275 RM |
219 | if (err == -EPROBE_DEFER) { |
220 | ops = ERR_PTR(err); | |
221 | } else if (err < 0) { | |
222 | dev_dbg(dev, "Adding to IOMMU failed: %d\n", err); | |
a37b19a3 S |
223 | ops = NULL; |
224 | } | |
225 | ||
7b07cbef | 226 | return ops; |
7eba1d51 | 227 | } |