[PATCH v2 49/50] mips: octeon: Add Octeon PCIe host controller driver

Stefan Roese
2021-04-07T06:43:35Z

This patch adds the PCIe host controller driver for MIPS Octeon II/III.
The driver mainly consist of the PCI config functions, as all of the
complex serdes related port / lane setup, is done in the serdes / pcie
code available in the "arch/mips/mach-octeon" directory.

Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Aaron Williams <awilliams@marvell.com>
Cc: Chandrakala Chavva <cchavva@marvell.com>
Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
---
I'm sending only this patch from the serdes / QLM series with the PCIe
support, as it's the only one that has some changes. To not "pollute"
the list with this huge series without need. Please let me know if I
should post the complete series again to the list or provide a gitlab
branch.

v2:
- Rebased on top of latest master
- Changed priv_auto_alloc_size to priv_auto

drivers/pci/Kconfig | 6 ++
drivers/pci/Makefile | 1 +
drivers/pci/pcie_octeon.c | 159 ++++++++++++++++++++++++++++++++++++++
3 files changed, 166 insertions(+)
create mode 100644 drivers/pci/pcie_octeon.c

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index ba41787f64dc..0b2daeac23b6 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -158,6 +158,12 @@ config PCI_OCTEONTX
These controllers provide PCI configuration access to all on-board
peripherals so it should only be disabled for testing purposes

+config PCIE_OCTEON
+ bool "MIPS Octeon PCIe support"
+ depends on ARCH_OCTEON
+ help
+ Enable support for the MIPS Octeon SoC family PCIe controllers.
+
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 5ed94bc95c27..dc04d1c7d675 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o
obj-$(CONFIG_PCIE_DW_ROCKCHIP) += pcie_dw_rockchip.o
obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o
obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o
+obj-$(CONFIG_PCIE_OCTEON) += pcie_octeon.o
diff --git a/drivers/pci/pcie_octeon.c b/drivers/pci/pcie_octeon.c
new file mode 100644
index 000000000000..3b28bd81439f
--- /dev/null
+++ b/drivers/pci/pcie_octeon.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Stefan Roese <sr@denx.de>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <pci.h>
+#include <linux/delay.h>
+
+#include <mach/octeon-model.h>
+#include <mach/octeon_pci.h>
+#include <mach/cvmx-regs.h>
+#include <mach/cvmx-pcie.h>
+#include <mach/cvmx-pemx-defs.h>
+
+struct octeon_pcie {
+ void *base;
+ int first_busno;
+ u32 port;
+ struct udevice *dev;
+ int pcie_port;
+};
+
+static bool octeon_bdf_invalid(pci_dev_t bdf, int first_busno)
+{
+ /*
+ * In PCIe only a single device (0) can exist on the local bus.
+ * Beyound the local bus, there might be a switch and everything
+ * is possible.
+ */
+ if ((PCI_BUS(bdf) == first_busno) && (PCI_DEV(bdf) > 0))
+ return true;
+
+ return false;
+}
+
+static int pcie_octeon_write_config(struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct octeon_pcie *pcie = dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ int busno;
+ int port;
+
+ debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
+ PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
+ debug("(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", offset, size, value);
+
+ port = pcie->pcie_port;
+ busno = PCI_BUS(bdf) - hose->first_busno + 1;
+
+ switch (size) {
+ case PCI_SIZE_8:
+ cvmx_pcie_config_write8(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset, value);
+ break;
+ case PCI_SIZE_16:
+ cvmx_pcie_config_write16(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset, value);
+ break;
+ case PCI_SIZE_32:
+ cvmx_pcie_config_write32(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset, value);
+ break;
+ default:
+ printf("Invalid size\n");
+ };
+
+ return 0;
+}
+
+static int pcie_octeon_read_config(const struct udevice *bus, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct octeon_pcie *pcie = dev_get_priv(bus);
+ struct pci_controller *hose = dev_get_uclass_priv(bus);
+ int busno;
+ int port;
+
+ port = pcie->pcie_port;
+ busno = PCI_BUS(bdf) - hose->first_busno + 1;
+ if (octeon_bdf_invalid(bdf, pcie->first_busno)) {
+ *valuep = pci_get_ff(size);
+ return 0;
+ }
+
+ switch (size) {
+ case PCI_SIZE_8:
+ *valuep = cvmx_pcie_config_read8(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset);
+ break;
+ case PCI_SIZE_16:
+ *valuep = cvmx_pcie_config_read16(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset);
+ break;
+ case PCI_SIZE_32:
+ *valuep = cvmx_pcie_config_read32(port, busno, PCI_DEV(bdf),
+ PCI_FUNC(bdf), offset);
+ break;
+ default:
+ printf("Invalid size\n");
+ };
+
+ 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 pcie_octeon_probe(struct udevice *dev)
+{
+ struct octeon_pcie *pcie = dev_get_priv(dev);
+ int node = cvmx_get_node_num();
+ int pcie_port;
+ int ret = 0;
+
+ /* Get port number, lane number and memory target / attr */
+ if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port",
+ &pcie->port)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ pcie->first_busno = dev_seq(dev);
+ pcie_port = ((node << 4) | pcie->port);
+ ret = cvmx_pcie_rc_initialize(pcie_port);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static const struct dm_pci_ops pcie_octeon_ops = {
+ .read_config = pcie_octeon_read_config,
+ .write_config = pcie_octeon_write_config,
+};
+
+static const struct udevice_id pcie_octeon_ids[] = {
+ { .compatible = "marvell,pcie-host-octeon" },
+ { }
+};
+
+U_BOOT_DRIVER(pcie_octeon) = {
+ .name = "pcie_octeon",
+ .id = UCLASS_PCI,
+ .of_match = pcie_octeon_ids,
+ .ops = &pcie_octeon_ops,
+ .probe = pcie_octeon_probe,
+ .priv_auto = sizeof(struct octeon_pcie),
+ .flags = DM_FLAG_PRE_RELOC,
+};
--
2.31.1