Commit 30d7b672 authored by Kory Maincent (Dent Project)'s avatar Kory Maincent (Dent Project) Committed by Jakub Kicinski

net: ethtool: Add new power limit get and set features

This patch expands the status information provided by ethtool for PSE c33
with available power limit and available power limit ranges. It also adds
a call to pse_ethtool_set_pw_limit() to configure the PSE control power
limit.
Reviewed-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: default avatarKory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20240704-feature_poe_power_cap-v6-5-320003204264@bootlin.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4a83abce
...@@ -1730,24 +1730,28 @@ Request contents: ...@@ -1730,24 +1730,28 @@ Request contents:
Kernel response contents: Kernel response contents:
====================================== ====== ============================= ========================================== ====== =============================
``ETHTOOL_A_PSE_HEADER`` nested reply header ``ETHTOOL_A_PSE_HEADER`` nested reply header
``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` u32 Operational state of the PoDL ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` u32 Operational state of the PoDL
PSE functions PSE functions
``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the ``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the
PoDL PSE. PoDL PSE.
``ETHTOOL_A_C33_PSE_ADMIN_STATE`` u32 Operational state of the PoE ``ETHTOOL_A_C33_PSE_ADMIN_STATE`` u32 Operational state of the PoE
PSE functions. PSE functions.
``ETHTOOL_A_C33_PSE_PW_D_STATUS`` u32 power detection status of the ``ETHTOOL_A_C33_PSE_PW_D_STATUS`` u32 power detection status of the
PoE PSE. PoE PSE.
``ETHTOOL_A_C33_PSE_PW_CLASS`` u32 power class of the PoE PSE. ``ETHTOOL_A_C33_PSE_PW_CLASS`` u32 power class of the PoE PSE.
``ETHTOOL_A_C33_PSE_ACTUAL_PW`` u32 actual power drawn on the ``ETHTOOL_A_C33_PSE_ACTUAL_PW`` u32 actual power drawn on the
PoE PSE. PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_STATE`` u32 power extended state of the ``ETHTOOL_A_C33_PSE_EXT_STATE`` u32 power extended state of the
PoE PSE. PoE PSE.
``ETHTOOL_A_C33_PSE_EXT_SUBSTATE`` u32 power extended substatus of ``ETHTOOL_A_C33_PSE_EXT_SUBSTATE`` u32 power extended substatus of
the PoE PSE. the PoE PSE.
====================================== ====== ============================= ``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT`` u32 currently configured power
limit of the PoE PSE.
``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested Supported power limit
configuration ranges.
========================================== ====== =============================
When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies
the operational state of the PoDL PSE functions. The operational state of the the operational state of the PoDL PSE functions. The operational state of the
...@@ -1809,6 +1813,16 @@ Possible values are: ...@@ -1809,6 +1813,16 @@ Possible values are:
ethtool_c33_pse_ext_substate_power_not_available ethtool_c33_pse_ext_substate_power_not_available
ethtool_c33_pse_ext_substate_short_detected ethtool_c33_pse_ext_substate_short_detected
When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT`` attribute
identifies the C33 PSE power limit in mW.
When set the optional ``ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES`` nested attribute
identifies the C33 PSE power limit ranges through
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MIN`` and
``ETHTOOL_A_C33_PSE_PWR_VAL_LIMIT_RANGE_MAX``.
If the controller works with fixed classes, the min and max values will be
equal.
PSE_SET PSE_SET
======= =======
...@@ -1820,6 +1834,8 @@ Request contents: ...@@ -1820,6 +1834,8 @@ Request contents:
``ETHTOOL_A_PSE_HEADER`` nested request header ``ETHTOOL_A_PSE_HEADER`` nested request header
``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` u32 Control PoDL PSE Admin state ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` u32 Control PoDL PSE Admin state
``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` u32 Control PSE Admin state ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` u32 Control PSE Admin state
``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` u32 Control PoE PSE available
power limit
====================================== ====== ============================= ====================================== ====== =============================
When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used
...@@ -1830,6 +1846,18 @@ to control PoDL PSE Admin functions. This option is implementing ...@@ -1830,6 +1846,18 @@ to control PoDL PSE Admin functions. This option is implementing
The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing The same goes for ``ETHTOOL_A_C33_PSE_ADMIN_CONTROL`` implementing
``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl. ``IEEE 802.3-2022`` 30.9.1.2.1 acPSEAdminControl.
When set, the optional ``ETHTOOL_A_C33_PSE_AVAIL_PWR_LIMIT`` attribute is
used to control the available power value limit for C33 PSE in milliwatts.
This attribute corresponds to the `pse_available_power` variable described in
``IEEE 802.3-2022`` 33.2.4.4 Variables and `pse_avail_pwr` in 145.2.5.4
Variables, which are described in power classes.
It was decided to use milliwatts for this interface to unify it with other
power monitoring interfaces, which also use milliwatts, and to align with
various existing products that document power consumption in watts rather than
classes. If power limit configuration based on classes is needed, the
conversion can be done in user space, for example by ethtool.
RSS_GET RSS_GET
======= =======
......
...@@ -1288,4 +1288,9 @@ struct ethtool_c33_pse_ext_state_info { ...@@ -1288,4 +1288,9 @@ struct ethtool_c33_pse_ext_state_info {
u32 __c33_pse_ext_substate; u32 __c33_pse_ext_substate;
}; };
}; };
struct ethtool_c33_pse_pw_limit_range {
u32 min;
u32 max;
};
#endif /* _LINUX_ETHTOOL_H */ #endif /* _LINUX_ETHTOOL_H */
...@@ -930,6 +930,12 @@ enum { ...@@ -930,6 +930,12 @@ enum {
}; };
/* Power Sourcing Equipment */ /* Power Sourcing Equipment */
enum {
ETHTOOL_A_C33_PSE_PW_LIMIT_UNSPEC,
ETHTOOL_A_C33_PSE_PW_LIMIT_MIN, /* u32 */
ETHTOOL_A_C33_PSE_PW_LIMIT_MAX, /* u32 */
};
enum { enum {
ETHTOOL_A_PSE_UNSPEC, ETHTOOL_A_PSE_UNSPEC,
ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */ ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */
...@@ -943,6 +949,8 @@ enum { ...@@ -943,6 +949,8 @@ enum {
ETHTOOL_A_C33_PSE_ACTUAL_PW, /* u32 */ ETHTOOL_A_C33_PSE_ACTUAL_PW, /* u32 */
ETHTOOL_A_C33_PSE_EXT_STATE, /* u32 */ ETHTOOL_A_C33_PSE_EXT_STATE, /* u32 */
ETHTOOL_A_C33_PSE_EXT_SUBSTATE, /* u32 */ ETHTOOL_A_C33_PSE_EXT_SUBSTATE, /* u32 */
ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT, /* u32 */
ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES, /* nest - _C33_PSE_PW_LIMIT_* */
/* add new constants above here */ /* add new constants above here */
__ETHTOOL_A_PSE_CNT, __ETHTOOL_A_PSE_CNT,
......
...@@ -96,9 +96,46 @@ static int pse_reply_size(const struct ethnl_req_info *req_base, ...@@ -96,9 +96,46 @@ static int pse_reply_size(const struct ethnl_req_info *req_base,
/* _C33_PSE_EXT_SUBSTATE */ /* _C33_PSE_EXT_SUBSTATE */
len += nla_total_size(sizeof(u32)); len += nla_total_size(sizeof(u32));
} }
if (st->c33_avail_pw_limit > 0)
/* _C33_AVAIL_PSE_PW_LIMIT */
len += nla_total_size(sizeof(u32));
if (st->c33_pw_limit_nb_ranges > 0)
/* _C33_PSE_PW_LIMIT_RANGES */
len += st->c33_pw_limit_nb_ranges *
(nla_total_size(0) +
nla_total_size(sizeof(u32)) * 2);
return len; return len;
} }
static int pse_put_pw_limit_ranges(struct sk_buff *skb,
const struct pse_control_status *st)
{
const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges;
int i;
pw_limit_ranges = st->c33_pw_limit_ranges;
for (i = 0; i < st->c33_pw_limit_nb_ranges; i++) {
struct nlattr *nest;
nest = nla_nest_start(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES);
if (!nest)
return -EMSGSIZE;
if (nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MIN,
pw_limit_ranges->min) ||
nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MAX,
pw_limit_ranges->max)) {
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
nla_nest_end(skb, nest);
pw_limit_ranges++;
}
return 0;
}
static int pse_fill_reply(struct sk_buff *skb, static int pse_fill_reply(struct sk_buff *skb,
const struct ethnl_req_info *req_base, const struct ethnl_req_info *req_base,
const struct ethnl_reply_data *reply_base) const struct ethnl_reply_data *reply_base)
...@@ -147,9 +184,25 @@ static int pse_fill_reply(struct sk_buff *skb, ...@@ -147,9 +184,25 @@ static int pse_fill_reply(struct sk_buff *skb,
return -EMSGSIZE; return -EMSGSIZE;
} }
if (st->c33_avail_pw_limit > 0 &&
nla_put_u32(skb, ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT,
st->c33_avail_pw_limit))
return -EMSGSIZE;
if (st->c33_pw_limit_nb_ranges > 0 &&
pse_put_pw_limit_ranges(skb, st))
return -EMSGSIZE;
return 0; return 0;
} }
static void pse_cleanup_data(struct ethnl_reply_data *reply_base)
{
const struct pse_reply_data *data = PSE_REPDATA(reply_base);
kfree(data->status.c33_pw_limit_ranges);
}
/* PSE_SET */ /* PSE_SET */
const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = { const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
...@@ -160,6 +213,7 @@ const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = { ...@@ -160,6 +213,7 @@ const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
[ETHTOOL_A_C33_PSE_ADMIN_CONTROL] = [ETHTOOL_A_C33_PSE_ADMIN_CONTROL] =
NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED, NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED,
ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED), ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED),
[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT] = { .type = NLA_U32 },
}; };
static int static int
...@@ -202,19 +256,39 @@ static int ...@@ -202,19 +256,39 @@ static int
ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info) ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
{ {
struct net_device *dev = req_info->dev; struct net_device *dev = req_info->dev;
struct pse_control_config config = {};
struct nlattr **tb = info->attrs; struct nlattr **tb = info->attrs;
struct phy_device *phydev; struct phy_device *phydev;
int ret = 0;
phydev = dev->phydev; phydev = dev->phydev;
if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) {
unsigned int pw_limit;
pw_limit = nla_get_u32(tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]);
ret = pse_ethtool_set_pw_limit(phydev->psec, info->extack,
pw_limit);
if (ret)
return ret;
}
/* These values are already validated by the ethnl_pse_set_policy */ /* These values are already validated by the ethnl_pse_set_policy */
if (pse_has_podl(phydev->psec)) if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] ||
config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]); tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]) {
if (pse_has_c33(phydev->psec)) struct pse_control_config config = {};
config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
if (pse_has_podl(phydev->psec))
config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
if (pse_has_c33(phydev->psec))
config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
ret = pse_ethtool_set_config(phydev->psec, info->extack,
&config);
if (ret)
return ret;
}
/* Return errno directly - PSE has no notification */ return ret;
return pse_ethtool_set_config(phydev->psec, info->extack, &config);
} }
const struct ethnl_request_ops ethnl_pse_request_ops = { const struct ethnl_request_ops ethnl_pse_request_ops = {
...@@ -227,6 +301,7 @@ const struct ethnl_request_ops ethnl_pse_request_ops = { ...@@ -227,6 +301,7 @@ const struct ethnl_request_ops ethnl_pse_request_ops = {
.prepare_data = pse_prepare_data, .prepare_data = pse_prepare_data,
.reply_size = pse_reply_size, .reply_size = pse_reply_size,
.fill_reply = pse_fill_reply, .fill_reply = pse_fill_reply,
.cleanup_data = pse_cleanup_data,
.set_validate = ethnl_set_pse_validate, .set_validate = ethnl_set_pse_validate,
.set = ethnl_set_pse, .set = ethnl_set_pse,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment