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