1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2024 Linaro Ltd. 4 * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> 5 */ 6 7 #include <linux/clk.h> 8 #include <linux/device.h> 9 #include <linux/mod_devicetable.h> 10 #include <linux/module.h> 11 #include <linux/of_graph.h> 12 #include <linux/pci-pwrctrl.h> 13 #include <linux/platform_device.h> 14 #include <linux/pwrseq/consumer.h> 15 #include <linux/regulator/consumer.h> 16 #include <linux/slab.h> 17 18 struct slot_pwrctrl { 19 struct pci_pwrctrl pwrctrl; 20 struct regulator_bulk_data *supplies; 21 int num_supplies; 22 struct clk *clk; 23 struct pwrseq_desc *pwrseq; 24 }; 25 26 static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl) 27 { 28 struct slot_pwrctrl *slot = container_of(pwrctrl, 29 struct slot_pwrctrl, pwrctrl); 30 int ret; 31 32 if (slot->pwrseq) { 33 pwrseq_power_on(slot->pwrseq); 34 return 0; 35 } 36 37 ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); 38 if (ret < 0) { 39 dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n"); 40 return ret; 41 } 42 43 return clk_prepare_enable(slot->clk); 44 } 45 46 static int slot_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl) 47 { 48 struct slot_pwrctrl *slot = container_of(pwrctrl, 49 struct slot_pwrctrl, pwrctrl); 50 51 if (slot->pwrseq) { 52 pwrseq_power_off(slot->pwrseq); 53 return 0; 54 } 55 56 regulator_bulk_disable(slot->num_supplies, slot->supplies); 57 clk_disable_unprepare(slot->clk); 58 59 return 0; 60 } 61 62 static void devm_slot_pwrctrl_release(void *data) 63 { 64 struct slot_pwrctrl *slot = data; 65 66 slot_pwrctrl_power_off(&slot->pwrctrl); 67 regulator_bulk_free(slot->num_supplies, slot->supplies); 68 } 69 70 static int slot_pwrctrl_probe(struct platform_device *pdev) 71 { 72 struct slot_pwrctrl *slot; 73 struct device *dev = &pdev->dev; 74 int ret; 75 76 slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL); 77 if (!slot) 78 return -ENOMEM; 79 80 if (of_graph_is_present(dev_of_node(dev))) { 81 slot->pwrseq = devm_pwrseq_get(dev, "pcie"); 82 if (IS_ERR(slot->pwrseq)) 83 return dev_err_probe(dev, PTR_ERR(slot->pwrseq), 84 "Failed to get the power sequencer\n"); 85 86 goto skip_resources; 87 } 88 89 ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), 90 &slot->supplies); 91 if (ret < 0) { 92 dev_err_probe(dev, ret, "Failed to get slot regulators\n"); 93 return ret; 94 } 95 96 slot->num_supplies = ret; 97 98 slot->clk = devm_clk_get_optional(dev, NULL); 99 if (IS_ERR(slot->clk)) { 100 return dev_err_probe(dev, PTR_ERR(slot->clk), 101 "Failed to enable slot clock\n"); 102 } 103 104 skip_resources: 105 slot->pwrctrl.power_on = slot_pwrctrl_power_on; 106 slot->pwrctrl.power_off = slot_pwrctrl_power_off; 107 108 ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot); 109 if (ret) 110 return ret; 111 112 pci_pwrctrl_init(&slot->pwrctrl, dev); 113 114 ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl); 115 if (ret) 116 return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); 117 118 return 0; 119 } 120 121 static const struct of_device_id slot_pwrctrl_of_match[] = { 122 { 123 .compatible = "pciclass,0604", 124 }, 125 { } 126 }; 127 MODULE_DEVICE_TABLE(of, slot_pwrctrl_of_match); 128 129 static struct platform_driver slot_pwrctrl_driver = { 130 .driver = { 131 .name = "pci-pwrctrl-slot", 132 .of_match_table = slot_pwrctrl_of_match, 133 }, 134 .probe = slot_pwrctrl_probe, 135 }; 136 module_platform_driver(slot_pwrctrl_driver); 137 138 MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>"); 139 MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots"); 140 MODULE_LICENSE("GPL"); 141