18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * PCI Endpoint *Function* (EPF) library
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Texas Instruments
68c2ecf20Sopenharmony_ci * Author: Kishon Vijay Abraham I <kishon@ti.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/pci-epc.h>
158c2ecf20Sopenharmony_ci#include <linux/pci-epf.h>
168c2ecf20Sopenharmony_ci#include <linux/pci-ep-cfs.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pci_epf_mutex);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct bus_type pci_epf_bus_type;
218c2ecf20Sopenharmony_cistatic const struct device_type pci_epf_type;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/**
248c2ecf20Sopenharmony_ci * pci_epf_unbind() - Notify the function driver that the binding between the
258c2ecf20Sopenharmony_ci *		      EPF device and EPC device has been lost
268c2ecf20Sopenharmony_ci * @epf: the EPF device which has lost the binding with the EPC device
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * Invoke to notify the function driver that the binding between the EPF device
298c2ecf20Sopenharmony_ci * and EPC device has been lost.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_civoid pci_epf_unbind(struct pci_epf *epf)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	if (!epf->driver) {
348c2ecf20Sopenharmony_ci		dev_WARN(&epf->dev, "epf device not bound to driver\n");
358c2ecf20Sopenharmony_ci		return;
368c2ecf20Sopenharmony_ci	}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	mutex_lock(&epf->lock);
398c2ecf20Sopenharmony_ci	epf->driver->ops->unbind(epf);
408c2ecf20Sopenharmony_ci	mutex_unlock(&epf->lock);
418c2ecf20Sopenharmony_ci	module_put(epf->driver->owner);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_unbind);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/**
468c2ecf20Sopenharmony_ci * pci_epf_bind() - Notify the function driver that the EPF device has been
478c2ecf20Sopenharmony_ci *		    bound to a EPC device
488c2ecf20Sopenharmony_ci * @epf: the EPF device which has been bound to the EPC device
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Invoke to notify the function driver that it has been bound to a EPC device
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ciint pci_epf_bind(struct pci_epf *epf)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	int ret;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (!epf->driver) {
578c2ecf20Sopenharmony_ci		dev_WARN(&epf->dev, "epf device not bound to driver\n");
588c2ecf20Sopenharmony_ci		return -EINVAL;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (!try_module_get(epf->driver->owner))
628c2ecf20Sopenharmony_ci		return -EAGAIN;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	mutex_lock(&epf->lock);
658c2ecf20Sopenharmony_ci	ret = epf->driver->ops->bind(epf);
668c2ecf20Sopenharmony_ci	mutex_unlock(&epf->lock);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return ret;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_bind);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/**
738c2ecf20Sopenharmony_ci * pci_epf_free_space() - free the allocated PCI EPF register space
748c2ecf20Sopenharmony_ci * @epf: the EPF device from whom to free the memory
758c2ecf20Sopenharmony_ci * @addr: the virtual address of the PCI EPF register space
768c2ecf20Sopenharmony_ci * @bar: the BAR number corresponding to the register space
778c2ecf20Sopenharmony_ci *
788c2ecf20Sopenharmony_ci * Invoke to free the allocated PCI EPF register space.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_civoid pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct device *dev = epf->epc->dev.parent;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (!addr)
858c2ecf20Sopenharmony_ci		return;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	dma_free_coherent(dev, epf->bar[bar].size, addr,
888c2ecf20Sopenharmony_ci			  epf->bar[bar].phys_addr);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	epf->bar[bar].phys_addr = 0;
918c2ecf20Sopenharmony_ci	epf->bar[bar].addr = NULL;
928c2ecf20Sopenharmony_ci	epf->bar[bar].size = 0;
938c2ecf20Sopenharmony_ci	epf->bar[bar].barno = 0;
948c2ecf20Sopenharmony_ci	epf->bar[bar].flags = 0;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_free_space);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * pci_epf_alloc_space() - allocate memory for the PCI EPF register space
1008c2ecf20Sopenharmony_ci * @epf: the EPF device to whom allocate the memory
1018c2ecf20Sopenharmony_ci * @size: the size of the memory that has to be allocated
1028c2ecf20Sopenharmony_ci * @bar: the BAR number corresponding to the allocated register space
1038c2ecf20Sopenharmony_ci * @align: alignment size for the allocation region
1048c2ecf20Sopenharmony_ci *
1058c2ecf20Sopenharmony_ci * Invoke to allocate memory for the PCI EPF register space.
1068c2ecf20Sopenharmony_ci */
1078c2ecf20Sopenharmony_civoid *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar,
1088c2ecf20Sopenharmony_ci			  size_t align)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	void *space;
1118c2ecf20Sopenharmony_ci	struct device *dev = epf->epc->dev.parent;
1128c2ecf20Sopenharmony_ci	dma_addr_t phys_addr;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (size < 128)
1158c2ecf20Sopenharmony_ci		size = 128;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (align)
1188c2ecf20Sopenharmony_ci		size = ALIGN(size, align);
1198c2ecf20Sopenharmony_ci	else
1208c2ecf20Sopenharmony_ci		size = roundup_pow_of_two(size);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
1238c2ecf20Sopenharmony_ci	if (!space) {
1248c2ecf20Sopenharmony_ci		dev_err(dev, "failed to allocate mem space\n");
1258c2ecf20Sopenharmony_ci		return NULL;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	epf->bar[bar].phys_addr = phys_addr;
1298c2ecf20Sopenharmony_ci	epf->bar[bar].addr = space;
1308c2ecf20Sopenharmony_ci	epf->bar[bar].size = size;
1318c2ecf20Sopenharmony_ci	epf->bar[bar].barno = bar;
1328c2ecf20Sopenharmony_ci	epf->bar[bar].flags |= upper_32_bits(size) ?
1338c2ecf20Sopenharmony_ci				PCI_BASE_ADDRESS_MEM_TYPE_64 :
1348c2ecf20Sopenharmony_ci				PCI_BASE_ADDRESS_MEM_TYPE_32;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	return space;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_alloc_space);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic void pci_epf_remove_cfs(struct pci_epf_driver *driver)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct config_group *group, *tmp;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	mutex_lock(&pci_epf_mutex);
1488c2ecf20Sopenharmony_ci	list_for_each_entry_safe(group, tmp, &driver->epf_group, group_entry)
1498c2ecf20Sopenharmony_ci		pci_ep_cfs_remove_epf_group(group);
1508c2ecf20Sopenharmony_ci	list_del(&driver->epf_group);
1518c2ecf20Sopenharmony_ci	mutex_unlock(&pci_epf_mutex);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci/**
1558c2ecf20Sopenharmony_ci * pci_epf_unregister_driver() - unregister the PCI EPF driver
1568c2ecf20Sopenharmony_ci * @driver: the PCI EPF driver that has to be unregistered
1578c2ecf20Sopenharmony_ci *
1588c2ecf20Sopenharmony_ci * Invoke to unregister the PCI EPF driver.
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_civoid pci_epf_unregister_driver(struct pci_epf_driver *driver)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	pci_epf_remove_cfs(driver);
1638c2ecf20Sopenharmony_ci	driver_unregister(&driver->driver);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_unregister_driver);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int pci_epf_add_cfs(struct pci_epf_driver *driver)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct config_group *group;
1708c2ecf20Sopenharmony_ci	const struct pci_epf_device_id *id;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!IS_ENABLED(CONFIG_PCI_ENDPOINT_CONFIGFS))
1738c2ecf20Sopenharmony_ci		return 0;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&driver->epf_group);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	id = driver->id_table;
1788c2ecf20Sopenharmony_ci	while (id->name[0]) {
1798c2ecf20Sopenharmony_ci		group = pci_ep_cfs_add_epf_group(id->name);
1808c2ecf20Sopenharmony_ci		if (IS_ERR(group)) {
1818c2ecf20Sopenharmony_ci			pci_epf_remove_cfs(driver);
1828c2ecf20Sopenharmony_ci			return PTR_ERR(group);
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		mutex_lock(&pci_epf_mutex);
1868c2ecf20Sopenharmony_ci		list_add_tail(&group->group_entry, &driver->epf_group);
1878c2ecf20Sopenharmony_ci		mutex_unlock(&pci_epf_mutex);
1888c2ecf20Sopenharmony_ci		id++;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return 0;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/**
1958c2ecf20Sopenharmony_ci * __pci_epf_register_driver() - register a new PCI EPF driver
1968c2ecf20Sopenharmony_ci * @driver: structure representing PCI EPF driver
1978c2ecf20Sopenharmony_ci * @owner: the owner of the module that registers the PCI EPF driver
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci * Invoke to register a new PCI EPF driver.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_ciint __pci_epf_register_driver(struct pci_epf_driver *driver,
2028c2ecf20Sopenharmony_ci			      struct module *owner)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	int ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (!driver->ops)
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	if (!driver->ops->bind || !driver->ops->unbind)
2108c2ecf20Sopenharmony_ci		return -EINVAL;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	driver->driver.bus = &pci_epf_bus_type;
2138c2ecf20Sopenharmony_ci	driver->driver.owner = owner;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	ret = driver_register(&driver->driver);
2168c2ecf20Sopenharmony_ci	if (ret)
2178c2ecf20Sopenharmony_ci		return ret;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	pci_epf_add_cfs(driver);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__pci_epf_register_driver);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci/**
2268c2ecf20Sopenharmony_ci * pci_epf_destroy() - destroy the created PCI EPF device
2278c2ecf20Sopenharmony_ci * @epf: the PCI EPF device that has to be destroyed.
2288c2ecf20Sopenharmony_ci *
2298c2ecf20Sopenharmony_ci * Invoke to destroy the PCI EPF device created by invoking pci_epf_create().
2308c2ecf20Sopenharmony_ci */
2318c2ecf20Sopenharmony_civoid pci_epf_destroy(struct pci_epf *epf)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	device_unregister(&epf->dev);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_destroy);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/**
2388c2ecf20Sopenharmony_ci * pci_epf_create() - create a new PCI EPF device
2398c2ecf20Sopenharmony_ci * @name: the name of the PCI EPF device. This name will be used to bind the
2408c2ecf20Sopenharmony_ci *	  the EPF device to a EPF driver
2418c2ecf20Sopenharmony_ci *
2428c2ecf20Sopenharmony_ci * Invoke to create a new PCI EPF device by providing the name of the function
2438c2ecf20Sopenharmony_ci * device.
2448c2ecf20Sopenharmony_ci */
2458c2ecf20Sopenharmony_cistruct pci_epf *pci_epf_create(const char *name)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	int ret;
2488c2ecf20Sopenharmony_ci	struct pci_epf *epf;
2498c2ecf20Sopenharmony_ci	struct device *dev;
2508c2ecf20Sopenharmony_ci	int len;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	epf = kzalloc(sizeof(*epf), GFP_KERNEL);
2538c2ecf20Sopenharmony_ci	if (!epf)
2548c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	len = strchrnul(name, '.') - name;
2578c2ecf20Sopenharmony_ci	epf->name = kstrndup(name, len, GFP_KERNEL);
2588c2ecf20Sopenharmony_ci	if (!epf->name) {
2598c2ecf20Sopenharmony_ci		kfree(epf);
2608c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	dev = &epf->dev;
2648c2ecf20Sopenharmony_ci	device_initialize(dev);
2658c2ecf20Sopenharmony_ci	dev->bus = &pci_epf_bus_type;
2668c2ecf20Sopenharmony_ci	dev->type = &pci_epf_type;
2678c2ecf20Sopenharmony_ci	mutex_init(&epf->lock);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ret = dev_set_name(dev, "%s", name);
2708c2ecf20Sopenharmony_ci	if (ret) {
2718c2ecf20Sopenharmony_ci		put_device(dev);
2728c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	ret = device_add(dev);
2768c2ecf20Sopenharmony_ci	if (ret) {
2778c2ecf20Sopenharmony_ci		put_device(dev);
2788c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return epf;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_create);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ciconst struct pci_epf_device_id *
2868c2ecf20Sopenharmony_cipci_epf_match_device(const struct pci_epf_device_id *id, struct pci_epf *epf)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	if (!id || !epf)
2898c2ecf20Sopenharmony_ci		return NULL;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	while (*id->name) {
2928c2ecf20Sopenharmony_ci		if (strcmp(epf->name, id->name) == 0)
2938c2ecf20Sopenharmony_ci			return id;
2948c2ecf20Sopenharmony_ci		id++;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return NULL;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_epf_match_device);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void pci_epf_dev_release(struct device *dev)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct pci_epf *epf = to_pci_epf(dev);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	kfree(epf->name);
3068c2ecf20Sopenharmony_ci	kfree(epf);
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_cistatic const struct device_type pci_epf_type = {
3108c2ecf20Sopenharmony_ci	.release	= pci_epf_dev_release,
3118c2ecf20Sopenharmony_ci};
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistatic int
3148c2ecf20Sopenharmony_cipci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	while (id->name[0]) {
3178c2ecf20Sopenharmony_ci		if (strcmp(epf->name, id->name) == 0)
3188c2ecf20Sopenharmony_ci			return true;
3198c2ecf20Sopenharmony_ci		id++;
3208c2ecf20Sopenharmony_ci	}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return false;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic int pci_epf_device_match(struct device *dev, struct device_driver *drv)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct pci_epf *epf = to_pci_epf(dev);
3288c2ecf20Sopenharmony_ci	struct pci_epf_driver *driver = to_pci_epf_driver(drv);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (driver->id_table)
3318c2ecf20Sopenharmony_ci		return pci_epf_match_id(driver->id_table, epf);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return !strcmp(epf->name, drv->name);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int pci_epf_device_probe(struct device *dev)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct pci_epf *epf = to_pci_epf(dev);
3398c2ecf20Sopenharmony_ci	struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	if (!driver->probe)
3428c2ecf20Sopenharmony_ci		return -ENODEV;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	epf->driver = driver;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return driver->probe(epf);
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic int pci_epf_device_remove(struct device *dev)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	int ret = 0;
3528c2ecf20Sopenharmony_ci	struct pci_epf *epf = to_pci_epf(dev);
3538c2ecf20Sopenharmony_ci	struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (driver->remove)
3568c2ecf20Sopenharmony_ci		ret = driver->remove(epf);
3578c2ecf20Sopenharmony_ci	epf->driver = NULL;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return ret;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic struct bus_type pci_epf_bus_type = {
3638c2ecf20Sopenharmony_ci	.name		= "pci-epf",
3648c2ecf20Sopenharmony_ci	.match		= pci_epf_device_match,
3658c2ecf20Sopenharmony_ci	.probe		= pci_epf_device_probe,
3668c2ecf20Sopenharmony_ci	.remove		= pci_epf_device_remove,
3678c2ecf20Sopenharmony_ci};
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int __init pci_epf_init(void)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	int ret;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	ret = bus_register(&pci_epf_bus_type);
3748c2ecf20Sopenharmony_ci	if (ret) {
3758c2ecf20Sopenharmony_ci		pr_err("failed to register pci epf bus --> %d\n", ret);
3768c2ecf20Sopenharmony_ci		return ret;
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_cimodule_init(pci_epf_init);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void __exit pci_epf_exit(void)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	bus_unregister(&pci_epf_bus_type);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_cimodule_exit(pci_epf_exit);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PCI EPF Library");
3908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
3918c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
392