mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	net: pse-pd: Add new power limit get and set c33 features
This patch add a way to get and set the power limit of a PSE PI. For that it uses regulator API callbacks wrapper like get_voltage() and get/set_current_limit() as power is simply V * I. We used mW unit as defined by the IEEE 802.3-2022 standards. set_current_limit() uses the voltage return by get_voltage() and the desired power limit to calculate the current limit. get_voltage() callback is then mandatory to set the power limit. get_current_limit() callback is by default looking at a driver callback and fallback to extracting the current limit from _pse_ethtool_get_status() if the driver does not set its callback. We prefer let the user the choice because ethtool_get_status return much more information than the current limit. expand pse status with c33_pw_limit_ranges to return the ranges available to configure the power limit. Reviewed-by: Sai Krishna <saikrishnag@marvell.com> Acked-by: Oleksij Rempel <o.rempel@pengutronix.de> Signed-off-by: Kory Maincent <kory.maincent@bootlin.com> Link: https://patch.msgid.link/20240704-feature_poe_power_cap-v6-4-320003204264@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									ae37dc5742
								
							
						
					
					
						commit
						4a83abcef5
					
				
					 2 changed files with 204 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -265,10 +265,113 @@ static int pse_pi_disable(struct regulator_dev *rdev)
 | 
			
		|||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _pse_pi_get_voltage(struct regulator_dev *rdev)
 | 
			
		||||
{
 | 
			
		||||
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
 | 
			
		||||
	const struct pse_controller_ops *ops;
 | 
			
		||||
	int id;
 | 
			
		||||
 | 
			
		||||
	ops = pcdev->ops;
 | 
			
		||||
	if (!ops->pi_get_voltage)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	id = rdev_get_id(rdev);
 | 
			
		||||
	return ops->pi_get_voltage(pcdev, id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pse_pi_get_voltage(struct regulator_dev *rdev)
 | 
			
		||||
{
 | 
			
		||||
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&pcdev->lock);
 | 
			
		||||
	ret = _pse_pi_get_voltage(rdev);
 | 
			
		||||
	mutex_unlock(&pcdev->lock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev,
 | 
			
		||||
				   int id,
 | 
			
		||||
				   struct netlink_ext_ack *extack,
 | 
			
		||||
				   struct pse_control_status *status);
 | 
			
		||||
 | 
			
		||||
static int pse_pi_get_current_limit(struct regulator_dev *rdev)
 | 
			
		||||
{
 | 
			
		||||
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
 | 
			
		||||
	const struct pse_controller_ops *ops;
 | 
			
		||||
	struct netlink_ext_ack extack = {};
 | 
			
		||||
	struct pse_control_status st = {};
 | 
			
		||||
	int id, uV, ret;
 | 
			
		||||
	s64 tmp_64;
 | 
			
		||||
 | 
			
		||||
	ops = pcdev->ops;
 | 
			
		||||
	id = rdev_get_id(rdev);
 | 
			
		||||
	mutex_lock(&pcdev->lock);
 | 
			
		||||
	if (ops->pi_get_current_limit) {
 | 
			
		||||
		ret = ops->pi_get_current_limit(pcdev, id);
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* If pi_get_current_limit() callback not populated get voltage
 | 
			
		||||
	 * from pi_get_voltage() and power limit from ethtool_get_status()
 | 
			
		||||
	 *  to calculate current limit.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = _pse_pi_get_voltage(rdev);
 | 
			
		||||
	if (!ret) {
 | 
			
		||||
		dev_err(pcdev->dev, "Voltage null\n");
 | 
			
		||||
		ret = -ERANGE;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto out;
 | 
			
		||||
	uV = ret;
 | 
			
		||||
 | 
			
		||||
	ret = _pse_ethtool_get_status(pcdev, id, &extack, &st);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (!st.c33_avail_pw_limit) {
 | 
			
		||||
		ret = -ENODATA;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tmp_64 = st.c33_avail_pw_limit;
 | 
			
		||||
	tmp_64 *= 1000000000ull;
 | 
			
		||||
	/* uA = mW * 1000000000 / uV */
 | 
			
		||||
	ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	mutex_unlock(&pcdev->lock);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA,
 | 
			
		||||
				    int max_uA)
 | 
			
		||||
{
 | 
			
		||||
	struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
 | 
			
		||||
	const struct pse_controller_ops *ops;
 | 
			
		||||
	int id, ret;
 | 
			
		||||
 | 
			
		||||
	ops = pcdev->ops;
 | 
			
		||||
	if (!ops->pi_set_current_limit)
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
 | 
			
		||||
	id = rdev_get_id(rdev);
 | 
			
		||||
	mutex_lock(&pcdev->lock);
 | 
			
		||||
	ret = ops->pi_set_current_limit(pcdev, id, max_uA);
 | 
			
		||||
	mutex_unlock(&pcdev->lock);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const struct regulator_ops pse_pi_ops = {
 | 
			
		||||
	.is_enabled = pse_pi_is_enabled,
 | 
			
		||||
	.enable = pse_pi_enable,
 | 
			
		||||
	.disable = pse_pi_disable,
 | 
			
		||||
	.get_voltage = pse_pi_get_voltage,
 | 
			
		||||
	.get_current_limit = pse_pi_get_current_limit,
 | 
			
		||||
	.set_current_limit = pse_pi_set_current_limit,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
| 
						 | 
				
			
			@ -298,7 +401,9 @@ devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
 | 
			
		|||
	rdesc->ops = &pse_pi_ops;
 | 
			
		||||
	rdesc->owner = pcdev->owner;
 | 
			
		||||
 | 
			
		||||
	rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
 | 
			
		||||
	rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS |
 | 
			
		||||
						 REGULATOR_CHANGE_CURRENT;
 | 
			
		||||
	rinit_data->constraints.max_uA = MAX_PI_CURRENT;
 | 
			
		||||
	rinit_data->supply_regulator = "vpwr";
 | 
			
		||||
 | 
			
		||||
	rconfig.dev = pcdev->dev;
 | 
			
		||||
| 
						 | 
				
			
			@ -626,6 +731,23 @@ struct pse_control *of_pse_control_get(struct device_node *node)
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(of_pse_control_get);
 | 
			
		||||
 | 
			
		||||
static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev,
 | 
			
		||||
				   int id,
 | 
			
		||||
				   struct netlink_ext_ack *extack,
 | 
			
		||||
				   struct pse_control_status *status)
 | 
			
		||||
{
 | 
			
		||||
	const struct pse_controller_ops *ops;
 | 
			
		||||
 | 
			
		||||
	ops = pcdev->ops;
 | 
			
		||||
	if (!ops->ethtool_get_status) {
 | 
			
		||||
		NL_SET_ERR_MSG(extack,
 | 
			
		||||
			       "PSE driver does not support status report");
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ops->ethtool_get_status(pcdev, id, extack, status);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pse_ethtool_get_status - get status of PSE control
 | 
			
		||||
 * @psec: PSE control pointer
 | 
			
		||||
| 
						 | 
				
			
			@ -638,19 +760,10 @@ int pse_ethtool_get_status(struct pse_control *psec,
 | 
			
		|||
			   struct netlink_ext_ack *extack,
 | 
			
		||||
			   struct pse_control_status *status)
 | 
			
		||||
{
 | 
			
		||||
	const struct pse_controller_ops *ops;
 | 
			
		||||
	int err;
 | 
			
		||||
 | 
			
		||||
	ops = psec->pcdev->ops;
 | 
			
		||||
 | 
			
		||||
	if (!ops->ethtool_get_status) {
 | 
			
		||||
		NL_SET_ERR_MSG(extack,
 | 
			
		||||
			       "PSE driver does not support status report");
 | 
			
		||||
		return -EOPNOTSUPP;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&psec->pcdev->lock);
 | 
			
		||||
	err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status);
 | 
			
		||||
	err = _pse_ethtool_get_status(psec->pcdev, psec->id, extack, status);
 | 
			
		||||
	mutex_unlock(&psec->pcdev->lock);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -732,6 +845,43 @@ int pse_ethtool_set_config(struct pse_control *psec,
 | 
			
		|||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(pse_ethtool_set_config);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * pse_ethtool_set_pw_limit - set PSE control power limit
 | 
			
		||||
 * @psec: PSE control pointer
 | 
			
		||||
 * @extack: extack for reporting useful error messages
 | 
			
		||||
 * @pw_limit: power limit value in mW
 | 
			
		||||
 *
 | 
			
		||||
 * Return: 0 on success and failure value on error
 | 
			
		||||
 */
 | 
			
		||||
int pse_ethtool_set_pw_limit(struct pse_control *psec,
 | 
			
		||||
			     struct netlink_ext_ack *extack,
 | 
			
		||||
			     const unsigned int pw_limit)
 | 
			
		||||
{
 | 
			
		||||
	int uV, uA, ret;
 | 
			
		||||
	s64 tmp_64;
 | 
			
		||||
 | 
			
		||||
	ret = regulator_get_voltage(psec->ps);
 | 
			
		||||
	if (!ret) {
 | 
			
		||||
		NL_SET_ERR_MSG(extack,
 | 
			
		||||
			       "Can't calculate the current, PSE voltage read is 0");
 | 
			
		||||
		return -ERANGE;
 | 
			
		||||
	}
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		NL_SET_ERR_MSG(extack,
 | 
			
		||||
			       "Error reading PSE voltage");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
	uV = ret;
 | 
			
		||||
 | 
			
		||||
	tmp_64 = pw_limit;
 | 
			
		||||
	tmp_64 *= 1000000000ull;
 | 
			
		||||
	/* uA = mW * 1000000000 / uV */
 | 
			
		||||
	uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
 | 
			
		||||
 | 
			
		||||
	return regulator_set_current_limit(psec->ps, 0, uA);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit);
 | 
			
		||||
 | 
			
		||||
bool pse_has_podl(struct pse_control *psec)
 | 
			
		||||
{
 | 
			
		||||
	return psec->pcdev->types & ETHTOOL_PSE_PODL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,9 @@
 | 
			
		|||
#include <linux/list.h>
 | 
			
		||||
#include <uapi/linux/ethtool.h>
 | 
			
		||||
 | 
			
		||||
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
 | 
			
		||||
#define MAX_PI_CURRENT 1920000
 | 
			
		||||
 | 
			
		||||
struct phy_device;
 | 
			
		||||
struct pse_controller_dev;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +44,12 @@ struct pse_control_config {
 | 
			
		|||
 * @c33_actual_pw: power currently delivered by the PSE in mW
 | 
			
		||||
 *	IEEE 802.3-2022 30.9.1.1.23 aPSEActualPower
 | 
			
		||||
 * @c33_ext_state_info: extended state information of the PSE
 | 
			
		||||
 * @c33_avail_pw_limit: available power limit of the PSE in mW
 | 
			
		||||
 *	IEEE 802.3-2022 145.2.5.4 pse_avail_pwr
 | 
			
		||||
 * @c33_pw_limit_ranges: supported power limit configuration range. The driver
 | 
			
		||||
 *	is in charge of the memory allocation.
 | 
			
		||||
 * @c33_pw_limit_nb_ranges: number of supported power limit configuration
 | 
			
		||||
 *	ranges
 | 
			
		||||
 */
 | 
			
		||||
struct pse_control_status {
 | 
			
		||||
	enum ethtool_podl_pse_admin_state podl_admin_state;
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +59,9 @@ struct pse_control_status {
 | 
			
		|||
	u32 c33_pw_class;
 | 
			
		||||
	u32 c33_actual_pw;
 | 
			
		||||
	struct ethtool_c33_pse_ext_state_info c33_ext_state_info;
 | 
			
		||||
	u32 c33_avail_pw_limit;
 | 
			
		||||
	struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges;
 | 
			
		||||
	u32 c33_pw_limit_nb_ranges;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -61,6 +73,14 @@ struct pse_control_status {
 | 
			
		|||
 *		   May also return negative errno.
 | 
			
		||||
 * @pi_enable: Configure the PSE PI as enabled.
 | 
			
		||||
 * @pi_disable: Configure the PSE PI as disabled.
 | 
			
		||||
 * @pi_get_voltage: Return voltage similarly to get_voltage regulator
 | 
			
		||||
 *		    callback.
 | 
			
		||||
 * @pi_get_current_limit: Get the configured current limit similarly to
 | 
			
		||||
 *			  get_current_limit regulator callback.
 | 
			
		||||
 * @pi_set_current_limit: Configure the current limit similarly to
 | 
			
		||||
 *			  set_current_limit regulator callback.
 | 
			
		||||
 *			  Should not return an error in case of MAX_PI_CURRENT
 | 
			
		||||
 *			  current value set.
 | 
			
		||||
 */
 | 
			
		||||
struct pse_controller_ops {
 | 
			
		||||
	int (*ethtool_get_status)(struct pse_controller_dev *pcdev,
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +90,11 @@ struct pse_controller_ops {
 | 
			
		|||
	int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id);
 | 
			
		||||
	int (*pi_enable)(struct pse_controller_dev *pcdev, int id);
 | 
			
		||||
	int (*pi_disable)(struct pse_controller_dev *pcdev, int id);
 | 
			
		||||
	int (*pi_get_voltage)(struct pse_controller_dev *pcdev, int id);
 | 
			
		||||
	int (*pi_get_current_limit)(struct pse_controller_dev *pcdev,
 | 
			
		||||
				    int id);
 | 
			
		||||
	int (*pi_set_current_limit)(struct pse_controller_dev *pcdev,
 | 
			
		||||
				    int id, int max_uA);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct module;
 | 
			
		||||
| 
						 | 
				
			
			@ -156,6 +181,11 @@ int pse_ethtool_get_status(struct pse_control *psec,
 | 
			
		|||
int pse_ethtool_set_config(struct pse_control *psec,
 | 
			
		||||
			   struct netlink_ext_ack *extack,
 | 
			
		||||
			   const struct pse_control_config *config);
 | 
			
		||||
int pse_ethtool_set_pw_limit(struct pse_control *psec,
 | 
			
		||||
			     struct netlink_ext_ack *extack,
 | 
			
		||||
			     const unsigned int pw_limit);
 | 
			
		||||
int pse_ethtool_get_pw_limit(struct pse_control *psec,
 | 
			
		||||
			     struct netlink_ext_ack *extack);
 | 
			
		||||
 | 
			
		||||
bool pse_has_podl(struct pse_control *psec);
 | 
			
		||||
bool pse_has_c33(struct pse_control *psec);
 | 
			
		||||
| 
						 | 
				
			
			@ -185,6 +215,19 @@ static inline int pse_ethtool_set_config(struct pse_control *psec,
 | 
			
		|||
	return -EOPNOTSUPP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int pse_ethtool_set_pw_limit(struct pse_control *psec,
 | 
			
		||||
					   struct netlink_ext_ack *extack,
 | 
			
		||||
					   const unsigned int pw_limit)
 | 
			
		||||
{
 | 
			
		||||
	return -EOPNOTSUPP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline int pse_ethtool_get_pw_limit(struct pse_control *psec,
 | 
			
		||||
					   struct netlink_ext_ack *extack)
 | 
			
		||||
{
 | 
			
		||||
	return -EOPNOTSUPP;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool pse_has_podl(struct pse_control *psec)
 | 
			
		||||
{
 | 
			
		||||
	return false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue