// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. * Author: Manivannan Sadhasivam */ #include #include #include #include #include #include #include #include #include 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 = of_node_get(dev->of_node); 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 "); MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector"); MODULE_LICENSE("GPL");