Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  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
 15struct 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
 21static 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
 26static int
 27pse_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
 58static int
 59pse_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
 81static 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
 86static int
 87pse_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
129static const __maybe_unused struct of_device_id pse_reg_of_match[] = {
130	{ .compatible = "podl-pse-regulator", },
131	{ },
132};
133MODULE_DEVICE_TABLE(of, pse_reg_of_match);
134
135static 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};
142module_platform_driver(pse_reg_driver);
143
144MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
145MODULE_DESCRIPTION("regulator based Ethernet Power Sourcing Equipment");
146MODULE_LICENSE("GPL v2");
147MODULE_ALIAS("platform:pse-regulator");