diff options
author | Paolo Abeni <pabeni@redhat.com> | 2025-01-14 13:56:36 +0100 |
---|---|---|
committer | Paolo Abeni <pabeni@redhat.com> | 2025-01-14 13:56:37 +0100 |
commit | 9c7ad35632297edc08d0f2c7b599137e9fb5f9ff (patch) | |
tree | 00cd26f7d83933351e6957290d586d037dcece09 | |
parent | 2b1d9116b0df5e65e78b2962942fe12672e25773 (diff) | |
parent | 5385f1e1923ca8131eb143567d509b101a344e06 (diff) |
Merge branch 'arrange-pse-core-and-update-tps23881-driver'
Kory Maincent says:
====================
Arrange PSE core and update TPS23881 driver
This patch includes several improvements to the PSE core for better
implementation and maintainability:
- Move the conversion between current limit and power limit from the driver
to the PSE core.
- Update power and current limit checks.
- Split the ethtool_get_status callback into multiple callbacks.
- Fix PSE PI of_node detection.
- Clean ethtool header of PSE structures.
Additionally, the TPS23881 driver has been updated to support power
limit and measurement features, aligning with the new PSE core
functionalities.
This patch series is the first part of the budget evaluation strategy
support patch series sent earlier:
https://lore.kernel.org/netdev/20250104161622.7b82dfdf@kmaincent-XPS-13-7390/T/#t
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
====================
Link: https://patch.msgid.link/20250110-b4-feature_poe_arrange-v3-0-142279aedb94@bootlin.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
-rw-r--r-- | drivers/net/pse-pd/pd692x0.c | 224 | ||||
-rw-r--r-- | drivers/net/pse-pd/pse_core.c | 183 | ||||
-rw-r--r-- | drivers/net/pse-pd/pse_regulator.c | 23 | ||||
-rw-r--r-- | drivers/net/pse-pd/tps23881.c | 449 | ||||
-rw-r--r-- | include/linux/ethtool.h | 20 | ||||
-rw-r--r-- | include/linux/pse-pd/pse.h | 134 | ||||
-rw-r--r-- | net/ethtool/pse-pd.c | 8 |
7 files changed, 733 insertions, 308 deletions
diff --git a/drivers/net/pse-pd/pd692x0.c b/drivers/net/pse-pd/pd692x0.c index 0af7db80b2f8..fc9e23927b3b 100644 --- a/drivers/net/pse-pd/pd692x0.c +++ b/drivers/net/pse-pd/pd692x0.c @@ -431,31 +431,6 @@ static int pd692x0_pi_disable(struct pse_controller_dev *pcdev, int id) return 0; } -static int pd692x0_pi_is_enabled(struct pse_controller_dev *pcdev, int id) -{ - struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); - struct pd692x0_msg msg, buf = {0}; - int ret; - - ret = pd692x0_fw_unavailable(priv); - if (ret) - return ret; - - msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; - msg.sub[2] = id; - ret = pd692x0_sendrecv_msg(priv, &msg, &buf); - if (ret < 0) - return ret; - - if (buf.sub[1]) { - priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; - return 1; - } else { - priv->admin_state[id] = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; - return 0; - } -} - struct pd692x0_pse_ext_state_mapping { u32 status_code; enum ethtool_c33_pse_ext_state pse_ext_state; @@ -517,21 +492,38 @@ pd692x0_pse_ext_state_map[] = { { /* sentinel */ } }; -static void -pd692x0_get_ext_state(struct ethtool_c33_pse_ext_state_info *c33_ext_state_info, - u32 status_code) +static int +pd692x0_pi_get_ext_state(struct pse_controller_dev *pcdev, int id, + struct pse_ext_state_info *ext_state_info) { + struct ethtool_c33_pse_ext_state_info *c33_ext_state_info; const struct pd692x0_pse_ext_state_mapping *ext_state_map; + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_msg msg, buf = {0}; + int ret; + ret = pd692x0_fw_unavailable(priv); + if (ret) + return ret; + + msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; + msg.sub[2] = id; + ret = pd692x0_sendrecv_msg(priv, &msg, &buf); + if (ret < 0) + return ret; + + c33_ext_state_info = &ext_state_info->c33_ext_state_info; ext_state_map = pd692x0_pse_ext_state_map; while (ext_state_map->status_code) { - if (ext_state_map->status_code == status_code) { + if (ext_state_map->status_code == buf.sub[0]) { c33_ext_state_info->c33_pse_ext_state = ext_state_map->pse_ext_state; c33_ext_state_info->__c33_pse_ext_substate = ext_state_map->pse_ext_substate; - return; + return 0; } ext_state_map++; } + + return 0; } struct pd692x0_class_pw { @@ -613,35 +605,36 @@ static int pd692x0_pi_set_pw_from_table(struct device *dev, } static int -pd692x0_pi_get_pw_ranges(struct pse_control_status *st) +pd692x0_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id, + struct pse_pw_limit_ranges *pw_limit_ranges) { + struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; const struct pd692x0_class_pw *pw_table; int i; pw_table = pd692x0_class_pw_table; - st->c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE, - sizeof(struct ethtool_c33_pse_pw_limit_range), - GFP_KERNEL); - if (!st->c33_pw_limit_ranges) + c33_pw_limit_ranges = kcalloc(PD692X0_CLASS_PW_TABLE_SIZE, + sizeof(*c33_pw_limit_ranges), + GFP_KERNEL); + if (!c33_pw_limit_ranges) return -ENOMEM; for (i = 0; i < PD692X0_CLASS_PW_TABLE_SIZE; i++, pw_table++) { - st->c33_pw_limit_ranges[i].min = pw_table->class_pw; - st->c33_pw_limit_ranges[i].max = pw_table->class_pw + pw_table->max_added_class_pw; + c33_pw_limit_ranges[i].min = pw_table->class_pw; + c33_pw_limit_ranges[i].max = pw_table->class_pw + + pw_table->max_added_class_pw; } - st->c33_pw_limit_nb_ranges = i; - return 0; + pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; + return i; } -static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev, - unsigned long id, - struct netlink_ext_ack *extack, - struct pse_control_status *status) +static int +pd692x0_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state) { struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); struct pd692x0_msg msg, buf = {0}; - u32 class; int ret; ret = pd692x0_fw_unavailable(priv); @@ -654,39 +647,65 @@ static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev, if (ret < 0) return ret; - /* Compare Port Status (Communication Protocol Document par. 7.1) */ - if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90) - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; - else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22) - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; - else if (buf.sub[0] == 0x12) - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_FAULT; - else - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; - if (buf.sub[1]) - status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; + admin_state->c33_admin_state = + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; else - status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; + admin_state->c33_admin_state = + ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; - priv->admin_state[id] = status->c33_admin_state; + priv->admin_state[id] = admin_state->c33_admin_state; - pd692x0_get_ext_state(&status->c33_ext_state_info, buf.sub[0]); - status->c33_actual_pw = (buf.data[0] << 4 | buf.data[1]) * 100; + return 0; +} - msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM]; +static int +pd692x0_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, + struct pse_pw_status *pw_status) +{ + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_msg msg, buf = {0}; + int ret; + + ret = pd692x0_fw_unavailable(priv); + if (ret) + return ret; + + msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; msg.sub[2] = id; - memset(&buf, 0, sizeof(buf)); ret = pd692x0_sendrecv_msg(priv, &msg, &buf); if (ret < 0) return ret; - ret = pd692x0_pi_get_pw_from_table(buf.data[0], buf.data[1]); - if (ret < 0) + /* Compare Port Status (Communication Protocol Document par. 7.1) */ + if ((buf.sub[0] & 0xf0) == 0x80 || (buf.sub[0] & 0xf0) == 0x90) + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; + else if (buf.sub[0] == 0x1b || buf.sub[0] == 0x22) + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_SEARCHING; + else if (buf.sub[0] == 0x12) + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_FAULT; + else + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; + + return 0; +} + +static int +pd692x0_pi_get_pw_class(struct pse_controller_dev *pcdev, int id) +{ + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_msg msg, buf = {0}; + u32 class; + int ret; + + ret = pd692x0_fw_unavailable(priv); + if (ret) return ret; - status->c33_avail_pw_limit = ret; - memset(&buf, 0, sizeof(buf)); msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_CLASS]; msg.sub[2] = id; ret = pd692x0_sendrecv_msg(priv, &msg, &buf); @@ -695,13 +714,29 @@ static int pd692x0_ethtool_get_status(struct pse_controller_dev *pcdev, class = buf.data[3] >> 4; if (class <= 8) - status->c33_pw_class = class; + return class; - ret = pd692x0_pi_get_pw_ranges(status); + return 0; +} + +static int +pd692x0_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) +{ + struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); + struct pd692x0_msg msg, buf = {0}; + int ret; + + ret = pd692x0_fw_unavailable(priv); + if (ret) + return ret; + + msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_STATUS]; + msg.sub[2] = id; + ret = pd692x0_sendrecv_msg(priv, &msg, &buf); if (ret < 0) return ret; - return 0; + return (buf.data[0] << 4 | buf.data[1]) * 100; } static struct pd692x0_msg_ver pd692x0_get_sw_version(struct pd692x0_priv *priv) @@ -999,13 +1034,12 @@ static int pd692x0_pi_get_voltage(struct pse_controller_dev *pcdev, int id) return (buf.sub[0] << 8 | buf.sub[1]) * 100000; } -static int pd692x0_pi_get_current_limit(struct pse_controller_dev *pcdev, - int id) +static int pd692x0_pi_get_pw_limit(struct pse_controller_dev *pcdev, + int id) { struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); struct pd692x0_msg msg, buf = {0}; - int mW, uV, uA, ret; - s64 tmp_64; + int ret; msg = pd692x0_msg_template_list[PD692X0_MSG_GET_PORT_PARAM]; msg.sub[2] = id; @@ -1013,48 +1047,24 @@ static int pd692x0_pi_get_current_limit(struct pse_controller_dev *pcdev, if (ret < 0) return ret; - ret = pd692x0_pi_get_pw_from_table(buf.data[2], buf.data[3]); - if (ret < 0) - return ret; - mW = ret; - - ret = pd692x0_pi_get_voltage(pcdev, id); - if (ret < 0) - return ret; - uV = ret; - - tmp_64 = mW; - tmp_64 *= 1000000000ull; - /* uA = mW * 1000000000 / uV */ - uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); - return uA; + return pd692x0_pi_get_pw_from_table(buf.data[2], buf.data[3]); } -static int pd692x0_pi_set_current_limit(struct pse_controller_dev *pcdev, - int id, int max_uA) +static int pd692x0_pi_set_pw_limit(struct pse_controller_dev *pcdev, + int id, int max_mW) { struct pd692x0_priv *priv = to_pd692x0_priv(pcdev); struct device *dev = &priv->client->dev; struct pd692x0_msg msg, buf = {0}; - int uV, ret, mW; - s64 tmp_64; + int ret; ret = pd692x0_fw_unavailable(priv); if (ret) return ret; - ret = pd692x0_pi_get_voltage(pcdev, id); - if (ret < 0) - return ret; - uV = ret; - msg = pd692x0_msg_template_list[PD692X0_MSG_SET_PORT_PARAM]; msg.sub[2] = id; - tmp_64 = uV; - tmp_64 *= max_uA; - /* mW = uV * uA / 1000000000 */ - mW = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); - ret = pd692x0_pi_set_pw_from_table(dev, &msg, mW); + ret = pd692x0_pi_set_pw_from_table(dev, &msg, max_mW); if (ret) return ret; @@ -1063,13 +1073,17 @@ static int pd692x0_pi_set_current_limit(struct pse_controller_dev *pcdev, static const struct pse_controller_ops pd692x0_ops = { .setup_pi_matrix = pd692x0_setup_pi_matrix, - .ethtool_get_status = pd692x0_ethtool_get_status, + .pi_get_admin_state = pd692x0_pi_get_admin_state, + .pi_get_pw_status = pd692x0_pi_get_pw_status, + .pi_get_ext_state = pd692x0_pi_get_ext_state, + .pi_get_pw_class = pd692x0_pi_get_pw_class, + .pi_get_actual_pw = pd692x0_pi_get_actual_pw, .pi_enable = pd692x0_pi_enable, .pi_disable = pd692x0_pi_disable, - .pi_is_enabled = pd692x0_pi_is_enabled, .pi_get_voltage = pd692x0_pi_get_voltage, - .pi_get_current_limit = pd692x0_pi_get_current_limit, - .pi_set_current_limit = pd692x0_pi_set_current_limit, + .pi_get_pw_limit = pd692x0_pi_get_pw_limit, + .pi_set_pw_limit = pd692x0_pi_set_pw_limit, + .pi_get_pw_limit_ranges = pd692x0_pi_get_pw_limit_ranges, }; #define PD692X0_FW_LINE_MAX_SZ 0xff diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 2906ce173f66..4f2a54afc4d0 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -6,6 +6,7 @@ // #include <linux/device.h> +#include <linux/ethtool.h> #include <linux/of.h> #include <linux/pse-pd/pse.h> #include <linux/regulator/driver.h> @@ -210,16 +211,25 @@ out: static int pse_pi_is_enabled(struct regulator_dev *rdev) { struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); + struct pse_admin_state admin_state = {0}; const struct pse_controller_ops *ops; int id, ret; ops = pcdev->ops; - if (!ops->pi_is_enabled) + if (!ops->pi_get_admin_state) return -EOPNOTSUPP; id = rdev_get_id(rdev); mutex_lock(&pcdev->lock); - ret = ops->pi_is_enabled(pcdev, id); + ret = ops->pi_get_admin_state(pcdev, id, &admin_state); + if (ret) + goto out; + + if (admin_state.podl_admin_state == ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED || + admin_state.c33_admin_state == ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED) + ret = 1; + +out: mutex_unlock(&pcdev->lock); return ret; @@ -291,33 +301,25 @@ static int pse_pi_get_voltage(struct regulator_dev *rdev) 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; + int id, uV, mW, ret; s64 tmp_64; ops = pcdev->ops; id = rdev_get_id(rdev); + if (!ops->pi_get_pw_limit || !ops->pi_get_voltage) + return -EOPNOTSUPP; + mutex_lock(&pcdev->lock); - if (ops->pi_get_current_limit) { - ret = ops->pi_get_current_limit(pcdev, id); + ret = ops->pi_get_pw_limit(pcdev, id); + if (ret < 0) goto out; - } + mW = ret; - /* 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); + ret = pse_pi_get_voltage(rdev); if (!ret) { dev_err(pcdev->dev, "Voltage null\n"); ret = -ERANGE; @@ -327,16 +329,7 @@ static int pse_pi_get_current_limit(struct regulator_dev *rdev) 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 = mW; tmp_64 *= 1000000000ull; /* uA = mW * 1000000000 / uV */ ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); @@ -351,15 +344,33 @@ static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA, { struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); const struct pse_controller_ops *ops; - int id, ret; + int id, mW, ret; + s64 tmp_64; ops = pcdev->ops; - if (!ops->pi_set_current_limit) + if (!ops->pi_set_pw_limit || !ops->pi_get_voltage) return -EOPNOTSUPP; + if (max_uA > MAX_PI_CURRENT) + return -ERANGE; + id = rdev_get_id(rdev); mutex_lock(&pcdev->lock); - ret = ops->pi_set_current_limit(pcdev, id, max_uA); + ret = pse_pi_get_voltage(rdev); + if (!ret) { + dev_err(pcdev->dev, "Voltage null\n"); + ret = -ERANGE; + goto out; + } + if (ret < 0) + goto out; + + tmp_64 = ret; + tmp_64 *= max_uA; + /* mW = uA * uV / 1000000000 */ + mW = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); + ret = ops->pi_set_pw_limit(pcdev, id, mW); +out: mutex_unlock(&pcdev->lock); return ret; @@ -403,17 +414,16 @@ devm_pse_pi_regulator_register(struct pse_controller_dev *pcdev, rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; - if (pcdev->ops->pi_set_current_limit) { + if (pcdev->ops->pi_set_pw_limit) rinit_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_CURRENT; - rinit_data->constraints.max_uA = MAX_PI_CURRENT; - } rinit_data->supply_regulator = "vpwr"; rconfig.dev = pcdev->dev; rconfig.driver_data = pcdev; rconfig.init_data = rinit_data; + rconfig.of_node = pcdev->pi[id].np; rdev = devm_regulator_register(pcdev->dev, rdesc, &rconfig); if (IS_ERR(rdev)) { @@ -444,6 +454,13 @@ int pse_controller_register(struct pse_controller_dev *pcdev) if (!pcdev->nr_lines) pcdev->nr_lines = 1; + if (!pcdev->ops->pi_get_admin_state || + !pcdev->ops->pi_get_pw_status) { + dev_err(pcdev->dev, + "Mandatory status report callbacks are missing"); + return -EINVAL; + } + ret = of_load_pse_pis(pcdev); if (ret) return ret; @@ -736,23 +753,6 @@ out: } 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 @@ -763,15 +763,81 @@ static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev, */ int pse_ethtool_get_status(struct pse_control *psec, struct netlink_ext_ack *extack, - struct pse_control_status *status) + struct ethtool_pse_control_status *status) { - int err; + struct pse_admin_state admin_state = {0}; + struct pse_pw_status pw_status = {0}; + const struct pse_controller_ops *ops; + struct pse_controller_dev *pcdev; + int ret; - mutex_lock(&psec->pcdev->lock); - err = _pse_ethtool_get_status(psec->pcdev, psec->id, extack, status); - mutex_unlock(&psec->pcdev->lock); + pcdev = psec->pcdev; + ops = pcdev->ops; + mutex_lock(&pcdev->lock); + ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); + if (ret) + goto out; + status->podl_admin_state = admin_state.podl_admin_state; + status->c33_admin_state = admin_state.c33_admin_state; - return err; + ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status); + if (ret) + goto out; + status->podl_pw_status = pw_status.podl_pw_status; + status->c33_pw_status = pw_status.c33_pw_status; + + if (ops->pi_get_ext_state) { + struct pse_ext_state_info ext_state_info = {0}; + + ret = ops->pi_get_ext_state(pcdev, psec->id, + &ext_state_info); + if (ret) + goto out; + + memcpy(&status->c33_ext_state_info, + &ext_state_info.c33_ext_state_info, + sizeof(status->c33_ext_state_info)); + } + + if (ops->pi_get_pw_class) { + ret = ops->pi_get_pw_class(pcdev, psec->id); + if (ret < 0) + goto out; + + status->c33_pw_class = ret; + } + + if (ops->pi_get_actual_pw) { + ret = ops->pi_get_actual_pw(pcdev, psec->id); + if (ret < 0) + goto out; + + status->c33_actual_pw = ret; + } + + if (ops->pi_get_pw_limit) { + ret = ops->pi_get_pw_limit(pcdev, psec->id); + if (ret < 0) + goto out; + + status->c33_avail_pw_limit = ret; + } + + if (ops->pi_get_pw_limit_ranges) { + struct pse_pw_limit_ranges pw_limit_ranges = {0}; + + ret = ops->pi_get_pw_limit_ranges(pcdev, psec->id, + &pw_limit_ranges); + if (ret < 0) + goto out; + + status->c33_pw_limit_ranges = + pw_limit_ranges.c33_pw_limit_ranges; + status->c33_pw_limit_nb_ranges = ret; + } +out: + mutex_unlock(&psec->pcdev->lock); + return ret; } EXPORT_SYMBOL_GPL(pse_ethtool_get_status); @@ -876,6 +942,9 @@ int pse_ethtool_set_pw_limit(struct pse_control *psec, int uV, uA, ret; s64 tmp_64; + if (pw_limit > MAX_PI_PW) + return -ERANGE; + ret = regulator_get_voltage(psec->ps); if (!ret) { NL_SET_ERR_MSG(extack, diff --git a/drivers/net/pse-pd/pse_regulator.c b/drivers/net/pse-pd/pse_regulator.c index 64ab36974fe0..6ce6773fff31 100644 --- a/drivers/net/pse-pd/pse_regulator.c +++ b/drivers/net/pse-pd/pse_regulator.c @@ -52,17 +52,19 @@ pse_reg_pi_disable(struct pse_controller_dev *pcdev, int id) } static int -pse_reg_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +pse_reg_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state) { struct pse_reg_priv *priv = to_pse_reg(pcdev); - return regulator_is_enabled(priv->ps); + admin_state->podl_admin_state = priv->admin_state; + + return 0; } static int -pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, - struct netlink_ext_ack *extack, - struct pse_control_status *status) +pse_reg_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, + struct pse_pw_status *pw_status) { struct pse_reg_priv *priv = to_pse_reg(pcdev); int ret; @@ -72,20 +74,19 @@ pse_reg_ethtool_get_status(struct pse_controller_dev *pcdev, unsigned long id, return ret; if (!ret) - status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; + pw_status->podl_pw_status = + ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED; else - status->podl_pw_status = + pw_status->podl_pw_status = ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING; - status->podl_admin_state = priv->admin_state; - return 0; } static const struct pse_controller_ops pse_reg_ops = { - .ethtool_get_status = pse_reg_ethtool_get_status, + .pi_get_admin_state = pse_reg_pi_get_admin_state, + .pi_get_pw_status = pse_reg_pi_get_pw_status, .pi_enable = pse_reg_pi_enable, - .pi_is_enabled = pse_reg_pi_is_enabled, .pi_disable = pse_reg_pi_disable, }; diff --git a/drivers/net/pse-pd/tps23881.c b/drivers/net/pse-pd/tps23881.c index 8797ca1a8a21..5e9dda2c0eac 100644 --- a/drivers/net/pse-pd/tps23881.c +++ b/drivers/net/pse-pd/tps23881.c @@ -25,20 +25,32 @@ #define TPS23881_REG_GEN_MASK 0x17 #define TPS23881_REG_NBITACC BIT(5) #define TPS23881_REG_PW_EN 0x19 +#define TPS23881_REG_2PAIR_POL1 0x1e #define TPS23881_REG_PORT_MAP 0x26 #define TPS23881_REG_PORT_POWER 0x29 -#define TPS23881_REG_POEPLUS 0x40 +#define TPS23881_REG_4PAIR_POL1 0x2a +#define TPS23881_REG_INPUT_V 0x2e +#define TPS23881_REG_CHAN1_A 0x30 +#define TPS23881_REG_CHAN1_V 0x32 +#define TPS23881_REG_FOLDBACK 0x40 #define TPS23881_REG_TPON BIT(0) #define TPS23881_REG_FWREV 0x41 #define TPS23881_REG_DEVID 0x43 #define TPS23881_REG_DEVID_MASK 0xF0 #define TPS23881_DEVICE_ID 0x02 +#define TPS23881_REG_CHAN1_CLASS 0x4c #define TPS23881_REG_SRAM_CTRL 0x60 #define TPS23881_REG_SRAM_DATA 0x61 +#define TPS23881_UV_STEP 3662 +#define TPS23881_NA_STEP 70190 +#define TPS23881_MW_STEP 500 +#define TPS23881_MIN_PI_PW_LIMIT_MW 2000 + struct tps23881_port_desc { u8 chan[2]; bool is_4p; + int pw_pol; }; struct tps23881_priv { @@ -53,36 +65,123 @@ static struct tps23881_priv *to_tps23881_priv(struct pse_controller_dev *pcdev) return container_of(pcdev, struct tps23881_priv, pcdev); } +/* + * Helper to extract a value from a u16 register value, which is made of two + * u8 registers. The function calculates the bit offset based on the channel + * and extracts the relevant bits using a provided field mask. + * + * @param reg_val: The u16 register value (composed of two u8 registers). + * @param chan: The channel number (0-7). + * @param field_offset: The base bit offset to apply (e.g., 0 or 4). + * @param field_mask: The mask to apply to extract the required bits. + * @return: The extracted value for the specific channel. + */ +static u16 tps23881_calc_val(u16 reg_val, u8 chan, u8 field_offset, + u16 field_mask) +{ + if (chan >= 4) + reg_val >>= 8; + + return (reg_val >> field_offset) & field_mask; +} + +/* + * Helper to combine individual channel values into a u16 register value. + * The function sets the value for a specific channel in the appropriate + * position. + * + * @param reg_val: The current u16 register value. + * @param chan: The channel number (0-7). + * @param field_offset: The base bit offset to apply (e.g., 0 or 4). + * @param field_mask: The mask to apply for the field (e.g., 0x0F). + * @param field_val: The value to set for the specific channel (masked by + * field_mask). + * @return: The updated u16 register value with the channel value set. + */ +static u16 tps23881_set_val(u16 reg_val, u8 chan, u8 field_offset, + u16 field_mask, u16 field_val) +{ + field_val &= field_mask; + + if (chan < 4) { + reg_val &= ~(field_mask << field_offset); + reg_val |= (field_val << field_offset); + } else { + reg_val &= ~(field_mask << (field_offset + 8)); + reg_val |= (field_val << (field_offset + 8)); + } + + return reg_val; +} + +static int +tps23881_pi_set_pw_pol_limit(struct tps23881_priv *priv, int id, u8 pw_pol, + bool is_4p) +{ + struct i2c_client *client = priv->client; + int ret, reg; + u16 val; + u8 chan; + + chan = priv->port[id].chan[0]; + if (!is_4p) { + reg = TPS23881_REG_2PAIR_POL1 + (chan % 4); + } else { + /* One chan is enough to configure the 4p PI power limit */ + if ((chan % 4) < 2) + reg = TPS23881_REG_4PAIR_POL1; + else + reg = TPS23881_REG_4PAIR_POL1 + 1; + } + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + val = tps23881_set_val(ret, chan, 0, 0xff, pw_pol); + return i2c_smbus_write_word_data(client, reg, val); +} + +static int tps23881_pi_enable_manual_pol(struct tps23881_priv *priv, int id) +{ + struct i2c_client *client = priv->client; + int ret; + u8 chan; + u16 val; + + ret = i2c_smbus_read_byte_data(client, TPS23881_REG_FOLDBACK); + if (ret < 0) + return ret; + + /* No need to test if the chan is PoE4 as setting either bit for a + * 4P configured port disables the automatic configuration on both + * channels. + */ + chan = priv->port[id].chan[0]; + val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); + return i2c_smbus_write_byte_data(client, TPS23881_REG_FOLDBACK, val); +} + static int tps23881_pi_enable(struct pse_controller_dev *pcdev, int id) { struct tps23881_priv *priv = to_tps23881_priv(pcdev); struct i2c_client *client = priv->client; u8 chan; u16 val; - int ret; if (id >= TPS23881_MAX_CHANS) return -ERANGE; chan = priv->port[id].chan[0]; - if (chan < 4) - val = BIT(chan); - else - val = BIT(chan + 4); + val = tps23881_set_val(0, chan, 0, BIT(chan % 4), BIT(chan % 4)); if (priv->port[id].is_4p) { chan = priv->port[id].chan[1]; - if (chan < 4) - val |= BIT(chan); - else - val |= BIT(chan + 4); + val = tps23881_set_val(val, chan, 0, BIT(chan % 4), + BIT(chan % 4)); } - ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); - if (ret) - return ret; - - return 0; + return i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); } static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) @@ -97,32 +196,67 @@ static int tps23881_pi_disable(struct pse_controller_dev *pcdev, int id) return -ERANGE; chan = priv->port[id].chan[0]; - if (chan < 4) - val = BIT(chan + 4); - else - val = BIT(chan + 8); + val = tps23881_set_val(0, chan, 4, BIT(chan % 4), BIT(chan % 4)); if (priv->port[id].is_4p) { chan = priv->port[id].chan[1]; - if (chan < 4) - val |= BIT(chan + 4); - else - val |= BIT(chan + 8); + val = tps23881_set_val(val, chan, 4, BIT(chan % 4), + BIT(chan % 4)); } ret = i2c_smbus_write_word_data(client, TPS23881_REG_PW_EN, val); if (ret) return ret; - return 0; + /* PWOFF command resets lots of register which need to be + * configured again. According to the datasheet "It may take upwards + * of 5ms after PWOFFn command for all register values to be updated" + */ + mdelay(5); + + /* Enable detection and classification */ + ret = i2c_smbus_read_word_data(client, TPS23881_REG_DET_CLA_EN); + if (ret < 0) + return ret; + + chan = priv->port[id].chan[0]; + val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), BIT(chan % 4)); + val = tps23881_set_val(val, chan, 4, BIT(chan % 4), BIT(chan % 4)); + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; + val = tps23881_set_val(ret, chan, 0, BIT(chan % 4), + BIT(chan % 4)); + val = tps23881_set_val(val, chan, 4, BIT(chan % 4), + BIT(chan % 4)); + } + + ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); + if (ret) + return ret; + + /* No power policy */ + if (priv->port[id].pw_pol < 0) + return 0; + + ret = tps23881_pi_enable_manual_pol(priv, id); + if (ret < 0) + return ret; + + /* Set power policy */ + return tps23881_pi_set_pw_pol_limit(priv, id, priv->port[id].pw_pol, + priv->port[id].is_4p); } -static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id) +static int +tps23881_pi_get_admin_state(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state) { struct tps23881_priv *priv = to_tps23881_priv(pcdev); struct i2c_client *client = priv->client; bool enabled; u8 chan; + u16 val; int ret; ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); @@ -130,32 +264,35 @@ static int tps23881_pi_is_enabled(struct pse_controller_dev *pcdev, int id) return ret; chan = priv->port[id].chan[0]; - if (chan < 4) - enabled = ret & BIT(chan); - else - enabled = ret & BIT(chan + 4); + val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); + enabled = !!(val); if (priv->port[id].is_4p) { chan = priv->port[id].chan[1]; - if (chan < 4) - enabled &= !!(ret & BIT(chan)); - else - enabled &= !!(ret & BIT(chan + 4)); + val = tps23881_calc_val(ret, chan, 0, BIT(chan % 4)); + enabled &= !!(val); } /* Return enabled status only if both channel are on this state */ - return enabled; + if (enabled) + admin_state->c33_admin_state = + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; + else + admin_state->c33_admin_state = + ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; + + return 0; } -static int tps23881_ethtool_get_status(struct pse_controller_dev *pcdev, - unsigned long id, - struct netlink_ext_ack *extack, - struct pse_control_status *status) +static int +tps23881_pi_get_pw_status(struct pse_controller_dev *pcdev, int id, + struct pse_pw_status *pw_status) { struct tps23881_priv *priv = to_tps23881_priv(pcdev); struct i2c_client *client = priv->client; - bool enabled, delivering; + bool delivering; u8 chan; + u16 val; int ret; ret = i2c_smbus_read_word_data(client, TPS23881_REG_PW_STATUS); @@ -163,40 +300,197 @@ static int tps23881_ethtool_get_status(struct pse_controller_dev *pcdev, return ret; chan = priv->port[id].chan[0]; - if (chan < 4) { - enabled = ret & BIT(chan); - delivering = ret & BIT(chan + 4); - } else { - enabled = ret & BIT(chan + 4); - delivering = ret & BIT(chan + 8); - } + val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); + delivering = !!(val); if (priv->port[id].is_4p) { chan = priv->port[id].chan[1]; - if (chan < 4) { - enabled &= !!(ret & BIT(chan)); - delivering &= !!(ret & BIT(chan + 4)); - } else { - enabled &= !!(ret & BIT(chan + 4)); - delivering &= !!(ret & BIT(chan + 8)); - } + val = tps23881_calc_val(ret, chan, 4, BIT(chan % 4)); + delivering &= !!(val); } /* Return delivering status only if both channel are on this state */ if (delivering) - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_DELIVERING; else - status->c33_pw_status = ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; - - /* Return enabled status only if both channel are on this state */ - if (enabled) - status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; - else - status->c33_admin_state = ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; + pw_status->c33_pw_status = + ETHTOOL_C33_PSE_PW_D_STATUS_DISABLED; return 0; } +static int tps23881_pi_get_voltage(struct pse_controller_dev *pcdev, int id) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + struct i2c_client *client = priv->client; + int ret; + u64 uV; + + ret = i2c_smbus_read_word_data(client, TPS23881_REG_INPUT_V); + if (ret < 0) + return ret; + + uV = ret & 0x3fff; + uV *= TPS23881_UV_STEP; + + return (int)uV; +} + +static int +tps23881_pi_get_chan_current(struct tps23881_priv *priv, u8 chan) +{ + struct i2c_client *client = priv->client; + int reg, ret; + u64 tmp_64; + + /* Registers 0x30 to 0x3d */ + reg = TPS23881_REG_CHAN1_A + (chan % 4) * 4 + (chan >= 4); + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + tmp_64 = ret & 0x3fff; + tmp_64 *= TPS23881_NA_STEP; + /* uA = nA / 1000 */ + tmp_64 = DIV_ROUND_CLOSEST_ULL(tmp_64, 1000); + return (int)tmp_64; +} + +static int tps23881_pi_get_pw_class(struct pse_controller_dev *pcdev, + int id) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + struct i2c_client *client = priv->client; + int ret, reg; + u8 chan; + + chan = priv->port[id].chan[0]; + reg = TPS23881_REG_CHAN1_CLASS + (chan % 4); + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + return tps23881_calc_val(ret, chan, 4, 0x0f); +} + +static int +tps23881_pi_get_actual_pw(struct pse_controller_dev *pcdev, int id) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + int ret, uV, uA; + u64 tmp_64; + u8 chan; + + ret = tps23881_pi_get_voltage(&priv->pcdev, id); + if (ret < 0) + return ret; + uV = ret; + + chan = priv->port[id].chan[0]; + ret = tps23881_pi_get_chan_current(priv, chan); + if (ret < 0) + return ret; + uA = ret; + + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; + ret = tps23881_pi_get_chan_current(priv, chan); + if (ret < 0) + return ret; + uA += ret; + } + + tmp_64 = uV; + tmp_64 *= uA; + /* mW = uV * uA / 1000000000 */ + return DIV_ROUND_CLOSEST_ULL(tmp_64, 1000000000); +} + +static int +tps23881_pi_get_pw_limit_chan(struct tps23881_priv *priv, u8 chan) +{ + struct i2c_client *client = priv->client; + int ret, reg; + u16 val; + + reg = TPS23881_REG_2PAIR_POL1 + (chan % 4); + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) + return ret; + + val = tps23881_calc_val(ret, chan, 0, 0xff); + return val * TPS23881_MW_STEP; +} + +static int tps23881_pi_get_pw_limit(struct pse_controller_dev *pcdev, int id) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + int ret, mW; + u8 chan; + + chan = priv->port[id].chan[0]; + ret = tps23881_pi_get_pw_limit_chan(priv, chan); + if (ret < 0) + return ret; + + mW = ret; + if (priv->port[id].is_4p) { + chan = priv->port[id].chan[1]; + ret = tps23881_pi_get_pw_limit_chan(priv, chan); + if (ret < 0) + return ret; + mW += ret; + } + + return mW; +} + +static int tps23881_pi_set_pw_limit(struct pse_controller_dev *pcdev, + int id, int max_mW) +{ + struct tps23881_priv *priv = to_tps23881_priv(pcdev); + u8 pw_pol; + int ret; + + if (max_mW < TPS23881_MIN_PI_PW_LIMIT_MW || MAX_PI_PW < max_mW) { + dev_err(&priv->client->dev, + "power limit %d out of ranges [%d,%d]", + max_mW, TPS23881_MIN_PI_PW_LIMIT_MW, MAX_PI_PW); + return -ERANGE; + } + + ret = tps23881_pi_enable_manual_pol(priv, id); + if (ret < 0) + return ret; + + pw_pol = DIV_ROUND_CLOSEST_ULL(max_mW, TPS23881_MW_STEP); + + /* Save power policy to reconfigure it after a disabled call */ + priv->port[id].pw_pol = pw_pol; + return tps23881_pi_set_pw_pol_limit(priv, id, pw_pol, + priv->port[id].is_4p); +} + +static int +tps23881_pi_get_pw_limit_ranges(struct pse_controller_dev *pcdev, int id, + struct pse_pw_limit_ranges *pw_limit_ranges) +{ + struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; + + c33_pw_limit_ranges = kzalloc(sizeof(*c33_pw_limit_ranges), + GFP_KERNEL); + if (!c33_pw_limit_ranges) + return -ENOMEM; + + c33_pw_limit_ranges->min = TPS23881_MIN_PI_PW_LIMIT_MW; + c33_pw_limit_ranges->max = MAX_PI_PW; + pw_limit_ranges->c33_pw_limit_ranges = c33_pw_limit_ranges; + + /* Return the number of ranges */ + return 1; +} + /* Parse managers subnode into a array of device node */ static int tps23881_get_of_channels(struct tps23881_priv *priv, @@ -480,7 +774,7 @@ tps23881_write_port_matrix(struct tps23881_priv *priv, struct i2c_client *client = priv->client; u8 pi_id, lgcl_chan, hw_chan; u16 val = 0; - int i, ret; + int i; for (i = 0; i < port_cnt; i++) { pi_id = port_matrix[i].pi_id; @@ -491,6 +785,9 @@ tps23881_write_port_matrix(struct tps23881_priv *priv, if (port_matrix[i].exist) priv->port[pi_id].chan[0] = lgcl_chan; + /* Initialize power policy internal value */ + priv->port[pi_id].pw_pol = -1; + /* Set hardware port matrix for all ports */ val |= hw_chan << (lgcl_chan * 2); @@ -511,11 +808,7 @@ tps23881_write_port_matrix(struct tps23881_priv *priv, } /* Write hardware ports matrix */ - ret = i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val); - if (ret) - return ret; - - return 0; + return i2c_smbus_write_word_data(client, TPS23881_REG_PORT_MAP, val); } static int @@ -564,11 +857,7 @@ tps23881_set_ports_conf(struct tps23881_priv *priv, val |= BIT(port_matrix[i].lgcl_chan[1]) | BIT(port_matrix[i].lgcl_chan[1] + 4); } - ret = i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); - if (ret) - return ret; - - return 0; + return i2c_smbus_write_word_data(client, TPS23881_REG_DET_CLA_EN, val); } static int @@ -594,11 +883,7 @@ tps23881_set_ports_matrix(struct tps23881_priv *priv, if (ret) return ret; - ret = tps23881_set_ports_conf(priv, port_matrix); - if (ret) - return ret; - - return 0; + return tps23881_set_ports_conf(priv, port_matrix); } static int tps23881_setup_pi_matrix(struct pse_controller_dev *pcdev) @@ -626,8 +911,14 @@ static const struct pse_controller_ops tps23881_ops = { .setup_pi_matrix = tps23881_setup_pi_matrix, .pi_enable = tps23881_pi_enable, .pi_disable = tps23881_pi_disable, - .pi_is_enabled = tps23881_pi_is_enabled, - .ethtool_get_status = tps23881_ethtool_get_status, + .pi_get_admin_state = tps23881_pi_get_admin_state, + .pi_get_pw_status = tps23881_pi_get_pw_status, + .pi_get_pw_class = tps23881_pi_get_pw_class, + .pi_get_actual_pw = tps23881_pi_get_actual_pw, + .pi_get_voltage = tps23881_pi_get_voltage, + .pi_get_pw_limit = tps23881_pi_get_pw_limit, + .pi_set_pw_limit = tps23881_pi_set_pw_limit, + .pi_get_pw_limit_ranges = tps23881_pi_get_pw_limit_ranges, }; static const char fw_parity_name[] = "ti/tps23881/tps23881-parity-14.bin"; diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 4bf70cfec826..20a86bd5f4e3 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -1326,24 +1326,4 @@ struct ethtool_forced_speed_map { void ethtool_forced_speed_maps_init(struct ethtool_forced_speed_map *maps, u32 size); - -/* C33 PSE extended state and substate. */ -struct ethtool_c33_pse_ext_state_info { - enum ethtool_c33_pse_ext_state c33_pse_ext_state; - union { - enum ethtool_c33_pse_ext_substate_error_condition error_condition; - enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable; - enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted; - enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim; - enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected; - enum ethtool_c33_pse_ext_substate_power_not_available power_not_available; - enum ethtool_c33_pse_ext_substate_short_detected short_detected; - u32 __c33_pse_ext_substate; - }; -}; - -struct ethtool_c33_pse_pw_limit_range { - u32 min; - u32 max; -}; #endif /* _LINUX_ETHTOOL_H */ diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h index 591a53e082e6..c773eeb92d04 100644 --- a/include/linux/pse-pd/pse.h +++ b/include/linux/pse-pd/pse.h @@ -5,15 +5,37 @@ #ifndef _LINUX_PSE_CONTROLLER_H #define _LINUX_PSE_CONTROLLER_H -#include <linux/ethtool.h> #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 +/* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */ +#define MAX_PI_PW 99900 struct phy_device; struct pse_controller_dev; +struct netlink_ext_ack; + +/* C33 PSE extended state and substate. */ +struct ethtool_c33_pse_ext_state_info { + enum ethtool_c33_pse_ext_state c33_pse_ext_state; + union { + enum ethtool_c33_pse_ext_substate_error_condition error_condition; + enum ethtool_c33_pse_ext_substate_mr_pse_enable mr_pse_enable; + enum ethtool_c33_pse_ext_substate_option_detect_ted option_detect_ted; + enum ethtool_c33_pse_ext_substate_option_vport_lim option_vport_lim; + enum ethtool_c33_pse_ext_substate_ovld_detected ovld_detected; + enum ethtool_c33_pse_ext_substate_power_not_available power_not_available; + enum ethtool_c33_pse_ext_substate_short_detected short_detected; + u32 __c33_pse_ext_substate; + }; +}; + +struct ethtool_c33_pse_pw_limit_range { + u32 min; + u32 max; +}; /** * struct pse_control_config - PSE control/channel configuration. @@ -29,7 +51,52 @@ struct pse_control_config { }; /** - * struct pse_control_status - PSE control/channel status. + * struct pse_admin_state - PSE operational state + * + * @podl_admin_state: operational state of the PoDL PSE + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState + * @c33_admin_state: operational state of the PSE + * functions. IEEE 802.3-2022 30.9.1.1.2 aPSEAdminState + */ +struct pse_admin_state { + enum ethtool_podl_pse_admin_state podl_admin_state; + enum ethtool_c33_pse_admin_state c33_admin_state; +}; + +/** + * struct pse_pw_status - PSE power detection status + * + * @podl_pw_status: power detection status of the PoDL PSE. + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: + * @c33_pw_status: power detection status of the PSE. + * IEEE 802.3-2022 30.9.1.1.5 aPSEPowerDetectionStatus: + */ +struct pse_pw_status { + enum ethtool_podl_pse_pw_d_status podl_pw_status; + enum ethtool_c33_pse_pw_d_status c33_pw_status; +}; + +/** + * struct pse_ext_state_info - PSE extended state information + * + * @c33_ext_state_info: extended state information of the PSE + */ +struct pse_ext_state_info { + struct ethtool_c33_pse_ext_state_info c33_ext_state_info; +}; + +/** + * struct pse_pw_limit_ranges - PSE power limit configuration range + * + * @c33_pw_limit_ranges: supported power limit configuration range. The driver + * is in charge of the memory allocation. + */ +struct pse_pw_limit_ranges { + struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; +}; + +/** + * struct ethtool_pse_control_status - PSE control/channel status. * * @podl_admin_state: operational state of the PoDL PSE * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState @@ -47,11 +114,11 @@ struct pse_control_config { * @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. + * is in charge of the memory allocation * @c33_pw_limit_nb_ranges: number of supported power limit configuration * ranges */ -struct pse_control_status { +struct ethtool_pse_control_status { enum ethtool_podl_pse_admin_state podl_admin_state; enum ethtool_podl_pse_pw_d_status podl_pw_status; enum ethtool_c33_pse_admin_state c33_admin_state; @@ -67,40 +134,51 @@ struct pse_control_status { /** * struct pse_controller_ops - PSE controller driver callbacks * - * @ethtool_get_status: get PSE control status for ethtool interface * @setup_pi_matrix: setup PI matrix of the PSE controller - * @pi_is_enabled: Return 1 if the PSE PI is enabled, 0 if not. - * May also return negative errno. + * @pi_get_admin_state: Get the operational state of the PSE PI. This ops + * is mandatory. + * @pi_get_pw_status: Get the power detection status of the PSE PI. This + * ops is mandatory. + * @pi_get_ext_state: Get the extended state of the PSE PI. + * @pi_get_pw_class: Get the power class of the PSE PI. + * @pi_get_actual_pw: Get actual power of the PSE PI in mW. * @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. + * callback in uV. + * @pi_get_pw_limit: Get the configured power limit of the PSE PI in mW. + * @pi_set_pw_limit: Configure the power limit of the PSE PI in mW. + * @pi_get_pw_limit_ranges: Get the supported power limit configuration + * range. The driver is in charge of the memory + * allocation and should return the number of + * ranges. */ struct pse_controller_ops { - int (*ethtool_get_status)(struct pse_controller_dev *pcdev, - unsigned long id, struct netlink_ext_ack *extack, - struct pse_control_status *status); int (*setup_pi_matrix)(struct pse_controller_dev *pcdev); - int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id); + int (*pi_get_admin_state)(struct pse_controller_dev *pcdev, int id, + struct pse_admin_state *admin_state); + int (*pi_get_pw_status)(struct pse_controller_dev *pcdev, int id, + struct pse_pw_status *pw_status); + int (*pi_get_ext_state)(struct pse_controller_dev *pcdev, int id, + struct pse_ext_state_info *ext_state_info); + int (*pi_get_pw_class)(struct pse_controller_dev *pcdev, int id); + int (*pi_get_actual_pw)(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); + int (*pi_get_pw_limit)(struct pse_controller_dev *pcdev, + int id); + int (*pi_set_pw_limit)(struct pse_controller_dev *pcdev, + int id, int max_mW); + int (*pi_get_pw_limit_ranges)(struct pse_controller_dev *pcdev, int id, + struct pse_pw_limit_ranges *pw_limit_ranges); }; struct module; struct device_node; struct of_phandle_args; struct pse_control; +struct ethtool_pse_control_status; /* PSE PI pairset pinout can either be Alternative A or Alternative B */ enum pse_pi_pairset_pinout { @@ -177,15 +255,13 @@ void pse_control_put(struct pse_control *psec); int pse_ethtool_get_status(struct pse_control *psec, struct netlink_ext_ack *extack, - struct pse_control_status *status); + struct ethtool_pse_control_status *status); 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); @@ -203,7 +279,7 @@ static inline void pse_control_put(struct pse_control *psec) static inline int pse_ethtool_get_status(struct pse_control *psec, struct netlink_ext_ack *extack, - struct pse_control_status *status) + struct ethtool_pse_control_status *status) { return -EOPNOTSUPP; } @@ -222,12 +298,6 @@ static inline int pse_ethtool_set_pw_limit(struct pse_control *psec, 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; diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c index a0705edca22a..2819e2ba6be2 100644 --- a/net/ethtool/pse-pd.c +++ b/net/ethtool/pse-pd.c @@ -19,7 +19,7 @@ struct pse_req_info { struct pse_reply_data { struct ethnl_reply_data base; - struct pse_control_status status; + struct ethtool_pse_control_status status; }; #define PSE_REPDATA(__reply_base) \ @@ -80,7 +80,7 @@ static int pse_reply_size(const struct ethnl_req_info *req_base, const struct ethnl_reply_data *reply_base) { const struct pse_reply_data *data = PSE_REPDATA(reply_base); - const struct pse_control_status *st = &data->status; + const struct ethtool_pse_control_status *st = &data->status; int len = 0; if (st->podl_admin_state > 0) @@ -114,7 +114,7 @@ static int pse_reply_size(const struct ethnl_req_info *req_base, } static int pse_put_pw_limit_ranges(struct sk_buff *skb, - const struct pse_control_status *st) + const struct ethtool_pse_control_status *st) { const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges; int i; @@ -146,7 +146,7 @@ static int pse_fill_reply(struct sk_buff *skb, const struct ethnl_reply_data *reply_base) { const struct pse_reply_data *data = PSE_REPDATA(reply_base); - const struct pse_control_status *st = &data->status; + const struct ethtool_pse_control_status *st = &data->status; if (st->podl_admin_state > 0 && nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE, |