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