Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kory Maincent (Dent Project) and committed by
Jakub Kicinski
4a83abce ae37dc57

+204 -11
+161 -11
drivers/net/pse-pd/pse_core.c
··· 265 265 return ret; 266 266 } 267 267 268 + static int _pse_pi_get_voltage(struct regulator_dev *rdev) 269 + { 270 + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 271 + const struct pse_controller_ops *ops; 272 + int id; 273 + 274 + ops = pcdev->ops; 275 + if (!ops->pi_get_voltage) 276 + return -EOPNOTSUPP; 277 + 278 + id = rdev_get_id(rdev); 279 + return ops->pi_get_voltage(pcdev, id); 280 + } 281 + 282 + static int pse_pi_get_voltage(struct regulator_dev *rdev) 283 + { 284 + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 285 + int ret; 286 + 287 + mutex_lock(&pcdev->lock); 288 + ret = _pse_pi_get_voltage(rdev); 289 + mutex_unlock(&pcdev->lock); 290 + 291 + return ret; 292 + } 293 + 294 + static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev, 295 + int id, 296 + struct netlink_ext_ack *extack, 297 + struct pse_control_status *status); 298 + 299 + static int pse_pi_get_current_limit(struct regulator_dev *rdev) 300 + { 301 + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 302 + const struct pse_controller_ops *ops; 303 + struct netlink_ext_ack extack = {}; 304 + struct pse_control_status st = {}; 305 + int id, uV, ret; 306 + s64 tmp_64; 307 + 308 + ops = pcdev->ops; 309 + id = rdev_get_id(rdev); 310 + mutex_lock(&pcdev->lock); 311 + if (ops->pi_get_current_limit) { 312 + ret = ops->pi_get_current_limit(pcdev, id); 313 + goto out; 314 + } 315 + 316 + /* If pi_get_current_limit() callback not populated get voltage 317 + * from pi_get_voltage() and power limit from ethtool_get_status() 318 + * to calculate current limit. 319 + */ 320 + ret = _pse_pi_get_voltage(rdev); 321 + if (!ret) { 322 + dev_err(pcdev->dev, "Voltage null\n"); 323 + ret = -ERANGE; 324 + goto out; 325 + } 326 + if (ret < 0) 327 + goto out; 328 + uV = ret; 329 + 330 + ret = _pse_ethtool_get_status(pcdev, id, &extack, &st); 331 + if (ret) 332 + goto out; 333 + 334 + if (!st.c33_avail_pw_limit) { 335 + ret = -ENODATA; 336 + goto out; 337 + } 338 + 339 + tmp_64 = st.c33_avail_pw_limit; 340 + tmp_64 *= 1000000000ull; 341 + /* uA = mW * 1000000000 / uV */ 342 + ret = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); 343 + 344 + out: 345 + mutex_unlock(&pcdev->lock); 346 + return ret; 347 + } 348 + 349 + static int pse_pi_set_current_limit(struct regulator_dev *rdev, int min_uA, 350 + int max_uA) 351 + { 352 + struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 353 + const struct pse_controller_ops *ops; 354 + int id, ret; 355 + 356 + ops = pcdev->ops; 357 + if (!ops->pi_set_current_limit) 358 + return -EOPNOTSUPP; 359 + 360 + id = rdev_get_id(rdev); 361 + mutex_lock(&pcdev->lock); 362 + ret = ops->pi_set_current_limit(pcdev, id, max_uA); 363 + mutex_unlock(&pcdev->lock); 364 + 365 + return ret; 366 + } 367 + 268 368 static const struct regulator_ops pse_pi_ops = { 269 369 .is_enabled = pse_pi_is_enabled, 270 370 .enable = pse_pi_enable, 271 371 .disable = pse_pi_disable, 372 + .get_voltage = pse_pi_get_voltage, 373 + .get_current_limit = pse_pi_get_current_limit, 374 + .set_current_limit = pse_pi_set_current_limit, 272 375 }; 273 376 274 377 static int ··· 401 298 rdesc->ops = &pse_pi_ops; 402 299 rdesc->owner = pcdev->owner; 403 300 404 - rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; 301 + rinit_data->constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS | 302 + REGULATOR_CHANGE_CURRENT; 303 + rinit_data->constraints.max_uA = MAX_PI_CURRENT; 405 304 rinit_data->supply_regulator = "vpwr"; 406 305 407 306 rconfig.dev = pcdev->dev; ··· 731 626 } 732 627 EXPORT_SYMBOL_GPL(of_pse_control_get); 733 628 629 + static int _pse_ethtool_get_status(struct pse_controller_dev *pcdev, 630 + int id, 631 + struct netlink_ext_ack *extack, 632 + struct pse_control_status *status) 633 + { 634 + const struct pse_controller_ops *ops; 635 + 636 + ops = pcdev->ops; 637 + if (!ops->ethtool_get_status) { 638 + NL_SET_ERR_MSG(extack, 639 + "PSE driver does not support status report"); 640 + return -EOPNOTSUPP; 641 + } 642 + 643 + return ops->ethtool_get_status(pcdev, id, extack, status); 644 + } 645 + 734 646 /** 735 647 * pse_ethtool_get_status - get status of PSE control 736 648 * @psec: PSE control pointer ··· 760 638 struct netlink_ext_ack *extack, 761 639 struct pse_control_status *status) 762 640 { 763 - const struct pse_controller_ops *ops; 764 641 int err; 765 642 766 - ops = psec->pcdev->ops; 767 - 768 - if (!ops->ethtool_get_status) { 769 - NL_SET_ERR_MSG(extack, 770 - "PSE driver does not support status report"); 771 - return -EOPNOTSUPP; 772 - } 773 - 774 643 mutex_lock(&psec->pcdev->lock); 775 - err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status); 644 + err = _pse_ethtool_get_status(psec->pcdev, psec->id, extack, status); 776 645 mutex_unlock(&psec->pcdev->lock); 777 646 778 647 return err; ··· 844 731 return err; 845 732 } 846 733 EXPORT_SYMBOL_GPL(pse_ethtool_set_config); 734 + 735 + /** 736 + * pse_ethtool_set_pw_limit - set PSE control power limit 737 + * @psec: PSE control pointer 738 + * @extack: extack for reporting useful error messages 739 + * @pw_limit: power limit value in mW 740 + * 741 + * Return: 0 on success and failure value on error 742 + */ 743 + int pse_ethtool_set_pw_limit(struct pse_control *psec, 744 + struct netlink_ext_ack *extack, 745 + const unsigned int pw_limit) 746 + { 747 + int uV, uA, ret; 748 + s64 tmp_64; 749 + 750 + ret = regulator_get_voltage(psec->ps); 751 + if (!ret) { 752 + NL_SET_ERR_MSG(extack, 753 + "Can't calculate the current, PSE voltage read is 0"); 754 + return -ERANGE; 755 + } 756 + if (ret < 0) { 757 + NL_SET_ERR_MSG(extack, 758 + "Error reading PSE voltage"); 759 + return ret; 760 + } 761 + uV = ret; 762 + 763 + tmp_64 = pw_limit; 764 + tmp_64 *= 1000000000ull; 765 + /* uA = mW * 1000000000 / uV */ 766 + uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); 767 + 768 + return regulator_set_current_limit(psec->ps, 0, uA); 769 + } 770 + EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit); 847 771 848 772 bool pse_has_podl(struct pse_control *psec) 849 773 {
+43
include/linux/pse-pd/pse.h
··· 9 9 #include <linux/list.h> 10 10 #include <uapi/linux/ethtool.h> 11 11 12 + /* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */ 13 + #define MAX_PI_CURRENT 1920000 14 + 12 15 struct phy_device; 13 16 struct pse_controller_dev; 14 17 ··· 44 41 * @c33_actual_pw: power currently delivered by the PSE in mW 45 42 * IEEE 802.3-2022 30.9.1.1.23 aPSEActualPower 46 43 * @c33_ext_state_info: extended state information of the PSE 44 + * @c33_avail_pw_limit: available power limit of the PSE in mW 45 + * IEEE 802.3-2022 145.2.5.4 pse_avail_pwr 46 + * @c33_pw_limit_ranges: supported power limit configuration range. The driver 47 + * is in charge of the memory allocation. 48 + * @c33_pw_limit_nb_ranges: number of supported power limit configuration 49 + * ranges 47 50 */ 48 51 struct pse_control_status { 49 52 enum ethtool_podl_pse_admin_state podl_admin_state; ··· 59 50 u32 c33_pw_class; 60 51 u32 c33_actual_pw; 61 52 struct ethtool_c33_pse_ext_state_info c33_ext_state_info; 53 + u32 c33_avail_pw_limit; 54 + struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; 55 + u32 c33_pw_limit_nb_ranges; 62 56 }; 63 57 64 58 /** ··· 73 61 * May also return negative errno. 74 62 * @pi_enable: Configure the PSE PI as enabled. 75 63 * @pi_disable: Configure the PSE PI as disabled. 64 + * @pi_get_voltage: Return voltage similarly to get_voltage regulator 65 + * callback. 66 + * @pi_get_current_limit: Get the configured current limit similarly to 67 + * get_current_limit regulator callback. 68 + * @pi_set_current_limit: Configure the current limit similarly to 69 + * set_current_limit regulator callback. 70 + * Should not return an error in case of MAX_PI_CURRENT 71 + * current value set. 76 72 */ 77 73 struct pse_controller_ops { 78 74 int (*ethtool_get_status)(struct pse_controller_dev *pcdev, ··· 90 70 int (*pi_is_enabled)(struct pse_controller_dev *pcdev, int id); 91 71 int (*pi_enable)(struct pse_controller_dev *pcdev, int id); 92 72 int (*pi_disable)(struct pse_controller_dev *pcdev, int id); 73 + int (*pi_get_voltage)(struct pse_controller_dev *pcdev, int id); 74 + int (*pi_get_current_limit)(struct pse_controller_dev *pcdev, 75 + int id); 76 + int (*pi_set_current_limit)(struct pse_controller_dev *pcdev, 77 + int id, int max_uA); 93 78 }; 94 79 95 80 struct module; ··· 181 156 int pse_ethtool_set_config(struct pse_control *psec, 182 157 struct netlink_ext_ack *extack, 183 158 const struct pse_control_config *config); 159 + int pse_ethtool_set_pw_limit(struct pse_control *psec, 160 + struct netlink_ext_ack *extack, 161 + const unsigned int pw_limit); 162 + int pse_ethtool_get_pw_limit(struct pse_control *psec, 163 + struct netlink_ext_ack *extack); 184 164 185 165 bool pse_has_podl(struct pse_control *psec); 186 166 bool pse_has_c33(struct pse_control *psec); ··· 211 181 static inline int pse_ethtool_set_config(struct pse_control *psec, 212 182 struct netlink_ext_ack *extack, 213 183 const struct pse_control_config *config) 184 + { 185 + return -EOPNOTSUPP; 186 + } 187 + 188 + static inline int pse_ethtool_set_pw_limit(struct pse_control *psec, 189 + struct netlink_ext_ack *extack, 190 + const unsigned int pw_limit) 191 + { 192 + return -EOPNOTSUPP; 193 + } 194 + 195 + static inline int pse_ethtool_get_pw_limit(struct pse_control *psec, 196 + struct netlink_ext_ack *extack) 214 197 { 215 198 return -EOPNOTSUPP; 216 199 }