public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Vidya Sagar <vidyas@nvidia.com>
To: <bhelgaas@google.com>, <robh+dt@kernel.org>,
	<krzysztof.kozlowski+dt@linaro.org>, <lpieralisi@kernel.org>,
	<kw@linux.com>, <thierry.reding@gmail.com>,
	<jonathanh@nvidia.com>, <mani@kernel.org>,
	<Sergey.Semin@baikalelectronics.ru>, <jszhang@kernel.org>
Cc: <linux-pci@vger.kernel.org>, <linux-tegra@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<kthota@nvidia.com>, <mmaddireddy@nvidia.com>,
	<vidyas@nvidia.com>, <sagar.tv@gmail.com>
Subject: [PATCH V1 2/4] PCI/hotplug: Add GPIO PCIe hotplug driver
Date: Sat, 1 Oct 2022 00:57:45 +0530	[thread overview]
Message-ID: <20220930192747.21471-3-vidyas@nvidia.com> (raw)
In-Reply-To: <20220930192747.21471-1-vidyas@nvidia.com>

This adds a standalone driver to support PCIe hotplug functionality
merely based on a GPIO indicating the status of a downstream device
connectivity. It looks for "hotplug-gpios" property in the corresponding
device node to get the GPIO information.

It also provides a mechanism for platform drivers of the controllers
to register ops to perform any platform specific operations while
enabling/disabling the slots.

Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
---
 drivers/pci/hotplug/Kconfig    |  11 ++
 drivers/pci/hotplug/Makefile   |   1 +
 drivers/pci/hotplug/gpio_php.c | 200 +++++++++++++++++++++++++++++++++
 drivers/pci/hotplug/gpiophp.h  |  40 +++++++
 4 files changed, 252 insertions(+)
 create mode 100644 drivers/pci/hotplug/gpio_php.c
 create mode 100644 drivers/pci/hotplug/gpiophp.h

diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 840a84bb5ee2..dbac64ff6915 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -158,4 +158,15 @@ config HOTPLUG_PCI_S390
 
 	  When in doubt, say Y.
 
+config HOTPLUG_PCI_GPIO
+	bool "GPIO based PCI Hotplug Support"
+	depends on OF_GPIO
+	help
+	  Say Y here if you want to have GPIO based PCIe hot-plug framework.
+	  This framework helps to register a GPIO based Hot-Plug controller
+	  with the system where a GPIO can be used to represent device
+	  connect and disconnect states.
+
+	  When in doubt, say N.
+
 endif # HOTPLUG_PCI
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 5196983220df..70e11d698807 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
 obj-$(CONFIG_HOTPLUG_PCI_S390)		+= s390_pci_hpc.o
+obj-$(CONFIG_HOTPLUG_PCI_GPIO)		+= gpio_php.o
 
 # acpiphp_ibm extends acpiphp, so should be linked afterwards.
 
diff --git a/drivers/pci/hotplug/gpio_php.c b/drivers/pci/hotplug/gpio_php.c
new file mode 100644
index 000000000000..33c8105aade5
--- /dev/null
+++ b/drivers/pci/hotplug/gpio_php.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * GPIO based PCI Hotplug Driver.
+ *
+ */
+
+#include <linux/libfdt.h>
+#include <linux/module.h>
+#include <linux/of_fdt.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include "../pci.h"
+#include "../pcie/portdrv.h"
+
+#include "gpiophp.h"
+
+static DEFINE_MUTEX(slot_mutex);
+
+static inline struct gpio_hotplug_slot *to_gpio_hotplug_slot(struct hotplug_slot *slot)
+{
+	return container_of(slot, struct gpio_hotplug_slot, hotplug_slot);
+}
+
+static int gpio_hp_get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
+{
+	struct gpio_hotplug_slot *slot = to_gpio_hotplug_slot(hotplug_slot);
+	struct pci_dev *dev;
+
+	pci_config_pm_runtime_get(slot->pdev);
+	dev = pci_get_slot(slot->pdev->subordinate, PCI_DEVFN(0, 0));
+	if (dev) {
+		pci_dev_put(dev);
+		*value = 1;
+	} else {
+		*value = 0;
+	}
+	pci_dbg(slot->pdev, "Power Status = %u\n", *value);
+	pci_config_pm_runtime_put(slot->pdev);
+
+	return 0;
+}
+
+static int gpio_hp_enable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct gpio_hotplug_slot *slot = to_gpio_hotplug_slot(hotplug_slot);
+	int ret = 0;
+	u8 value;
+
+	mutex_lock(&slot_mutex);
+
+	gpio_hp_get_power_status(hotplug_slot, &value);
+	if (value) {
+		pci_info(slot->pdev, "Device is already plugged-in\n");
+		goto exit;
+	}
+
+	if (slot->plat_ops && slot->plat_ops->enable)
+		slot->plat_ops->enable(slot);
+
+	pm_runtime_get_sync(&slot->pdev->dev);
+
+	pci_lock_rescan_remove();
+	pci_rescan_bus(slot->pdev->bus);
+	pci_unlock_rescan_remove();
+
+	pm_runtime_put(&slot->pdev->dev);
+
+exit:
+	mutex_unlock(&slot_mutex);
+	return ret;
+}
+
+static int gpio_hp_disable_slot(struct hotplug_slot *hotplug_slot)
+{
+	struct gpio_hotplug_slot *slot = to_gpio_hotplug_slot(hotplug_slot);
+	struct pci_dev *dev, *temp;
+	u8 value;
+
+	mutex_lock(&slot_mutex);
+
+	gpio_hp_get_power_status(hotplug_slot, &value);
+	if (!value) {
+		pci_info(slot->pdev, "Device is already removed\n");
+		goto exit;
+	}
+
+	pci_lock_rescan_remove();
+
+	list_for_each_entry_safe_reverse(dev, temp, &slot->pdev->subordinate->devices, bus_list) {
+		pci_dev_get(dev);
+		pci_stop_and_remove_bus_device(dev);
+		pci_dev_put(dev);
+	}
+
+	pci_unlock_rescan_remove();
+
+exit:
+	if (slot->plat_ops && slot->plat_ops->disable)
+		slot->plat_ops->disable(slot);
+	mutex_unlock(&slot_mutex);
+	return 0;
+}
+
+static const struct hotplug_slot_ops gpio_hotplug_slot_ops = {
+	.enable_slot = gpio_hp_enable_slot,
+	.disable_slot = gpio_hp_disable_slot,
+	.get_power_status = gpio_hp_get_power_status,
+};
+
+static irqreturn_t pcie_gpio_hp_irq(int irq, void *arg)
+{
+	struct gpio_hotplug_slot *slot = arg;
+
+	if (gpiod_get_value(slot->gpiod)) {
+		pci_dbg(slot->pdev, "Hot-Plug Event\n");
+		gpio_hp_enable_slot(&slot->hotplug_slot);
+	} else {
+		pci_dbg(slot->pdev, "Hot-UnPlug Event\n");
+		gpio_hp_disable_slot(&slot->hotplug_slot);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int register_gpio_hotplug_slot(struct gpio_hotplug_slot *slot)
+{
+	struct device *dev = &slot->pdev->dev;
+	struct pci_dev *pdev = slot->pdev;
+	struct gpio_desc *gpiod;
+	unsigned int irq;
+	char *name;
+	int ret;
+
+	gpiod = devm_gpiod_get(&pdev->bus->dev, "hotplug", GPIOD_IN);
+	if (IS_ERR(gpiod)) {
+		ret = PTR_ERR(gpiod);
+		pci_err(pdev, "Failed to find GPIO for Hot-Plug functionality: %d\n", ret);
+		return ret;
+	}
+
+	ret = gpiod_to_irq(gpiod);
+	if (ret < 0) {
+		pci_err(pdev, "Failed to get IRQ for Hot_Plug GPIO: %d\n", ret);
+		return ret;
+	}
+	irq = (unsigned int)ret;
+
+	slot->gpiod = gpiod;
+	slot->hotplug_slot.ops = &gpio_hotplug_slot_ops;
+	slot->irq = irq;
+
+	name = devm_kasprintf(dev, GFP_KERNEL, "slot_%u", slot->slot_nr);
+	if (!name) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	ret = pci_hp_register(&slot->hotplug_slot, pdev->subordinate, 0, name);
+	if (ret) {
+		pci_err(pdev, "Failed to register hotplug slot: %d\n", ret);
+		goto exit;
+	}
+
+	name = devm_kasprintf(dev, GFP_KERNEL, "pcie_gpio_hp_irq_%u", slot->slot_nr);
+	if (!name) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	ret = devm_request_threaded_irq(dev, slot->irq,
+					NULL, pcie_gpio_hp_irq,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					name, slot);
+	if (ret < 0) {
+		pci_err(pdev, "Failed to request IRQ for Hot-Plug: %d\n", ret);
+		goto exit;
+	}
+
+	pci_dbg(pdev, "Hot-Plug Slot registered for %s\n", pci_name(pdev));
+
+	gpio_hp_enable_slot(&slot->hotplug_slot);
+
+	return 0;
+
+exit:
+	return ret;
+}
+
+void unregister_gpio_hotplug_slot(struct gpio_hotplug_slot *slot)
+{
+	struct device *dev = &slot->pdev->dev;
+	struct pci_dev *pdev = slot->pdev;
+
+	gpio_hp_disable_slot(&slot->hotplug_slot);
+	devm_free_irq(dev, slot->irq, slot);
+	pci_hp_deregister(&slot->hotplug_slot);
+	devm_gpiod_put(&pdev->bus->dev, slot->gpiod);
+}
+
diff --git a/drivers/pci/hotplug/gpiophp.h b/drivers/pci/hotplug/gpiophp.h
new file mode 100644
index 000000000000..77cc76976e0d
--- /dev/null
+++ b/drivers/pci/hotplug/gpiophp.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * GPIO based PCI Hotplug Driver.
+ *
+ */
+
+#ifndef _GPIOPHP_H
+#define _GPIOPHP_H
+
+#include <linux/pci_hotplug.h>
+
+struct gpio_hotplug_slot;
+
+struct gpio_hotplug_slot_plat_ops {
+	int (*enable)(struct gpio_hotplug_slot *hp_slot);
+	int (*disable)(struct gpio_hotplug_slot *hp_slot);
+};
+
+struct gpio_hotplug_slot {
+	struct device_node *np;
+	int slot_nr;
+	const struct gpio_hotplug_slot_plat_ops *plat_ops;
+	struct pci_dev *pdev;
+
+	struct gpio_desc *gpiod;
+	unsigned int irq;
+
+	struct hotplug_slot hotplug_slot;
+};
+
+#ifdef CONFIG_HOTPLUG_PCI_GPIO
+int register_gpio_hotplug_slot(struct gpio_hotplug_slot *hp_slot);
+void unregister_gpio_hotplug_slot(struct gpio_hotplug_slot *hp_slot);
+#else
+static inline int register_gpio_hotplug_slot(struct gpio_hotplug_slot *hp_slot)
+{ return 0; }
+static inline void unregister_gpio_hotplug_slot(struct gpio_hotplug_slot *hp_slot) {}
+#endif
+
+#endif //_GPIOPHP_H
-- 
2.17.1


  parent reply	other threads:[~2022-09-30 19:28 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-30 19:27 [PATCH V1 0/4] GPIO based PCIe Hot-Plug support Vidya Sagar
2022-09-30 19:27 ` [PATCH V1 1/4] dt-bindings: Add "hotplug-gpios" PCIe property Vidya Sagar
2022-10-01 15:56   ` Lukas Wunner
2022-10-01 16:10     ` Pali Rohár
2022-09-30 19:27 ` Vidya Sagar [this message]
2022-09-30 19:27 ` [PATCH V1 3/4] PCI: tegra194: Add support to configure a pluggable slot Vidya Sagar
2022-09-30 19:27 ` [PATCH V1 4/4] PCI: tegra194: Enable GPIO based Hot-Plug support Vidya Sagar
2022-10-01 16:00 ` [PATCH V1 0/4] GPIO based PCIe " Lukas Wunner
2022-10-01 16:20   ` Pali Rohár
2022-10-01 23:50     ` Jonathan Derrick
2022-10-03 18:09       ` Bjorn Helgaas
2022-10-03 18:21         ` Pali Rohár
2022-10-03 19:18           ` Jonathan Derrick
2022-10-04  4:04           ` Vidya Sagar
2022-10-10  6:14             ` Vidya Sagar
2022-10-17  2:46               ` Vidya Sagar
2022-11-09 15:35                 ` Manivannan Sadhasivam
2022-10-03 17:04 ` Rob Herring

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220930192747.21471-3-vidyas@nvidia.com \
    --to=vidyas@nvidia.com \
    --cc=Sergey.Semin@baikalelectronics.ru \
    --cc=bhelgaas@google.com \
    --cc=devicetree@vger.kernel.org \
    --cc=jonathanh@nvidia.com \
    --cc=jszhang@kernel.org \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=kthota@nvidia.com \
    --cc=kw@linux.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=lpieralisi@kernel.org \
    --cc=mani@kernel.org \
    --cc=mmaddireddy@nvidia.com \
    --cc=robh+dt@kernel.org \
    --cc=sagar.tv@gmail.com \
    --cc=thierry.reding@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox