lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260121051439.1882086-1-pragneshp@marvell.com>
Date: Tue, 20 Jan 2026 21:14:29 -0800
From: Pragnesh Patel <pragneshp@...vell.com>
To:
CC: <gcherian@...vell.com>, Suneel Garapati <sgarapati@...vell.com>,
        "Pragnesh
 Patel" <pragneshp@...vell.com>,
        Lorenzo Pieralisi <lpieralisi@...nel.org>,
        Krzysztof WilczyƄski <kwilczynski@...nel.org>,
        "Manivannan
 Sadhasivam" <mani@...nel.org>,
        Rob Herring <robh@...nel.org>, Bjorn Helgaas
	<bhelgaas@...gle.com>,
        <linux-kernel@...r.kernel.org>, <linux-pci@...r.kernel.org>
Subject: [PATCH] PCI: octeon: Add link down handler support for PCIe MAC controller

From: Suneel Garapati <sgarapati@...vell.com>

This driver adds support for link-down interrupt in PCIe MAC (PEM)
controller in Root complex mode to support hot-plug removal of
endpoint devices.

An interrupt handler is registered for RST_INT msix vector
which is triggered with link going down. This handler
performs cleanup of root bridge and its children and
re-initializes root bridge to kernel for next link-up event.

Signed-off-by: Suneel Garapati <sgarapati@...vell.com>
Signed-off-by: Pragnesh Patel <pragneshp@...vell.com>
---
 MAINTAINERS                             |   6 +
 drivers/pci/controller/Kconfig          |   9 +
 drivers/pci/controller/Makefile         |   1 +
 drivers/pci/controller/pci-octeon-pem.c | 278 ++++++++++++++++++++++++
 4 files changed, 294 insertions(+)
 create mode 100644 drivers/pci/controller/pci-octeon-pem.c

diff --git a/MAINTAINERS b/MAINTAINERS
index f1b020588597..9af8f676c027 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15363,6 +15363,12 @@ R:	Vamsi Attunuru <vattunuru@...vell.com>
 S:	Supported
 F:	drivers/pci/hotplug/octep_hp.c
 
+MARVELL OCTEON PEM CONTROLLER DRIVER
+R:	George Cherian <gcherian@...vell.com>
+R:	Pragnesh Patel <pragneshp@...vell.com>
+S:	Supported
+F:	drivers/pci/controller/pci-octeon-pem.c
+
 MATROX FRAMEBUFFER DRIVER
 L:	linux-fbdev@...r.kernel.org
 S:	Orphan
diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
index c254d2b8bf17..5251dc8b1188 100644
--- a/drivers/pci/controller/Kconfig
+++ b/drivers/pci/controller/Kconfig
@@ -231,6 +231,15 @@ config PCI_HYPERV_INTERFACE
 	  drivers to have a common interface with the Hyper-V PCI frontend
 	  driver.
 
+config PCI_OCTEON_PEM
+	bool "Marvell Octeon PEM (PCIe MAC) controller"
+	depends on ARM64 || COMPILE_TEST
+	depends on PCI_MSI
+	depends on HOTPLUG_PCI_PCIE
+	help
+	  Say Y here if you want PEM controller support for Marvell ARM64 Octeon SoCs
+	  in root complex mode.
+
 config PCI_TEGRA
 	bool "NVIDIA Tegra PCIe controller"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
index 229929a945c2..dd1dab50dd07 100644
--- a/drivers/pci/controller/Makefile
+++ b/drivers/pci/controller/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_PCI_V3_SEMI) += pci-v3-semi.o
 obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
 obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
 obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
+obj-$(CONFIG_PCI_OCTEON_PEM) += pci-octeon-pem.o
 obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
 obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o
 obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o
diff --git a/drivers/pci/controller/pci-octeon-pem.c b/drivers/pci/controller/pci-octeon-pem.c
new file mode 100644
index 000000000000..2d67e2b16e9e
--- /dev/null
+++ b/drivers/pci/controller/pci-octeon-pem.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Octeon PEM driver
+ *
+ * Copyright (C) 2021 Marvell.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sysfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "../hotplug/pciehp.h"
+#include "../pci.h"
+
+#define DRV_NAME	"octeon-pem"
+#define DRV_VERSION	"1.0"
+
+#define PCI_DEVID_OCTEON_PEM	0xA06C
+
+#define ID_SHIFT		36
+#define DOMAIN_OFFSET		0x3
+#define ON_OFFSET		0xE0
+#define RST_SOFT_PERST_OFFSET	0x298
+#define RST_INT_OFFSET		0x300
+#define RST_INT_ENA_W1C_OFFSET	0x310
+#define RST_INT_ENA_W1S_OFFSET	0x318
+#define RST_INT_LINKDOWN	BIT(1)
+
+struct pem_ctlr {
+	int			index;
+	char			irq_name[32];
+	void __iomem		*base;
+	struct pci_dev		*pdev;
+	struct work_struct	recover_rc_work;
+};
+
+static struct pcie_device *to_pciehp_dev(struct pci_dev *dev)
+{
+	struct device *device;
+
+	device = pcie_port_find_device(dev, PCIE_PORT_SERVICE_HP);
+	if (!device)
+		return NULL;
+	return to_pcie_device(device);
+}
+
+static void pem_recover_rc_link(struct work_struct *ws)
+{
+	struct pem_ctlr *pem = container_of(ws, struct pem_ctlr,
+					    recover_rc_work);
+	struct pci_dev *pem_dev = pem->pdev;
+	struct pci_dev *root_port;
+	struct pci_bus *bus;
+	struct pcie_device *pcie;
+	struct controller *ctrl;
+	int rc_domain, timeout = 100;
+	u64 pem_reg;
+
+	rc_domain = pem->index + DOMAIN_OFFSET;
+
+	root_port = pci_get_domain_bus_and_slot(rc_domain, 0, 0);
+	if (!root_port) {
+		dev_err(&pem_dev->dev, "failed to get root port\n");
+		return;
+	}
+
+	dev_dbg(&pem_dev->dev, "PEM%d rcvr work\n", pem->index);
+
+	/* Check if HP interrupt thread is in progress
+	 * and wait for it to complete
+	 */
+	pcie = to_pciehp_dev(root_port);
+	if (!pcie)
+		return;
+	ctrl = get_service_data(pcie);
+	wait_event(ctrl->requester,
+		   !atomic_read(&ctrl->pending_events) &&
+		   !ctrl->ist_running);
+	dev_dbg(&pem_dev->dev, "PEM%d HP ist done\n", pem->index);
+
+	/* Disable hot-plug interrupt
+	 * Removal and rescan below would setup again.
+	 */
+	pcie_disable_interrupt(ctrl);
+	dev_dbg(&pem_dev->dev, "PEM%d Disable interrupt\n", pem->index);
+
+	pci_lock_rescan_remove();
+
+	pci_walk_bus(root_port->subordinate, pci_dev_set_disconnected, NULL);
+
+	/* Clean-up device and RC bridge */
+	pci_stop_and_remove_bus_device(root_port);
+
+	pci_unlock_rescan_remove();
+
+	usleep_range(100, 200);
+
+	writeq(0x0, pem->base + RST_SOFT_PERST_OFFSET);
+
+	while (timeout--) {
+		/* Check for PEM_OOR to be set */
+		pem_reg = readq(pem->base + ON_OFFSET);
+		if (pem_reg & BIT(1))
+			break;
+		usleep_range(1000, 2000);
+	}
+	if (!timeout) {
+		dev_warn(&pem_dev->dev,
+			 "PEM failed to get out of reset\n");
+		return;
+	}
+
+	pci_lock_rescan_remove();
+
+	/*
+	 * Hardware resets and initializes config space of RC bridge
+	 * on every link down event with auto-mode in use.
+	 * Re-scan will setup RC bridge cleanly in kernel
+	 * after removal and to be ready for next link-up event.
+	 */
+	bus = NULL;
+	while ((bus = pci_find_next_bus(bus)) != NULL)
+		if (bus->domain_nr == rc_domain)
+			pci_rescan_bus(bus);
+	pci_unlock_rescan_remove();
+	pci_dev_put(root_port);
+
+	/* Ack interrupt */
+	writeq(RST_INT_LINKDOWN, pem->base + RST_INT_OFFSET);
+	/* Enable RST_INT[LINKDOWN] interrupt */
+	writeq(RST_INT_LINKDOWN, pem->base + RST_INT_ENA_W1S_OFFSET);
+}
+
+static irqreturn_t pem_irq_handler(int irq, void *dev_id)
+{
+	struct pem_ctlr *pem = (struct pem_ctlr *)dev_id;
+
+	/* Disable RST_INT[LINKDOWN] interrupt */
+	writeq(RST_INT_LINKDOWN, pem->base + RST_INT_ENA_W1C_OFFSET);
+	schedule_work(&pem->recover_rc_work);
+
+	return IRQ_HANDLED;
+}
+
+static int pem_register_interrupts(struct pci_dev *pdev)
+{
+	struct pem_ctlr *pem = pci_get_drvdata(pdev);
+	int nvec, err;
+
+	nvec = pci_msix_vec_count(pdev);
+	/* Some earlier silicon versions do not support RST vector
+	 * so check on table size before registering otherwise
+	 * return with info message.
+	 */
+	if (nvec != 10) {
+		dev_info(&pdev->dev,
+			 "No RST MSI-X vector support on silicon\n");
+		return 0;
+	}
+	err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX);
+	if (err < 0) {
+		dev_err(&pdev->dev, "pci_alloc_irq_vectors() failed %d\n",
+			nvec);
+		return -ENOSPC;
+	}
+
+	snprintf(pem->irq_name, 32, "PEM%d RST_INT", pem->index);
+
+	/* register interrupt for RST_INT */
+	return devm_request_irq(&pdev->dev, pci_irq_vector(pdev, 9),
+				pem_irq_handler, 0,
+				pem->irq_name, pem);
+}
+
+static int pem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct device *dev = &pdev->dev;
+	struct pem_ctlr *pem;
+	struct pci_dev *root_port;
+	int err, rc_domain;
+
+	pem = devm_kzalloc(dev, sizeof(struct pem_ctlr), GFP_KERNEL);
+	if (!pem)
+		return -ENOMEM;
+
+	pem->pdev = pdev;
+	pci_set_drvdata(pdev, pem);
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(dev, "Failed to enable PCI device\n");
+		goto enable_failed;
+	}
+
+	err = pci_request_regions(pdev, DRV_NAME);
+	if (err) {
+		dev_err(dev, "PCI request regions failed 0x%x\n", err);
+		goto region_failed;
+	}
+
+	pci_set_master(pdev);
+
+	/* CSR Space mapping */
+	pem->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
+	if (!pem->base) {
+		dev_err(&pdev->dev, "Unable to map BAR0\n");
+		err = -ENODEV;
+		goto bar0_map_failed;
+	}
+	pem->index = ((u64)pci_resource_start(pdev, 0) >> ID_SHIFT) & 0xf;
+
+	rc_domain = pem->index + DOMAIN_OFFSET;
+
+	root_port = pci_get_domain_bus_and_slot(rc_domain, 0, 0);
+	if (!root_port) {
+		dev_err(&pdev->dev, "failed to get root port\n");
+		goto bar0_map_failed;
+	}
+	if (!root_port->is_hotplug_bridge) {
+		dev_info(&pdev->dev, "Hot-plug disabled skip registration\n");
+		goto bar0_map_failed;
+	}
+
+	err = pem_register_interrupts(pdev);
+	if (err < 0) {
+		dev_err(dev, "Register interrupt failed\n");
+		goto irq_failed;
+	}
+
+	INIT_WORK(&pem->recover_rc_work, pem_recover_rc_link);
+
+	/* Enable RST_INT[LINKDOWN] interrupt */
+	writeq(RST_INT_LINKDOWN, pem->base + RST_INT_ENA_W1S_OFFSET);
+
+	dev_info(&pdev->dev, "PEM%d probed\n", pem->index);
+	return 0;
+
+irq_failed:
+bar0_map_failed:
+	pci_release_regions(pdev);
+region_failed:
+enable_failed:
+	pci_set_drvdata(pdev, NULL);
+	return err;
+}
+
+static void pem_remove(struct pci_dev *pdev)
+{
+	pci_release_regions(pdev);
+}
+
+/* Supported devices */
+static const struct pci_device_id pem_id_table[] = {
+	{PCI_VDEVICE(CAVIUM, PCI_DEVID_OCTEON_PEM)},
+	{0} /* end of table */
+};
+
+static struct pci_driver pem_driver = {
+	.name = DRV_NAME,
+	.id_table = pem_id_table,
+	.probe = pem_probe,
+	.remove = pem_remove,
+};
+
+module_pci_driver(pem_driver);
+
+MODULE_AUTHOR("Marvell Inc.");
+MODULE_DESCRIPTION("Marvell Octeon PEM Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_DEVICE_TABLE(pci, pem_id_table);
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ