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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 <kis...@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 */
> 

Reply via email to