| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
- * Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
- */
- #include <linux/device.h>
- #include <linux/mod_devicetable.h>
- #include <linux/module.h>
- #include <linux/of.h>
- #include <linux/of_graph.h>
- #include <linux/platform_device.h>
- #include <linux/pwrseq/provider.h>
- #include <linux/regulator/consumer.h>
- #include <linux/slab.h>
- struct pwrseq_pcie_m2_pdata {
- const struct pwrseq_target_data **targets;
- };
- struct pwrseq_pcie_m2_ctx {
- struct pwrseq_device *pwrseq;
- struct device_node *of_node;
- const struct pwrseq_pcie_m2_pdata *pdata;
- struct regulator_bulk_data *regs;
- size_t num_vregs;
- struct notifier_block nb;
- };
- static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq)
- {
- struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
- return regulator_bulk_enable(ctx->num_vregs, ctx->regs);
- }
- static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq)
- {
- struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
- return regulator_bulk_disable(ctx->num_vregs, ctx->regs);
- }
- static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = {
- .name = "regulators-enable",
- .enable = pwrseq_pcie_m2_m_vregs_enable,
- .disable = pwrseq_pcie_m2_m_vregs_disable,
- };
- static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = {
- &pwrseq_pcie_m2_vregs_unit_data,
- NULL
- };
- static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = {
- .name = "pcie-enable",
- .deps = pwrseq_pcie_m2_m_unit_deps,
- };
- static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = {
- .name = "pcie",
- .unit = &pwrseq_pcie_m2_m_pcie_unit_data,
- };
- static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = {
- &pwrseq_pcie_m2_m_pcie_target_data,
- NULL
- };
- static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = {
- .targets = pwrseq_pcie_m2_m_targets,
- };
- static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
- struct device *dev)
- {
- struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
- struct device_node *endpoint __free(device_node) = NULL;
- /*
- * Traverse the 'remote-endpoint' nodes and check if the remote node's
- * parent matches the OF node of 'dev'.
- */
- for_each_endpoint_of_node(ctx->of_node, endpoint) {
- struct device_node *remote __free(device_node) =
- of_graph_get_remote_port_parent(endpoint);
- if (remote && (remote == dev_of_node(dev)))
- return PWRSEQ_MATCH_OK;
- }
- return PWRSEQ_NO_MATCH;
- }
- static void pwrseq_pcie_m2_free_regulators(void *data)
- {
- struct pwrseq_pcie_m2_ctx *ctx = data;
- regulator_bulk_free(ctx->num_vregs, ctx->regs);
- }
- static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct pwrseq_pcie_m2_ctx *ctx;
- struct pwrseq_config config = {};
- int ret;
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- ctx->of_node = dev_of_node(dev);
- ctx->pdata = device_get_match_data(dev);
- if (!ctx->pdata)
- return dev_err_probe(dev, -ENODEV,
- "Failed to obtain platform data\n");
- /*
- * Currently, of_regulator_bulk_get_all() is the only regulator API that
- * allows to get all supplies in the devicetree node without manually
- * specifying them.
- */
- ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &ctx->regs);
- if (ret < 0)
- return dev_err_probe(dev, ret,
- "Failed to get all regulators\n");
- ctx->num_vregs = ret;
- ret = devm_add_action_or_reset(dev, pwrseq_pcie_m2_free_regulators, ctx);
- if (ret)
- return ret;
- config.parent = dev;
- config.owner = THIS_MODULE;
- config.drvdata = ctx;
- config.match = pwrseq_pcie_m2_match;
- config.targets = ctx->pdata->targets;
- ctx->pwrseq = devm_pwrseq_device_register(dev, &config);
- if (IS_ERR(ctx->pwrseq))
- return dev_err_probe(dev, PTR_ERR(ctx->pwrseq),
- "Failed to register the power sequencer\n");
- return 0;
- }
- static const struct of_device_id pwrseq_pcie_m2_of_match[] = {
- {
- .compatible = "pcie-m2-m-connector",
- .data = &pwrseq_pcie_m2_m_of_data,
- },
- { }
- };
- MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match);
- static struct platform_driver pwrseq_pcie_m2_driver = {
- .driver = {
- .name = "pwrseq-pcie-m2",
- .of_match_table = pwrseq_pcie_m2_of_match,
- },
- .probe = pwrseq_pcie_m2_probe,
- };
- module_platform_driver(pwrseq_pcie_m2_driver);
- MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>");
- MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector");
- MODULE_LICENSE("GPL");
|