pci: xilinx: Add a driver for Xilinx AXI to PCIe bridge
authorPaul Burton <paul.burton@imgtec.com>
Thu, 8 Sep 2016 06:47:31 +0000 (07:47 +0100)
committerDaniel Schwierzeck <daniel.schwierzeck@gmail.com>
Wed, 21 Sep 2016 13:04:32 +0000 (15:04 +0200)
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@imgtec.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
drivers/pci/Kconfig
drivers/pci/Makefile
drivers/pci/pcie_xilinx.c [new file with mode: 0644]

index 669e37b..9a7c187 100644 (file)
@@ -39,4 +39,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
index f8be9bf..9583e91 100644 (file)
@@ -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 (file)
index 0000000..5216001
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * 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 -ENODEV
+ */
+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;
+
+       /*
+        * Busses 0 (host-PCIe bridge) & 1 (its immediate child) are
+        * limited to a single device each.
+        */
+       if ((bus < 2) && (dev > 0))
+               return -ENODEV;
+
+       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 -ENODEV or -EINVAL
+ */
+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 -ENODEV or -EINVAL
+ */
+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;
+
+       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 -EINVAL
+ */
+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 err;
+       }
+
+       pcie->cfg_base = map_physmem(reg_res.start,
+                                    fdt_resource_size(&reg_res),
+                                    MAP_NOCACHE);
+
+       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),
+};