1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Driver for the regulator based Ethernet Power Sourcing Equipment, without 4 // auto classification support. 5 // 6 // Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 7 // 8 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/platform_device.h> 12 #include <linux/pse-pd/pse.h> 13 #include <linux/regulator/consumer.h> 14 15 struct pse_reg_priv { 16 struct pse_controller_dev pcdev; 17 struct regulator *ps; /*power source */ 18 enum ethtool_podl_pse_admin_state admin_state; 19 }; 20 21 static struct pse_reg_priv *to_pse_reg(struct pse_controller_dev *pcdev) 22 { 23 return container_of(pcdev, struct pse_reg_priv, pcdev); 24 } 25 26 static int 27 pse_reg_pi_enable(struct pse_controller_dev *pcdev, int id) 28 { 29 struct pse_reg_priv *priv = to_pse_reg(pcdev); 30 int ret; 31 32 ret = regulator_enable(priv->ps); 33 if (ret) 34 return ret; 35 36 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; 37 return 0; 38 } 39 40 static int 41 pse_reg_pi_disable(struct pse_controller_dev *pcdev, int id) 42 { 43 struct pse_reg_priv *priv = to_pse_reg(pcdev); 44 int ret; 45 46 ret = regulator_disable(priv->ps); 47 if (ret) 48 return ret; 49 50 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; 51 return 0; 52 } 53 54 static int 55 pse_reg_pi_is_enabled(struct pse_controller_dev *pcdev, int id) 56 { 57 struct pse_reg_priv *priv = to_pse_reg(pcdev); 58 59 return regulator_is_enabled(priv->ps); 60 } 61 62 static int 63 pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, 64 struct netlink_ext_ack *extack, 65 struct pse_control_status *status) 66 { 67 struct pse_reg_priv *priv = to_pse_reg(pcdev); 68 int ret; 69 70 ret = regulator_is_enabled(priv->ps); 71 if (ret < 0) 72 return ret; 73 74 if (!ret) 75 status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; 76 else 77 status->podl_pw_status = 78 ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; 79 80 status->podl_admin_state = priv->admin_state; 81 82 return 0; 83 } 84 85 static const struct pse_controller_ops pse_reg_ops = { 86 .ethtool_get_status = pse_reg_ethtool_get_status, 87 .pi_enable = pse_reg_pi_enable, 88 .pi_is_enabled = pse_reg_pi_is_enabled, 89 .pi_disable = pse_reg_pi_disable, 90 }; 91 92 static int 93 pse_reg_probe(struct platform_device *pdev) 94 { 95 struct device *dev = &pdev->dev; 96 struct pse_reg_priv *priv; 97 int ret; 98 99 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 100 if (!priv) 101 return -ENOMEM; 102 103 if (!pdev->dev.of_node) 104 return -ENOENT; 105 106 priv->ps = devm_regulator_get_exclusive(dev, "pse"); 107 if (IS_ERR(priv->ps)) 108 return dev_err_probe(dev, PTR_ERR(priv->ps), 109 "failed to get PSE regulator.\n"); 110 111 platform_set_drvdata(pdev, priv); 112 113 ret = regulator_is_enabled(priv->ps); 114 if (ret < 0) 115 return ret; 116 117 if (ret) 118 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; 119 else 120 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; 121 122 priv->pcdev.owner = THIS_MODULE; 123 priv->pcdev.ops = &pse_reg_ops; 124 priv->pcdev.dev = dev; 125 priv->pcdev.types = ETHTOOL_PSE_PODL; 126 ret = devm_pse_controller_register(dev, &priv->pcdev); 127 if (ret) { 128 dev_err(dev, "failed to register PSE controller (%pe)\n", 129 ERR_PTR(ret)); 130 return ret; 131 } 132 133 return 0; 134 } 135 136 static const __maybe_unused struct of_device_id pse_reg_of_match[] = { 137 { .compatible = "podl-pse-regulator", }, 138 { }, 139 }; 140 MODULE_DEVICE_TABLE(of, pse_reg_of_match); 141 142 static struct platform_driver pse_reg_driver = { 143 .probe = pse_reg_probe, 144 .driver = { 145 .name = "PSE regulator", 146 .of_match_table = of_match_ptr(pse_reg_of_match), 147 }, 148 }; 149 module_platform_driver(pse_reg_driver); 150 151 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); 152 MODULE_DESCRIPTION("regulator based Ethernet Power Sourcing Equipment"); 153 MODULE_LICENSE("GPL v2"); 154 MODULE_ALIAS("platform:pse-regulator"); 155