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_enable(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_disable(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 kfree(slot->supplies); 67 } 68 69 static int slot_pwrctrl_probe(struct platform_device *pdev) 70 { 71 struct slot_pwrctrl *slot; 72 struct device *dev = &pdev->dev; 73 int ret; 74 75 slot = devm_kzalloc_obj(dev, *slot); 76 if (!slot) 77 return -ENOMEM; 78 79 if (of_graph_is_present(dev_of_node(dev))) { 80 slot->pwrseq = devm_pwrseq_get(dev, "pcie"); 81 if (IS_ERR(slot->pwrseq)) 82 return dev_err_probe(dev, PTR_ERR(slot->pwrseq), 83 "Failed to get the power sequencer\n"); 84 85 goto skip_resources; 86 } 87 88 ret = of_regulator_bulk_get_all(dev, dev_of_node(dev), 89 &slot->supplies); 90 if (ret < 0) 91 return dev_err_probe(dev, ret, "Failed to get slot regulators\n"); 92 93 slot->num_supplies = ret; 94 95 slot->clk = devm_clk_get_optional(dev, NULL); 96 if (IS_ERR(slot->clk)) 97 return dev_err_probe(dev, PTR_ERR(slot->clk), 98 "Failed to enable slot clock\n"); 99 100 skip_resources: 101 slot->pwrctrl.power_on = slot_pwrctrl_power_on; 102 slot->pwrctrl.power_off = slot_pwrctrl_power_off; 103 104 ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot); 105 if (ret) 106 return ret; 107 108 pci_pwrctrl_init(&slot->pwrctrl, dev); 109 110 ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl); 111 if (ret) 112 return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); 113 114 return 0; 115 } 116 117 static const struct of_device_id slot_pwrctrl_of_match[] = { 118 { 119 .compatible = "pciclass,0604", 120 }, 121 /* Renesas UPD720201/UPD720202 USB 3.0 xHCI Host Controller */ 122 { 123 .compatible = "pci1912,0014", 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