Commit 9cb64f61 authored by Manivannan Sadhasivam's avatar Manivannan Sadhasivam Committed by Bjorn Helgaas
Browse files

PCI/pwrctrl: Add PCIe M.2 connector support



Add support for handling PCIe M.2 connectors as Power Sequencing devices.
These connectors are exposed as Power Sequencing devices as they often
support multiple interfaces like PCIe/SATA, USB/UART to the host machine,
and the interfaces may be driven by different client drivers at the same
time.

This driver handles the PCIe interface of these connectors. It first checks
for the presence of the graph port in the Root Port node with the help of
of_graph_is_present() API. If present, it acquires/powers ON the
corresponding pwrseq device.

Once the pwrseq device is powered ON, the driver will skip parsing the Root
Port/Slot resources and register with the pwrctrl framework.

Signed-off-by: default avatarManivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Link: https://patch.msgid.link/20260128-pci-m2-v7-1-9b3a5fe3d244@oss.qualcomm.com
parent 54786d98
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ config PCI_PWRCTRL_PWRSEQ

config PCI_PWRCTRL_SLOT
	tristate "PCI Power Control driver for PCI slots"
	select POWER_SEQUENCING
	select PCI_PWRCTRL
	help
	  Say Y here to enable the PCI Power Control driver to control the power
+27 −4
Original line number Diff line number Diff line
@@ -8,8 +8,10 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_graph.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>

@@ -18,6 +20,7 @@ struct slot_pwrctrl {
	struct regulator_bulk_data *supplies;
	int num_supplies;
	struct clk *clk;
	struct pwrseq_desc *pwrseq;
};

static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
@@ -26,6 +29,11 @@ static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
						struct slot_pwrctrl, pwrctrl);
	int ret;

	if (slot->pwrseq) {
		pwrseq_power_on(slot->pwrseq);
		return 0;
	}

	ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
	if (ret < 0) {
		dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n");
@@ -40,6 +48,11 @@ static int slot_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
	struct slot_pwrctrl *slot = container_of(pwrctrl,
						struct slot_pwrctrl, pwrctrl);

	if (slot->pwrseq) {
		pwrseq_power_off(slot->pwrseq);
		return 0;
	}

	regulator_bulk_disable(slot->num_supplies, slot->supplies);
	clk_disable_unprepare(slot->clk);

@@ -64,6 +77,15 @@ static int slot_pwrctrl_probe(struct platform_device *pdev)
	if (!slot)
		return -ENOMEM;

	if (of_graph_is_present(dev_of_node(dev))) {
		slot->pwrseq = devm_pwrseq_get(dev, "pcie");
		if (IS_ERR(slot->pwrseq))
			return dev_err_probe(dev, PTR_ERR(slot->pwrseq),
				     "Failed to get the power sequencer\n");

		goto skip_resources;
	}

	ret = of_regulator_bulk_get_all(dev, dev_of_node(dev),
					&slot->supplies);
	if (ret < 0) {
@@ -73,19 +95,20 @@ static int slot_pwrctrl_probe(struct platform_device *pdev)

	slot->num_supplies = ret;

	ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot);
	if (ret)
		return ret;

	slot->clk = devm_clk_get_optional(dev, NULL);
	if (IS_ERR(slot->clk)) {
		return dev_err_probe(dev, PTR_ERR(slot->clk),
				     "Failed to enable slot clock\n");
	}

skip_resources:
	slot->pwrctrl.power_on = slot_pwrctrl_power_on;
	slot->pwrctrl.power_off = slot_pwrctrl_power_off;

	ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot);
	if (ret)
		return ret;

	pci_pwrctrl_init(&slot->pwrctrl, dev);

	ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl);