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

net: pse-pd: Add support for budget evaluation strategies

This patch introduces the ability to configure the PSE PI budget evaluation
strategies. Budget evaluation strategies is utilized by PSE controllers to
determine which ports to turn off first in scenarios such as power budget
exceedance.

The pis_prio_max value is used to define the maximum priority level
supported by the controller. Both the current priority and the maximum
priority are exposed to the user through the pse_ethtool_get_status call.

This patch add support for two mode of budget evaluation strategies.
1. Static Method:

This method involves distributing power based on PD classification.
It’s straightforward and stable, the PSE core keeping track of the
budget and subtracting the power requested by each PD’s class.

Advantages: Every PD gets its promised power at any time, which
guarantees reliability.

Disadvantages: PD classification steps are large, meaning devices
request much more power than they actually need. As a result, the power
supply may only operate at, say, 50% capacity, which is inefficient and
wastes money.

Priority max value is matching the number of PSE PIs within the PSE.

2. Dynamic Method:

To address the inefficiencies of the static method, vendors like
Microchip have introduced dynamic power budgeting, as seen in the
PD692x0 firmware. This method monitors the current consumption per port
and subtracts it from the available power budget. When the budget is
exceeded, lower-priority ports are shut down.

Advantages: This method optimizes resource utilization, saving costs.

Disadvantages: Low-priority devices may experience instability.

Priority max value is set by the PSE controller driver.

For now, budget evaluation methods are not configurable and cannot be
mixed. They are hardcoded in the PSE driver itself, as no current PSE
controller supports both methods.

Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
Acked-by: Oleksij Rempel <o.rempel@pengutronix.de>
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-7-78a1a645e2ee@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kory Maincent (Dent Project) and committed by
Jakub Kicinski
ffef61d6 c394e757

+816 -41
+28 -2
Documentation/netlink/specs/ethtool.yaml
··· 122 122 name: pse-event 123 123 doc: PSE event list for the PSE controller 124 124 type: flags 125 + name-prefix: ethtool- 125 126 entries: 126 127 - 127 - name: over-current 128 + name: pse-event-over-current 128 129 doc: PSE output current is too high 129 130 - 130 - name: over-temp 131 + name: pse-event-over-temp 131 132 doc: PSE in over temperature state 133 + - 134 + name: c33-pse-event-detection 135 + doc: | 136 + detection process occur on the PSE. IEEE 802.3-2022 33.2.5 and 137 + 145.2.6 PSE detection of PDs. IEEE 802.3-202 30.9.1.1.5 138 + aPSEPowerDetectionStatus 139 + - 140 + name: c33-pse-event-classification 141 + doc: | 142 + classification process occur on the PSE. IEEE 802.3-2022 33.2.6 143 + and 145.2.8 classification of PDs mutual identification. 144 + IEEE 802.3-2022 30.9.1.1.8 aPSEPowerClassification. 145 + - 146 + name: c33-pse-event-disconnection 147 + doc: | 148 + PD has been disconnected on the PSE. IEEE 802.3-2022 33.3.8 149 + and 145.3.9 PD Maintain Power Signature. IEEE 802.3-2022 150 + 33.5.1.2.9 MPS Absent. IEEE 802.3-2022 30.9.1.1.20 151 + aPSEMPSAbsentCounter. 152 + - 153 + name: pse-event-over-budget 154 + doc: PSE turned off due to over budget situation 155 + - 156 + name: pse-event-sw-pw-control-error 157 + doc: PSE faced an error managing the power control from software 132 158 133 159 attribute-sets: 134 160 -
+694 -39
drivers/net/pse-pd/pse_core.c
··· 47 47 * @id: ID of the power domain 48 48 * @supply: Power supply the Power Domain 49 49 * @refcnt: Number of gets of this pse_power_domain 50 + * @budget_eval_strategy: Current power budget evaluation strategy of the 51 + * power domain 50 52 */ 51 53 struct pse_power_domain { 52 54 int id; 53 55 struct regulator *supply; 54 56 struct kref refcnt; 57 + u32 budget_eval_strategy; 55 58 }; 56 59 57 60 static int of_load_single_pse_pi_pairset(struct device_node *node, ··· 300 297 return 0; 301 298 } 302 299 300 + /** 301 + * pse_pi_is_admin_enable_pending - Check if PI is in admin enable pending state 302 + * which mean the power is not yet being 303 + * delivered 304 + * @pcdev: a pointer to the PSE controller device 305 + * @id: Index of the PI 306 + * 307 + * Detects if a PI is enabled in software with a PD detected, but the hardware 308 + * admin state hasn't been applied yet. 309 + * 310 + * This function is used in the power delivery and retry mechanisms to determine 311 + * which PIs need to have power delivery attempted again. 312 + * 313 + * Return: true if the PI has admin enable flag set in software but not yet 314 + * reflected in the hardware admin state, false otherwise. 315 + */ 316 + static bool 317 + pse_pi_is_admin_enable_pending(struct pse_controller_dev *pcdev, int id) 318 + { 319 + int ret; 320 + 321 + /* PI not enabled or nothing is plugged */ 322 + if (!pcdev->pi[id].admin_state_enabled || 323 + !pcdev->pi[id].isr_pd_detected) 324 + return false; 325 + 326 + ret = pse_pi_is_hw_enabled(pcdev, id); 327 + /* PSE PI is already enabled at hardware level */ 328 + if (ret == 1) 329 + return false; 330 + 331 + return true; 332 + } 333 + 334 + static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev, 335 + int id, 336 + struct netlink_ext_ack *extack); 337 + 338 + /** 339 + * pse_pw_d_retry_power_delivery - Retry power delivery for pending ports in a 340 + * PSE power domain 341 + * @pcdev: a pointer to the PSE controller device 342 + * @pw_d: a pointer to the PSE power domain 343 + * 344 + * Scans all ports in the specified power domain and attempts to enable power 345 + * delivery to any ports that have admin enable state set but don't yet have 346 + * hardware power enabled. Used when there are changes in connection status, 347 + * admin state, or priority that might allow previously unpowered ports to 348 + * receive power, especially in over-budget conditions. 349 + */ 350 + static void pse_pw_d_retry_power_delivery(struct pse_controller_dev *pcdev, 351 + struct pse_power_domain *pw_d) 352 + { 353 + int i, ret = 0; 354 + 355 + for (i = 0; i < pcdev->nr_lines; i++) { 356 + int prio_max = pcdev->nr_lines; 357 + struct netlink_ext_ack extack; 358 + 359 + if (pcdev->pi[i].pw_d != pw_d) 360 + continue; 361 + 362 + if (!pse_pi_is_admin_enable_pending(pcdev, i)) 363 + continue; 364 + 365 + /* Do not try to enable PI with a lower prio (higher value) 366 + * than one which already can't be enabled. 367 + */ 368 + if (pcdev->pi[i].prio > prio_max) 369 + continue; 370 + 371 + ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, i, &extack); 372 + if (ret == -ERANGE) 373 + prio_max = pcdev->pi[i].prio; 374 + } 375 + } 376 + 377 + /** 378 + * pse_pw_d_is_sw_pw_control - Determine if power control is software managed 379 + * @pcdev: a pointer to the PSE controller device 380 + * @pw_d: a pointer to the PSE power domain 381 + * 382 + * This function determines whether the power control for a specific power 383 + * domain is managed by software in the interrupt handler rather than directly 384 + * by hardware. 385 + * 386 + * Software power control is active in the following cases: 387 + * - When the budget evaluation strategy is set to static 388 + * - When the budget evaluation strategy is disabled but the PSE controller 389 + * has an interrupt handler that can report if a Powered Device is connected 390 + * 391 + * Return: true if the power control of the power domain is managed by software, 392 + * false otherwise 393 + */ 394 + static bool pse_pw_d_is_sw_pw_control(struct pse_controller_dev *pcdev, 395 + struct pse_power_domain *pw_d) 396 + { 397 + if (!pw_d) 398 + return false; 399 + 400 + if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC) 401 + return true; 402 + if (pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_DISABLED && 403 + pcdev->ops->pi_enable && pcdev->irq) 404 + return true; 405 + 406 + return false; 407 + } 408 + 303 409 static int pse_pi_is_enabled(struct regulator_dev *rdev) 304 410 { 305 411 struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); ··· 421 309 422 310 id = rdev_get_id(rdev); 423 311 mutex_lock(&pcdev->lock); 312 + if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) { 313 + ret = pcdev->pi[id].admin_state_enabled; 314 + goto out; 315 + } 316 + 424 317 ret = pse_pi_is_hw_enabled(pcdev, id); 318 + 319 + out: 425 320 mutex_unlock(&pcdev->lock); 426 321 427 322 return ret; 323 + } 324 + 325 + /** 326 + * pse_pi_deallocate_pw_budget - Deallocate power budget of the PI 327 + * @pi: a pointer to the PSE PI 328 + */ 329 + static void pse_pi_deallocate_pw_budget(struct pse_pi *pi) 330 + { 331 + if (!pi->pw_d || !pi->pw_allocated_mW) 332 + return; 333 + 334 + regulator_free_power_budget(pi->pw_d->supply, pi->pw_allocated_mW); 335 + pi->pw_allocated_mW = 0; 336 + } 337 + 338 + /** 339 + * _pse_pi_disable - Call disable operation. Assumes the PSE lock has been 340 + * acquired. 341 + * @pcdev: a pointer to the PSE 342 + * @id: index of the PSE control 343 + * 344 + * Return: 0 on success and failure value on error 345 + */ 346 + static int _pse_pi_disable(struct pse_controller_dev *pcdev, int id) 347 + { 348 + const struct pse_controller_ops *ops = pcdev->ops; 349 + int ret; 350 + 351 + if (!ops->pi_disable) 352 + return -EOPNOTSUPP; 353 + 354 + ret = ops->pi_disable(pcdev, id); 355 + if (ret) 356 + return ret; 357 + 358 + pse_pi_deallocate_pw_budget(&pcdev->pi[id]); 359 + 360 + if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) 361 + pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[id].pw_d); 362 + 363 + return 0; 364 + } 365 + 366 + /** 367 + * pse_disable_pi_pol - Disable a PI on a power budget policy 368 + * @pcdev: a pointer to the PSE 369 + * @id: index of the PSE PI 370 + * 371 + * Return: 0 on success and failure value on error 372 + */ 373 + static int pse_disable_pi_pol(struct pse_controller_dev *pcdev, int id) 374 + { 375 + unsigned long notifs = ETHTOOL_PSE_EVENT_OVER_BUDGET; 376 + struct pse_ntf ntf = {}; 377 + int ret; 378 + 379 + dev_dbg(pcdev->dev, "Disabling PI %d to free power budget\n", id); 380 + 381 + ret = _pse_pi_disable(pcdev, id); 382 + if (ret) 383 + notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; 384 + 385 + ntf.notifs = notifs; 386 + ntf.id = id; 387 + kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, &pcdev->ntf_fifo_lock); 388 + schedule_work(&pcdev->ntf_work); 389 + 390 + return ret; 391 + } 392 + 393 + /** 394 + * pse_disable_pi_prio - Disable all PIs of a given priority inside a PSE 395 + * power domain 396 + * @pcdev: a pointer to the PSE 397 + * @pw_d: a pointer to the PSE power domain 398 + * @prio: priority 399 + * 400 + * Return: 0 on success and failure value on error 401 + */ 402 + static int pse_disable_pi_prio(struct pse_controller_dev *pcdev, 403 + struct pse_power_domain *pw_d, 404 + int prio) 405 + { 406 + int i; 407 + 408 + for (i = 0; i < pcdev->nr_lines; i++) { 409 + int ret; 410 + 411 + if (pcdev->pi[i].prio != prio || 412 + pcdev->pi[i].pw_d != pw_d || 413 + pse_pi_is_hw_enabled(pcdev, i) <= 0) 414 + continue; 415 + 416 + ret = pse_disable_pi_pol(pcdev, i); 417 + if (ret) 418 + return ret; 419 + } 420 + 421 + return 0; 422 + } 423 + 424 + /** 425 + * pse_pi_allocate_pw_budget_static_prio - Allocate power budget for the PI 426 + * when the budget eval strategy is 427 + * static 428 + * @pcdev: a pointer to the PSE 429 + * @id: index of the PSE control 430 + * @pw_req: power requested in mW 431 + * @extack: extack for error reporting 432 + * 433 + * Allocates power using static budget evaluation strategy, where allocation 434 + * is based on PD classification. When insufficient budget is available, 435 + * lower-priority ports (higher priority numbers) are turned off first. 436 + * 437 + * Return: 0 on success and failure value on error 438 + */ 439 + static int 440 + pse_pi_allocate_pw_budget_static_prio(struct pse_controller_dev *pcdev, int id, 441 + int pw_req, struct netlink_ext_ack *extack) 442 + { 443 + struct pse_pi *pi = &pcdev->pi[id]; 444 + int ret, _prio; 445 + 446 + _prio = pcdev->nr_lines; 447 + while (regulator_request_power_budget(pi->pw_d->supply, pw_req) == -ERANGE) { 448 + if (_prio <= pi->prio) { 449 + NL_SET_ERR_MSG_FMT(extack, 450 + "PI %d: not enough power budget available", 451 + id); 452 + return -ERANGE; 453 + } 454 + 455 + ret = pse_disable_pi_prio(pcdev, pi->pw_d, _prio); 456 + if (ret < 0) 457 + return ret; 458 + 459 + _prio--; 460 + } 461 + 462 + pi->pw_allocated_mW = pw_req; 463 + return 0; 464 + } 465 + 466 + /** 467 + * pse_pi_allocate_pw_budget - Allocate power budget for the PI 468 + * @pcdev: a pointer to the PSE 469 + * @id: index of the PSE control 470 + * @pw_req: power requested in mW 471 + * @extack: extack for error reporting 472 + * 473 + * Return: 0 on success and failure value on error 474 + */ 475 + static int pse_pi_allocate_pw_budget(struct pse_controller_dev *pcdev, int id, 476 + int pw_req, struct netlink_ext_ack *extack) 477 + { 478 + struct pse_pi *pi = &pcdev->pi[id]; 479 + 480 + if (!pi->pw_d) 481 + return 0; 482 + 483 + /* PSE_BUDGET_EVAL_STRAT_STATIC */ 484 + if (pi->pw_d->budget_eval_strategy == PSE_BUDGET_EVAL_STRAT_STATIC) 485 + return pse_pi_allocate_pw_budget_static_prio(pcdev, id, pw_req, 486 + extack); 487 + 488 + return 0; 489 + } 490 + 491 + /** 492 + * _pse_pi_delivery_power_sw_pw_ctrl - Enable PSE PI in case of software power 493 + * control. Assumes the PSE lock has been 494 + * acquired. 495 + * @pcdev: a pointer to the PSE 496 + * @id: index of the PSE control 497 + * @extack: extack for error reporting 498 + * 499 + * Return: 0 on success and failure value on error 500 + */ 501 + static int _pse_pi_delivery_power_sw_pw_ctrl(struct pse_controller_dev *pcdev, 502 + int id, 503 + struct netlink_ext_ack *extack) 504 + { 505 + const struct pse_controller_ops *ops = pcdev->ops; 506 + struct pse_pi *pi = &pcdev->pi[id]; 507 + int ret, pw_req; 508 + 509 + if (!ops->pi_get_pw_req) { 510 + /* No power allocation management */ 511 + ret = ops->pi_enable(pcdev, id); 512 + if (ret) 513 + NL_SET_ERR_MSG_FMT(extack, 514 + "PI %d: enable error %d", 515 + id, ret); 516 + return ret; 517 + } 518 + 519 + ret = ops->pi_get_pw_req(pcdev, id); 520 + if (ret < 0) 521 + return ret; 522 + 523 + pw_req = ret; 524 + 525 + /* Compare requested power with port power limit and use the lowest 526 + * one. 527 + */ 528 + if (ops->pi_get_pw_limit) { 529 + ret = ops->pi_get_pw_limit(pcdev, id); 530 + if (ret < 0) 531 + return ret; 532 + 533 + if (ret < pw_req) 534 + pw_req = ret; 535 + } 536 + 537 + ret = pse_pi_allocate_pw_budget(pcdev, id, pw_req, extack); 538 + if (ret) 539 + return ret; 540 + 541 + ret = ops->pi_enable(pcdev, id); 542 + if (ret) { 543 + pse_pi_deallocate_pw_budget(pi); 544 + NL_SET_ERR_MSG_FMT(extack, 545 + "PI %d: enable error %d", 546 + id, ret); 547 + return ret; 548 + } 549 + 550 + return 0; 428 551 } 429 552 430 553 static int pse_pi_enable(struct regulator_dev *rdev) 431 554 { 432 555 struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 433 556 const struct pse_controller_ops *ops; 434 - int id, ret; 557 + int id, ret = 0; 435 558 436 559 ops = pcdev->ops; 437 560 if (!ops->pi_enable) ··· 674 327 675 328 id = rdev_get_id(rdev); 676 329 mutex_lock(&pcdev->lock); 330 + if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) { 331 + /* Manage enabled status by software. 332 + * Real enable process will happen if a port is connected. 333 + */ 334 + if (pcdev->pi[id].isr_pd_detected) { 335 + struct netlink_ext_ack extack; 336 + 337 + ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id, &extack); 338 + } 339 + if (!ret || ret == -ERANGE) { 340 + pcdev->pi[id].admin_state_enabled = 1; 341 + ret = 0; 342 + } 343 + mutex_unlock(&pcdev->lock); 344 + return ret; 345 + } 346 + 677 347 ret = ops->pi_enable(pcdev, id); 678 348 if (!ret) 679 349 pcdev->pi[id].admin_state_enabled = 1; ··· 702 338 static int pse_pi_disable(struct regulator_dev *rdev) 703 339 { 704 340 struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev); 705 - const struct pse_controller_ops *ops; 341 + struct pse_pi *pi; 706 342 int id, ret; 707 343 708 - ops = pcdev->ops; 709 - if (!ops->pi_disable) 710 - return -EOPNOTSUPP; 711 - 712 344 id = rdev_get_id(rdev); 345 + pi = &pcdev->pi[id]; 713 346 mutex_lock(&pcdev->lock); 714 - ret = ops->pi_disable(pcdev, id); 347 + ret = _pse_pi_disable(pcdev, id); 715 348 if (!ret) 716 - pcdev->pi[id].admin_state_enabled = 0; 717 - mutex_unlock(&pcdev->lock); 349 + pi->admin_state_enabled = 0; 718 350 719 - return ret; 351 + mutex_unlock(&pcdev->lock); 352 + return 0; 720 353 } 721 354 722 355 static int _pse_pi_get_voltage(struct regulator_dev *rdev) ··· 989 628 } 990 629 991 630 pw_d->supply = supply; 631 + if (pcdev->supp_budget_eval_strategies) 632 + pw_d->budget_eval_strategy = pcdev->supp_budget_eval_strategies; 633 + else 634 + pw_d->budget_eval_strategy = PSE_BUDGET_EVAL_STRAT_DISABLED; 635 + kref_init(&pw_d->refcnt); 992 636 pcdev->pi[i].pw_d = pw_d; 993 637 } 994 638 995 639 out: 996 640 mutex_unlock(&pse_pw_d_mutex); 997 641 return ret; 642 + } 643 + 644 + /** 645 + * pse_send_ntf_worker - Worker to send PSE notifications 646 + * @work: work object 647 + * 648 + * Manage and send PSE netlink notifications using a workqueue to avoid 649 + * deadlock between pcdev_lock and pse_list_mutex. 650 + */ 651 + static void pse_send_ntf_worker(struct work_struct *work) 652 + { 653 + struct pse_controller_dev *pcdev; 654 + struct pse_ntf ntf; 655 + 656 + pcdev = container_of(work, struct pse_controller_dev, ntf_work); 657 + 658 + while (kfifo_out(&pcdev->ntf_fifo, &ntf, 1)) { 659 + struct net_device *netdev; 660 + struct pse_control *psec; 661 + 662 + psec = pse_control_find_by_id(pcdev, ntf.id); 663 + rtnl_lock(); 664 + netdev = pse_control_get_netdev(psec); 665 + if (netdev) 666 + ethnl_pse_send_ntf(netdev, ntf.notifs); 667 + rtnl_unlock(); 668 + pse_control_put(psec); 669 + } 998 670 } 999 671 1000 672 /** ··· 1043 649 1044 650 mutex_init(&pcdev->lock); 1045 651 INIT_LIST_HEAD(&pcdev->pse_control_head); 652 + spin_lock_init(&pcdev->ntf_fifo_lock); 653 + ret = kfifo_alloc(&pcdev->ntf_fifo, pcdev->nr_lines, GFP_KERNEL); 654 + if (ret) { 655 + dev_err(pcdev->dev, "failed to allocate kfifo notifications\n"); 656 + return ret; 657 + } 658 + INIT_WORK(&pcdev->ntf_work, pse_send_ntf_worker); 1046 659 1047 660 if (!pcdev->nr_lines) 1048 661 pcdev->nr_lines = 1; ··· 1116 715 { 1117 716 pse_flush_pw_ds(pcdev); 1118 717 pse_release_pis(pcdev); 718 + if (pcdev->irq) 719 + disable_irq(pcdev->irq); 720 + cancel_work_sync(&pcdev->ntf_work); 721 + kfifo_free(&pcdev->ntf_fifo); 1119 722 mutex_lock(&pse_list_mutex); 1120 723 list_del(&pcdev->list); 1121 724 mutex_unlock(&pse_list_mutex); ··· 1192 787 } 1193 788 1194 789 /** 790 + * pse_set_config_isr - Set PSE control config according to the PSE 791 + * notifications 792 + * @pcdev: a pointer to the PSE 793 + * @id: index of the PSE control 794 + * @notifs: PSE event notifications 795 + * 796 + * Return: 0 on success and failure value on error 797 + */ 798 + static int pse_set_config_isr(struct pse_controller_dev *pcdev, int id, 799 + unsigned long notifs) 800 + { 801 + int ret = 0; 802 + 803 + if (notifs & PSE_BUDGET_EVAL_STRAT_DYNAMIC) 804 + return 0; 805 + 806 + if ((notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) && 807 + ((notifs & ETHTOOL_C33_PSE_EVENT_DETECTION) || 808 + (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION))) { 809 + dev_dbg(pcdev->dev, 810 + "PI %d: error, connection and disconnection reported simultaneously", 811 + id); 812 + return -EINVAL; 813 + } 814 + 815 + if (notifs & ETHTOOL_C33_PSE_EVENT_CLASSIFICATION) { 816 + struct netlink_ext_ack extack; 817 + 818 + pcdev->pi[id].isr_pd_detected = true; 819 + if (pcdev->pi[id].admin_state_enabled) { 820 + ret = _pse_pi_delivery_power_sw_pw_ctrl(pcdev, id, 821 + &extack); 822 + if (ret == -ERANGE) 823 + ret = 0; 824 + } 825 + } else if (notifs & ETHTOOL_C33_PSE_EVENT_DISCONNECTION) { 826 + if (pcdev->pi[id].admin_state_enabled && 827 + pcdev->pi[id].isr_pd_detected) 828 + ret = _pse_pi_disable(pcdev, id); 829 + pcdev->pi[id].isr_pd_detected = false; 830 + } 831 + 832 + return ret; 833 + } 834 + 835 + /** 1195 836 * pse_isr - IRQ handler for PSE 1196 837 * @irq: irq number 1197 838 * @data: pointer to user interrupt structure ··· 1259 808 memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); 1260 809 mutex_lock(&pcdev->lock); 1261 810 ret = desc->map_event(irq, pcdev, h->notifs, &notifs_mask); 1262 - mutex_unlock(&pcdev->lock); 1263 - if (ret || !notifs_mask) 811 + if (ret || !notifs_mask) { 812 + mutex_unlock(&pcdev->lock); 1264 813 return IRQ_NONE; 814 + } 1265 815 1266 816 for_each_set_bit(i, &notifs_mask, pcdev->nr_lines) { 1267 817 unsigned long notifs, rnotifs; 1268 - struct net_device *netdev; 1269 - struct pse_control *psec; 818 + struct pse_ntf ntf = {}; 1270 819 1271 820 /* Do nothing PI not described */ 1272 821 if (!pcdev->pi[i].rdev) 1273 822 continue; 1274 823 1275 824 notifs = h->notifs[i]; 825 + if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[i].pw_d)) { 826 + ret = pse_set_config_isr(pcdev, i, notifs); 827 + if (ret) 828 + notifs |= ETHTOOL_PSE_EVENT_SW_PW_CONTROL_ERROR; 829 + } 830 + 1276 831 dev_dbg(h->pcdev->dev, 1277 832 "Sending PSE notification EVT 0x%lx\n", notifs); 1278 833 1279 - psec = pse_control_find_by_id(pcdev, i); 1280 - rtnl_lock(); 1281 - netdev = pse_control_get_netdev(psec); 1282 - if (netdev) 1283 - ethnl_pse_send_ntf(netdev, notifs); 1284 - rtnl_unlock(); 1285 - pse_control_put(psec); 834 + ntf.notifs = notifs; 835 + ntf.id = i; 836 + kfifo_in_spinlocked(&pcdev->ntf_fifo, &ntf, 1, 837 + &pcdev->ntf_fifo_lock); 838 + schedule_work(&pcdev->ntf_work); 1286 839 1287 840 rnotifs = pse_to_regulator_notifs(notifs); 1288 841 regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs, 1289 842 NULL); 1290 843 } 844 + 845 + mutex_unlock(&pcdev->lock); 1291 846 1292 847 return IRQ_HANDLED; 1293 848 } ··· 1417 960 goto free_psec; 1418 961 } 1419 962 963 + if (!pcdev->ops->pi_get_admin_state) { 964 + ret = -EOPNOTSUPP; 965 + goto free_psec; 966 + } 967 + 968 + /* Initialize admin_state_enabled before the regulator_get. This 969 + * aims to have the right value reported in the first is_enabled 970 + * call in case of control managed by software. 971 + */ 972 + ret = pse_pi_is_hw_enabled(pcdev, index); 973 + if (ret < 0) 974 + goto free_psec; 975 + 976 + pcdev->pi[index].admin_state_enabled = ret; 1420 977 psec->ps = devm_regulator_get_exclusive(pcdev->dev, 1421 978 rdev_get_name(pcdev->pi[index].rdev)); 1422 979 if (IS_ERR(psec->ps)) { 1423 980 ret = PTR_ERR(psec->ps); 1424 981 goto put_module; 1425 982 } 1426 - 1427 - ret = regulator_is_enabled(psec->ps); 1428 - if (ret < 0) 1429 - goto regulator_put; 1430 - 1431 - pcdev->pi[index].admin_state_enabled = ret; 1432 983 1433 984 psec->pcdev = pcdev; 1434 985 list_add(&psec->list, &pcdev->pse_control_head); ··· 1446 981 1447 982 return psec; 1448 983 1449 - regulator_put: 1450 - devm_regulator_put(psec->ps); 1451 984 put_module: 1452 985 module_put(pcdev->owner); 1453 986 free_psec: ··· 1557 1094 EXPORT_SYMBOL_GPL(of_pse_control_get); 1558 1095 1559 1096 /** 1097 + * pse_get_sw_admin_state - Convert the software admin state to c33 or podl 1098 + * admin state value used in the standard 1099 + * @psec: PSE control pointer 1100 + * @admin_state: a pointer to the admin_state structure 1101 + */ 1102 + static void pse_get_sw_admin_state(struct pse_control *psec, 1103 + struct pse_admin_state *admin_state) 1104 + { 1105 + struct pse_pi *pi = &psec->pcdev->pi[psec->id]; 1106 + 1107 + if (pse_has_podl(psec)) { 1108 + if (pi->admin_state_enabled) 1109 + admin_state->podl_admin_state = 1110 + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED; 1111 + else 1112 + admin_state->podl_admin_state = 1113 + ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED; 1114 + } 1115 + if (pse_has_c33(psec)) { 1116 + if (pi->admin_state_enabled) 1117 + admin_state->c33_admin_state = 1118 + ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED; 1119 + else 1120 + admin_state->c33_admin_state = 1121 + ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED; 1122 + } 1123 + } 1124 + 1125 + /** 1560 1126 * pse_ethtool_get_status - get status of PSE control 1561 1127 * @psec: PSE control pointer 1562 1128 * @extack: extack for reporting useful error messages ··· 1601 1109 struct pse_pw_status pw_status = {0}; 1602 1110 const struct pse_controller_ops *ops; 1603 1111 struct pse_controller_dev *pcdev; 1112 + struct pse_pi *pi; 1604 1113 int ret; 1605 1114 1606 1115 pcdev = psec->pcdev; 1607 1116 ops = pcdev->ops; 1608 - mutex_lock(&pcdev->lock); 1609 - if (pcdev->pi[psec->id].pw_d) 1610 - status->pw_d_id = pcdev->pi[psec->id].pw_d->id; 1611 1117 1612 - ret = ops->pi_get_admin_state(pcdev, psec->id, &admin_state); 1613 - if (ret) 1614 - goto out; 1615 - status->podl_admin_state = admin_state.podl_admin_state; 1616 - status->c33_admin_state = admin_state.c33_admin_state; 1118 + pi = &pcdev->pi[psec->id]; 1119 + mutex_lock(&pcdev->lock); 1120 + if (pi->pw_d) { 1121 + status->pw_d_id = pi->pw_d->id; 1122 + if (pse_pw_d_is_sw_pw_control(pcdev, pi->pw_d)) { 1123 + pse_get_sw_admin_state(psec, &admin_state); 1124 + } else { 1125 + ret = ops->pi_get_admin_state(pcdev, psec->id, 1126 + &admin_state); 1127 + if (ret) 1128 + goto out; 1129 + } 1130 + status->podl_admin_state = admin_state.podl_admin_state; 1131 + status->c33_admin_state = admin_state.c33_admin_state; 1132 + 1133 + switch (pi->pw_d->budget_eval_strategy) { 1134 + case PSE_BUDGET_EVAL_STRAT_STATIC: 1135 + status->prio_max = pcdev->nr_lines - 1; 1136 + status->prio = pi->prio; 1137 + break; 1138 + case PSE_BUDGET_EVAL_STRAT_DYNAMIC: 1139 + status->prio_max = pcdev->pis_prio_max; 1140 + if (ops->pi_get_prio) { 1141 + ret = ops->pi_get_prio(pcdev, psec->id); 1142 + if (ret < 0) 1143 + goto out; 1144 + 1145 + status->prio = ret; 1146 + } 1147 + break; 1148 + default: 1149 + break; 1150 + } 1151 + } 1617 1152 1618 1153 ret = ops->pi_get_pw_status(pcdev, psec->id, &pw_status); 1619 1154 if (ret) ··· 1790 1271 EXPORT_SYMBOL_GPL(pse_ethtool_set_config); 1791 1272 1792 1273 /** 1274 + * pse_pi_update_pw_budget - Update PSE power budget allocated with new 1275 + * power in mW 1276 + * @pcdev: a pointer to the PSE controller device 1277 + * @id: index of the PSE PI 1278 + * @pw_req: power requested 1279 + * @extack: extack for reporting useful error messages 1280 + * 1281 + * Return: Previous power allocated on success and failure value on error 1282 + */ 1283 + static int pse_pi_update_pw_budget(struct pse_controller_dev *pcdev, int id, 1284 + const unsigned int pw_req, 1285 + struct netlink_ext_ack *extack) 1286 + { 1287 + struct pse_pi *pi = &pcdev->pi[id]; 1288 + int previous_pw_allocated; 1289 + int pw_diff, ret = 0; 1290 + 1291 + /* We don't want pw_allocated_mW value change in the middle of an 1292 + * power budget update 1293 + */ 1294 + mutex_lock(&pcdev->lock); 1295 + previous_pw_allocated = pi->pw_allocated_mW; 1296 + pw_diff = pw_req - previous_pw_allocated; 1297 + if (!pw_diff) { 1298 + goto out; 1299 + } else if (pw_diff > 0) { 1300 + ret = regulator_request_power_budget(pi->pw_d->supply, pw_diff); 1301 + if (ret) { 1302 + NL_SET_ERR_MSG_FMT(extack, 1303 + "PI %d: not enough power budget available", 1304 + id); 1305 + goto out; 1306 + } 1307 + 1308 + } else { 1309 + regulator_free_power_budget(pi->pw_d->supply, -pw_diff); 1310 + } 1311 + pi->pw_allocated_mW = pw_req; 1312 + ret = previous_pw_allocated; 1313 + 1314 + out: 1315 + mutex_unlock(&pcdev->lock); 1316 + return ret; 1317 + } 1318 + 1319 + /** 1793 1320 * pse_ethtool_set_pw_limit - set PSE control power limit 1794 1321 * @psec: PSE control pointer 1795 1322 * @extack: extack for reporting useful error messages ··· 1847 1282 struct netlink_ext_ack *extack, 1848 1283 const unsigned int pw_limit) 1849 1284 { 1850 - int uV, uA, ret; 1285 + int uV, uA, ret, previous_pw_allocated = 0; 1851 1286 s64 tmp_64; 1852 1287 1853 1288 if (pw_limit > MAX_PI_PW) ··· 1871 1306 /* uA = mW * 1000000000 / uV */ 1872 1307 uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV); 1873 1308 1874 - return regulator_set_current_limit(psec->ps, 0, uA); 1309 + /* Update power budget only in software power control case and 1310 + * if a Power Device is powered. 1311 + */ 1312 + if (pse_pw_d_is_sw_pw_control(psec->pcdev, 1313 + psec->pcdev->pi[psec->id].pw_d) && 1314 + psec->pcdev->pi[psec->id].admin_state_enabled && 1315 + psec->pcdev->pi[psec->id].isr_pd_detected) { 1316 + ret = pse_pi_update_pw_budget(psec->pcdev, psec->id, 1317 + pw_limit, extack); 1318 + if (ret < 0) 1319 + return ret; 1320 + previous_pw_allocated = ret; 1321 + } 1322 + 1323 + ret = regulator_set_current_limit(psec->ps, 0, uA); 1324 + if (ret < 0 && previous_pw_allocated) { 1325 + pse_pi_update_pw_budget(psec->pcdev, psec->id, 1326 + previous_pw_allocated, extack); 1327 + } 1328 + 1329 + return ret; 1875 1330 } 1876 1331 EXPORT_SYMBOL_GPL(pse_ethtool_set_pw_limit); 1332 + 1333 + /** 1334 + * pse_ethtool_set_prio - Set PSE PI priority according to the budget 1335 + * evaluation strategy 1336 + * @psec: PSE control pointer 1337 + * @extack: extack for reporting useful error messages 1338 + * @prio: priovity value 1339 + * 1340 + * Return: 0 on success and failure value on error 1341 + */ 1342 + int pse_ethtool_set_prio(struct pse_control *psec, 1343 + struct netlink_ext_ack *extack, 1344 + unsigned int prio) 1345 + { 1346 + struct pse_controller_dev *pcdev = psec->pcdev; 1347 + const struct pse_controller_ops *ops; 1348 + int ret = 0; 1349 + 1350 + if (!pcdev->pi[psec->id].pw_d) { 1351 + NL_SET_ERR_MSG(extack, "no power domain attached"); 1352 + return -EOPNOTSUPP; 1353 + } 1354 + 1355 + /* We don't want priority change in the middle of an 1356 + * enable/disable call or a priority mode change 1357 + */ 1358 + mutex_lock(&pcdev->lock); 1359 + switch (pcdev->pi[psec->id].pw_d->budget_eval_strategy) { 1360 + case PSE_BUDGET_EVAL_STRAT_STATIC: 1361 + if (prio >= pcdev->nr_lines) { 1362 + NL_SET_ERR_MSG_FMT(extack, 1363 + "priority %d exceed priority max %d", 1364 + prio, pcdev->nr_lines); 1365 + ret = -ERANGE; 1366 + goto out; 1367 + } 1368 + 1369 + pcdev->pi[psec->id].prio = prio; 1370 + pse_pw_d_retry_power_delivery(pcdev, pcdev->pi[psec->id].pw_d); 1371 + break; 1372 + 1373 + case PSE_BUDGET_EVAL_STRAT_DYNAMIC: 1374 + ops = psec->pcdev->ops; 1375 + if (!ops->pi_set_prio) { 1376 + NL_SET_ERR_MSG(extack, 1377 + "pse driver does not support setting port priority"); 1378 + ret = -EOPNOTSUPP; 1379 + goto out; 1380 + } 1381 + 1382 + if (prio > pcdev->pis_prio_max) { 1383 + NL_SET_ERR_MSG_FMT(extack, 1384 + "priority %d exceed priority max %d", 1385 + prio, pcdev->pis_prio_max); 1386 + ret = -ERANGE; 1387 + goto out; 1388 + } 1389 + 1390 + ret = ops->pi_set_prio(pcdev, psec->id, prio); 1391 + break; 1392 + 1393 + default: 1394 + ret = -EOPNOTSUPP; 1395 + } 1396 + 1397 + out: 1398 + mutex_unlock(&pcdev->lock); 1399 + return ret; 1400 + } 1401 + EXPORT_SYMBOL_GPL(pse_ethtool_set_prio); 1877 1402 1878 1403 bool pse_has_podl(struct pse_control *psec) 1879 1404 {
+76
include/linux/pse-pd/pse.h
··· 6 6 #define _LINUX_PSE_CONTROLLER_H 7 7 8 8 #include <linux/list.h> 9 + #include <linux/netlink.h> 10 + #include <linux/kfifo.h> 9 11 #include <uapi/linux/ethtool.h> 10 12 #include <uapi/linux/ethtool_netlink_generated.h> 11 13 #include <linux/regulator/driver.h> ··· 136 134 * is in charge of the memory allocation 137 135 * @c33_pw_limit_nb_ranges: number of supported power limit configuration 138 136 * ranges 137 + * @prio_max: max priority allowed for the c33_prio variable value. 138 + * @prio: priority of the PSE. Managed by PSE core in case of static budget 139 + * evaluation strategy. 139 140 */ 140 141 struct ethtool_pse_control_status { 141 142 u32 pw_d_id; ··· 152 147 u32 c33_avail_pw_limit; 153 148 struct ethtool_c33_pse_pw_limit_range *c33_pw_limit_ranges; 154 149 u32 c33_pw_limit_nb_ranges; 150 + u32 prio_max; 151 + u32 prio; 155 152 }; 156 153 157 154 /** ··· 177 170 * range. The driver is in charge of the memory 178 171 * allocation and should return the number of 179 172 * ranges. 173 + * @pi_get_prio: Get the PSE PI priority. 174 + * @pi_set_prio: Configure the PSE PI priority. 175 + * @pi_get_pw_req: Get the power requested by a PD before enabling the PSE PI. 176 + * This is only relevant when an interrupt is registered using 177 + * devm_pse_irq_helper helper. 180 178 */ 181 179 struct pse_controller_ops { 182 180 int (*setup_pi_matrix)(struct pse_controller_dev *pcdev); ··· 202 190 int id, int max_mW); 203 191 int (*pi_get_pw_limit_ranges)(struct pse_controller_dev *pcdev, int id, 204 192 struct pse_pw_limit_ranges *pw_limit_ranges); 193 + int (*pi_get_prio)(struct pse_controller_dev *pcdev, int id); 194 + int (*pi_set_prio)(struct pse_controller_dev *pcdev, int id, 195 + unsigned int prio); 196 + int (*pi_get_pw_req)(struct pse_controller_dev *pcdev, int id); 205 197 }; 206 198 207 199 struct module; ··· 241 225 * @rdev: regulator represented by the PSE PI 242 226 * @admin_state_enabled: PI enabled state 243 227 * @pw_d: Power domain of the PSE PI 228 + * @prio: Priority of the PSE PI. Used in static budget evaluation strategy 229 + * @isr_pd_detected: PSE PI detection status managed by the interruption 230 + * handler. This variable is relevant when the power enabled 231 + * management is managed in software like the static 232 + * budget evaluation strategy. 233 + * @pw_allocated_mW: Power allocated to a PSE PI to manage power budget in 234 + * static budget evaluation strategy. 244 235 */ 245 236 struct pse_pi { 246 237 struct pse_pi_pairset pairset[2]; ··· 255 232 struct regulator_dev *rdev; 256 233 bool admin_state_enabled; 257 234 struct pse_power_domain *pw_d; 235 + int prio; 236 + bool isr_pd_detected; 237 + int pw_allocated_mW; 238 + }; 239 + 240 + /** 241 + * struct pse_ntf - PSE notification element 242 + * 243 + * @id: ID of the PSE control 244 + * @notifs: PSE notifications to be reported 245 + */ 246 + struct pse_ntf { 247 + int id; 248 + unsigned long notifs; 258 249 }; 259 250 260 251 /** ··· 286 249 * @pi: table of PSE PIs described in this controller device 287 250 * @no_of_pse_pi: flag set if the pse_pis devicetree node is not used 288 251 * @irq: PSE interrupt 252 + * @pis_prio_max: Maximum value allowed for the PSE PIs priority 253 + * @supp_budget_eval_strategies: budget evaluation strategies supported 254 + * by the PSE 255 + * @ntf_work: workqueue for PSE notification management 256 + * @ntf_fifo: PSE notifications FIFO 257 + * @ntf_fifo_lock: protect @ntf_fifo writer 289 258 */ 290 259 struct pse_controller_dev { 291 260 const struct pse_controller_ops *ops; ··· 306 263 struct pse_pi *pi; 307 264 bool no_of_pse_pi; 308 265 int irq; 266 + unsigned int pis_prio_max; 267 + u32 supp_budget_eval_strategies; 268 + struct work_struct ntf_work; 269 + DECLARE_KFIFO_PTR(ntf_fifo, struct pse_ntf); 270 + spinlock_t ntf_fifo_lock; /* Protect @ntf_fifo writer */ 271 + }; 272 + 273 + /** 274 + * enum pse_budget_eval_strategies - PSE budget evaluation strategies. 275 + * @PSE_BUDGET_EVAL_STRAT_DISABLED: Budget evaluation strategy disabled. 276 + * @PSE_BUDGET_EVAL_STRAT_STATIC: PSE static budget evaluation strategy. 277 + * Budget evaluation strategy based on the power requested during PD 278 + * classification. This strategy is managed by the PSE core. 279 + * @PSE_BUDGET_EVAL_STRAT_DYNAMIC: PSE dynamic budget evaluation 280 + * strategy. Budget evaluation strategy based on the current consumption 281 + * per ports compared to the total power budget. This mode is managed by 282 + * the PSE controller. 283 + */ 284 + 285 + enum pse_budget_eval_strategies { 286 + PSE_BUDGET_EVAL_STRAT_DISABLED = 1 << 0, 287 + PSE_BUDGET_EVAL_STRAT_STATIC = 1 << 1, 288 + PSE_BUDGET_EVAL_STRAT_DYNAMIC = 1 << 2, 309 289 }; 310 290 311 291 #if IS_ENABLED(CONFIG_PSE_CONTROLLER) ··· 353 287 int pse_ethtool_set_pw_limit(struct pse_control *psec, 354 288 struct netlink_ext_ack *extack, 355 289 const unsigned int pw_limit); 290 + int pse_ethtool_set_prio(struct pse_control *psec, 291 + struct netlink_ext_ack *extack, 292 + unsigned int prio); 356 293 357 294 bool pse_has_podl(struct pse_control *psec); 358 295 bool pse_has_c33(struct pse_control *psec); ··· 389 320 static inline int pse_ethtool_set_pw_limit(struct pse_control *psec, 390 321 struct netlink_ext_ack *extack, 391 322 const unsigned int pw_limit) 323 + { 324 + return -EOPNOTSUPP; 325 + } 326 + 327 + static inline int pse_ethtool_set_prio(struct pse_control *psec, 328 + struct netlink_ext_ack *extack, 329 + unsigned int prio) 392 330 { 393 331 return -EOPNOTSUPP; 394 332 }