UBUNTU: SAUCE: net: wwan: t7xx: PCIe reset rescan

BugLink: https://bugs.launchpad.net/bugs/2002089

PCI rescan module implements "rescan work queue".
In firmware flashing or coredump collection procedure
WWAN device is programmed to boot in fastboot mode and
a work item is scheduled for removal & detection.

The WWAN device is reset using APCI call as part driver
removal flow. Work queue rescans pci bus at fixed interval
for device detection, later when device is detect work queue
exits.

Change-Id: I4bc254ae0eae76c41da75b1b76fcef36ec866e84
Signed-off-by: Haijun Liu <haijun.liu@mediatek.com>
Co-developed-by: Madhusmita Sahu <madhusmita.sahu@intel.com>
Signed-off-by: Madhusmita Sahu <madhusmita.sahu@intel.com>
Signed-off-by: Ricardo Martinez <ricardo.martinez@linux.intel.com>
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
Signed-off-by: Devegowda Chandrashekar <chandrashekar.devegowda@intel.com>
(cherry picked from https://patchwork.kernel.org/project/netdevbpf/patch/20230105154245.198858-1-m.chetan.kumar@linux.intel.com/)
Signed-off-by: You-Sheng Yang (vicamo) <vicamo.yang@canonical.com>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
This commit is contained in:
M Chetan Kumar
2023-01-16 17:15:02 +08:00
committed by Paolo Pisati
parent cce5a4b5a8
commit 8a6a53cd0c
5 changed files with 184 additions and 2 deletions
+2 -1
View File
@@ -15,7 +15,8 @@ mtk_t7xx-y:= t7xx_pci.o \
t7xx_hif_dpmaif_tx.o \
t7xx_hif_dpmaif_rx.o \
t7xx_dpmaif.o \
t7xx_netdev.o
t7xx_netdev.o \
t7xx_pci_rescan.o
mtk_t7xx-$(CONFIG_WWAN_DEBUGFS) += \
t7xx_port_trace.o \
+3
View File
@@ -37,6 +37,7 @@
#include "t7xx_modem_ops.h"
#include "t7xx_netdev.h"
#include "t7xx_pci.h"
#include "t7xx_pci_rescan.h"
#include "t7xx_pcie_mac.h"
#include "t7xx_port.h"
#include "t7xx_port_proxy.h"
@@ -194,6 +195,8 @@ static irqreturn_t t7xx_rgu_isr_thread(int irq, void *data)
msleep(RGU_RESET_DELAY_MS);
t7xx_reset_device_via_pmic(t7xx_dev);
t7xx_rescan_queue_work(t7xx_dev->pdev);
return IRQ_HANDLED;
}
+55 -1
View File
@@ -38,6 +38,7 @@
#include "t7xx_mhccif.h"
#include "t7xx_modem_ops.h"
#include "t7xx_pci.h"
#include "t7xx_pci_rescan.h"
#include "t7xx_pcie_mac.h"
#include "t7xx_reg.h"
#include "t7xx_state_monitor.h"
@@ -735,6 +736,7 @@ static int t7xx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return ret;
}
t7xx_rescan_done();
t7xx_pcie_mac_set_int(t7xx_dev, MHCCIF_INT);
t7xx_pcie_mac_interrupts_en(t7xx_dev);
@@ -774,7 +776,59 @@ static struct pci_driver t7xx_pci_driver = {
.shutdown = t7xx_pci_shutdown,
};
module_pci_driver(t7xx_pci_driver);
static int __init t7xx_pci_init(void)
{
int ret;
t7xx_pci_dev_rescan();
ret = t7xx_rescan_init();
if (ret) {
pr_err("Failed to init t7xx rescan work\n");
return ret;
}
return pci_register_driver(&t7xx_pci_driver);
}
module_init(t7xx_pci_init);
static int t7xx_always_match(struct device *dev, const void *data)
{
struct pci_dev *pdev = to_pci_dev(dev);
const struct pci_device_id *id = data;
if (pci_match_id(id, pdev))
return 1;
return 0;
}
static void __exit t7xx_pci_cleanup(void)
{
int remove_flag = 0;
struct device *dev;
dev = driver_find_device(&t7xx_pci_driver.driver, NULL, &t7xx_pci_table[0],
t7xx_always_match);
if (dev) {
pr_debug("unregister t7xx PCIe driver while device is still exist.\n");
put_device(dev);
remove_flag = 1;
} else {
pr_debug("no t7xx PCIe driver found.\n");
}
pci_lock_rescan_remove();
pci_unregister_driver(&t7xx_pci_driver);
pci_unlock_rescan_remove();
t7xx_rescan_deinit();
if (remove_flag) {
pr_debug("remove t7xx PCI device\n");
pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
}
}
module_exit(t7xx_pci_cleanup);
MODULE_AUTHOR("MediaTek Inc");
MODULE_DESCRIPTION("MediaTek PCIe 5G WWAN modem T7xx driver");
+96
View File
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021, MediaTek Inc.
* Copyright (c) 2021-2023, Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":t7xx:%s: " fmt, __func__
#define dev_fmt(fmt) "t7xx: " fmt
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include "t7xx_pci.h"
#include "t7xx_pci_rescan.h"
static struct remove_rescan_context t7xx_rescan_ctx;
void t7xx_pci_dev_rescan(void)
{
struct pci_bus *b = NULL;
pci_lock_rescan_remove();
while ((b = pci_find_next_bus(b)))
pci_rescan_bus(b);
pci_unlock_rescan_remove();
}
void t7xx_rescan_done(void)
{
if (!atomic_read(&t7xx_rescan_ctx.rescan_done)) {
atomic_set(&t7xx_rescan_ctx.rescan_done, 1);
pr_debug("Rescan probe\n");
} else {
pr_debug("Init probe\n");
}
}
static void t7xx_remove_rescan(struct work_struct *work)
{
int num_retries = RESCAN_RETRIES;
struct pci_dev *pdev;
atomic_set(&t7xx_rescan_ctx.rescan_done, 0);
pdev = t7xx_rescan_ctx.dev;
if (pdev) {
pci_stop_and_remove_bus_device_locked(pdev);
pr_debug("start remove and rescan flow\n");
}
do {
t7xx_pci_dev_rescan();
if (atomic_read(&t7xx_rescan_ctx.rescan_done))
break;
msleep(DELAY_RESCAN_MTIME);
} while (num_retries--);
}
void t7xx_rescan_queue_work(struct pci_dev *pdev)
{
if (!atomic_read(&t7xx_rescan_ctx.rescan_done)) {
dev_err(&pdev->dev, "Rescan failed\n");
return;
}
t7xx_rescan_ctx.dev = pdev;
queue_work(t7xx_rescan_ctx.pcie_rescan_wq, &t7xx_rescan_ctx.service_task);
}
int t7xx_rescan_init(void)
{
atomic_set(&t7xx_rescan_ctx.rescan_done, 1);
t7xx_rescan_ctx.dev = NULL;
t7xx_rescan_ctx.pcie_rescan_wq = create_singlethread_workqueue(MTK_RESCAN_WQ);
if (!t7xx_rescan_ctx.pcie_rescan_wq) {
pr_err("Failed to create workqueue: %s\n", MTK_RESCAN_WQ);
return -ENOMEM;
}
INIT_WORK(&t7xx_rescan_ctx.service_task, t7xx_remove_rescan);
return 0;
}
void t7xx_rescan_deinit(void)
{
t7xx_rescan_ctx.dev = NULL;
atomic_set(&t7xx_rescan_ctx.rescan_done, 0);
cancel_work_sync(&t7xx_rescan_ctx.service_task);
destroy_workqueue(t7xx_rescan_ctx.pcie_rescan_wq);
}
+28
View File
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2021, MediaTek Inc.
* Copyright (c) 2021-2023, Intel Corporation.
*/
#ifndef __T7XX_PCI_RESCAN_H__
#define __T7XX_PCI_RESCAN_H__
#define MTK_RESCAN_WQ "mtk_rescan_wq"
#define DELAY_RESCAN_MTIME 1000
#define RESCAN_RETRIES 35
struct remove_rescan_context {
struct work_struct service_task;
struct workqueue_struct *pcie_rescan_wq;
struct pci_dev *dev;
atomic_t rescan_done;
};
void t7xx_pci_dev_rescan(void);
void t7xx_rescan_queue_work(struct pci_dev *pdev);
int t7xx_rescan_init(void);
void t7xx_rescan_deinit(void);
void t7xx_rescan_done(void);
#endif /* __T7XX_PCI_RESCAN_H__ */