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

Configure Feed

Select the types of activity you want to include in your feed.

net: pse-pd: Add support for reporting events

Add support for devm_pse_irq_helper() to register PSE interrupts and report
events such as over-current or over-temperature conditions. This follows a
similar approach to the regulator API but also sends notifications using a
dedicated PSE ethtool netlink socket.

Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-2-78a1a645e2ee@bootlin.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Kory Maincent (Dent Project) and committed by
Jakub Kicinski
fc0e6db3 fa2f0454

+317
+34
Documentation/netlink/specs/ethtool.yaml
··· 118 118 doc: | 119 119 Hardware timestamp comes from one PHY device 120 120 of the network topology 121 + - 122 + name: pse-event 123 + doc: PSE event list for the PSE controller 124 + type: flags 125 + entries: 126 + - 127 + name: over-current 128 + doc: PSE output current is too high 129 + - 130 + name: over-temp 131 + doc: PSE in over temperature state 121 132 122 133 attribute-sets: 123 134 - ··· 1566 1555 name: hwtstamp-flags 1567 1556 type: nest 1568 1557 nested-attributes: bitset 1558 + - 1559 + name: pse-ntf 1560 + attr-cnt-name: --ethtool-a-pse-ntf-cnt 1561 + attributes: 1562 + - 1563 + name: header 1564 + type: nest 1565 + nested-attributes: header 1566 + - 1567 + name: events 1568 + type: uint 1569 + enum: pse-event 1570 + doc: List of events reported by the PSE controller 1569 1571 1570 1572 operations: 1571 1573 enum-model: directional ··· 2437 2413 attributes: *tsconfig 2438 2414 reply: 2439 2415 attributes: *tsconfig 2416 + - 2417 + name: pse-ntf 2418 + doc: Notification for PSE events. 2419 + 2420 + attribute-set: pse-ntf 2421 + 2422 + event: 2423 + attributes: 2424 + - header 2425 + - events
+19
Documentation/networking/ethtool-netlink.rst
··· 290 290 ``ETHTOOL_MSG_PHY_NTF`` Ethernet PHY information change 291 291 ``ETHTOOL_MSG_TSCONFIG_GET_REPLY`` hw timestamping configuration 292 292 ``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration 293 + ``ETHTOOL_MSG_PSE_NTF`` PSE events notification 293 294 ======================================== ================================= 294 295 295 296 ``GET`` requests are sent by userspace applications to retrieve device ··· 1896 1895 various existing products that document power consumption in watts rather than 1897 1896 classes. If power limit configuration based on classes is needed, the 1898 1897 conversion can be done in user space, for example by ethtool. 1898 + 1899 + PSE_NTF 1900 + ======= 1901 + 1902 + Notify PSE events. 1903 + 1904 + Notification contents: 1905 + 1906 + =============================== ====== ======================== 1907 + ``ETHTOOL_A_PSE_HEADER`` nested request header 1908 + ``ETHTOOL_A_PSE_EVENTS`` bitset PSE events 1909 + =============================== ====== ======================== 1910 + 1911 + When set, the optional ``ETHTOOL_A_PSE_EVENTS`` attribute identifies the 1912 + PSE events. 1913 + 1914 + .. kernel-doc:: include/uapi/linux/ethtool_netlink_generated.h 1915 + :identifiers: ethtool_pse_event 1899 1916 1900 1917 RSS_GET 1901 1918 =======
+179
drivers/net/pse-pd/pse_core.c
··· 7 7 8 8 #include <linux/device.h> 9 9 #include <linux/ethtool.h> 10 + #include <linux/ethtool_netlink.h> 10 11 #include <linux/of.h> 12 + #include <linux/phy.h> 11 13 #include <linux/pse-pd/pse.h> 12 14 #include <linux/regulator/driver.h> 13 15 #include <linux/regulator/machine.h> 16 + #include <linux/rtnetlink.h> 17 + #include <net/net_trackers.h> 14 18 15 19 static DEFINE_MUTEX(pse_list_mutex); 16 20 static LIST_HEAD(pse_controller_list); ··· 212 208 of_node_put(node); 213 209 of_node_put(pis); 214 210 return ret; 211 + } 212 + 213 + /** 214 + * pse_control_find_net_by_id - Find net attached to the pse control id 215 + * @pcdev: a pointer to the PSE 216 + * @id: index of the PSE control 217 + * 218 + * Return: pse_control pointer or NULL. The device returned has had a 219 + * reference added and the pointer is safe until the user calls 220 + * pse_control_put() to indicate they have finished with it. 221 + */ 222 + static struct pse_control * 223 + pse_control_find_by_id(struct pse_controller_dev *pcdev, int id) 224 + { 225 + struct pse_control *psec; 226 + 227 + mutex_lock(&pse_list_mutex); 228 + list_for_each_entry(psec, &pcdev->pse_control_head, list) { 229 + if (psec->id == id) { 230 + kref_get(&psec->refcnt); 231 + mutex_unlock(&pse_list_mutex); 232 + return psec; 233 + } 234 + } 235 + mutex_unlock(&pse_list_mutex); 236 + return NULL; 237 + } 238 + 239 + /** 240 + * pse_control_get_netdev - Return netdev associated to a PSE control 241 + * @psec: PSE control pointer 242 + * 243 + * Return: netdev pointer or NULL 244 + */ 245 + static struct net_device *pse_control_get_netdev(struct pse_control *psec) 246 + { 247 + ASSERT_RTNL(); 248 + 249 + if (!psec || !psec->attached_phydev) 250 + return NULL; 251 + 252 + return psec->attached_phydev->attached_dev; 215 253 } 216 254 217 255 static int pse_pi_is_enabled(struct regulator_dev *rdev) ··· 604 558 return 0; 605 559 } 606 560 EXPORT_SYMBOL_GPL(devm_pse_controller_register); 561 + 562 + struct pse_irq { 563 + struct pse_controller_dev *pcdev; 564 + struct pse_irq_desc desc; 565 + unsigned long *notifs; 566 + }; 567 + 568 + /** 569 + * pse_to_regulator_notifs - Convert PSE notifications to Regulator 570 + * notifications 571 + * @notifs: PSE notifications 572 + * 573 + * Return: Regulator notifications 574 + */ 575 + static unsigned long pse_to_regulator_notifs(unsigned long notifs) 576 + { 577 + unsigned long rnotifs = 0; 578 + 579 + if (notifs & ETHTOOL_PSE_EVENT_OVER_CURRENT) 580 + rnotifs |= REGULATOR_EVENT_OVER_CURRENT; 581 + if (notifs & ETHTOOL_PSE_EVENT_OVER_TEMP) 582 + rnotifs |= REGULATOR_EVENT_OVER_TEMP; 583 + 584 + return rnotifs; 585 + } 586 + 587 + /** 588 + * pse_isr - IRQ handler for PSE 589 + * @irq: irq number 590 + * @data: pointer to user interrupt structure 591 + * 592 + * Return: irqreturn_t - status of IRQ 593 + */ 594 + static irqreturn_t pse_isr(int irq, void *data) 595 + { 596 + struct pse_controller_dev *pcdev; 597 + unsigned long notifs_mask = 0; 598 + struct pse_irq_desc *desc; 599 + struct pse_irq *h = data; 600 + int ret, i; 601 + 602 + desc = &h->desc; 603 + pcdev = h->pcdev; 604 + 605 + /* Clear notifs mask */ 606 + memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs)); 607 + mutex_lock(&pcdev->lock); 608 + ret = desc->map_event(irq, pcdev, h->notifs, &notifs_mask); 609 + mutex_unlock(&pcdev->lock); 610 + if (ret || !notifs_mask) 611 + return IRQ_NONE; 612 + 613 + for_each_set_bit(i, &notifs_mask, pcdev->nr_lines) { 614 + unsigned long notifs, rnotifs; 615 + struct net_device *netdev; 616 + struct pse_control *psec; 617 + 618 + /* Do nothing PI not described */ 619 + if (!pcdev->pi[i].rdev) 620 + continue; 621 + 622 + notifs = h->notifs[i]; 623 + dev_dbg(h->pcdev->dev, 624 + "Sending PSE notification EVT 0x%lx\n", notifs); 625 + 626 + psec = pse_control_find_by_id(pcdev, i); 627 + rtnl_lock(); 628 + netdev = pse_control_get_netdev(psec); 629 + if (netdev) 630 + ethnl_pse_send_ntf(netdev, notifs); 631 + rtnl_unlock(); 632 + pse_control_put(psec); 633 + 634 + rnotifs = pse_to_regulator_notifs(notifs); 635 + regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs, 636 + NULL); 637 + } 638 + 639 + return IRQ_HANDLED; 640 + } 641 + 642 + /** 643 + * devm_pse_irq_helper - Register IRQ based PSE event notifier 644 + * @pcdev: a pointer to the PSE 645 + * @irq: the irq value to be passed to request_irq 646 + * @irq_flags: the flags to be passed to request_irq 647 + * @d: PSE interrupt description 648 + * 649 + * Return: 0 on success and errno on failure 650 + */ 651 + int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, 652 + int irq_flags, const struct pse_irq_desc *d) 653 + { 654 + struct device *dev = pcdev->dev; 655 + size_t irq_name_len; 656 + struct pse_irq *h; 657 + char *irq_name; 658 + int ret; 659 + 660 + if (!d || !d->map_event || !d->name) 661 + return -EINVAL; 662 + 663 + h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); 664 + if (!h) 665 + return -ENOMEM; 666 + 667 + h->pcdev = pcdev; 668 + h->desc = *d; 669 + 670 + /* IRQ name len is pcdev dev name + 5 char + irq desc name + 1 */ 671 + irq_name_len = strlen(dev_name(pcdev->dev)) + 5 + strlen(d->name) + 1; 672 + irq_name = devm_kzalloc(dev, irq_name_len, GFP_KERNEL); 673 + if (!irq_name) 674 + return -ENOMEM; 675 + 676 + snprintf(irq_name, irq_name_len, "pse-%s:%s", dev_name(pcdev->dev), 677 + d->name); 678 + 679 + h->notifs = devm_kcalloc(dev, pcdev->nr_lines, 680 + sizeof(*h->notifs), GFP_KERNEL); 681 + if (!h->notifs) 682 + return -ENOMEM; 683 + 684 + ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr, 685 + IRQF_ONESHOT | irq_flags, 686 + irq_name, h); 687 + if (ret) 688 + dev_err(pcdev->dev, "Failed to request IRQ %d\n", irq); 689 + 690 + pcdev->irq = irq; 691 + return ret; 692 + } 693 + EXPORT_SYMBOL_GPL(devm_pse_irq_helper); 607 694 608 695 /* PSE control section */ 609 696
+7
include/linux/ethtool_netlink.h
··· 43 43 struct ethtool_rmon_stats *rmon_stats); 44 44 bool ethtool_dev_mm_supported(struct net_device *dev); 45 45 46 + void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notif); 47 + 46 48 #else 47 49 static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd) 48 50 { ··· 120 118 static inline bool ethtool_dev_mm_supported(struct net_device *dev) 121 119 { 122 120 return false; 121 + } 122 + 123 + static inline void ethnl_pse_send_ntf(struct phy_device *phydev, 124 + unsigned long notif) 125 + { 123 126 } 124 127 125 128 #endif /* IS_ENABLED(CONFIG_ETHTOOL_NETLINK) */
+20
include/linux/pse-pd/pse.h
··· 7 7 8 8 #include <linux/list.h> 9 9 #include <uapi/linux/ethtool.h> 10 + #include <uapi/linux/ethtool_netlink_generated.h> 11 + #include <linux/regulator/driver.h> 10 12 11 13 /* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */ 12 14 #define MAX_PI_CURRENT 1920000 13 15 /* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */ 14 16 #define MAX_PI_PW 99900 15 17 18 + struct net_device; 16 19 struct phy_device; 17 20 struct pse_controller_dev; 18 21 struct netlink_ext_ack; ··· 38 35 struct ethtool_c33_pse_pw_limit_range { 39 36 u32 min; 40 37 u32 max; 38 + }; 39 + 40 + /** 41 + * struct pse_irq_desc - notification sender description for IRQ based events. 42 + * 43 + * @name: the visible name for the IRQ 44 + * @map_event: driver callback to map IRQ status into PSE devices with events. 45 + */ 46 + struct pse_irq_desc { 47 + const char *name; 48 + int (*map_event)(int irq, struct pse_controller_dev *pcdev, 49 + unsigned long *notifs, 50 + unsigned long *notifs_mask); 41 51 }; 42 52 43 53 /** ··· 244 228 * @types: types of the PSE controller 245 229 * @pi: table of PSE PIs described in this controller device 246 230 * @no_of_pse_pi: flag set if the pse_pis devicetree node is not used 231 + * @irq: PSE interrupt 247 232 */ 248 233 struct pse_controller_dev { 249 234 const struct pse_controller_ops *ops; ··· 258 241 enum ethtool_pse_types types; 259 242 struct pse_pi *pi; 260 243 bool no_of_pse_pi; 244 + int irq; 261 245 }; 262 246 263 247 #if IS_ENABLED(CONFIG_PSE_CONTROLLER) ··· 267 249 struct device; 268 250 int devm_pse_controller_register(struct device *dev, 269 251 struct pse_controller_dev *pcdev); 252 + int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq, 253 + int irq_flags, const struct pse_irq_desc *d); 270 254 271 255 struct pse_control *of_pse_control_get(struct device_node *node, 272 256 struct phy_device *phydev);
+39
net/ethtool/pse-pd.c
··· 315 315 .set = ethnl_set_pse, 316 316 /* PSE has no notification */ 317 317 }; 318 + 319 + void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notifs) 320 + { 321 + void *reply_payload; 322 + struct sk_buff *skb; 323 + int reply_len; 324 + int ret; 325 + 326 + ASSERT_RTNL(); 327 + 328 + if (!netdev || !notifs) 329 + return; 330 + 331 + reply_len = ethnl_reply_header_size() + 332 + nla_total_size(sizeof(u32)); /* _PSE_NTF_EVENTS */ 333 + 334 + skb = genlmsg_new(reply_len, GFP_KERNEL); 335 + if (!skb) 336 + return; 337 + 338 + reply_payload = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_PSE_NTF); 339 + if (!reply_payload) 340 + goto err_skb; 341 + 342 + ret = ethnl_fill_reply_header(skb, netdev, ETHTOOL_A_PSE_NTF_HEADER); 343 + if (ret < 0) 344 + goto err_skb; 345 + 346 + if (nla_put_uint(skb, ETHTOOL_A_PSE_NTF_EVENTS, notifs)) 347 + goto err_skb; 348 + 349 + genlmsg_end(skb, reply_payload); 350 + ethnl_multicast(skb, netdev); 351 + return; 352 + 353 + err_skb: 354 + nlmsg_free(skb); 355 + } 356 + EXPORT_SYMBOL_GPL(ethnl_pse_send_ntf);