[U-Boot] [PATCH v1 1/3] drivers: pci_ep: Introduce UCLASS_PCI_EP uclass

Simon Glass
2019-04-22T02:56:01Z

Hi Ramon,

On Fri, 5 Apr 2019 at 19:12, Ramon Fried <ramon.fried@gmail.com> wrote:
>
> Introduce new UCLASS_PCI_EP class for handling PCI endpoint
> devices, allowing to set various attributes of the PCI endpoint
> device, such as:
> * configuration space header
> * BAR definitions
> * outband memory mapping
> * start/stop PCI link
>
> Signed-off-by: Ramon Fried <ramon.fried@gmail.com>
>
> ---
>
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/pci_endpoint/Kconfig | 16 ++
> drivers/pci_endpoint/Makefile | 6 +
> drivers/pci_endpoint/pci_ep-uclass.c | 192 ++++++++++++++
> include/dm/uclass-id.h | 1 +
> include/pci_ep.h | 375 +++++++++++++++++++++++++++
> 7 files changed, 593 insertions(+)
> create mode 100644 drivers/pci_endpoint/Kconfig
> create mode 100644 drivers/pci_endpoint/Makefile
> create mode 100644 drivers/pci_endpoint/pci_ep-uclass.c
> create mode 100644 include/pci_ep.h

This looks pretty good but I have a lot of nits, sorry.

Please can you add a sandbox driver and a test for this (can be in a
separate patch).

>
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index f24351ac4f..59e2c22cc6 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -64,6 +64,8 @@ source "drivers/nvme/Kconfig"
>
> source "drivers/pci/Kconfig"
>
> +source "drivers/pci_endpoint/Kconfig"
> +
> source "drivers/pch/Kconfig"
>
> source "drivers/pcmcia/Kconfig"
> diff --git a/drivers/Makefile b/drivers/Makefile
> index a7bba3ed56..480b97ef58 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -85,6 +85,7 @@ obj-$(CONFIG_FPGA) += fpga/
> obj-y += misc/
> obj-$(CONFIG_MMC) += mmc/
> obj-$(CONFIG_NVME) += nvme/
> +obj-$(CONFIG_PCI_ENDPOINT) += pci_endpoint/
> obj-y += pcmcia/
> obj-y += dfu/
> obj-$(CONFIG_PCH) += pch/
> diff --git a/drivers/pci_endpoint/Kconfig b/drivers/pci_endpoint/Kconfig
> new file mode 100644
> index 0000000000..2c0a399a08
> --- /dev/null
> +++ b/drivers/pci_endpoint/Kconfig
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# PCI Endpoint Support
> +#
> +
> +menu "PCI Endpoint"
> +
> +config PCI_ENDPOINT
> + bool "PCI Endpoint Support"
> + depends on DM
> + help
> + Enable this configuration option to support configurable PCI
> + endpoint. This should be enabled if the platform has a PCI

s/endpoint/endpoints/ I think

> + controller that can operate in endpoint mode.

Can you explain a bit more about what this means. I understand that
people can look at the spec, but what is the purpose of 'endpoint
mode' and what does it enable?

> +
> +endmenu
> diff --git a/drivers/pci_endpoint/Makefile b/drivers/pci_endpoint/Makefile
> new file mode 100644
> index 0000000000..80a1066925
> --- /dev/null
> +++ b/drivers/pci_endpoint/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +# (C) Copyright 2019
> +# Ramon Fried <ramon.fried@gmail.com>
> +
> +obj-y += pci_ep-uclass.o
> diff --git a/drivers/pci_endpoint/pci_ep-uclass.c b/drivers/pci_endpoint/pci_ep-uclass.c
> new file mode 100644
> index 0000000000..06fcfc5d14
> --- /dev/null
> +++ b/drivers/pci_endpoint/pci_ep-uclass.c
> @@ -0,0 +1,192 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * PCI Endpoint uclass
> + *
> + * Based on Linux PCI-EP driver written by
> + * Kishon Vijay Abraham I <kishon@ti.com>
> + *
> + * Copyright (c) 2019
> + * Written by Ramon Fried <ramon.fried@gmail.com>
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <linux/log2.h>
> +#include <pci_ep.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +int dm_pci_ep_write_header(struct udevice *dev, u8 fn,
> + struct pci_ep_header *hdr)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->write_header)
> + return -ENODEV;

-ENOSYS (please fix globally)

There is a device. The problem here is that the driver does not
implement the method.

> +
> + return ops->write_header(dev, fn, hdr);
> +}
> +
> +int dm_pci_ep_read_header(struct udevice *dev, u8 fn,
> + struct pci_ep_header *hdr)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->read_header)
> + return -ENODEV;
> +
> + return ops->read_header(dev, fn, hdr);
> +}
> +
> +int dm_pci_ep_set_bar(struct udevice *dev, u8 func_no,

Please use uint for func_no. There is no need to limit it to 8 bits,
and this may not be efficient for non-8-bit machines. Please fix
globally.

> + struct pci_bar *ep_bar)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> + int flags = ep_bar->flags;
> +
> + /* Some basic bar validity checks */
> + if ((ep_bar->barno == BAR_5 && flags &
> + PCI_BASE_ADDRESS_MEM_TYPE_64) ||
> + (flags & PCI_BASE_ADDRESS_SPACE_IO &&
> + flags & PCI_BASE_ADDRESS_IO_MASK) ||

Can you add another set of () in those two lines?

> + (upper_32_bits(ep_bar->size) &&
> + !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
> + return -EINVAL;
> +
> + if (!ops->set_bar)
> + return -ENODEV;
> +
> + return ops->set_bar(dev, func_no, ep_bar);
> +}
> +
> +void pci_ep_clear_bar(struct udevice *dev, u8 func_num, enum pci_barno bar)

This should return an error code.

> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->clear_bar)
> + return;
> +
> + ops->clear_bar(dev, func_num, bar);
> +}
> +
> +int dm_pci_ep_map_addr(struct udevice *dev, u8 func_no, phys_addr_t addr,
> + u64 pci_addr, size_t size)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->map_addr)
> + return -ENODEV;
> +
> + return ops->map_addr(dev, func_no, addr, pci_addr, size);
> +}
> +
> +void dm_pci_ep_unmap_addr(struct udevice *dev, u8 func_no, phys_addr_t addr)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->unmap_addr)
> + return;
> +
> + ops->unmap_addr(dev, func_no, addr);
> +}
> +
> +int dm_pci_ep_set_msi(struct udevice *dev, u8 func_no, u8 interrupts)

Similar with interrupts (use uint).

> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> + u8 encode_int;

uint

> +
> + if (interrupts > 32)
> + return -EINVAL;
> +
> + if (!ops->set_msi)
> + return -ENODEV;
> +
> + encode_int = order_base_2(interrupts);
> +
> + return ops->set_msi(dev, func_no, encode_int);
> +}
> +
> +int dm_pci_ep_get_msi(struct udevice *dev, u8 func_no)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> + int interrupt;
> +
> + if (!ops->get_msi)
> + return -ENODEV;
> +
> + interrupt = ops->get_msi(dev, func_no);
> +
> + if (interrupt < 0)
> + return 0;
> +
> + interrupt = 1 << interrupt;

Perhaps you should declare a 'mask' variable? In any case, it is
confusing for the same variable to have two different meanings, and it
does not save code@run-time.

> +
> + return interrupt;
> +}
> +
> +int dm_pci_ep_set_msix(struct udevice *dev, u8 func_no, u16 interrupts)

s/u16/uint/

There is no saving by using u16, only potential cost. You range-check
it below anyway. Please fix globally.

> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (interrupts < 1 || interrupts > 2048)
> + return -EINVAL;
> +
> + if (!ops->set_msix)
> + return -ENODEV;
> +
> + return ops->set_msix(dev, func_no, interrupts);
> +}
> +
> +int dm_pci_ep_get_msix(struct udevice *dev, u8 func_no)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> + int interrupt;
> +
> + if (!ops->get_msix)
> + return -ENODEV;
> +
> + interrupt = ops->get_msix(dev, func_no);
> +
> + if (interrupt < 0)
> + return 0;
> +
> + return interrupt + 1;

It's odd that your uclass function returns a different value from your
driver function. I think that will be confusing. Is it necessary?

> +}
> +
> +int dm_pci_ep_raise_irq(struct udevice *dev, u8 func_no,
> + enum pci_ep_irq_type type, u16 interrupt_num)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->raise_irq)
> + return -ENODEV;
> +
> + return ops->raise_irq(dev, func_no, type, interrupt_num);
> +}
> +
> +int dm_pci_ep_start(struct udevice *dev)
> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->start)
> + return -ENODEV;
> +
> + return ops->start(dev);
> +}
> +
> +void dm_pci_ep_stop(struct udevice *dev)

Needs a return value.

> +{
> + struct dm_pci_ep_ops *ops = pci_ep_get_ops(dev);
> +
> + if (!ops->stop)
> + return;
> +
> + ops->stop(dev);
> +}
> +
> +UCLASS_DRIVER(pci_ep) = {
> + .id = UCLASS_PCI_EP,
> + .name = "pci_ep",
> + .flags = DM_UC_FLAG_SEQ_ALIAS,
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 86e59781b0..d29f7d5a34 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -67,6 +67,7 @@ enum uclass_id {
> UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */
> UCLASS_PCH, /* x86 platform controller hub */
> UCLASS_PCI, /* PCI bus */
> + UCLASS_PCI_EP, /* PCI endpoint device */
> UCLASS_PCI_GENERIC, /* Generic PCI bus device */
> UCLASS_PHY, /* Physical Layer (PHY) device */
> UCLASS_PINCONFIG, /* Pin configuration node device */
> diff --git a/include/pci_ep.h b/include/pci_ep.h
> new file mode 100644
> index 0000000000..c98453ccfa
> --- /dev/null
> +++ b/include/pci_ep.h
> @@ -0,0 +1,375 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Adapted from Linux kernel driver
> + * Copyright (C) 2017 Texas Instruments
> + * Author: Kishon Vijay Abraham I <kishon@ti.com>
> + *
> + * (C) Copyright 2019
> + * Ramon Fried <ramon.fried@gmail.com>
> + */
> +
> +#ifndef _PCI_EP_H
> +#define _PCI_EP_H
> +
> +#include <pci.h>
> +
> +/**
> + * enum pci_interrupt_pin - PCI INTx interrupt values
> + * @PCI_INTERRUPT_UNKNOWN: Unknown or unassigned interrupt
> + * @PCI_INTERRUPT_INTA: PCI INTA pin
> + * @PCI_INTERRUPT_INTB: PCI INTB pin
> + * @PCI_INTERRUPT_INTC: PCI INTC pin
> + * @PCI_INTERRUPT_INTD: PCI INTD pin
> + *
> + * Corresponds to values for legacy PCI INTx interrupts, as can be found in the
> + * PCI_INTERRUPT_PIN register.
> + */
> +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,
> +};
> +
> +enum pci_ep_irq_type {
> + PCI_EP_IRQ_UNKNOWN,
> + PCI_EP_IRQ_LEGACY,
> + PCI_EP_IRQ_MSI,
> + PCI_EP_IRQ_MSIX,
> +};
> +
> +/**
> + * struct pci_bar - represents the BAR of EP device

What is a BAR and what is an EP? Please spell things out in comments,
at least ones per file:

base-address register (BAR)

> + * @phys_addr: physical address that should be mapped to the BAR
> + * @size: the size of the address space present in BAR

Please add the other two also

> + */
> +struct pci_bar {
> + dma_addr_t phys_addr;
> + size_t size;
> + enum pci_barno barno;
> + int flags;
> +};
> +
> +/**
> + * struct pci_ep_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

What is a DWORD? I think i is 32-bits, so just say that, since WORD is
a confusing term on a non-16-bit machine.

> + * @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_ep_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;

Shouldn't that be a u16 or something? The enum does not have a defined
size, I think.

> +};
> +
> +/* PCI endpoint operations */
> +struct dm_pci_ep_ops {
> + /**
> + * write_header() - Write a PCI configuration space header
> + *
> + * @dev: device to write to
> + * @func_num: Function to fill

Is this the function number within the device? If so, how about
'Function number (within device) to write to'? Similarly below.

> + * @hdr: header to write
> + * @return 0 if OK, -ve on error
> + */
> + int (*write_header)(struct udevice *dev, u8 func_num,
> + struct pci_ep_header *hdr);
> + /**
> + * read_header() - Read a PCI configuration space header
> + *
> + * @dev: device to write to
> + * @func_num: Function to fill
> + * @hdr: header to read to
> + * @return 0 if OK, -ve on error
> + */
> + int (*read_header)(struct udevice *dev, u8 func_num,
> + struct pci_ep_header *hdr);
> + /**
> + * set_bar() - set BAR (Base Address Register)

Good to expand BAR as you have here

> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @bar: bar data
> + * @return 0 if OK, -ve on error
> + */
> + int (*set_bar)(struct udevice *dev, u8 func_num,
> + struct pci_bar *bar);
> + /**
> + * clear_bar() - clear BAR (Base Address Register)
> + *
> + * @dev: device to clear
> + * @func_num: Function to clear
> + * @bar: bar number
> + */
> + void (*clear_bar)(struct udevice *dev, u8 func_num,
> + enum pci_barno bar);
> + /**
> + * map_addr() - map CPU address to PCI address
> + *
> + * outband region is used in order to generate PCI read/write
> + * transaction from local memory/write.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @addr: local physical address base
> + * @pci_addr: pci address to translate to
> + * @size: region size
> + * @return 0 if OK, -ve on error
> + */
> + int (*map_addr)(struct udevice *dev, u8 func_num,
> + phys_addr_t addr, u64 pci_addr, size_t size);
> + /**
> + * unmap_addr() - unmap CPU address to PCI address
> + *
> + * unmap previously mapped region.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @addr: local physical address base
> + */
> + void (*unmap_addr)(struct udevice *dev, u8 func_num,
> + phys_addr_t addr);
> + /**
> + * set_msi() - set msi capability property
> + *
> + * set the number of required MSI vectors the device
> + * needs for operation.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @interrupts: required interrupts count
> + * @return 0 if OK, -ve on error
> + */
> + int (*set_msi)(struct udevice *dev, u8 func_num, u8 interrupts);
> +
> + /**
> + * get_msi() - get the number of MSI interrupts allocated by the host.
> + *
> + * Read the Multiple Message Enable bitfield from
> + * Message control register.
> + *
> + * @dev: device to use
> + * @func_num: Function to use
> + * @return msi count if OK, -EINVAL if msi were not enabled at host.
> + */
> + int (*get_msi)(struct udevice *dev, u8 func_num);
> +
> + /**
> + * set_msix() - set msix capability property
> + *
> + * set the number of required MSIx vectors the device
> + * needs for operation.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @interrupts: required interrupts count

This is too vague, please expand it.

> + * @return 0 if OK, -ve on error
> + */
> + int (*set_msix)(struct udevice *dev, u8 func_num, u16 interrupts);
> +
> + /**
> + * get_msix() - get the number of MSIx interrupts allocated by the host.
> + *
> + * Read the Multiple Message Enable bitfield from
> + * Message control register.
> + *
> + * @dev: device to use
> + * @func_num: Function to use
> + * @return msi count if OK, -EINVAL if msi were not enabled at host.
> + */
> + int (*get_msix)(struct udevice *dev, u8 func_num);
> +
> + /**
> + * raise_irq() - raise a legacy, MSI or MSI-X interrupt
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @type: type of irq to send
> + * @interrupt_num: interrupt vector to use
> + * @return 0 if OK, -ve on error
> + */
> + int (*raise_irq)(struct udevice *dev, u8 func_num,
> + enum pci_ep_irq_type type, u16 interrupt_num);
> + /**
> + * start() - start the PCI link
> + *

Needs more docs. What is the effect of this?

> + * @dev: device to set
> + * @return 0 if OK, -ve on error
> + */
> + int (*start)(struct udevice *dev);
> +
> + /**
> + * stop() - stop the PCI link

Needs more docs. What is the effect of this?

> + *
> + * @dev: device to set
> + */
> + void (*stop)(struct udevice *dev);
> +};
> +
> +#define pci_ep_get_ops(dev) ((struct dm_pci_ep_ops *)(dev)->driver->ops)
> +
> +/**
> + * pci_ep_write_header() - Write a PCI configuration space header
> + *
> + * @dev: device to write to
> + * @func_num: Function to fill
> + * @hdr: header to write
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_write_header(struct udevice *dev, u8 func_num,
> + struct pci_ep_header *hdr);
> +
> +/**
> + * dm_pci_ep_read_header() - Read a PCI configuration space header
> + *
> + * @dev: device to write to
> + * @func_num: Function to fill
> + * @hdr: header to read to
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_read_header(struct udevice *dev, u8 func_num,
> + struct pci_ep_header *hdr);
> +/**
> + * pci_ep_set_bar() - set BAR (Base Address Register)
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @bar: bar data
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_set_bar(struct udevice *dev, u8 func_num, struct pci_bar *bar);
> +
> +/**
> + * pci_ep_clear_bar() - clear BAR (Base Address Register)
> + *
> + * @dev: device to clear
> + * @func_num: Function to clear
> + * @bar: bar number
> + */
> +void pci_ep_clear_bar(struct udevice *dev, u8 func_num, enum pci_barno bar);
> +/**
> + * pci_ep_map_addr() - map CPU address to PCI address
> + *
> + * outband region is used in order to generate PCI read/write
> + * transaction from local memory/write.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @addr: local physical address base
> + * @pci_addr: pci address to translate to
> + * @size: region size
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_map_addr(struct udevice *dev, u8 func_num, phys_addr_t addr,
> + u64 pci_addr, size_t size);
> +/**
> + * pci_ep_unmap_addr() - unmap CPU address to PCI address
> + *
> + * unmap previously mapped region.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @addr: local physical address base
> + */
> +void pci_ep_unmap_addr(struct udevice *dev, u8 func_num, phys_addr_t addr);
> +/**
> + * pci_ep_set_msi() - set msi capability property
> + *
> + * set the number of required MSI vectors the device
> + * needs for operation.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @interrupts: required interrupts count
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_set_msi(struct udevice *dev, u8 func_num, u8 interrupts);
> +
> +/**
> + * pci_ep_get_msi() - get the number of MSI interrupts allocated by the host.
> + *
> + * Read the Multiple Message Enable bitfield from
> + * Message control register.
> + *
> + * @dev: device to use
> + * @func_num: Function to use
> + * @return msi count if OK, -EINVAL if msi were not enabled at host.
> + */
> +int pci_ep_get_msi(struct udevice *dev, u8 func_num);
> +
> +/**
> + * pci_ep_set_msix() - set msi capability property
> + *
> + * set the number of required MSIx vectors the device
> + * needs for operation.
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @interrupts: required interrupts count
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_set_msix(struct udevice *dev, u8 func_num, u16 interrupts);
> +
> +/**
> + * pci_ep_get_msix() - get the number of MSIx interrupts allocated by the host.
> + *
> + * Read the Multiple Message Enable bitfield from
> + * Message control register.
> + *
> + * @dev: device to use
> + * @func_num: Function to use
> + * @return msi count if OK, -EINVAL if msi were not enabled at host.
> + */
> +int pci_ep_get_msix(struct udevice *dev, u8 func_num);
> +
> +/**
> + * pci_ep_raise_irq() - raise a legacy, MSI or MSI-X interrupt
> + *
> + * @dev: device to set
> + * @func_num: Function to set
> + * @type: type of irq to send
> + * @interrupt_num: interrupt vector to use
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_raise_irq(struct udevice *dev, u8 func_num,
> + enum pci_ep_irq_type type, u16 interrupt_num);
> +/**
> + * pci_ep_start() - start the PCI link
> + *
> + * @dev: device to set
> + * @return 0 if OK, -ve on error
> + */
> +int pci_ep_start(struct udevice *dev);
> +
> +/**
> + * pci_ep_stop() - stop the PCI link
> + *
> + * @dev: device to set
> + */
> +void pci_ep_stop(struct udevice *dev);
> +
> +#endif
> --
> 2.21.0
>

Regards,
Simon