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