]>
Commit | Line | Data |
---|---|---|
fc2100eb JR |
1 | /* |
2 | * Copyright (C) 2007-2008 Advanced Micro Devices, Inc. | |
3 | * Author: Joerg Roedel <joerg.roedel@amd.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the Free Software | |
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
17 | */ | |
18 | ||
905d66c1 | 19 | #include <linux/device.h> |
40998188 | 20 | #include <linux/kernel.h> |
fc2100eb JR |
21 | #include <linux/bug.h> |
22 | #include <linux/types.h> | |
60db4027 AM |
23 | #include <linux/module.h> |
24 | #include <linux/slab.h> | |
fc2100eb JR |
25 | #include <linux/errno.h> |
26 | #include <linux/iommu.h> | |
27 | ||
28 | static struct iommu_ops *iommu_ops; | |
29 | ||
30 | void register_iommu(struct iommu_ops *ops) | |
31 | { | |
32 | if (iommu_ops) | |
33 | BUG(); | |
34 | ||
35 | iommu_ops = ops; | |
36 | } | |
37 | ||
ff21776d JR |
38 | static void iommu_bus_init(struct bus_type *bus, struct iommu_ops *ops) |
39 | { | |
40 | } | |
41 | ||
42 | /** | |
43 | * bus_set_iommu - set iommu-callbacks for the bus | |
44 | * @bus: bus. | |
45 | * @ops: the callbacks provided by the iommu-driver | |
46 | * | |
47 | * This function is called by an iommu driver to set the iommu methods | |
48 | * used for a particular bus. Drivers for devices on that bus can use | |
49 | * the iommu-api after these ops are registered. | |
50 | * This special function is needed because IOMMUs are usually devices on | |
51 | * the bus itself, so the iommu drivers are not initialized when the bus | |
52 | * is set up. With this function the iommu-driver can set the iommu-ops | |
53 | * afterwards. | |
54 | */ | |
55 | int bus_set_iommu(struct bus_type *bus, struct iommu_ops *ops) | |
56 | { | |
57 | if (bus->iommu_ops != NULL) | |
58 | return -EBUSY; | |
59 | ||
60 | bus->iommu_ops = ops; | |
61 | ||
62 | /* Do IOMMU specific setup for this bus-type */ | |
63 | iommu_bus_init(bus, ops); | |
64 | ||
65 | return 0; | |
66 | } | |
67 | EXPORT_SYMBOL_GPL(bus_set_iommu); | |
68 | ||
a1b60c1c | 69 | bool iommu_present(struct bus_type *bus) |
fc2100eb | 70 | { |
a1b60c1c JR |
71 | if (bus->iommu_ops != NULL) |
72 | return true; | |
73 | else | |
74 | return iommu_ops != NULL; | |
fc2100eb | 75 | } |
a1b60c1c | 76 | EXPORT_SYMBOL_GPL(iommu_present); |
fc2100eb | 77 | |
905d66c1 | 78 | struct iommu_domain *iommu_domain_alloc(struct bus_type *bus) |
fc2100eb JR |
79 | { |
80 | struct iommu_domain *domain; | |
905d66c1 | 81 | struct iommu_ops *ops; |
fc2100eb JR |
82 | int ret; |
83 | ||
905d66c1 JR |
84 | if (bus->iommu_ops) |
85 | ops = bus->iommu_ops; | |
86 | else | |
87 | ops = iommu_ops; | |
88 | ||
89 | if (ops == NULL) | |
90 | return NULL; | |
91 | ||
fc2100eb JR |
92 | domain = kmalloc(sizeof(*domain), GFP_KERNEL); |
93 | if (!domain) | |
94 | return NULL; | |
95 | ||
905d66c1 JR |
96 | domain->ops = ops; |
97 | ||
fc2100eb JR |
98 | ret = iommu_ops->domain_init(domain); |
99 | if (ret) | |
100 | goto out_free; | |
101 | ||
102 | return domain; | |
103 | ||
104 | out_free: | |
105 | kfree(domain); | |
106 | ||
107 | return NULL; | |
108 | } | |
109 | EXPORT_SYMBOL_GPL(iommu_domain_alloc); | |
110 | ||
111 | void iommu_domain_free(struct iommu_domain *domain) | |
112 | { | |
e5aa7f00 JR |
113 | if (likely(domain->ops->domain_destroy != NULL)) |
114 | domain->ops->domain_destroy(domain); | |
115 | ||
fc2100eb JR |
116 | kfree(domain); |
117 | } | |
118 | EXPORT_SYMBOL_GPL(iommu_domain_free); | |
119 | ||
120 | int iommu_attach_device(struct iommu_domain *domain, struct device *dev) | |
121 | { | |
e5aa7f00 JR |
122 | if (unlikely(domain->ops->attach_dev == NULL)) |
123 | return -ENODEV; | |
124 | ||
125 | return domain->ops->attach_dev(domain, dev); | |
fc2100eb JR |
126 | } |
127 | EXPORT_SYMBOL_GPL(iommu_attach_device); | |
128 | ||
129 | void iommu_detach_device(struct iommu_domain *domain, struct device *dev) | |
130 | { | |
e5aa7f00 JR |
131 | if (unlikely(domain->ops->detach_dev == NULL)) |
132 | return; | |
133 | ||
134 | domain->ops->detach_dev(domain, dev); | |
fc2100eb JR |
135 | } |
136 | EXPORT_SYMBOL_GPL(iommu_detach_device); | |
137 | ||
fc2100eb JR |
138 | phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, |
139 | unsigned long iova) | |
140 | { | |
e5aa7f00 JR |
141 | if (unlikely(domain->ops->iova_to_phys == NULL)) |
142 | return 0; | |
143 | ||
144 | return domain->ops->iova_to_phys(domain, iova); | |
fc2100eb JR |
145 | } |
146 | EXPORT_SYMBOL_GPL(iommu_iova_to_phys); | |
dbb9fd86 SY |
147 | |
148 | int iommu_domain_has_cap(struct iommu_domain *domain, | |
149 | unsigned long cap) | |
150 | { | |
e5aa7f00 JR |
151 | if (unlikely(domain->ops->domain_has_cap == NULL)) |
152 | return 0; | |
153 | ||
154 | return domain->ops->domain_has_cap(domain, cap); | |
dbb9fd86 SY |
155 | } |
156 | EXPORT_SYMBOL_GPL(iommu_domain_has_cap); | |
cefc53c7 JR |
157 | |
158 | int iommu_map(struct iommu_domain *domain, unsigned long iova, | |
159 | phys_addr_t paddr, int gfp_order, int prot) | |
160 | { | |
cefc53c7 JR |
161 | size_t size; |
162 | ||
e5aa7f00 JR |
163 | if (unlikely(domain->ops->map == NULL)) |
164 | return -ENODEV; | |
165 | ||
85410340 | 166 | size = PAGE_SIZE << gfp_order; |
cefc53c7 | 167 | |
40998188 | 168 | BUG_ON(!IS_ALIGNED(iova | paddr, size)); |
cefc53c7 | 169 | |
e5aa7f00 | 170 | return domain->ops->map(domain, iova, paddr, gfp_order, prot); |
cefc53c7 JR |
171 | } |
172 | EXPORT_SYMBOL_GPL(iommu_map); | |
173 | ||
174 | int iommu_unmap(struct iommu_domain *domain, unsigned long iova, int gfp_order) | |
175 | { | |
cefc53c7 JR |
176 | size_t size; |
177 | ||
e5aa7f00 JR |
178 | if (unlikely(domain->ops->unmap == NULL)) |
179 | return -ENODEV; | |
180 | ||
85410340 | 181 | size = PAGE_SIZE << gfp_order; |
cefc53c7 | 182 | |
40998188 | 183 | BUG_ON(!IS_ALIGNED(iova, size)); |
cefc53c7 | 184 | |
e5aa7f00 | 185 | return domain->ops->unmap(domain, iova, gfp_order); |
cefc53c7 JR |
186 | } |
187 | EXPORT_SYMBOL_GPL(iommu_unmap); |