Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
66741b4e f05dfdaf

+160
+11
drivers/net/pse-pd/Kconfig
··· 9 9 Generic Power Sourcing Equipment Controller support. 10 10 11 11 If unsure, say no. 12 + 13 + if PSE_CONTROLLER 14 + 15 + config PSE_REGULATOR 16 + tristate "Regulator based PSE controller" 17 + help 18 + This module provides support for simple regulator based Ethernet Power 19 + Sourcing Equipment without automatic classification support. For 20 + example for basic implementation of PoDL (802.3bu) specification. 21 + 22 + endif
+2
drivers/net/pse-pd/Makefile
··· 2 2 # Makefile for Linux PSE drivers 3 3 4 4 obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o 5 + 6 + obj-$(CONFIG_PSE_REGULATOR) += pse_regulator.o
+147
drivers/net/pse-pd/pse_regulator.c
··· 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");