net: pse-pd: add regulator based PSE driver

Add generic, regulator based PSE driver to support simple Power Sourcing
Equipment without automatic classification support.

This driver was tested on 10Bast-T1L switch with regulator based PoDL PSE.

Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Oleksij Rempel 2022-10-03 08:52:02 +02:00 committed by Jakub Kicinski
parent f05dfdaf56
commit 66741b4e94
3 changed files with 160 additions and 0 deletions

View File

@ -9,3 +9,14 @@ menuconfig PSE_CONTROLLER
Generic Power Sourcing Equipment Controller support.
If unsure, say no.
if PSE_CONTROLLER
config PSE_REGULATOR
tristate "Regulator based PSE controller"
help
This module provides support for simple regulator based Ethernet Power
Sourcing Equipment without automatic classification support. For
example for basic implementation of PoDL (802.3bu) specification.
endif

View File

@ -2,3 +2,5 @@
# Makefile for Linux PSE drivers
obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o
obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o

View File

@ -0,0 +1,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");