Blame view

drivers/iommu/of_iommu.c 6.14 KB
4e0ee78f2   Hiroshi Doyu   iommu: Add DMA wi...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * OF helpers for IOMMU
   *
   * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * This program is distributed in the hope it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
   * more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
   */
  
  #include <linux/export.h>
7eba1d514   Will Deacon   iommu: provide he...
21
  #include <linux/iommu.h>
4e0ee78f2   Hiroshi Doyu   iommu: Add DMA wi...
22
23
  #include <linux/limits.h>
  #include <linux/of.h>
cbff5634d   Brian Norris   iommu: add missin...
24
  #include <linux/of_iommu.h>
b996444cf   Robin Murphy   iommu/of: Handle ...
25
  #include <linux/of_pci.h>
a42a7a1fb   Robin Murphy   iommu: store DT-p...
26
  #include <linux/slab.h>
4e0ee78f2   Hiroshi Doyu   iommu: Add DMA wi...
27

1cd076bf6   Will Deacon   iommu: provide ea...
28
29
  static const struct of_device_id __iommu_of_table_sentinel
  	__used __section(__iommu_of_table_end);
4e0ee78f2   Hiroshi Doyu   iommu: Add DMA wi...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
  /**
   * of_get_dma_window - Parse *dma-window property and returns 0 if found.
   *
   * @dn: device node
   * @prefix: prefix for property name if any
   * @index: index to start to parse
   * @busno: Returns busno if supported. Otherwise pass NULL
   * @addr: Returns address that DMA starts
   * @size: Returns the range that DMA can handle
   *
   * This supports different formats flexibly. "prefix" can be
   * configured if any. "busno" and "index" are optionally
   * specified. Set 0(or NULL) if not used.
   */
  int of_get_dma_window(struct device_node *dn, const char *prefix, int index,
  		      unsigned long *busno, dma_addr_t *addr, size_t *size)
  {
  	const __be32 *dma_window, *end;
  	int bytes, cur_index = 0;
  	char propname[NAME_MAX], addrname[NAME_MAX], sizename[NAME_MAX];
  
  	if (!dn || !addr || !size)
  		return -EINVAL;
  
  	if (!prefix)
  		prefix = "";
  
  	snprintf(propname, sizeof(propname), "%sdma-window", prefix);
  	snprintf(addrname, sizeof(addrname), "%s#dma-address-cells", prefix);
  	snprintf(sizename, sizeof(sizename), "%s#dma-size-cells", prefix);
  
  	dma_window = of_get_property(dn, propname, &bytes);
  	if (!dma_window)
  		return -ENODEV;
  	end = dma_window + bytes / sizeof(*dma_window);
  
  	while (dma_window < end) {
  		u32 cells;
  		const void *prop;
  
  		/* busno is one cell if supported */
  		if (busno)
  			*busno = be32_to_cpup(dma_window++);
  
  		prop = of_get_property(dn, addrname, NULL);
  		if (!prop)
  			prop = of_get_property(dn, "#address-cells", NULL);
  
  		cells = prop ? be32_to_cpup(prop) : of_n_addr_cells(dn);
  		if (!cells)
  			return -EINVAL;
  		*addr = of_read_number(dma_window, cells);
  		dma_window += cells;
  
  		prop = of_get_property(dn, sizename, NULL);
  		cells = prop ? be32_to_cpup(prop) : of_n_size_cells(dn);
  		if (!cells)
  			return -EINVAL;
  		*size = of_read_number(dma_window, cells);
  		dma_window += cells;
  
  		if (cur_index++ == index)
  			break;
  	}
  	return 0;
  }
  EXPORT_SYMBOL_GPL(of_get_dma_window);
1cd076bf6   Will Deacon   iommu: provide ea...
97

a42a7a1fb   Robin Murphy   iommu: store DT-p...
98
99
100
  struct of_iommu_node {
  	struct list_head list;
  	struct device_node *np;
53c92d793   Robin Murphy   iommu: of: enforc...
101
  	const struct iommu_ops *ops;
a42a7a1fb   Robin Murphy   iommu: store DT-p...
102
103
104
  };
  static LIST_HEAD(of_iommu_list);
  static DEFINE_SPINLOCK(of_iommu_lock);
53c92d793   Robin Murphy   iommu: of: enforc...
105
  void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops)
a42a7a1fb   Robin Murphy   iommu: store DT-p...
106
107
108
109
110
  {
  	struct of_iommu_node *iommu = kzalloc(sizeof(*iommu), GFP_KERNEL);
  
  	if (WARN_ON(!iommu))
  		return;
45bb966d3   Anup Patel   of: iommu: Increm...
111
  	of_node_get(np);
a42a7a1fb   Robin Murphy   iommu: store DT-p...
112
113
114
115
116
117
118
  	INIT_LIST_HEAD(&iommu->list);
  	iommu->np = np;
  	iommu->ops = ops;
  	spin_lock(&of_iommu_lock);
  	list_add_tail(&iommu->list, &of_iommu_list);
  	spin_unlock(&of_iommu_lock);
  }
53c92d793   Robin Murphy   iommu: of: enforc...
119
  const struct iommu_ops *of_iommu_get_ops(struct device_node *np)
a42a7a1fb   Robin Murphy   iommu: store DT-p...
120
121
  {
  	struct of_iommu_node *node;
53c92d793   Robin Murphy   iommu: of: enforc...
122
  	const struct iommu_ops *ops = NULL;
a42a7a1fb   Robin Murphy   iommu: store DT-p...
123
124
125
126
127
128
129
130
131
132
  
  	spin_lock(&of_iommu_lock);
  	list_for_each_entry(node, &of_iommu_list, list)
  		if (node->np == np) {
  			ops = node->ops;
  			break;
  		}
  	spin_unlock(&of_iommu_lock);
  	return ops;
  }
b996444cf   Robin Murphy   iommu/of: Handle ...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
  static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
  {
  	struct of_phandle_args *iommu_spec = data;
  
  	iommu_spec->args[0] = alias;
  	return iommu_spec->np == pdev->bus->dev.of_node;
  }
  
  static const struct iommu_ops
  *of_pci_iommu_configure(struct pci_dev *pdev, struct device_node *bridge_np)
  {
  	const struct iommu_ops *ops;
  	struct of_phandle_args iommu_spec;
  
  	/*
  	 * Start by tracing the RID alias down the PCI topology as
  	 * far as the host bridge whose OF node we have...
  	 * (we're not even attempting to handle multi-alias devices yet)
  	 */
  	iommu_spec.args_count = 1;
  	iommu_spec.np = bridge_np;
  	pci_for_each_dma_alias(pdev, __get_pci_rid, &iommu_spec);
  	/*
  	 * ...then find out what that becomes once it escapes the PCI
  	 * bus into the system beyond, and which IOMMU it ends up at.
  	 */
  	iommu_spec.np = NULL;
  	if (of_pci_map_rid(bridge_np, iommu_spec.args[0], "iommu-map",
  			   "iommu-map-mask", &iommu_spec.np, iommu_spec.args))
  		return NULL;
  
  	ops = of_iommu_get_ops(iommu_spec.np);
57f98d2f6   Robin Murphy   iommu: Introduce ...
165
166
167
  	if (!ops || !ops->of_xlate ||
  	    iommu_fwspec_init(&pdev->dev, &iommu_spec.np->fwnode, ops) ||
  	    ops->of_xlate(&pdev->dev, &iommu_spec))
b996444cf   Robin Murphy   iommu/of: Handle ...
168
169
170
171
172
  		ops = NULL;
  
  	of_node_put(iommu_spec.np);
  	return ops;
  }
53c92d793   Robin Murphy   iommu: of: enforc...
173
174
  const struct iommu_ops *of_iommu_configure(struct device *dev,
  					   struct device_node *master_np)
7eba1d514   Will Deacon   iommu: provide he...
175
176
177
  {
  	struct of_phandle_args iommu_spec;
  	struct device_node *np;
53c92d793   Robin Murphy   iommu: of: enforc...
178
  	const struct iommu_ops *ops = NULL;
7eba1d514   Will Deacon   iommu: provide he...
179
  	int idx = 0;
7b0ce727b   Robin Murphy   of: iommu: Silenc...
180
  	if (dev_is_pci(dev))
b996444cf   Robin Murphy   iommu/of: Handle ...
181
  		return of_pci_iommu_configure(to_pci_dev(dev), master_np);
ed7486210   Murali Karicheri   of: iommu: Add pt...
182

7eba1d514   Will Deacon   iommu: provide he...
183
184
185
186
187
  	/*
  	 * We don't currently walk up the tree looking for a parent IOMMU.
  	 * See the `Notes:' section of
  	 * Documentation/devicetree/bindings/iommu/iommu.txt
  	 */
ed7486210   Murali Karicheri   of: iommu: Add pt...
188
  	while (!of_parse_phandle_with_args(master_np, "iommus",
7eba1d514   Will Deacon   iommu: provide he...
189
190
191
192
  					   "#iommu-cells", idx,
  					   &iommu_spec)) {
  		np = iommu_spec.np;
  		ops = of_iommu_get_ops(np);
57f98d2f6   Robin Murphy   iommu: Introduce ...
193
194
195
  		if (!ops || !ops->of_xlate ||
  		    iommu_fwspec_init(dev, &np->fwnode, ops) ||
  		    ops->of_xlate(dev, &iommu_spec))
7eba1d514   Will Deacon   iommu: provide he...
196
197
198
199
200
201
202
203
204
205
206
207
  			goto err_put_node;
  
  		of_node_put(np);
  		idx++;
  	}
  
  	return ops;
  
  err_put_node:
  	of_node_put(np);
  	return NULL;
  }
bb8e15d60   Kefeng Wang   of: iommu: make o...
208
  static int __init of_iommu_init(void)
1cd076bf6   Will Deacon   iommu: provide ea...
209
210
211
212
213
214
215
216
217
218
219
220
  {
  	struct device_node *np;
  	const struct of_device_id *match, *matches = &__iommu_of_table;
  
  	for_each_matching_node_and_match(np, matches, &match) {
  		const of_iommu_init_fn init_fn = match->data;
  
  		if (init_fn(np))
  			pr_err("Failed to initialise IOMMU %s
  ",
  				of_node_full_name(np));
  	}
bb8e15d60   Kefeng Wang   of: iommu: make o...
221
222
  
  	return 0;
1cd076bf6   Will Deacon   iommu: provide ea...
223
  }
bb8e15d60   Kefeng Wang   of: iommu: make o...
224
  postcore_initcall_sync(of_iommu_init);