[PATCH v2 01/22] PCI: endpoint: Add EP core layer to enable EP controller and EP functions

Joao Pinto Joao.Pinto at synopsys.com
Fri Feb 17 03:26:23 PST 2017


Hi Kishon,

Às 9:50 AM de 2/17/2017, Kishon Vijay Abraham I escreveu:
> Introduce a new EP core layer in order to support endpoint functions
> in linux kernel. This comprises of EPC library
> (Endpoint Controller Library) and EPF library (Endpoint
> Function Library). EPC library implements functions that is specific
> to an endpoint controller and EPF library implements functions
> that is specific to an endpoint function.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon at ti.com>
> ---
>  drivers/Makefile                    |    2 +
>  drivers/pci/Kconfig                 |    1 +
>  drivers/pci/endpoint/Kconfig        |   21 ++
>  drivers/pci/endpoint/Makefile       |    6 +
>  drivers/pci/endpoint/pci-epc-core.c |  548 +++++++++++++++++++++++++++++++++++
>  drivers/pci/endpoint/pci-epc-mem.c  |  143 +++++++++
>  drivers/pci/endpoint/pci-epf-core.c |  347 ++++++++++++++++++++++
>  include/linux/mod_devicetable.h     |   10 +
>  include/linux/pci-epc.h             |  141 +++++++++
>  include/linux/pci-epf.h             |  160 ++++++++++
>  10 files changed, 1379 insertions(+)
>  create mode 100644 drivers/pci/endpoint/Kconfig
>  create mode 100644 drivers/pci/endpoint/Makefile
>  create mode 100644 drivers/pci/endpoint/pci-epc-core.c
>  create mode 100644 drivers/pci/endpoint/pci-epc-mem.c
>  create mode 100644 drivers/pci/endpoint/pci-epf-core.c
>  create mode 100644 include/linux/pci-epc.h
>  create mode 100644 include/linux/pci-epf.h
> 
> diff --git a/drivers/Makefile b/drivers/Makefile
> index f521cb0..a300bb1 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -14,7 +14,9 @@ obj-$(CONFIG_GENERIC_PHY)	+= phy/
>  obj-$(CONFIG_PINCTRL)		+= pinctrl/
>  obj-$(CONFIG_GPIOLIB)		+= gpio/
>  obj-y				+= pwm/
> +
>  obj-$(CONFIG_PCI)		+= pci/
> +obj-$(CONFIG_PCI_ENDPOINT)	+= pci/endpoint/
>  # PCI dwc controller drivers
>  obj-y				+= pci/dwc/

Any special reason to include pci/endpoint and pci/dwc in drivers/Makefile
instead of being inside pci/Makefile? pci/host is still inside pci/Makefile.

>  
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index df14142..9747c1e 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -134,3 +134,4 @@ config PCI_HYPERV
>  source "drivers/pci/hotplug/Kconfig"
>  source "drivers/pci/dwc/Kconfig"
>  source "drivers/pci/host/Kconfig"
> +source "drivers/pci/endpoint/Kconfig"
> diff --git a/drivers/pci/endpoint/Kconfig b/drivers/pci/endpoint/Kconfig
> new file mode 100644
> index 0000000..7eb1c79
> --- /dev/null
> +++ b/drivers/pci/endpoint/Kconfig
> @@ -0,0 +1,21 @@
> +#
> +# PCI Endpoint Support
> +#
> +
> +menu "PCI Endpoint"
> +
> +config PCI_ENDPOINT
> +	bool "PCI Endpoint Support"
> +	select CONFIGFS_FS
> +	help
> +	   Enable this configuration option to support configurable PCI
> +	   endpoint. This should be enabled if the platform has a PCI
> +	   controller that can operate in endpoint mode.
> +
> +	   Enabling this option will build the endpoint library, which
> +	   includes endpoint controller library and endpoint function
> +	   library.
> +
> +	   If in doubt, say "N" to disable Endpoint support.
> +
> +endmenu
> diff --git a/drivers/pci/endpoint/Makefile b/drivers/pci/endpoint/Makefile
> new file mode 100644
> index 0000000..dc1bc16
> --- /dev/null
> +++ b/drivers/pci/endpoint/Makefile
> @@ -0,0 +1,6 @@
> +#
> +# Makefile for PCI Endpoint Support
> +#
> +
> +obj-$(CONFIG_PCI_ENDPOINT)		+= pci-epc-core.o pci-epf-core.o\
> +					   pci-epc-mem.o
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> new file mode 100644
> index 0000000..2c33e8a
> --- /dev/null
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -0,0 +1,548 @@
> +/**
> + * PCI Endpoint *Controller* (EPC) library
> + *
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon at ti.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that 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, see <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIBAg&c=DPL6_X_6JkXFx7AXWqB0tg&r=s2fO0hii0OGNOv9qQy_HRXy-xAJUD1NNoEcc3io_kx0&m=swFEoYaziG2Fdcvbq0MAtOjv2PLxgqEMssA9yjQVqXI&s=ibUotwnXH20Q3a_vRJYtFUvGbSQN_43xAmUcsQ4FNvo&e= >.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +static struct class *pci_epc_class;
> +
> +static void devm_pci_epc_release(struct device *dev, void *res)
> +{
> +	struct pci_epc *epc = *(struct pci_epc **)res;
> +
> +	pci_epc_destroy(epc);
> +}
> +
> +static int devm_pci_epc_match(struct device *dev, void *res, void *match_data)
> +{
> +	struct pci_epc **epc = res;
> +
> +	return *epc == match_data;
> +}
> +
> +/**
> + * pci_epc_get() - get the pci endpoint controller
> + * @epc_name: device name of the endpoint controller
> + *
> + * Invoke to get struct pci_epc * corresponding to the device name of the
> + * endpoint controller
> + */
> +struct pci_epc *pci_epc_get(char *epc_name)
> +{
> +	int ret = -EINVAL;
> +	struct pci_epc *epc;
> +	struct device *dev;
> +	struct class_dev_iter iter;
> +
> +	class_dev_iter_init(&iter, pci_epc_class, NULL, NULL);
> +	while ((dev = class_dev_iter_next(&iter))) {
> +		if (strcmp(epc_name, dev_name(dev)))
> +			continue;
> +
> +		epc = to_pci_epc(dev);
> +		if (!try_module_get(epc->ops->owner)) {
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +
> +		get_device(&epc->dev);
> +		return epc;
> +	}
> +
> +err:
> +	class_dev_iter_exit(&iter);
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_get);
> +
> +/**
> + * pci_epc_put() - release the pci endpoint controller
> + * @epc: epc returned by pci_epc_get()
> + *
> + * release the refcount the caller obtained by invoking pci_epc_get()
> + */
> +void pci_epc_put(struct pci_epc *epc)
> +{
> +	if (!epc || IS_ERR(epc))
> +		return;
> +
> +	module_put(epc->ops->owner);
> +	put_device(&epc->dev);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_put);
> +
> +/**
> + * pci_epc_stop() - stop the PCI link
> + * @epc: the link of the EPC device that has to be stopped
> + *
> + * Invoke to stop the PCI link
> + */
> +void pci_epc_stop(struct pci_epc *epc)
> +{
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc) || !epc->ops->stop)
> +		return;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	epc->ops->stop(epc);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_stop);
> +
> +/**
> + * pci_epc_start() - start the PCI link
> + * @epc: the link of *this* EPC device has to be started
> + *
> + * Invoke to start the PCI link
> + */
> +int pci_epc_start(struct pci_epc *epc)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->start)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	ret = epc->ops->start(epc);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_start);
> +
> +/**
> + * pci_epc_raise_irq() - interrupt the host system
> + * @epc: the EPC device which has to interrupt the host
> + * @type: specify the type of interrupt; legacy or MSI
> + * @interrupt_num: the MSI interrupt number
> + *
> + * Invoke to raise an MSI or legacy interrupt
> + */
> +int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
> +		      u8 interrupt_num)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->raise_irq)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	ret = epc->ops->raise_irq(epc, type, interrupt_num);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_raise_irq);
> +
> +/**
> + * pci_epc_get_msi() - get the number of MSI interrupt numbers allocated
> + * @epc: the EPC device to which MSI interrupts was requested
> + *
> + * Invoke to get the number of MSI interrupts allocated by the RC
> + */
> +int pci_epc_get_msi(struct pci_epc *epc)
> +{
> +	int interrupt;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return 0;
> +
> +	if (!epc->ops->get_msi)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	interrupt = epc->ops->get_msi(epc);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	if (interrupt < 0)
> +		return 0;
> +
> +	interrupt = 1 << interrupt;
> +
> +	return interrupt;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_get_msi);
> +
> +/**
> + * pci_epc_set_msi() - set the number of MSI interrupt numbers required
> + * @epc: the EPC device on which MSI has to be configured
> + * @interrupts: number of MSI interrupts required by the EPF
> + *
> + * Invoke to set the required number of MSI interrupts.
> + */
> +int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts)
> +{
> +	int ret;
> +	u8 encode_int;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->set_msi)
> +		return 0;
> +
> +	encode_int = order_base_2(interrupts);
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	ret = epc->ops->set_msi(epc, encode_int);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_set_msi);
> +
> +/**
> + * pci_epc_unmap_addr() - unmap cpu address from pci address
> + * @epc: the EPC device on which address is allocated
> + * @phys_addr: physical address of the local system
> + *
> + * Invoke to unmap the cpu address from pci address.
> + */
> +void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr)
> +{
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return;
> +
> +	if (!epc->ops->unmap_addr)
> +		return;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	epc->ops->unmap_addr(epc, phys_addr);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_unmap_addr);
> +
> +/**
> + * pci_epc_map_addr() - map cpu address to pci address
> + * @epc: the EPC device on which address is allocated
> + * @phys_addr: physical address of the local system
> + * @pci_addr: pci address to which the physical address should be mapped
> + * @size: the size of the allocation
> + *
> + * Invoke to map cpu address with pci address.
> + */
> +int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
> +		     u64 pci_addr, size_t size)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->map_addr)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	ret = epc->ops->map_addr(epc, phys_addr, pci_addr, size);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_map_addr);
> +
> +/**
> + * pci_epc_clear_bar() - reset the BAR
> + * @epc: the EPC device for which the BAR has to be cleared
> + * @bar: the bar number that has to be reset
> + *
> + * Invoke to reset the BAR of the endpoint device.
> + */
> +void pci_epc_clear_bar(struct pci_epc *epc, int bar)
> +{
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return;
> +
> +	if (!epc->ops->clear_bar)
> +		return;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	epc->ops->clear_bar(epc, bar);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_clear_bar);
> +
> +/**
> + * pci_epc_set_bar() - configure BAR in order for host to assign PCI addr space
> + * @epc: the EPC device on which BAR has to be configured
> + * @bar: the bar number that has to be configured
> + * @size: the size of the addr space
> + * @flags: specify memory allocation/io allocation/32bit address/64 bit address
> + *
> + * Invoke to configure the BAR of the endpoint device.
> + */
> +int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
> +		    dma_addr_t bar_phys, size_t size, int flags)
> +{
> +	int ret;
> +	unsigned long irq_flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->set_bar)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, irq_flags);
> +	ret = epc->ops->set_bar(epc, bar, bar_phys, size, flags);
> +	spin_unlock_irqrestore(&epc->lock, irq_flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_set_bar);
> +
> +/**
> + * pci_epc_write_header() - write standard configuration header
> + * @epc: the EPC device to which the configuration header should be written
> + * @header: standard configuration header fields
> + *
> + * Invoke to write the configuration header to the endpoint controller. Every
> + * endpoint controller will have a dedicated location to which the standard
> + * configuration header would be written. The callback function should write
> + * the header fields to this dedicated location.
> + */
> +int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *header)
> +{
> +	int ret;
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (!epc->ops->write_header)
> +		return 0;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	ret = epc->ops->write_header(epc, header);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_write_header);
> +
> +/**
> + * pci_epc_add_epf() - bind pci endpoint function to an endpoint controller
> + * @epc: the EPC device to which the endpoint function should be added
> + * @epf: the endpoint function to be added
> + *
> + * A PCI endpoint device can have one or more functions. In the case of PCIe,
> + * the specification allows upto 8 PCIe endpoint functions. Invoke
> + * pci_epc_add_epf() to add a pci endpoint function to an endpoint controller.
> + */
> +int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf)
> +{
> +	unsigned long flags;
> +
> +	if (IS_ERR(epc))
> +		return -EINVAL;
> +
> +	if (epf->func_no > epc->max_functions - 1)
> +		return -EINVAL;
> +
> +	dma_set_coherent_mask(&epf->dev, epc->dev.coherent_dma_mask);
> +	epf->dev.dma_mask = epc->dev.dma_mask;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	list_add_tail(&epf->list, &epc->pci_epf);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_add_epf);
> +
> +/**
> + * pci_epc_remove_epf() - remove pci endpoint function from endpoint controller
> + * @epc: the EPC device from which the endpoint function should be removed
> + * @epf: the endpoint function to be removed
> + *
> + * Invoke to remove pci endpoint function from the endpoint controller.
> + */
> +void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf)
> +{
> +	unsigned long flags;
> +
> +	if (!epc || IS_ERR(epc))
> +		return;
> +
> +	spin_lock_irqsave(&epc->lock, flags);
> +	list_del(&epf->list);
> +	spin_unlock_irqrestore(&epc->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_remove_epf);
> +
> +/**
> + * pci_epc_destroy() - destroy the EPC device
> + * @epc: the EPC device that has to be destroyed
> + *
> + * Invoke to destroy the PCI EPC device
> + */
> +void pci_epc_destroy(struct pci_epc *epc)
> +{
> +	device_unregister(&epc->dev);
> +	kfree(epc);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_destroy);
> +
> +/**
> + * devm_pci_epc_destroy() - destroy the EPC device
> + * @dev: device that wants to destroy the EPC
> + * @epc: the EPC device that has to be destroyed
> + *
> + * Invoke to destroy the devres associated with this
> + * pci_epc and destroy the EPC device.
> + */
> +void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc)
> +{
> +	int r;
> +
> +	r = devres_destroy(dev, devm_pci_epc_release, devm_pci_epc_match,
> +			   epc);
> +	dev_WARN_ONCE(dev, r, "couldn't find PCI EPC resource\n");
> +}
> +EXPORT_SYMBOL_GPL(devm_pci_epc_destroy);
> +
> +/**
> + * __pci_epc_create() - create a new endpoint controller (EPC) device
> + * @dev: device that is creating the new EPC
> + * @ops: function pointers for performing EPC operations
> + * @owner: the owner of the module that creates the EPC device
> + *
> + * Invoke to create a new EPC device and add it to pci_epc class.
> + */
> +struct pci_epc *
> +__pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
> +		 struct module *owner)
> +{
> +	int ret;
> +	struct pci_epc *epc;
> +
> +	if (WARN_ON(!dev)) {
> +		ret = -EINVAL;
> +		goto err_ret;
> +	}
> +
> +	epc = kzalloc(sizeof(*epc), GFP_KERNEL);
> +	if (!epc) {
> +		ret = -ENOMEM;
> +		goto err_ret;
> +	}
> +
> +	spin_lock_init(&epc->lock);
> +	INIT_LIST_HEAD(&epc->pci_epf);
> +
> +	device_initialize(&epc->dev);
> +	dma_set_coherent_mask(&epc->dev, dev->coherent_dma_mask);
> +	epc->dev.class = pci_epc_class;
> +	epc->dev.dma_mask = dev->dma_mask;
> +	epc->ops = ops;
> +
> +	ret = dev_set_name(&epc->dev, "%s", dev_name(dev));
> +	if (ret)
> +		goto put_dev;
> +
> +	ret = device_add(&epc->dev);
> +	if (ret)
> +		goto put_dev;
> +
> +	return epc;
> +
> +put_dev:
> +	put_device(&epc->dev);
> +	kfree(epc);
> +
> +err_ret:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(__pci_epc_create);
> +
> +/**
> + * __devm_pci_epc_create() - create a new endpoint controller (EPC) device
> + * @dev: device that is creating the new EPC
> + * @ops: function pointers for performing EPC operations
> + * @owner: the owner of the module that creates the EPC device
> + *
> + * Invoke to create a new EPC device and add it to pci_epc class.
> + * While at that, it also associates the device with the pci_epc using devres.
> + * On driver detach, release function is invoked on the devres data,
> + * then, devres data is freed.
> + */
> +struct pci_epc *
> +__devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
> +		      struct module *owner)
> +{
> +	struct pci_epc **ptr, *epc;
> +
> +	ptr = devres_alloc(devm_pci_epc_release, sizeof(*ptr), GFP_KERNEL);
> +	if (!ptr)
> +		return ERR_PTR(-ENOMEM);
> +
> +	epc = __pci_epc_create(dev, ops, owner);
> +	if (!IS_ERR(epc)) {
> +		*ptr = epc;
> +		devres_add(dev, ptr);
> +	} else {
> +		devres_free(ptr);
> +	}
> +
> +	return epc;
> +}
> +EXPORT_SYMBOL_GPL(__devm_pci_epc_create);
> +
> +static int __init pci_epc_init(void)
> +{
> +	pci_epc_class = class_create(THIS_MODULE, "pci_epc");
> +	if (IS_ERR(pci_epc_class)) {
> +		pr_err("failed to create pci epc class --> %ld\n",
> +		       PTR_ERR(pci_epc_class));
> +		return PTR_ERR(pci_epc_class);
> +	}
> +
> +	return 0;
> +}
> +module_init(pci_epc_init);
> +
> +static void __exit pci_epc_exit(void)
> +{
> +	class_destroy(pci_epc_class);
> +}
> +module_exit(pci_epc_exit);
> +
> +MODULE_DESCRIPTION("PCI EPC Library");
> +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon at ti.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c
> new file mode 100644
> index 0000000..3a94cc1
> --- /dev/null
> +++ b/drivers/pci/endpoint/pci-epc-mem.c
> @@ -0,0 +1,143 @@
> +/**
> + * PCI Endpoint *Controller* Address Space Management
> + *
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon at ti.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that 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, see <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIBAg&c=DPL6_X_6JkXFx7AXWqB0tg&r=s2fO0hii0OGNOv9qQy_HRXy-xAJUD1NNoEcc3io_kx0&m=swFEoYaziG2Fdcvbq0MAtOjv2PLxgqEMssA9yjQVqXI&s=ibUotwnXH20Q3a_vRJYtFUvGbSQN_43xAmUcsQ4FNvo&e= >.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/pci-epc.h>
> +
> +/**
> + * pci_epc_mem_init() - initialize the pci_epc_mem structure
> + * @epc: the EPC device that invoked pci_epc_mem_init
> + * @phys_base: the physical address of the base
> + * @size: the size of the address space
> + *
> + * Invoke to initialize the pci_epc_mem structure used by the
> + * endpoint functions to allocate mapped PCI address.
> + */
> +int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_base, size_t size)
> +{
> +	int ret;
> +	struct pci_epc_mem *mem;
> +	unsigned long *bitmap;
> +	int pages = size >> PAGE_SHIFT;
> +	int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
> +
> +	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
> +	if (!mem) {
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +
> +	bitmap = kzalloc(bitmap_size, GFP_KERNEL);
> +	if (!bitmap) {
> +		ret = -ENOMEM;
> +		goto err_mem;
> +	}
> +
> +	mem->bitmap = bitmap;
> +	mem->phys_base = phys_base;
> +	mem->pages = pages;
> +	mem->size = size;
> +
> +	epc->mem = mem;
> +
> +	return 0;
> +
> +err_mem:
> +	kfree(mem);
> +
> +err:
> +return ret;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_init);
> +
> +/**
> + * pci_epc_mem_exit() - cleanup the pci_epc_mem structure
> + * @epc: the EPC device that invoked pci_epc_mem_exit
> + *
> + * Invoke to cleanup the pci_epc_mem structure allocated in
> + * pci_epc_mem_init().
> + */
> +void pci_epc_mem_exit(struct pci_epc *epc)
> +{
> +	struct pci_epc_mem *mem = epc->mem;
> +
> +	epc->mem = NULL;
> +	kfree(mem->bitmap);
> +	kfree(mem);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
> +
> +/**
> + * pci_epc_mem_alloc_addr() - allocate memory address from EPC addr space
> + * @epc: the EPC device on which memory has to be allocated
> + * @phys_addr: populate the allocated physical address here
> + * @size: the size of the address space that has to be allocated
> + *
> + * Invoke to allocate memory address from the EPC address space. This
> + * is usually done to map the remote RC address into the local system.
> + */
> +void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
> +				     phys_addr_t *phys_addr, size_t size)
> +{
> +	int pageno;
> +	void __iomem *virt_addr;
> +	struct pci_epc_mem *mem = epc->mem;
> +	int order = get_order(size);
> +
> +	pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order);
> +	if (pageno < 0)
> +		return NULL;
> +
> +	*phys_addr = mem->phys_base + (pageno << PAGE_SHIFT);
> +	virt_addr = ioremap(*phys_addr, size);
> +	if (!virt_addr)
> +		bitmap_release_region(mem->bitmap, pageno, order);
> +
> +	return virt_addr;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
> +
> +/**
> + * pci_epc_mem_free_addr() - free the allocated memory address
> + * @epc: the EPC device on which memory was allocated
> + * @phys_addr: the allocated physical address
> + * @virt_addr: virtual address of the allocated mem space
> + * @size: the size of the allocated address space
> + *
> + * Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
> + */
> +void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
> +			   void __iomem *virt_addr, size_t size)
> +{
> +	int pageno;
> +	int order = get_order(size);
> +	struct pci_epc_mem *mem = epc->mem;
> +
> +	iounmap(virt_addr);
> +	pageno = (phys_addr - mem->phys_base) >> PAGE_SHIFT;
> +	bitmap_release_region(mem->bitmap, pageno, order);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr);
> +
> +MODULE_DESCRIPTION("PCI EPC Address Space Management");
> +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon at ti.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c
> new file mode 100644
> index 0000000..4c903fc
> --- /dev/null
> +++ b/drivers/pci/endpoint/pci-epf-core.c
> @@ -0,0 +1,347 @@
> +/**
> + * PCI Endpoint *Function* (EPF) library
> + *
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon at ti.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that 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, see <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIBAg&c=DPL6_X_6JkXFx7AXWqB0tg&r=s2fO0hii0OGNOv9qQy_HRXy-xAJUD1NNoEcc3io_kx0&m=swFEoYaziG2Fdcvbq0MAtOjv2PLxgqEMssA9yjQVqXI&s=ibUotwnXH20Q3a_vRJYtFUvGbSQN_43xAmUcsQ4FNvo&e= >.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +static struct bus_type pci_epf_bus_type;
> +static struct device_type pci_epf_type;
> +
> +/**
> + * pci_epf_linkup() - Notify the function driver that EPC device has
> + *		      established a connection with the Root Complex.
> + * @epf: the EPF device bound to the EPC device which has established
> + *	 the connection with the host
> + *
> + * Invoke to notify the function driver that EPC device has established
> + * a connection with the Root Complex.
> + */
> +void pci_epf_linkup(struct pci_epf *epf)
> +{
> +	if (!epf->driver)
> +		dev_WARN(&epf->dev, "epf device not bound to driver\n");
> +
> +	epf->driver->ops->linkup(epf);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_linkup);
> +
> +/**
> + * pci_epf_unbind() - Notify the function driver that the binding between the
> + *		      EPF device and EPC device has been lost
> + * @epf: the EPF device which has lost the binding with the EPC device
> + *
> + * Invoke to notify the function driver that the binding between the EPF device
> + * and EPC device has been lost.
> + */
> +void pci_epf_unbind(struct pci_epf *epf)
> +{
> +	if (!epf->driver)
> +		dev_WARN(&epf->dev, "epf device not bound to driver\n");
> +
> +	epf->driver->ops->unbind(epf);
> +	module_put(epf->driver->owner);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_unbind);
> +
> +/**
> + * pci_epf_bind() - Notify the function driver that the EPF device has been
> + *		    bound to a EPC device
> + * @epf: the EPF device which has been bound to the EPC device
> + *
> + * Invoke to notify the function driver that it has been bound to a EPC device
> + */
> +int pci_epf_bind(struct pci_epf *epf)
> +{
> +	if (!epf->driver)
> +		dev_WARN(&epf->dev, "epf device not bound to driver\n");
> +
> +	if (!try_module_get(epf->driver->owner))
> +		return -EAGAIN;
> +
> +	return epf->driver->ops->bind(epf);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_bind);
> +
> +/**
> + * pci_epf_free_space() - free the allocated PCI EPF register space
> + * @addr: the virtual address of the PCI EPF register space
> + * @bar: the bar number corresponding to the register space
> + *
> + * Invoke to free the allocated PCI EPF register space.
> + */
> +void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar)
> +{
> +	struct device *dev = &epf->dev;
> +
> +	if (!addr)
> +		return;
> +
> +	dma_free_coherent(dev, epf->bar[bar].size, addr,
> +			  epf->bar[bar].phys_addr);
> +
> +	epf->bar[bar].phys_addr = 0;
> +	epf->bar[bar].size = 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_free_space);
> +
> +/**
> + * pci_epf_alloc_space() - allocate memory for the PCI EPF register space
> + * @size: the size of the memory that has to be allocated
> + * @bar: the bar number corresponding to the allocated register space
> + *
> + * Invoke to allocate memory for the PCI EPF register space.
> + */
> +void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
> +{
> +	void *space;
> +	struct device *dev = &epf->dev;
> +	dma_addr_t phys_addr;
> +
> +	if (size < 128)
> +		size = 128;
> +	size = roundup_pow_of_two(size);
> +
> +	space = dma_alloc_coherent(dev, size, &phys_addr, GFP_KERNEL);
> +	if (!space) {
> +		dev_err(dev, "failed to allocate mem space\n");
> +		return NULL;
> +	}
> +
> +	epf->bar[bar].phys_addr = phys_addr;
> +	epf->bar[bar].size = size;
> +
> +	return space;
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_alloc_space);
> +
> +/**
> + * pci_epf_unregister_driver() - unregister the PCI EPF driver
> + * @driver: the PCI EPF driver that has to be unregistered
> + *
> + * Invoke to unregister the PCI EPF driver.
> + */
> +void pci_epf_unregister_driver(struct pci_epf_driver *driver)
> +{
> +	driver_unregister(&driver->driver);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_unregister_driver);
> +
> +/**
> + * __pci_epf_register_driver() - register a new PCI EPF driver
> + * @driver: structure representing PCI EPF driver
> + * @owner: the owner of the module that registers the PCI EPF driver
> + *
> + * Invoke to register a new PCI EPF driver.
> + */
> +int __pci_epf_register_driver(struct pci_epf_driver *driver,
> +			      struct module *owner)
> +{
> +	int ret;
> +
> +	if (!driver->ops)
> +		return -EINVAL;
> +
> +	if (!driver->ops->bind || !driver->ops->unbind || !driver->ops->linkup)
> +		return -EINVAL;
> +
> +	driver->driver.bus = &pci_epf_bus_type;
> +	driver->driver.owner = owner;
> +
> +	ret = driver_register(&driver->driver);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(__pci_epf_register_driver);
> +
> +/**
> + * pci_epf_destroy() - destroy the created PCI EPF device
> + * @epf: the PCI EPF device that has to be destroyed.
> + *
> + * Invoke to destroy the PCI EPF device created by invoking pci_epf_create().
> + */
> +void pci_epf_destroy(struct pci_epf *epf)
> +{
> +	device_unregister(&epf->dev);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_destroy);
> +
> +/**
> + * pci_epf_create() - create a new PCI EPF device
> + * @name: the name of the PCI EPF device. This name will be used to bind the
> + *	  the EPF device to a EPF driver
> + *
> + * Invoke to create a new PCI EPF device by providing the name of the function
> + * device.
> + */
> +struct pci_epf *pci_epf_create(const char *name)
> +{
> +	int ret;
> +	struct pci_epf *epf;
> +	struct device *dev;
> +	char *func_name;
> +	char *buf;
> +
> +	epf = kzalloc(sizeof(*epf), GFP_KERNEL);
> +	if (!epf) {
> +		ret = -ENOMEM;
> +		goto err_ret;
> +	}
> +
> +	buf = kstrdup(name, GFP_KERNEL);
> +	if (!buf) {
> +		ret = -ENOMEM;
> +		goto free_epf;
> +	}
> +
> +	func_name = buf;
> +	buf = strchrnul(buf, '.');
> +	*buf = '\0';
> +
> +	epf->name = kstrdup(func_name, GFP_KERNEL);
> +	if (!epf->name) {
> +		ret = -ENOMEM;
> +		goto free_epf;
> +	}
> +
> +	dev = &epf->dev;
> +	device_initialize(dev);
> +	dev->bus = &pci_epf_bus_type;
> +	dev->type = &pci_epf_type;
> +
> +	ret = dev_set_name(dev, "%s", name);
> +	if (ret)
> +		goto put_dev;
> +
> +	ret = device_add(dev);
> +	if (ret)
> +		goto put_dev;
> +
> +	kfree(func_name);
> +	return epf;
> +
> +put_dev:
> +	put_device(dev);
> +	kfree(epf->name);
> +	kfree(func_name);
> +
> +free_epf:
> +	kfree(epf);
> +
> +err_ret:
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL_GPL(pci_epf_create);
> +
> +static void pci_epf_dev_release(struct device *dev)
> +{
> +	struct pci_epf *epf = to_pci_epf(dev);
> +
> +	kfree(epf->name);
> +	kfree(epf);
> +}
> +
> +static struct device_type pci_epf_type = {
> +	.release	= pci_epf_dev_release,
> +};
> +
> +static int
> +pci_epf_match_id(const struct pci_epf_device_id *id, const struct pci_epf *epf)
> +{
> +	while (id->name[0]) {
> +		if (strcmp(epf->name, id->name) == 0)
> +			return true;
> +		id++;
> +	}
> +
> +	return false;
> +}
> +
> +static int pci_epf_device_match(struct device *dev, struct device_driver *drv)
> +{
> +	struct pci_epf *epf = to_pci_epf(dev);
> +	struct pci_epf_driver *driver = to_pci_epf_driver(drv);
> +
> +	if (driver->id_table)
> +		return pci_epf_match_id(driver->id_table, epf);
> +
> +	return !strcmp(epf->name, drv->name);
> +}
> +
> +static int pci_epf_device_probe(struct device *dev)
> +{
> +	struct pci_epf *epf = to_pci_epf(dev);
> +	struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
> +
> +	if (!driver->probe)
> +		return -ENODEV;
> +
> +	epf->driver = driver;
> +
> +	return driver->probe(epf);
> +}
> +
> +static int pci_epf_device_remove(struct device *dev)
> +{
> +	int ret;
> +	struct pci_epf *epf = to_pci_epf(dev);
> +	struct pci_epf_driver *driver = to_pci_epf_driver(dev->driver);
> +
> +	ret = driver->remove(epf);
> +	epf->driver = NULL;
> +
> +	return ret;
> +}
> +
> +static struct bus_type pci_epf_bus_type = {
> +	.name		= "pci-epf",
> +	.match		= pci_epf_device_match,
> +	.probe		= pci_epf_device_probe,
> +	.remove		= pci_epf_device_remove,
> +};
> +
> +static int __init pci_epf_init(void)
> +{
> +	int ret;
> +
> +	ret = bus_register(&pci_epf_bus_type);
> +	if (ret) {
> +		pr_err("failed to register pci epf bus --> %d\n", ret);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +module_init(pci_epf_init);
> +
> +static void __exit pci_epf_exit(void)
> +{
> +	bus_unregister(&pci_epf_bus_type);
> +}
> +module_exit(pci_epf_exit);
> +
> +MODULE_DESCRIPTION("PCI EPF Library");
> +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon at ti.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index 8a57f0b..bf706c1 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -428,6 +428,16 @@ struct i2c_device_id {
>  	kernel_ulong_t driver_data;	/* Data private to the driver */
>  };
>  
> +/* pci_epf */
> +
> +#define PCI_EPF_NAME_SIZE	20
> +#define PCI_EPF_MODULE_PREFIX	"pci_epf:"
> +
> +struct pci_epf_device_id {
> +	char name[PCI_EPF_NAME_SIZE];
> +	kernel_ulong_t driver_data;
> +};
> +
>  /* spi */
>  
>  #define SPI_NAME_SIZE	32
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> new file mode 100644
> index 0000000..b62f39d
> --- /dev/null
> +++ b/include/linux/pci-epc.h
> @@ -0,0 +1,141 @@
> +/**
> + * PCI Endpoint *Controller* (EPC) header file
> + *
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon at ti.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + */
> +
> +#ifndef __LINUX_PCI_EPC_H
> +#define __LINUX_PCI_EPC_H
> +
> +#include <linux/pci-epf.h>
> +
> +struct pci_epc;
> +
> +enum pci_epc_irq_type {
> +	PCI_EPC_IRQ_UNKNOWN,
> +	PCI_EPC_IRQ_LEGACY,
> +	PCI_EPC_IRQ_MSI,
> +};
> +
> +/**
> + * struct pci_epc_ops - set of function pointers for performing EPC operations
> + * @write_header: ops to populate configuration space header
> + * @set_bar: ops to configure the BAR
> + * @clear_bar: ops to reset the BAR
> + * @map_addr: ops to map cpu address to pci address
> + * @unmap_addr: ops to unmap cpu address and pci address
> + * @set_msi: ops to set the requested number of MSI interrupts in the MSI
> + *	     capability register
> + * @get_msi: ops to get the number of MSI interrupts allocated by the RC from
> + *	     the MSI capability register
> + * @raise_irq: ops to raise a legacy or MSI interrupt
> + * @start: ops to start the PCI link
> + * @stop: ops to stop the PCI link
> + * @owner: the module owner containing the ops
> + */
> +struct pci_epc_ops {
> +	int	(*write_header)(struct pci_epc *pci_epc,
> +				struct pci_epf_header *hdr);
> +	int	(*set_bar)(struct pci_epc *epc, enum pci_barno bar,
> +			   dma_addr_t bar_phys, size_t size, int flags);
> +	void	(*clear_bar)(struct pci_epc *epc, enum pci_barno bar);
> +	int	(*map_addr)(struct pci_epc *epc, phys_addr_t addr,
> +			    u64 pci_addr, size_t size);
> +	void	(*unmap_addr)(struct pci_epc *epc, phys_addr_t addr);
> +	int	(*set_msi)(struct pci_epc *epc, u8 interrupts);
> +	int	(*get_msi)(struct pci_epc *epc);
> +	int	(*raise_irq)(struct pci_epc *pci_epc,
> +			     enum pci_epc_irq_type type, u8 interrupt_num);
> +	int	(*start)(struct pci_epc *epc);
> +	void	(*stop)(struct pci_epc *epc);
> +	struct module *owner;
> +};
> +
> +/**
> + * struct pci_epc_mem - address space of the endpoint controller
> + * @phys_base: physical base address of the pci address space
> + * @size: the size of the pci address space
> + * @bitmap: bitmap to manage the pci address space
> + * @pages: number of bits representing the address region
> + */
> +struct pci_epc_mem {
> +	phys_addr_t	phys_base;
> +	size_t		size;
> +	unsigned long	*bitmap;
> +	int		pages;
> +};
> +
> +/**
> + * struct pci_epc - represents the PCI EPC device
> + * @dev: PCI EPC device
> + * @pci_epf: list of endpoint functions present in this EPC device
> + * @ops: function pointers for performing endpoint operations
> + * @mem: address space of the endpoint controller
> + * @max_functions: max number of functions that can be configured in this EPC
> + * @lock: spinlock to protect pci_epc ops
> + */
> +struct pci_epc {
> +	struct device			dev;
> +	struct list_head		pci_epf;
> +	const struct pci_epc_ops	*ops;
> +	struct pci_epc_mem		*mem;
> +	u8				max_functions;
> +	/* spinlock to protect against concurrent access of EP controller */
> +	spinlock_t			lock;
> +};
> +
> +#define to_pci_epc(device) container_of((device), struct pci_epc, dev)
> +
> +#define pci_epc_create(dev, ops)    \
> +		__pci_epc_create((dev), (ops), THIS_MODULE)
> +#define devm_pci_epc_create(dev, ops)    \
> +		__devm_pci_epc_create((dev), (ops), THIS_MODULE)
> +
> +static inline void epc_set_drvdata(struct pci_epc *epc, void *data)
> +{
> +	dev_set_drvdata(&epc->dev, data);
> +}
> +
> +static inline void *epc_get_drvdata(struct pci_epc *epc)
> +{
> +	return dev_get_drvdata(&epc->dev);
> +}
> +
> +struct pci_epc *
> +__devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
> +		      struct module *owner);
> +struct pci_epc *
> +__pci_epc_create(struct device *dev, const struct pci_epc_ops *ops,
> +		 struct module *owner);
> +void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc);
> +void pci_epc_destroy(struct pci_epc *epc);
> +int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf);
> +void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf);
> +int pci_epc_write_header(struct pci_epc *epc, struct pci_epf_header *hdr);
> +int pci_epc_set_bar(struct pci_epc *epc, enum pci_barno bar,
> +		    dma_addr_t bar_phys, size_t size, int flags);
> +void pci_epc_clear_bar(struct pci_epc *epc, int bar);
> +int pci_epc_map_addr(struct pci_epc *epc, phys_addr_t phys_addr,
> +		     u64 pci_addr, size_t size);
> +void pci_epc_unmap_addr(struct pci_epc *epc, phys_addr_t phys_addr);
> +int pci_epc_set_msi(struct pci_epc *epc, u8 interrupts);
> +int pci_epc_get_msi(struct pci_epc *epc);
> +int pci_epc_raise_irq(struct pci_epc *epc, enum pci_epc_irq_type type,
> +		      u8 interrupt_num);
> +int pci_epc_start(struct pci_epc *epc);
> +void pci_epc_stop(struct pci_epc *epc);
> +struct pci_epc *pci_epc_get(char *epc_name);
> +void pci_epc_put(struct pci_epc *epc);
> +
> +int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_addr, size_t size);
> +void pci_epc_mem_exit(struct pci_epc *epc);
> +void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
> +				     phys_addr_t *phys_addr, size_t size);
> +void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
> +			   void __iomem *virt_addr, size_t size);
> +#endif /* __LINUX_PCI_EPC_H */
> diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
> new file mode 100644
> index 0000000..54f1338
> --- /dev/null
> +++ b/include/linux/pci-epf.h
> @@ -0,0 +1,160 @@
> +/**
> + * PCI Endpoint *Function* (EPF) header file
> + *
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon at ti.com>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 of
> + * the License as published by the Free Software Foundation.
> + */
> +
> +#ifndef __LINUX_PCI_EPF_H
> +#define __LINUX_PCI_EPF_H
> +
> +#include <linux/device.h>
> +#include <linux/mod_devicetable.h>
> +
> +struct pci_epf;
> +
> +enum pci_interrupt_pin {
> +	PCI_INTERRUPT_UNKNOWN,
> +	PCI_INTERRUPT_INTA,
> +	PCI_INTERRUPT_INTB,
> +	PCI_INTERRUPT_INTC,
> +	PCI_INTERRUPT_INTD,
> +};
> +
> +enum pci_barno {
> +	BAR_0,
> +	BAR_1,
> +	BAR_2,
> +	BAR_3,
> +	BAR_4,
> +	BAR_5,
> +};
> +
> +/**
> + * struct pci_epf_header - represents standard configuration header
> + * @vendorid: identifies device manufacturer
> + * @deviceid: identifies a particular device
> + * @revid: specifies a device specific revision identifier
> + * @progif_code: identifies a specific register-level programming interface
> + * @subclass_code: identifies more specifically the function of the device
> + * @baseclass_code: broadly classifies the type of function the device performs
> + * @cache_line_size: specifies the system cacheline size in units of DWORDs
> + * @subsys_vendor_id: vendor of the add-in card or subsystem
> + * @subsys_id: id specific to vendor
> + * @interrupt_pin: interrupt pin the device (or device function) uses
> + */
> +struct pci_epf_header {
> +	u16	vendorid;
> +	u16	deviceid;
> +	u8	revid;
> +	u8	progif_code;
> +	u8	subclass_code;
> +	u8	baseclass_code;
> +	u8	cache_line_size;
> +	u16	subsys_vendor_id;
> +	u16	subsys_id;
> +	enum pci_interrupt_pin interrupt_pin;
> +};
> +
> +/**
> + * struct pci_epf_ops - set of function pointers for performing EPF operations
> + * @bind: ops to perform when a EPC device has been bound to EPF device
> + * @unbind: ops to perform when a binding has been lost between a EPC device
> + *	    and EPF device
> + * @linkup: ops to perform when the EPC device has established a connection with
> + *	    a host system
> + */
> +struct pci_epf_ops {
> +	int	(*bind)(struct pci_epf *epf);
> +	void	(*unbind)(struct pci_epf *epf);
> +	void	(*linkup)(struct pci_epf *epf);
> +};
> +
> +/**
> + * struct pci_epf_driver - represents the PCI EPF driver
> + * @probe: ops to perform when a new EPF device has been bound to the EPF driver
> + * @remove: ops to perform when the binding between the EPF device and EPF
> + *	    driver is broken
> + * @driver: PCI EPF driver
> + * @ops: set of function pointers for performing EPF operations
> + * @owner: the owner of the module that registers the PCI EPF driver
> + * @id_table: identifies EPF devices for probing
> + */
> +struct pci_epf_driver {
> +	int	(*probe)(struct pci_epf *epf);
> +	int	(*remove)(struct pci_epf *epf);
> +
> +	struct device_driver	driver;
> +	struct pci_epf_ops	*ops;
> +	struct module		*owner;
> +	const struct pci_epf_device_id	*id_table;
> +};
> +
> +#define to_pci_epf_driver(drv) (container_of((drv), struct pci_epf_driver, \
> +				driver))
> +
> +/**
> + * struct pci_epf_bar - represents the BAR of EPF device
> + * @phys_addr: physical address that should be mapped to the BAR
> + * @size: the size of the address space present in BAR
> + */
> +struct pci_epf_bar {
> +	dma_addr_t	phys_addr;
> +	size_t		size;
> +};
> +
> +/**
> + * struct pci_epf - represents the PCI EPF device
> + * @dev: the PCI EPF device
> + * @name: the name of the PCI EPF device
> + * @header: represents standard configuration header
> + * @bar: represents the BAR of EPF device
> + * @msi_interrupts: number of msi interrupts required by this function
> + * @func_no: unique function number within this endpoint device
> + * @epc: the EPC device to which this EPF device is bound
> + * @driver: the EPF driver to which this EPF device is bound
> + * @list: to add pci_epf as a list of pci endpoint functions to pci_epc
> + */
> +struct pci_epf {
> +	struct device		dev;
> +	const char		*name;
> +	struct pci_epf_header	*header;
> +	struct pci_epf_bar	bar[6];
> +	u8			msi_interrupts;
> +	u8			func_no;
> +
> +	struct pci_epc		*epc;
> +	struct pci_epf_driver	*driver;
> +	struct list_head	list;
> +};
> +
> +#define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev)
> +
> +#define pci_epf_register_driver(driver)    \
> +		__pci_epf_register_driver((driver), THIS_MODULE)
> +
> +static inline void epf_set_drvdata(struct pci_epf *epf, void *data)
> +{
> +	dev_set_drvdata(&epf->dev, data);
> +}
> +
> +static inline void *epf_get_drvdata(struct pci_epf *epf)
> +{
> +	return dev_get_drvdata(&epf->dev);
> +}
> +
> +struct pci_epf *pci_epf_create(const char *name);
> +void pci_epf_destroy(struct pci_epf *epf);
> +int __pci_epf_register_driver(struct pci_epf_driver *driver,
> +			      struct module *owner);
> +void pci_epf_unregister_driver(struct pci_epf_driver *driver);
> +void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar);
> +void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar);
> +int pci_epf_bind(struct pci_epf *epf);
> +void pci_epf_unbind(struct pci_epf *epf);
> +void pci_epf_linkup(struct pci_epf *epf);
> +#endif /* __LINUX_PCI_EPF_H */
> 




More information about the linux-arm-kernel mailing list