[U-Boot] [PATCH 3/7] pci: xilinx: Add a driver for Xilinx AXI to PCIe bridge

Simon Glass sjg at chromium.org
Wed Jul 27 01:43:08 CEST 2016


Hi Paul,

On 26 July 2016 at 16:24, Paul Burton <paul.burton at imgtec.com> wrote:
> This patch adds a driver for the Xilinx AXI bridge for PCI express, an
> IP block which can be used on some generations of Xilinx FPGAs. This is
> mostly a case of implementing PCIe ECAM specification, but with some
> quirks about what devices are valid to access.
>
> Signed-off-by: Paul Burton <paul.burton at imgtec.com>
> ---
>
>  drivers/pci/Kconfig       |   7 ++
>  drivers/pci/Makefile      |   1 +
>  drivers/pci/pcie_xilinx.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 227 insertions(+)
>  create mode 100644 drivers/pci/pcie_xilinx.c

Reviewed-by: Simon Glass <sjg at chromium.org>

Nits below.

>
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index 26aa2b0..1f9ea66 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -38,4 +38,11 @@ config PCI_TEGRA
>           with a total of 5 lanes. Some boards require this for Ethernet
>           support to work (e.g. beaver, jetson-tk1).
>
> +config PCI_XILINX
> +       bool "Xilinx AXI Bridge for PCI Express"
> +       depends on DM_PCI
> +       help
> +         Enable support for the Xilinx AXI bridge for PCI express, an IP block
> +         which can be used on some generations of Xilinx FPGAs.
> +
>  endmenu
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index f8be9bf..9583e91 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -31,3 +31,4 @@ obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o
>  obj-$(CONFIG_TSI108_PCI) += tsi108_pci.o
>  obj-$(CONFIG_WINBOND_83C553) += w83c553f.o
>  obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o
> +obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o
> diff --git a/drivers/pci/pcie_xilinx.c b/drivers/pci/pcie_xilinx.c
> new file mode 100644
> index 0000000..55c3454
> --- /dev/null
> +++ b/drivers/pci/pcie_xilinx.c
> @@ -0,0 +1,219 @@
> +/*
> + * Xilinx AXI Bridge for PCI Express Driver
> + *
> + * Copyright (C) 2016 Imagination Technologies
> + *
> + * SPDX-License-Identifier:    GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <pci.h>
> +
> +#include <asm/io.h>
> +
> +/**
> + * struct xilinx_pcie - Xilinx PCIe controller state
> + * @hose: The parent classes PCI controller state
> + * @cfg_base: The base address of memory mapped configuration space
> + */
> +struct xilinx_pcie {
> +       struct pci_controller hose;
> +       void *cfg_base;
> +};
> +
> +/* Register definitions */
> +#define XILINX_PCIE_REG_PSCR           0x144
> +#define XILINX_PCIE_REG_PSCR_LNKUP     BIT(11)
> +
> +/**
> + * pcie_xilinx_link_up() - Check whether the PCIe link is up
> + * @pcie: Pointer to the PCI controller state
> + *
> + * Checks whether the PCIe link for the given device is up or down.
> + *
> + * Return: true if the link is up, else false
> + */
> +static bool pcie_xilinx_link_up(struct xilinx_pcie *pcie)
> +{
> +       uint32_t pscr = __raw_readl(pcie->cfg_base + XILINX_PCIE_REG_PSCR);
> +
> +       return pscr & XILINX_PCIE_REG_PSCR_LNKUP;
> +}
> +
> +/**
> + * pcie_xilinx_config_address() - Calculate the address of a config access
> + * @pcie: Pointer to the PCI controller state
> + * @bdf: Identifies the PCIe device to access
> + * @offset: The offset into the device's configuration space
> + * @paddress: Pointer to the pointer to write the calculates address to
> + *
> + * Calculates the address that should be accessed to perform a PCIe
> + * configuration space access for a given device identified by the PCIe
> + * controller device @pcie and the bus, device & function numbers in @bdf. If
> + * access to the device is not valid then the function will return an error
> + * code. Otherwise the address to access will be written to the pointer pointed
> + * to by @paddress.
> + *
> + * Return: 0 on success, else -ERRNO

-EINVAL or something

> + */
> +static int pcie_xilinx_config_address(struct xilinx_pcie *pcie, pci_dev_t bdf,
> +                                     uint offset, void **paddress)
> +{
> +       unsigned int bus = PCI_BUS(bdf);
> +       unsigned int dev = PCI_DEV(bdf);
> +       unsigned int func = PCI_FUNC(bdf);
> +       void *addr;
> +
> +       if ((bus > 0) && !pcie_xilinx_link_up(pcie))
> +               return -ENODEV;

There is a device, so perhaps -EINVAL would be better?

> +
> +       /*
> +        * Busses 0 (host-PCIe bridge) & 1 (its immediate child) are
> +        * limited to a single device each.
> +        */
> +       if ((bus < 2) && (dev > 0))
> +               return -ENODEV;

Same here.

> +
> +       addr = pcie->cfg_base;
> +       addr += bus << 20;
> +       addr += dev << 15;
> +       addr += func << 12;
> +       addr += offset;
> +       *paddress = addr;
> +
> +       return 0;
> +}
> +
> +/**
> + * pcie_xilinx_read_config() - Read from configuration space
> + * @pcie: Pointer to the PCI controller state
> + * @bdf: Identifies the PCIe device to access
> + * @offset: The offset into the device's configuration space
> + * @valuep: A pointer at which to store the read value
> + * @size: Indicates the size of access to perform
> + *
> + * Read a value of size @size from offset @offset within the configuration
> + * space of the device identified by the bus, device & function numbers in @bdf
> + * on the PCI bus @bus.
> + *
> + * Return: 0 on success, else -ERRNO
> + */
> +static int pcie_xilinx_read_config(struct udevice *bus, pci_dev_t bdf,
> +                                  uint offset, ulong *valuep,
> +                                  enum pci_size_t size)
> +{
> +       struct xilinx_pcie *pcie = dev_get_priv(bus);
> +       void *address;
> +       int err;
> +
> +       err = pcie_xilinx_config_address(pcie, bdf, offset, &address);
> +       if (err < 0) {
> +               *valuep = pci_get_ff(size);
> +               return 0;
> +       }
> +
> +       switch (size) {
> +       case PCI_SIZE_8:
> +               *valuep = __raw_readb(address);
> +               return 0;
> +       case PCI_SIZE_16:
> +               *valuep = __raw_readw(address);
> +               return 0;
> +       case PCI_SIZE_32:
> +               *valuep = __raw_readl(address);
> +               return 0;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +/**
> + * pcie_xilinx_write_config() - Write to configuration space
> + * @pcie: Pointer to the PCI controller state
> + * @bdf: Identifies the PCIe device to access
> + * @offset: The offset into the device's configuration space
> + * @value: The value to write
> + * @size: Indicates the size of access to perform
> + *
> + * Write the value @value of size @size from offset @offset within the
> + * configuration space of the device identified by the bus, device & function
> + * numbers in @bdf on the PCI bus @bus.
> + *
> + * Return: 0 on success, else -ERRNO
> + */
> +static int pcie_xilinx_write_config(struct udevice *bus, pci_dev_t bdf,
> +                                   uint offset, ulong value,
> +                                   enum pci_size_t size)
> +{
> +       struct xilinx_pcie *pcie = dev_get_priv(bus);
> +       void *address;
> +       int err;
> +
> +       err = pcie_xilinx_config_address(pcie, bdf, offset, &address);
> +       if (err < 0)
> +               return 0;

Why squash the error?

> +
> +       switch (size) {
> +       case PCI_SIZE_8:
> +               __raw_writeb(value, address);
> +               return 0;
> +       case PCI_SIZE_16:
> +               __raw_writew(value, address);
> +               return 0;
> +       case PCI_SIZE_32:
> +               __raw_writel(value, address);
> +               return 0;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +/**
> + * pcie_xilinx_ofdata_to_platdata() - Translate from DT to device state
> + * @dev: A pointer to the device being operated on
> + *
> + * Translate relevant data from the device tree pertaining to device @dev into
> + * state that the driver will later make use of. This state is stored in the
> + * device's private data structure.
> + *
> + * Return: 0 on success, else -ERRNO
> + */
> +static int pcie_xilinx_ofdata_to_platdata(struct udevice *dev)
> +{
> +       struct xilinx_pcie *pcie = dev_get_priv(dev);
> +       struct fdt_resource reg_res;
> +       DECLARE_GLOBAL_DATA_PTR;
> +       int err;
> +
> +       err = fdt_get_resource(gd->fdt_blob, dev->of_offset, "reg",
> +                              0, &reg_res);
> +       if (err < 0) {
> +               error("\"reg\" resource not found\n");
> +               return -EINVAL;

return ret?

> +       }
> +
> +       pcie->cfg_base = ioremap_nocache(reg_res.start,
> +                                        fdt_resource_size(&reg_res));
> +
> +       return 0;
> +}
> +
> +static const struct dm_pci_ops pcie_xilinx_ops = {
> +       .read_config    = pcie_xilinx_read_config,
> +       .write_config   = pcie_xilinx_write_config,
> +};
> +
> +static const struct udevice_id pcie_xilinx_ids[] = {
> +       { .compatible = "xlnx,axi-pcie-host-1.00.a" },
> +       { }
> +};
> +
> +U_BOOT_DRIVER(pcie_xilinx) = {
> +       .name                   = "pcie_xilinx",
> +       .id                     = UCLASS_PCI,
> +       .of_match               = pcie_xilinx_ids,
> +       .ops                    = &pcie_xilinx_ops,
> +       .ofdata_to_platdata     = pcie_xilinx_ofdata_to_platdata,
> +       .priv_auto_alloc_size   = sizeof(struct xilinx_pcie),
> +};
> --
> 2.9.0
>

Regards,
Simon


More information about the U-Boot mailing list