1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Endpoint *Function* (EPF) library
5 * Copyright (C) 2017 Texas Instruments
6 * Author: Kishon Vijay Abraham I <kishon@ti.com>
9 #include <linux/device.h>
10 #include <linux/dma-mapping.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
14 #include <linux/pci-epc.h>
15 #include <linux/pci-epf.h>
16 #include <linux/pci-ep-cfs.h>
18 static DEFINE_MUTEX(pci_epf_mutex
);
20 static struct bus_type pci_epf_bus_type
;
21 static const struct device_type pci_epf_type
;
24 * pci_epf_unbind() - Notify the function driver that the binding between the
25 * EPF device and EPC device has been lost
26 * @epf: the EPF device which has lost the binding with the EPC device
28 * Invoke to notify the function driver that the binding between the EPF device
29 * and EPC device has been lost.
31 void pci_epf_unbind(struct pci_epf
*epf
)
34 dev_WARN(&epf
->dev
, "epf device not bound to driver\n");
38 mutex_lock(&epf
->lock
);
39 epf
->driver
->ops
->unbind(epf
);
40 mutex_unlock(&epf
->lock
);
41 module_put(epf
->driver
->owner
);
43 EXPORT_SYMBOL_GPL(pci_epf_unbind
);
46 * pci_epf_bind() - Notify the function driver that the EPF device has been
47 * bound to a EPC device
48 * @epf: the EPF device which has been bound to the EPC device
50 * Invoke to notify the function driver that it has been bound to a EPC device
52 int pci_epf_bind(struct pci_epf
*epf
)
57 dev_WARN(&epf
->dev
, "epf device not bound to driver\n");
61 if (!try_module_get(epf
->driver
->owner
))
64 mutex_lock(&epf
->lock
);
65 ret
= epf
->driver
->ops
->bind(epf
);
66 mutex_unlock(&epf
->lock
);
70 EXPORT_SYMBOL_GPL(pci_epf_bind
);
73 * pci_epf_free_space() - free the allocated PCI EPF register space
74 * @addr: the virtual address of the PCI EPF register space
75 * @bar: the BAR number corresponding to the register space
77 * Invoke to free the allocated PCI EPF register space.
79 void pci_epf_free_space(struct pci_epf
*epf
, void *addr
, enum pci_barno bar
)
81 struct device
*dev
= epf
->epc
->dev
.parent
;
86 dma_free_coherent(dev
, epf
->bar
[bar
].size
, addr
,
87 epf
->bar
[bar
].phys_addr
);
89 epf
->bar
[bar
].phys_addr
= 0;
90 epf
->bar
[bar
].addr
= NULL
;
91 epf
->bar
[bar
].size
= 0;
92 epf
->bar
[bar
].barno
= 0;
93 epf
->bar
[bar
].flags
= 0;
95 EXPORT_SYMBOL_GPL(pci_epf_free_space
);
98 * pci_epf_alloc_space() - allocate memory for the PCI EPF register space
99 * @size: the size of the memory that has to be allocated
100 * @bar: the BAR number corresponding to the allocated register space
101 * @align: alignment size for the allocation region
103 * Invoke to allocate memory for the PCI EPF register space.
105 void *pci_epf_alloc_space(struct pci_epf
*epf
, size_t size
, enum pci_barno bar
,
109 struct device
*dev
= epf
->epc
->dev
.parent
;
110 dma_addr_t phys_addr
;
116 size
= ALIGN(size
, align
);
118 size
= roundup_pow_of_two(size
);
120 space
= dma_alloc_coherent(dev
, size
, &phys_addr
, GFP_KERNEL
);
122 dev_err(dev
, "failed to allocate mem space\n");
126 epf
->bar
[bar
].phys_addr
= phys_addr
;
127 epf
->bar
[bar
].addr
= space
;
128 epf
->bar
[bar
].size
= size
;
129 epf
->bar
[bar
].barno
= bar
;
130 epf
->bar
[bar
].flags
|= upper_32_bits(size
) ?
131 PCI_BASE_ADDRESS_MEM_TYPE_64
:
132 PCI_BASE_ADDRESS_MEM_TYPE_32
;
136 EXPORT_SYMBOL_GPL(pci_epf_alloc_space
);
138 static void pci_epf_remove_cfs(struct pci_epf_driver
*driver
)
140 struct config_group
*group
, *tmp
;
142 if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS
))
145 mutex_lock(&pci_epf_mutex
);
146 list_for_each_entry_safe(group
, tmp
, &driver
->epf_group
, group_entry
)
147 pci_ep_cfs_remove_epf_group(group
);
148 list_del(&driver
->epf_group
);
149 mutex_unlock(&pci_epf_mutex
);
153 * pci_epf_unregister_driver() - unregister the PCI EPF driver
154 * @driver: the PCI EPF driver that has to be unregistered
156 * Invoke to unregister the PCI EPF driver.
158 void pci_epf_unregister_driver(struct pci_epf_driver
*driver
)
160 pci_epf_remove_cfs(driver
);
161 driver_unregister(&driver
->driver
);
163 EXPORT_SYMBOL_GPL(pci_epf_unregister_driver
);
165 static int pci_epf_add_cfs(struct pci_epf_driver
*driver
)
167 struct config_group
*group
;
168 const struct pci_epf_device_id
*id
;
170 if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS
))
173 INIT_LIST_HEAD(&driver
->epf_group
);
175 id
= driver
->id_table
;
176 while (id
->name
[0]) {
177 group
= pci_ep_cfs_add_epf_group(id
->name
);
179 pci_epf_remove_cfs(driver
);
180 return PTR_ERR(group
);
183 mutex_lock(&pci_epf_mutex
);
184 list_add_tail(&group
->group_entry
, &driver
->epf_group
);
185 mutex_unlock(&pci_epf_mutex
);
193 * __pci_epf_register_driver() - register a new PCI EPF driver
194 * @driver: structure representing PCI EPF driver
195 * @owner: the owner of the module that registers the PCI EPF driver
197 * Invoke to register a new PCI EPF driver.
199 int __pci_epf_register_driver(struct pci_epf_driver
*driver
,
200 struct module
*owner
)
207 if (!driver
->ops
->bind
|| !driver
->ops
->unbind
)
210 driver
->driver
.bus
= &pci_epf_bus_type
;
211 driver
->driver
.owner
= owner
;
213 ret
= driver_register(&driver
->driver
);
217 pci_epf_add_cfs(driver
);
221 EXPORT_SYMBOL_GPL(__pci_epf_register_driver
);
224 * pci_epf_destroy() - destroy the created PCI EPF device
225 * @epf: the PCI EPF device that has to be destroyed.
227 * Invoke to destroy the PCI EPF device created by invoking pci_epf_create().
229 void pci_epf_destroy(struct pci_epf
*epf
)
231 device_unregister(&epf
->dev
);
233 EXPORT_SYMBOL_GPL(pci_epf_destroy
);
236 * pci_epf_create() - create a new PCI EPF device
237 * @name: the name of the PCI EPF device. This name will be used to bind the
238 * the EPF device to a EPF driver
240 * Invoke to create a new PCI EPF device by providing the name of the function
243 struct pci_epf
*pci_epf_create(const char *name
)
250 epf
= kzalloc(sizeof(*epf
), GFP_KERNEL
);
252 return ERR_PTR(-ENOMEM
);
254 len
= strchrnul(name
, '.') - name
;
255 epf
->name
= kstrndup(name
, len
, GFP_KERNEL
);
258 return ERR_PTR(-ENOMEM
);
262 device_initialize(dev
);
263 dev
->bus
= &pci_epf_bus_type
;
264 dev
->type
= &pci_epf_type
;
265 mutex_init(&epf
->lock
);
267 ret
= dev_set_name(dev
, "%s", name
);
273 ret
= device_add(dev
);
281 EXPORT_SYMBOL_GPL(pci_epf_create
);
283 const struct pci_epf_device_id
*
284 pci_epf_match_device(const struct pci_epf_device_id
*id
, struct pci_epf
*epf
)
290 if (strcmp(epf
->name
, id
->name
) == 0)
297 EXPORT_SYMBOL_GPL(pci_epf_match_device
);
299 static void pci_epf_dev_release(struct device
*dev
)
301 struct pci_epf
*epf
= to_pci_epf(dev
);
307 static const struct device_type pci_epf_type
= {
308 .release
= pci_epf_dev_release
,
312 pci_epf_match_id(const struct pci_epf_device_id
*id
, const struct pci_epf
*epf
)
314 while (id
->name
[0]) {
315 if (strcmp(epf
->name
, id
->name
) == 0)
323 static int pci_epf_device_match(struct device
*dev
, struct device_driver
*drv
)
325 struct pci_epf
*epf
= to_pci_epf(dev
);
326 struct pci_epf_driver
*driver
= to_pci_epf_driver(drv
);
328 if (driver
->id_table
)
329 return pci_epf_match_id(driver
->id_table
, epf
);
331 return !strcmp(epf
->name
, drv
->name
);
334 static int pci_epf_device_probe(struct device
*dev
)
336 struct pci_epf
*epf
= to_pci_epf(dev
);
337 struct pci_epf_driver
*driver
= to_pci_epf_driver(dev
->driver
);
342 epf
->driver
= driver
;
344 return driver
->probe(epf
);
347 static int pci_epf_device_remove(struct device
*dev
)
350 struct pci_epf
*epf
= to_pci_epf(dev
);
351 struct pci_epf_driver
*driver
= to_pci_epf_driver(dev
->driver
);
354 ret
= driver
->remove(epf
);
360 static struct bus_type pci_epf_bus_type
= {
362 .match
= pci_epf_device_match
,
363 .probe
= pci_epf_device_probe
,
364 .remove
= pci_epf_device_remove
,
367 static int __init
pci_epf_init(void)
371 ret
= bus_register(&pci_epf_bus_type
);
373 pr_err("failed to register pci epf bus --> %d\n", ret
);
379 module_init(pci_epf_init
);
381 static void __exit
pci_epf_exit(void)
383 bus_unregister(&pci_epf_bus_type
);
385 module_exit(pci_epf_exit
);
387 MODULE_DESCRIPTION("PCI EPF Library");
388 MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
389 MODULE_LICENSE("GPL v2");