Skip to content

Commit 0b6f659

Browse files
maciejsszmigierokuba-moo
authored andcommitted
net: wwan: iosm: Fix hibernation by re-binding the driver around it
Currently, the driver is seriously broken with respect to the hibernation (S4): after image restore the device is back into IPC_MEM_EXEC_STAGE_BOOT (which AFAIK means bootloader stage) and needs full re-launch of the rest of its firmware, but the driver restore handler treats the device as merely sleeping and just sends it a wake-up command. This wake-up command times out but device nodes (/dev/wwan*) remain accessible. However attempting to use them causes the bootloader to crash and enter IPC_MEM_EXEC_STAGE_CD_READY stage (which apparently means "a crash dump is ready"). It seems that the device cannot be re-initialized from this crashed stage without toggling some reset pin (on my test platform that's apparently what the device _RST ACPI method does). While it would theoretically be possible to rewrite the driver to tear down the whole MUX / IPC layers on hibernation (so the bootloader does not crash from improper access) and then re-launch the device on restore this would require significant refactoring of the driver (believe me, I've tried), since there are quite a few assumptions hard-coded in the driver about the device never being partially de-initialized (like channels other than devlink cannot be closed, for example). Probably this would also need some programming guide for this hardware. Considering that the driver seems orphaned [1] and other people are hitting this issue too [2] fix it by simply unbinding the PCI driver before hibernation and re-binding it after restore, much like USB_QUIRK_RESET_RESUME does for USB devices that exhibit a similar problem. Tested on XMM7360 in HP EliteBook 855 G7 both with s2idle (which uses the existing suspend / resume handlers) and S4 (which uses the new code). [1]: https://lore.kernel.org/all/c248f0b4-2114-4c61-905f-466a786bdebb@leemhuis.info/ [2]: xmm7360/xmm7360-pci#211 (comment) Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> Signed-off-by: Maciej S. Szmigiero <mail@maciej.szmigiero.name> Link: https://patch.msgid.link/e60287ebdb0ab54c4075071b72568a40a75d0205.1736372610.git.mail@maciej.szmigiero.name Signed-off-by: Jakub Kicinski <kuba@kernel.org>
1 parent 1da742e commit 0b6f659

File tree

1 file changed

+55
-1
lines changed

1 file changed

+55
-1
lines changed

drivers/net/wwan/iosm/iosm_ipc_pcie.c

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/acpi.h>
77
#include <linux/bitfield.h>
88
#include <linux/module.h>
9+
#include <linux/suspend.h>
910
#include <net/rtnetlink.h>
1011

1112
#include "iosm_ipc_imem.h"
@@ -18,6 +19,7 @@ MODULE_LICENSE("GPL v2");
1819
/* WWAN GUID */
1920
static guid_t wwan_acpi_guid = GUID_INIT(0xbad01b75, 0x22a8, 0x4f48, 0x87, 0x92,
2021
0xbd, 0xde, 0x94, 0x67, 0x74, 0x7d);
22+
static bool pci_registered;
2123

2224
static void ipc_pcie_resources_release(struct iosm_pcie *ipc_pcie)
2325
{
@@ -448,7 +450,6 @@ static struct pci_driver iosm_ipc_driver = {
448450
},
449451
.id_table = iosm_ipc_ids,
450452
};
451-
module_pci_driver(iosm_ipc_driver);
452453

453454
int ipc_pcie_addr_map(struct iosm_pcie *ipc_pcie, unsigned char *data,
454455
size_t size, dma_addr_t *mapping, int direction)
@@ -530,3 +531,56 @@ void ipc_pcie_kfree_skb(struct iosm_pcie *ipc_pcie, struct sk_buff *skb)
530531
IPC_CB(skb)->mapping = 0;
531532
dev_kfree_skb(skb);
532533
}
534+
535+
static int pm_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
536+
{
537+
if (mode == PM_HIBERNATION_PREPARE || mode == PM_RESTORE_PREPARE) {
538+
if (pci_registered) {
539+
pci_unregister_driver(&iosm_ipc_driver);
540+
pci_registered = false;
541+
}
542+
} else if (mode == PM_POST_HIBERNATION || mode == PM_POST_RESTORE) {
543+
if (!pci_registered) {
544+
int ret;
545+
546+
ret = pci_register_driver(&iosm_ipc_driver);
547+
if (ret) {
548+
pr_err(KBUILD_MODNAME ": unable to re-register PCI driver: %d\n",
549+
ret);
550+
} else {
551+
pci_registered = true;
552+
}
553+
}
554+
}
555+
556+
return 0;
557+
}
558+
559+
static struct notifier_block pm_notifier = {
560+
.notifier_call = pm_notify,
561+
};
562+
563+
static int __init iosm_ipc_driver_init(void)
564+
{
565+
int ret;
566+
567+
ret = pci_register_driver(&iosm_ipc_driver);
568+
if (ret)
569+
return ret;
570+
571+
pci_registered = true;
572+
573+
register_pm_notifier(&pm_notifier);
574+
575+
return 0;
576+
}
577+
module_init(iosm_ipc_driver_init);
578+
579+
static void __exit iosm_ipc_driver_exit(void)
580+
{
581+
unregister_pm_notifier(&pm_notifier);
582+
583+
if (pci_registered)
584+
pci_unregister_driver(&iosm_ipc_driver);
585+
}
586+
module_exit(iosm_ipc_driver_exit);

0 commit comments

Comments
 (0)