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

phy: usb: Leave some clocks running during suspend

The PHY client driver does a phy_exit() call on suspend or rmmod and
the PHY driver needs to know the difference because some clocks need
to be kept running for suspend but can be shutdown on unbind/rmmod
(or if there are no PHY clients at all).

The fix is to use a PM notifier so the driver can tell if a PHY
client is calling exit() because of a system suspend or a driver
unbind/rmmod.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Link: https://lore.kernel.org/r/20211201180653.35097-2-alcooperx@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Al Cooper and committed by
Vinod Koul
42fed570 e783362e

+38
+38
drivers/phy/broadcom/phy-brcm-usb.c
··· 18 18 #include <linux/soc/brcmstb/brcmstb.h> 19 19 #include <dt-bindings/phy/phy.h> 20 20 #include <linux/mfd/syscon.h> 21 + #include <linux/suspend.h> 21 22 22 23 #include "phy-brcm-usb-init.h" 23 24 ··· 71 70 int init_count; 72 71 int wake_irq; 73 72 struct brcm_usb_phy phys[BRCM_USB_PHY_ID_MAX]; 73 + struct notifier_block pm_notifier; 74 + bool pm_active; 74 75 }; 75 76 76 77 static s8 *node_reg_names[BRCM_REGS_MAX] = { 77 78 "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec" 78 79 }; 80 + 81 + static int brcm_pm_notifier(struct notifier_block *notifier, 82 + unsigned long pm_event, 83 + void *unused) 84 + { 85 + struct brcm_usb_phy_data *priv = 86 + container_of(notifier, struct brcm_usb_phy_data, pm_notifier); 87 + 88 + switch (pm_event) { 89 + case PM_HIBERNATION_PREPARE: 90 + case PM_SUSPEND_PREPARE: 91 + priv->pm_active = true; 92 + break; 93 + case PM_POST_RESTORE: 94 + case PM_POST_HIBERNATION: 95 + case PM_POST_SUSPEND: 96 + priv->pm_active = false; 97 + break; 98 + } 99 + return NOTIFY_DONE; 100 + } 79 101 80 102 static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id) 81 103 { ··· 114 90 struct brcm_usb_phy *phy = phy_get_drvdata(gphy); 115 91 struct brcm_usb_phy_data *priv = 116 92 container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); 93 + 94 + if (priv->pm_active) 95 + return 0; 117 96 118 97 /* 119 98 * Use a lock to make sure a second caller waits until ··· 146 119 struct brcm_usb_phy *phy = phy_get_drvdata(gphy); 147 120 struct brcm_usb_phy_data *priv = 148 121 container_of(phy, struct brcm_usb_phy_data, phys[phy->id]); 122 + 123 + if (priv->pm_active) 124 + return 0; 149 125 150 126 dev_dbg(&gphy->dev, "EXIT\n"); 151 127 if (phy->id == BRCM_USB_PHY_2_0) ··· 518 488 if (err) 519 489 return err; 520 490 491 + priv->pm_notifier.notifier_call = brcm_pm_notifier; 492 + register_pm_notifier(&priv->pm_notifier); 493 + 521 494 mutex_init(&priv->mutex); 522 495 523 496 /* make sure invert settings are correct */ ··· 561 528 562 529 static int brcm_usb_phy_remove(struct platform_device *pdev) 563 530 { 531 + struct brcm_usb_phy_data *priv = dev_get_drvdata(&pdev->dev); 532 + 564 533 sysfs_remove_group(&pdev->dev.kobj, &brcm_usb_phy_group); 534 + unregister_pm_notifier(&priv->pm_notifier); 565 535 566 536 return 0; 567 537 } ··· 575 539 struct brcm_usb_phy_data *priv = dev_get_drvdata(dev); 576 540 577 541 if (priv->init_count) { 542 + dev_dbg(dev, "SUSPEND\n"); 578 543 priv->ini.wake_enabled = device_may_wakeup(dev); 579 544 if (priv->phys[BRCM_USB_PHY_3_0].inited) 580 545 brcm_usb_uninit_xhci(&priv->ini); ··· 615 578 * Uninitialize anything that wasn't previously initialized. 616 579 */ 617 580 if (priv->init_count) { 581 + dev_dbg(dev, "RESUME\n"); 618 582 if (priv->wake_irq >= 0) 619 583 disable_irq_wake(priv->wake_irq); 620 584 brcm_usb_init_common(&priv->ini);