diff options
Diffstat (limited to 'drivers/pci/endpoint/pci-epf-core.c')
-rw-r--r-- | drivers/pci/endpoint/pci-epf-core.c | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c new file mode 100644 index 000000000000..6877d6a5bcc9 --- /dev/null +++ b/drivers/pci/endpoint/pci-epf-core.c | |||
@@ -0,0 +1,359 @@ | |||
1 | /** | ||
2 | * PCI Endpoint *Function* (EPF) library | ||
3 | * | ||
4 | * Copyright (C) 2017 Texas Instruments | ||
5 | * Author: Kishon Vijay Abraham I <kishon@ti.com> | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 of | ||
9 | * the License as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | #include <linux/device.h> | ||
21 | #include <linux/dma-mapping.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/module.h> | ||
24 | |||
25 | #include <linux/pci-epc.h> | ||
26 | #include <linux/pci-epf.h> | ||
27 | #include <linux/pci-ep-cfs.h> | ||
28 | |||
29 | static struct bus_type pci_epf_bus_type; | ||
30 | static struct device_type pci_epf_type; | ||
31 | |||
32 | /** | ||
33 | * pci_epf_linkup() - Notify the function driver that EPC device has | ||
34 | * established a connection with the Root Complex. | ||
35 | * @epf: the EPF device bound to the EPC device which has established | ||
36 | * the connection with the host | ||
37 | * | ||
38 | * Invoke to notify the function driver that EPC device has established | ||
39 | * a connection with the Root Complex. | ||
40 | */ | ||
41 | void pci_epf_linkup(struct pci_epf *epf) | ||
42 | { | ||
43 | if (!epf->driver) { | ||
44 | dev_WARN(&epf->dev, "epf device not bound to driver\n"); | ||
45 | return; | ||
46 | } | ||
47 | |||
48 | epf->driver->ops->linkup(epf); | ||
49 | } | ||
50 | EXPORT_SYMBOL_GPL(pci_epf_linkup); | ||
51 | |||
52 | /** | ||
53 | * pci_epf_unbind() - Notify the function driver that the binding between the | ||
54 | * EPF device and EPC device has been lost | ||
55 | * @epf: the EPF device which has lost the binding with the EPC device | ||
56 | * | ||
57 | * Invoke to notify the function driver that the binding between the EPF device | ||
58 | * and EPC device has been lost. | ||
59 | */ | ||
60 | void pci_epf_unbind(struct pci_epf *epf) | ||
61 | { | ||
62 | if (!epf->driver) { | ||
63 | dev_WARN(&epf->dev, "epf device not bound to driver\n"); | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | epf->driver->ops->unbind(epf); | ||
68 | module_put(epf->driver->owner); | ||
69 | } | ||
70 | EXPORT_SYMBOL_GPL(pci_epf_unbind); | ||
71 | |||
72 | /** | ||
73 | * pci_epf_bind() - Notify the function driver that the EPF device has been | ||
74 | * bound to a EPC device | ||
75 | * @epf: the EPF device which has been bound to the EPC device | ||
76 | * | ||
77 | * Invoke to notify the function driver that it has been bound to a EPC device | ||
78 | */ | ||
79 | int pci_epf_bind(struct pci_epf *epf) | ||
80 | { | ||
81 | if (!epf->driver) { | ||
82 | dev_WARN(&epf->dev, "epf device not bound to driver\n"); | ||
83 | return -EINVAL; | ||
84 | } | ||
85 | |||
86 | if (!try_module_get(epf->driver->owner)) | ||
87 | return -EAGAIN; | ||
88 | |||
89 | return epf->driver->ops->bind(epf); | ||
90 | } | ||
91 | EXPORT_SYMBOL_GPL(pci_epf_bind); | ||
92 | |||
93 | /** | ||
94 | * pci_epf_free_space() - free the allocated PCI EPF register space | ||
95 | * @addr: the virtual address of the PCI EPF register space | ||
96 | * @bar: the BAR number corresponding to the register space | ||
97 | * | ||
98 | * Invoke to free the allocated PCI EPF register space. | ||
99 | */ | ||
100 | void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar) | ||
101 | { | ||
102 | struct device *dev = &epf->dev; | ||
103 | |||
104 | if (!addr) | ||
105 | return; | ||
106 | |||
107 | dma_free_coherent(dev, epf->bar[bar].size, addr, | ||
108 | epf->bar[bar].phys_addr); | ||
109 | |||
110 | epf->bar[bar].phys_addr = 0; | ||
111 | epf->bar[bar].size = 0; | ||
112 | } | ||
113 | EXPORT_SYMBOL_GPL(pci_epf_free_space); | ||
114 | |||
115 | /** | ||
116 | * pci_epf_alloc_space() - allocate memory for the PCI EPF register space | ||
117 | * @size: the size of the memory that has to be allocated | ||
118 | * @bar: the BAR number corresponding to the allocated register space | ||
119 | * | ||
120 | * Invoke to allocate memory for the PCI EPF register space. | ||
121 | */ | ||
122 | void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar) | ||
123 | { | ||
124 | void *space; | ||
125 | struct device *dev = &epf->dev; | ||
126 | dma_addr_t phys_addr; | ||
127 | |||
128 | if (size < 128) | ||
129 | size = 128; | ||
130 | size = roundup_pow_of_two(size); | ||
131 | |||
132 | space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL); | ||
133 | if (!space) { | ||
134 | dev_err(dev, "failed to allocate mem space\n"); | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | epf->bar[bar].phys_addr = phys_addr; | ||
139 | epf->bar[bar].size = size; | ||
140 | |||
141 | return space; | ||
142 | } | ||
143 | EXPORT_SYMBOL_GPL(pci_epf_alloc_space); | ||
144 | |||
145 | /** | ||
146 | * pci_epf_unregister_driver() - unregister the PCI EPF driver | ||
147 | * @driver: the PCI EPF driver that has to be unregistered | ||
148 | * | ||
149 | * Invoke to unregister the PCI EPF driver. | ||
150 | */ | ||
151 | void pci_epf_unregister_driver(struct pci_epf_driver *driver) | ||
152 | { | ||
153 | pci_ep_cfs_remove_epf_group(driver->group); | ||
154 | driver_unregister(&driver->driver); | ||
155 | } | ||
156 | EXPORT_SYMBOL_GPL(pci_epf_unregister_driver); | ||
157 | |||
158 | /** | ||
159 | * __pci_epf_register_driver() - register a new PCI EPF driver | ||
160 | * @driver: structure representing PCI EPF driver | ||
161 | * @owner: the owner of the module that registers the PCI EPF driver | ||
162 | * | ||
163 | * Invoke to register a new PCI EPF driver. | ||
164 | */ | ||
165 | int __pci_epf_register_driver(struct pci_epf_driver *driver, | ||
166 | struct module *owner) | ||
167 | { | ||
168 | int ret; | ||
169 | |||
170 | if (!driver->ops) | ||
171 | return -EINVAL; | ||
172 | |||
173 | if (!driver->ops->bind || !driver->ops->unbind || !driver->ops->linkup) | ||
174 | return -EINVAL; | ||
175 | |||
176 | driver->driver.bus = &pci_epf_bus_type; | ||
177 | driver->driver.owner = owner; | ||
178 | |||
179 | ret = driver_register(&driver->driver); | ||
180 | if (ret) | ||
181 | return ret; | ||
182 | |||
183 | driver->group = pci_ep_cfs_add_epf_group(driver->driver.name); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | EXPORT_SYMBOL_GPL(__pci_epf_register_driver); | ||
188 | |||
189 | /** | ||
190 | * pci_epf_destroy() - destroy the created PCI EPF device | ||
191 | * @epf: the PCI EPF device that has to be destroyed. | ||
192 | * | ||
193 | * Invoke to destroy the PCI EPF device created by invoking pci_epf_create(). | ||
194 | */ | ||
195 | void pci_epf_destroy(struct pci_epf *epf) | ||
196 | { | ||
197 | device_unregister(&epf->dev); | ||
198 | } | ||
199 | EXPORT_SYMBOL_GPL(pci_epf_destroy); | ||
200 | |||
201 | /** | ||
202 | * pci_epf_create() - create a new PCI EPF device | ||
203 | * @name: the name of the PCI EPF device. This name will be used to bind the | ||
204 | * the EPF device to a EPF driver | ||
205 | * | ||
206 | * Invoke to create a new PCI EPF device by providing the name of the function | ||
207 | * device. | ||
208 | */ | ||
209 | struct pci_epf *pci_epf_create(const char *name) | ||
210 | { | ||
211 | int ret; | ||
212 | struct pci_epf *epf; | ||
213 | struct device *dev; | ||
214 | char *func_name; | ||
215 | char *buf; | ||
216 | |||
217 | epf = kzalloc(sizeof(*epf), GFP_KERNEL); | ||
218 | if (!epf) { | ||
219 | ret = -ENOMEM; | ||
220 | goto err_ret; | ||
221 | } | ||
222 | |||
223 | buf = kstrdup(name, GFP_KERNEL); | ||
224 | if (!buf) { | ||
225 | ret = -ENOMEM; | ||
226 | goto free_epf; | ||
227 | } | ||
228 | |||
229 | func_name = buf; | ||
230 | buf = strchrnul(buf, '.'); | ||
231 | *buf = '\0'; | ||
232 | |||
233 | epf->name = kstrdup(func_name, GFP_KERNEL); | ||
234 | if (!epf->name) { | ||
235 | ret = -ENOMEM; | ||
236 | goto free_func_name; | ||
237 | } | ||
238 | |||
239 | dev = &epf->dev; | ||
240 | device_initialize(dev); | ||
241 | dev->bus = &pci_epf_bus_type; | ||
242 | dev->type = &pci_epf_type; | ||
243 | |||
244 | ret = dev_set_name(dev, "%s", name); | ||
245 | if (ret) | ||
246 | goto put_dev; | ||
247 | |||
248 | ret = device_add(dev); | ||
249 | if (ret) | ||
250 | goto put_dev; | ||
251 | |||
252 | kfree(func_name); | ||
253 | return epf; | ||
254 | |||
255 | put_dev: | ||
256 | put_device(dev); | ||
257 | kfree(epf->name); | ||
258 | |||
259 | free_func_name: | ||
260 | kfree(func_name); | ||
261 | |||
262 | free_epf: | ||
263 | kfree(epf); | ||
264 | |||
265 | err_ret: | ||
266 | return ERR_PTR(ret); | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(pci_epf_create); | ||
269 | |||
270 | static void pci_epf_dev_release(struct device *dev) | ||
271 | { | ||
272 | struct pci_epf *epf = to_pci_epf(dev); | ||
273 | |||
274 | kfree(epf->name); | ||
275 | kfree(epf); | ||
276 | } | ||
277 | |||
278 | static struct device_type pci_epf_type = { | ||
279 | .release = pci_epf_dev_release, | ||
280 | }; | ||
281 | |||
282 | static int | ||
283 | pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf) | ||
284 | { | ||
285 | while (id->name[0]) { | ||
286 | if (strcmp(epf->name, id->name) == 0) | ||
287 | return true; | ||
288 | id++; | ||
289 | } | ||
290 | |||
291 | return false; | ||
292 | } | ||
293 | |||
294 | static int pci_epf_device_match(struct device *dev, struct device_driver *drv) | ||
295 | { | ||
296 | struct pci_epf *epf = to_pci_epf(dev); | ||
297 | struct pci_epf_driver *driver = to_pci_epf_driver(drv); | ||
298 | |||
299 | if (driver->id_table) | ||
300 | return pci_epf_match_id(driver->id_table, epf); | ||
301 | |||
302 | return !strcmp(epf->name, drv->name); | ||
303 | } | ||
304 | |||
305 | static int pci_epf_device_probe(struct device *dev) | ||
306 | { | ||
307 | struct pci_epf *epf = to_pci_epf(dev); | ||
308 | struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver); | ||
309 | |||
310 | if (!driver->probe) | ||
311 | return -ENODEV; | ||
312 | |||
313 | epf->driver = driver; | ||
314 | |||
315 | return driver->probe(epf); | ||
316 | } | ||
317 | |||
318 | static int pci_epf_device_remove(struct device *dev) | ||
319 | { | ||
320 | int ret; | ||
321 | struct pci_epf *epf = to_pci_epf(dev); | ||
322 | struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver); | ||
323 | |||
324 | ret = driver->remove(epf); | ||
325 | epf->driver = NULL; | ||
326 | |||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static struct bus_type pci_epf_bus_type = { | ||
331 | .name = "pci-epf", | ||
332 | .match = pci_epf_device_match, | ||
333 | .probe = pci_epf_device_probe, | ||
334 | .remove = pci_epf_device_remove, | ||
335 | }; | ||
336 | |||
337 | static int __init pci_epf_init(void) | ||
338 | { | ||
339 | int ret; | ||
340 | |||
341 | ret = bus_register(&pci_epf_bus_type); | ||
342 | if (ret) { | ||
343 | pr_err("failed to register pci epf bus --> %d\n", ret); | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | return 0; | ||
348 | } | ||
349 | module_init(pci_epf_init); | ||
350 | |||
351 | static void __exit pci_epf_exit(void) | ||
352 | { | ||
353 | bus_unregister(&pci_epf_bus_type); | ||
354 | } | ||
355 | module_exit(pci_epf_exit); | ||
356 | |||
357 | MODULE_DESCRIPTION("PCI EPF Library"); | ||
358 | MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>"); | ||
359 | MODULE_LICENSE("GPL v2"); | ||