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_ethtool_set_config(struct pse_controller_dev *pcdev, unsigned long id, 28 struct netlink_ext_ack *extack, 29 const struct pse_control_config *config) 30 { 31 struct pse_reg_priv *priv = to_pse_reg(pcdev); 32 int ret; 33 34 if (priv->admin_state == config->admin_cotrol) 35 return 0; 36 37 switch (config->admin_cotrol) { 38 case ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: 39 ret = regulator_enable(priv->ps); 40 break; 41 case ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: 42 ret = regulator_disable(priv->ps); 43 break; 44 default: 45 dev_err(pcdev->dev, "Unknown admin state %i\n", 46 config->admin_cotrol); 47 ret = -ENOTSUPP; 48 } 49 50 if (ret) 51 return ret; 52 53 priv->admin_state = config->admin_cotrol; 54 55 return 0; 56 } 57 58 static int 59 pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, 60 struct netlink_ext_ack *extack, 61 struct pse_control_status *status) 62 { 63 struct pse_reg_priv *priv = to_pse_reg(pcdev); 64 int ret; 65 66 ret = regulator_is_enabled(priv->ps); 67 if (ret < 0) 68 return ret; 69 70 if (!ret) 71 status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; 72 else 73 status->podl_pw_status = 74 ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; 75 76 status->podl_admin_state = priv->admin_state; 77 78 return 0; 79 } 80 81 static const struct pse_controller_ops pse_reg_ops = { 82 .ethtool_get_status = pse_reg_ethtool_get_status, 83 .ethtool_set_config = pse_reg_ethtool_set_config, 84 }; 85 86 static int 87 pse_reg_probe(struct platform_device *pdev) 88 { 89 struct device *dev = &pdev->dev; 90 struct pse_reg_priv *priv; 91 int ret; 92 93 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 94 if (!priv) 95 return -ENOMEM; 96 97 if (!pdev->dev.of_node) 98 return -ENOENT; 99 100 priv->ps = devm_regulator_get_exclusive(dev, "pse"); 101 if (IS_ERR(priv->ps)) 102 return dev_err_probe(dev, PTR_ERR(priv->ps), 103 "failed to get PSE regulator.\n"); 104 105 platform_set_drvdata(pdev, priv); 106 107 ret = regulator_is_enabled(priv->ps); 108 if (ret < 0) 109 return ret; 110 111 if (ret) 112 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; 113 else 114 priv->admin_state = ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; 115 116 priv->pcdev.owner = THIS_MODULE; 117 priv->pcdev.ops = &pse_reg_ops; 118 priv->pcdev.dev = dev; 119 ret = devm_pse_controller_register(dev, &priv->pcdev); 120 if (ret) { 121 dev_err(dev, "failed to register PSE controller (%pe)\n", 122 ERR_PTR(ret)); 123 return ret; 124 } 125 126 return 0; 127 } 128 129 static const __maybe_unused struct of_device_id pse_reg_of_match[] = { 130 { .compatible = "podl-pse-regulator", }, 131 { }, 132 }; 133 MODULE_DEVICE_TABLE(of, pse_reg_of_match); 134 135 static struct platform_driver pse_reg_driver = { 136 .probe = pse_reg_probe, 137 .driver = { 138 .name = "PSE regulator", 139 .of_match_table = of_match_ptr(pse_reg_of_match), 140 }, 141 }; 142 module_platform_driver(pse_reg_driver); 143 144 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); 145 MODULE_DESCRIPTION("regulator based Ethernet Power Sourcing Equipment"); 146 MODULE_LICENSE("GPL v2"); 147 MODULE_ALIAS("platform:pse-regulator"); 148