1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 * Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com> 5 */ 6 7 #include <linux/device.h> 8 #include <linux/mod_devicetable.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_graph.h> 12 #include <linux/platform_device.h> 13 #include <linux/pwrseq/provider.h> 14 #include <linux/regulator/consumer.h> 15 #include <linux/slab.h> 16 17 struct pwrseq_pcie_m2_pdata { 18 const struct pwrseq_target_data **targets; 19 }; 20 21 struct pwrseq_pcie_m2_ctx { 22 struct pwrseq_device *pwrseq; 23 struct device_node *of_node; 24 const struct pwrseq_pcie_m2_pdata *pdata; 25 struct regulator_bulk_data *regs; 26 size_t num_vregs; 27 struct notifier_block nb; 28 }; 29 30 static int pwrseq_pcie_m2_m_vregs_enable(struct pwrseq_device *pwrseq) 31 { 32 struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 33 34 return regulator_bulk_enable(ctx->num_vregs, ctx->regs); 35 } 36 37 static int pwrseq_pcie_m2_m_vregs_disable(struct pwrseq_device *pwrseq) 38 { 39 struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 40 41 return regulator_bulk_disable(ctx->num_vregs, ctx->regs); 42 } 43 44 static const struct pwrseq_unit_data pwrseq_pcie_m2_vregs_unit_data = { 45 .name = "regulators-enable", 46 .enable = pwrseq_pcie_m2_m_vregs_enable, 47 .disable = pwrseq_pcie_m2_m_vregs_disable, 48 }; 49 50 static const struct pwrseq_unit_data *pwrseq_pcie_m2_m_unit_deps[] = { 51 &pwrseq_pcie_m2_vregs_unit_data, 52 NULL 53 }; 54 55 static const struct pwrseq_unit_data pwrseq_pcie_m2_m_pcie_unit_data = { 56 .name = "pcie-enable", 57 .deps = pwrseq_pcie_m2_m_unit_deps, 58 }; 59 60 static const struct pwrseq_target_data pwrseq_pcie_m2_m_pcie_target_data = { 61 .name = "pcie", 62 .unit = &pwrseq_pcie_m2_m_pcie_unit_data, 63 }; 64 65 static const struct pwrseq_target_data *pwrseq_pcie_m2_m_targets[] = { 66 &pwrseq_pcie_m2_m_pcie_target_data, 67 NULL 68 }; 69 70 static const struct pwrseq_pcie_m2_pdata pwrseq_pcie_m2_m_of_data = { 71 .targets = pwrseq_pcie_m2_m_targets, 72 }; 73 74 static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq, 75 struct device *dev) 76 { 77 struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq); 78 struct device_node *endpoint __free(device_node) = NULL; 79 80 /* 81 * Traverse the 'remote-endpoint' nodes and check if the remote node's 82 * parent matches the OF node of 'dev'. 83 */ 84 for_each_endpoint_of_node(ctx->of_node, endpoint) { 85 struct device_node *remote __free(device_node) = 86 of_graph_get_remote_port_parent(endpoint); 87 if (remote && (remote == dev_of_node(dev))) 88 return PWRSEQ_MATCH_OK; 89 } 90 91 return PWRSEQ_NO_MATCH; 92 } 93 94 static void pwrseq_pcie_m2_free_regulators(void *data) 95 { 96 struct pwrseq_pcie_m2_ctx *ctx = data; 97 98 regulator_bulk_free(ctx->num_vregs, ctx->regs); 99 } 100 101 static int pwrseq_pcie_m2_probe(struct platform_device *pdev) 102 { 103 struct device *dev = &pdev->dev; 104 struct pwrseq_pcie_m2_ctx *ctx; 105 struct pwrseq_config config = {}; 106 int ret; 107 108 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 109 if (!ctx) 110 return -ENOMEM; 111 112 ctx->of_node = of_node_get(dev->of_node); 113 ctx->pdata = device_get_match_data(dev); 114 if (!ctx->pdata) 115 return dev_err_probe(dev, -ENODEV, 116 "Failed to obtain platform data\n"); 117 118 /* 119 * Currently, of_regulator_bulk_get_all() is the only regulator API that 120 * allows to get all supplies in the devicetree node without manually 121 * specifying them. 122 */ 123 ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), &ctx->regs); 124 if (ret < 0) 125 return dev_err_probe(dev, ret, 126 "Failed to get all regulators\n"); 127 128 ctx->num_vregs = ret; 129 130 ret = devm_add_action_or_reset(dev, pwrseq_pcie_m2_free_regulators, ctx); 131 if (ret) 132 return ret; 133 134 config.parent = dev; 135 config.owner = THIS_MODULE; 136 config.drvdata = ctx; 137 config.match = pwrseq_pcie_m2_match; 138 config.targets = ctx->pdata->targets; 139 140 ctx->pwrseq = devm_pwrseq_device_register(dev, &config); 141 if (IS_ERR(ctx->pwrseq)) 142 return dev_err_probe(dev, PTR_ERR(ctx->pwrseq), 143 "Failed to register the power sequencer\n"); 144 145 return 0; 146 } 147 148 static const struct of_device_id pwrseq_pcie_m2_of_match[] = { 149 { 150 .compatible = "pcie-m2-m-connector", 151 .data = &pwrseq_pcie_m2_m_of_data, 152 }, 153 { } 154 }; 155 MODULE_DEVICE_TABLE(of, pwrseq_pcie_m2_of_match); 156 157 static struct platform_driver pwrseq_pcie_m2_driver = { 158 .driver = { 159 .name = "pwrseq-pcie-m2", 160 .of_match_table = pwrseq_pcie_m2_of_match, 161 }, 162 .probe = pwrseq_pcie_m2_probe, 163 }; 164 module_platform_driver(pwrseq_pcie_m2_driver); 165 166 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>"); 167 MODULE_DESCRIPTION("Power Sequencing driver for PCIe M.2 connector"); 168 MODULE_LICENSE("GPL"); 169