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;
|
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 = {
|
static const struct regulator_ops pse_pi_ops = {
|
||||||
.is_enabled = pse_pi_is_enabled,
|
.is_enabled = pse_pi_is_enabled,
|
||||||
.enable = pse_pi_enable,
|
.enable = pse_pi_enable,
|
||||||
.disable = pse_pi_disable,
|
.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
|
static int
|
||||||
|
@ -298,7 +401,9 @@ devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev,
|
||||||
rdesc->ops = &pse_pi_ops;
|
rdesc->ops = &pse_pi_ops;
|
||||||
rdesc->owner = pcdev->owner;
|
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";
|
rinit_data->supply_regulator = "vpwr";
|
||||||
|
|
||||||
rconfig.dev = pcdev->dev;
|
rconfig.dev = pcdev->dev;
|
||||||
|
@ -626,6 +731,23 @@ out:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(of_pse_control_get);
|
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
|
* pse_ethtool_get_status - get status of PSE control
|
||||||
* @psec: PSE control pointer
|
* @psec: PSE control pointer
|
||||||
|
@ -638,19 +760,10 @@ int pse_ethtool_get_status(struct pse_control *psec,
|
||||||
struct netlink_ext_ack *extack,
|
struct netlink_ext_ack *extack,
|
||||||
struct pse_control_status *status)
|
struct pse_control_status *status)
|
||||||
{
|
{
|
||||||
const struct pse_controller_ops *ops;
|
|
||||||
int err;
|
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);
|
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);
|
mutex_unlock(&psec->pcdev->lock);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -732,6 +845,43 @@ int pse_ethtool_set_config(struct pse_control *psec,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(pse_ethtool_set_config);
|
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)
|
bool pse_has_podl(struct pse_control *psec)
|
||||||
{
|
{
|
||||||
return psec->pcdev->types & ETHTOOL_PSE_PODL;
|
return psec->pcdev->types & ETHTOOL_PSE_PODL;
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#include <linux/list.h>
|
#include <linux/list.h>
|
||||||
#include <uapi/linux/ethtool.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 phy_device;
|
||||||
struct pse_controller_dev;
|
struct pse_controller_dev;
|
||||||
|
|
||||||
|
@ -41,6 +44,12 @@ struct pse_control_config {
|
||||||
* @c33_actual_pw: power currently delivered by the PSE in mW
|
* @c33_actual_pw: power currently delivered by the PSE in mW
|
||||||
* IEEE 802.3-2022 30.9.1.1.23 aPSEActualPower
|
* IEEE 802.3-2022 30.9.1.1.23 aPSEActualPower
|
||||||
* @c33_ext_state_info: extended state information of the PSE
|
* @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 {
|
struct pse_control_status {
|
||||||
enum ethtool_podl_pse_admin_state podl_admin_state;
|
enum ethtool_podl_pse_admin_state podl_admin_state;
|
||||||
|
@ -50,6 +59,9 @@ struct pse_control_status {
|
||||||
u32 c33_pw_class;
|
u32 c33_pw_class;
|
||||||
u32 c33_actual_pw;
|
u32 c33_actual_pw;
|
||||||
struct ethtool_c33_pse_ext_state_info c33_ext_state_info;
|
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.
|
* May also return negative errno.
|
||||||
* @pi_enable: Configure the PSE PI as enabled.
|
* @pi_enable: Configure the PSE PI as enabled.
|
||||||
* @pi_disable: Configure the PSE PI as disabled.
|
* @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 {
|
struct pse_controller_ops {
|
||||||
int (*ethtool_get_status)(struct pse_controller_dev *pcdev,
|
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_is_enabled)(struct pse_controller_dev *pcdev, int id);
|
||||||
int (*pi_enable)(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_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;
|
struct module;
|
||||||
|
@ -156,6 +181,11 @@ int pse_ethtool_get_status(struct pse_control *psec,
|
||||||
int pse_ethtool_set_config(struct pse_control *psec,
|
int pse_ethtool_set_config(struct pse_control *psec,
|
||||||
struct netlink_ext_ack *extack,
|
struct netlink_ext_ack *extack,
|
||||||
const struct pse_control_config *config);
|
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_podl(struct pse_control *psec);
|
||||||
bool pse_has_c33(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;
|
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)
|
static inline bool pse_has_podl(struct pse_control *psec)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Add table
Reference in a new issue