From: Suneel Garapati <sgarap...@marvell.com>

Adds support for PCI ECAM/PEM controllers found on OcteonTX
or OcteonTX2 SoC platforms.

Signed-off-by: Suneel Garapati <sgarap...@marvell.com>
Reviewed-by: Simon Glass <s...@chromium.org>
Cc: Bin Meng <bmeng...@gmail.com>

Signed-off-by: Stefan Roese <s...@denx.de>
---

Changes in v2:
- Add Reviewed-by tag from Simon
- Add comments to enum declaration and "type" declaration
- Return -EINVAL in default case statement for unsupported length
- Add helper function octeontx_bdf_invalid() and return -EPERM
  in case of an errror
- Use livetree API

Changes in v1:
- Change patch subject
- Remove inclusion of common.h
- Remove #ifdef's and use driver specific data instead
- Add comments to struct
- Add some helper functions to reduce code size
- Misc coding style changes (blank lines etc)
- Use debug() instead of printf() in some cases

 drivers/pci/Kconfig        |   8 +
 drivers/pci/Makefile       |   1 +
 drivers/pci/pci_octeontx.c | 364 +++++++++++++++++++++++++++++++++++++
 3 files changed, 373 insertions(+)
 create mode 100644 drivers/pci/pci_octeontx.c

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 377806f3b0..06d39df1d3 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -149,6 +149,14 @@ 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_OCTEONTX
+       bool "OcteonTX PCI support"
+       depends on (ARCH_OCTEONTX || ARCH_OCTEONTX2)
+       help
+         Enable support for the OcteonTX/TX2 SoC family ECAM/PEM controllers.
+         These controllers provide PCI configuration access to all on-board
+         peripherals so it should only be disabled for testing purposes
+
 config PCI_XILINX
        bool "Xilinx AXI Bridge for PCI Express"
        depends on DM_PCI
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 9db90fb53c..8b4d49a590 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -49,3 +49,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
 obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
 obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
 obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
+obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o
diff --git a/drivers/pci/pci_octeontx.c b/drivers/pci/pci_octeontx.c
new file mode 100644
index 0000000000..30537543a0
--- /dev/null
+++ b/drivers/pci/pci_octeontx.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier:    GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ *
+ * https://spdx.org/licenses
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <pci.h>
+
+#include <asm/io.h>
+
+#include <linux/ioport.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * This driver supports multiple types of operations / host bridges / busses:
+ *
+ * OTX_ECAM: Octeon TX & TX2 ECAM (Enhanced Configuration Access Mechanism)
+ *          Used to access the internal on-chip devices which are connected
+ *          to internal buses
+ * OTX_PEM:  Octeon TX PEM (PCI Express MAC)
+ *          Used to access the external (off-chip) PCI devices
+ * OTX2_PEM: Octeon TX2 PEM (PCI Express MAC)
+ *          Used to access the external (off-chip) PCI devices
+ */
+enum {
+       OTX_ECAM,
+       OTX_PEM,
+       OTX2_PEM,
+};
+
+/**
+ * struct octeontx_pci - Driver private data
+ * @type:      Device type matched via compatible (e.g. OTX_ECAM etc)
+ * @cfg:       Config resource
+ * @bus:       Bus resource
+ */
+struct octeontx_pci {
+       unsigned int type;
+
+       struct resource cfg;
+       struct resource bus;
+};
+
+static uintptr_t octeontx_cfg_addr(struct octeontx_pci *pcie,
+                                  int bus_offs, int shift_offs,
+                                  pci_dev_t bdf, uint offset)
+{
+       u32 bus, dev, func;
+       uintptr_t address;
+
+       bus = PCI_BUS(bdf) + bus_offs;
+       dev = PCI_DEV(bdf);
+       func = PCI_FUNC(bdf);
+
+       address = (bus << (20 + shift_offs)) |
+               (dev << (15 + shift_offs)) |
+               (func << (12 + shift_offs)) | offset;
+       address += pcie->cfg.start;
+
+       return address;
+}
+
+static ulong readl_size(uintptr_t addr, enum pci_size_t size)
+{
+       ulong val;
+
+       switch (size) {
+       case PCI_SIZE_8:
+               val = readb(addr);
+               break;
+       case PCI_SIZE_16:
+               val = readw(addr);
+               break;
+       case PCI_SIZE_32:
+               val = readl(addr);
+               break;
+       default:
+               printf("Invalid size\n");
+               return -EINVAL;
+       };
+
+       return val;
+}
+
+static void writel_size(uintptr_t addr, enum pci_size_t size, ulong valuep)
+{
+       switch (size) {
+       case PCI_SIZE_8:
+               writeb(valuep, addr);
+               break;
+       case PCI_SIZE_16:
+               writew(valuep, addr);
+               break;
+       case PCI_SIZE_32:
+               writel(valuep, addr);
+               break;
+       default:
+               printf("Invalid size\n");
+       };
+}
+
+static bool octeontx_bdf_invalid(pci_dev_t bdf)
+{
+       if (PCI_BUS(bdf) == 1 && PCI_DEV(bdf) > 0)
+               return true;
+
+       return false;
+}
+
+static int octeontx_ecam_read_config(const struct udevice *bus, pci_dev_t bdf,
+                                    uint offset, ulong *valuep,
+                                    enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+
+       address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
+                                   0, bdf, offset);
+       *valuep = readl_size(address, size);
+
+       debug("%02x.%02x.%02x: u%d %x -> %lx\n",
+             PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, *valuep);
+
+       return 0;
+}
+
+static int octeontx_ecam_write_config(struct udevice *bus, pci_dev_t bdf,
+                                     uint offset, ulong value,
+                                     enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+
+       address = octeontx_cfg_addr(pcie, pcie->bus.start - hose->first_busno,
+                                   0, bdf, offset);
+       writel_size(address, size, value);
+
+       debug("%02x.%02x.%02x: u%d %x <- %lx\n",
+             PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset, value);
+
+       return 0;
+}
+
+static int octeontx_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
+                                   uint offset, ulong *valuep,
+                                   enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+       u8 hdrtype;
+       u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
+       u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
+
+       address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4,
+                                   bdf, 0);
+
+       *valuep = pci_conv_32_to_size(~0UL, offset, size);
+
+       if (octeontx_bdf_invalid(bdf))
+               return -EPERM;
+
+       *valuep = readl_size(address + offset, size);
+
+       hdrtype = readb(address + PCI_HEADER_TYPE);
+       if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
+           offset >= PCI_PRIMARY_BUS &&
+           offset <= PCI_SUBORDINATE_BUS &&
+           *valuep != pci_conv_32_to_size(~0UL, offset, size))
+               *valuep -= pci_conv_32_to_size(bus_offs, offset, size);
+
+       return 0;
+}
+
+static int octeontx_pem_write_config(struct udevice *bus, pci_dev_t bdf,
+                                    uint offset, ulong value,
+                                    enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+       u8 hdrtype;
+       u8 pri_bus = pcie->bus.start + 1 - hose->first_busno;
+       u32 bus_offs = (pri_bus << 16) | (pri_bus << 8) | (pri_bus << 0);
+
+       address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 4, bdf, 0);
+
+       hdrtype = readb(address + PCI_HEADER_TYPE);
+       if (hdrtype == PCI_HEADER_TYPE_BRIDGE &&
+           offset >= PCI_PRIMARY_BUS &&
+           offset <= PCI_SUBORDINATE_BUS &&
+           value != pci_conv_32_to_size(~0UL, offset, size))
+               value +=  pci_conv_32_to_size(bus_offs, offset, size);
+
+       if (octeontx_bdf_invalid(bdf))
+               return -EPERM;
+
+       writel_size(address + offset, size, value);
+
+       debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
+             PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+             address, value);
+
+       return 0;
+}
+
+static int octeontx2_pem_read_config(const struct udevice *bus, pci_dev_t bdf,
+                                    uint offset, ulong *valuep,
+                                    enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+
+       address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
+                                   bdf, 0);
+
+       *valuep = pci_conv_32_to_size(~0UL, offset, size);
+
+       if (octeontx_bdf_invalid(bdf))
+               return -EPERM;
+
+       *valuep = readl_size(address + offset, size);
+
+       debug("%02x.%02x.%02x: u%d %x (%lx) -> %lx\n",
+             PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+             address, *valuep);
+
+       return 0;
+}
+
+static int octeontx2_pem_write_config(struct udevice *bus, pci_dev_t bdf,
+                                     uint offset, ulong value,
+                                     enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(bus);
+       uintptr_t address;
+
+       address = octeontx_cfg_addr(pcie, 1 - hose->first_busno, 0,
+                                   bdf, 0);
+
+       if (octeontx_bdf_invalid(bdf))
+               return -EPERM;
+
+       writel_size(address + offset, size, value);
+
+       debug("%02x.%02x.%02x: u%d %x (%lx) <- %lx\n",
+             PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), size, offset,
+             address, value);
+
+       return 0;
+}
+
+int pci_octeontx_read_config(const struct udevice *bus, pci_dev_t bdf,
+                            uint offset, ulong *valuep,
+                            enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       int ret = -EIO;
+
+       switch (pcie->type) {
+       case OTX_ECAM:
+               ret = octeontx_ecam_read_config(bus, bdf, offset, valuep,
+                                               size);
+               break;
+       case OTX_PEM:
+               ret = octeontx_pem_read_config(bus, bdf, offset, valuep,
+                                              size);
+               break;
+       case OTX2_PEM:
+               ret = octeontx2_pem_read_config(bus, bdf, offset, valuep,
+                                               size);
+               break;
+       }
+
+       return ret;
+}
+
+int pci_octeontx_write_config(struct udevice *bus, pci_dev_t bdf,
+                             uint offset, ulong value,
+                             enum pci_size_t size)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(bus);
+       int ret = -EIO;
+
+       switch (pcie->type) {
+       case OTX_ECAM:
+               ret = octeontx_ecam_write_config(bus, bdf, offset, value,
+                                                size);
+               break;
+       case OTX_PEM:
+               ret = octeontx_pem_write_config(bus, bdf, offset, value,
+                                               size);
+               break;
+       case OTX2_PEM:
+               ret = octeontx2_pem_write_config(bus, bdf, offset, value,
+                                                size);
+               break;
+       }
+
+       return ret;
+}
+
+static int pci_octeontx_ofdata_to_platdata(struct udevice *dev)
+{
+       return 0;
+}
+
+static int pci_octeontx_probe(struct udevice *dev)
+{
+       struct octeontx_pci *pcie = (struct octeontx_pci *)dev_get_priv(dev);
+       int err;
+
+       pcie->type = dev_get_driver_data(dev);
+
+       err = dev_read_resource(dev, 0, &pcie->cfg);
+       if (err) {
+               debug("Error reading resource: %s\n", fdt_strerror(err));
+               return err;
+       }
+
+       err = dev_read_pci_bus_range(dev, &pcie->bus);
+       if (err) {
+               debug("Error reading resource: %s\n", fdt_strerror(err));
+               return err;
+       }
+
+       return 0;
+}
+
+static const struct dm_pci_ops pci_octeontx_ops = {
+       .read_config    = pci_octeontx_read_config,
+       .write_config   = pci_octeontx_write_config,
+};
+
+static const struct udevice_id pci_octeontx_ids[] = {
+       { .compatible = "cavium,pci-host-thunder-ecam", .data = OTX_ECAM },
+       { .compatible = "cavium,pci-host-octeontx-ecam", .data = OTX_ECAM },
+       { .compatible = "pci-host-ecam-generic", .data = OTX_ECAM },
+       { .compatible = "cavium,pci-host-thunder-pem", .data = OTX_PEM },
+       { .compatible = "marvell,pci-host-octeontx2-pem", .data = OTX2_PEM },
+       { }
+};
+
+U_BOOT_DRIVER(pci_octeontx) = {
+       .name   = "pci_octeontx",
+       .id     = UCLASS_PCI,
+       .of_match = pci_octeontx_ids,
+       .ops    = &pci_octeontx_ops,
+       .ofdata_to_platdata = pci_octeontx_ofdata_to_platdata,
+       .probe  = pci_octeontx_probe,
+       .priv_auto_alloc_size = sizeof(struct octeontx_pci),
+       .flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.28.0

Reply via email to