Commit 140424d9 authored by Haijun Liu's avatar Haijun Liu Committed by David S. Miller
Browse files

net: wwan: t7xx: PCIe reset rescan



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.

Signed-off-by: default avatarHaijun Liu <haijun.liu@mediatek.com>
Co-developed-by: default avatarMadhusmita Sahu <madhusmita.sahu@intel.com>
Signed-off-by: default avatarMadhusmita Sahu <madhusmita.sahu@intel.com>
Signed-off-by: default avatarRicardo Martinez <ricardo.martinez@linux.intel.com>
Signed-off-by: default avatarM Chetan Kumar <m.chetan.kumar@linux.intel.com>
Signed-off-by: default avatarDevegowda Chandrashekar <chandrashekar.devegowda@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 007f26f0
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -17,4 +17,5 @@ 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
+5 −0
Original line number Diff line number Diff line
@@ -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"
@@ -192,6 +193,10 @@ static irqreturn_t t7xx_rgu_isr_thread(int irq, void *data)

	msleep(RGU_RESET_DELAY_MS);
	t7xx_reset_device_via_pmic(t7xx_dev);

	if (!t7xx_dev->hp_enable)
		t7xx_rescan_queue_work(t7xx_dev->pdev);

	return IRQ_HANDLED;
}

+50 −1
Original line number Diff line number Diff line
@@ -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"
@@ -715,8 +716,11 @@ 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);
	if (!t7xx_dev->hp_enable)
		pci_ignore_hotplug(pdev);

	return 0;
}
@@ -754,7 +758,52 @@ 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)
{
	return dev->parent->fwnode == data;
}

static void __exit t7xx_pci_cleanup(void)
{
	int remove_flag = 0;
	struct device *dev;

	dev = driver_find_device(&t7xx_pci_driver.driver, NULL, NULL, 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");
+1 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ struct t7xx_pci_dev {
	struct t7xx_modem	*md;
	struct t7xx_ccmni_ctrl	*ccmni_ctlb;
	bool			rgu_pci_irq_en;
	bool			hp_enable;

	/* Low Power Items */
	struct list_head	md_pm_entities;
+117 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2021, MediaTek Inc.
 * Copyright (c) 2021-2022, 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 g_mtk_rescan_context;

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)
{
	unsigned long flags;

	spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
	if (g_mtk_rescan_context.rescan_done == 0) {
		pr_debug("this is a rescan probe\n");
		g_mtk_rescan_context.rescan_done = 1;
	} else {
		pr_debug("this is a init probe\n");
	}
	spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
}

static void t7xx_remove_rescan(struct work_struct *work)
{
	struct pci_dev *pdev;
	int num_retries = RESCAN_RETRIES;
	unsigned long flags;

	spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
	g_mtk_rescan_context.rescan_done = 0;
	pdev = g_mtk_rescan_context.dev;
	spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);

	if (pdev) {
		pci_stop_and_remove_bus_device_locked(pdev);
		pr_debug("start remove and rescan flow\n");
	}

	do {
		t7xx_pci_dev_rescan();
		spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
		if (g_mtk_rescan_context.rescan_done) {
			spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
			break;
		}

		spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
		msleep(DELAY_RESCAN_MTIME);
	} while (num_retries--);
}

void t7xx_rescan_queue_work(struct pci_dev *pdev)
{
	unsigned long flags;

	dev_info(&pdev->dev, "start queue_mtk_rescan_work\n");
	spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
	if (!g_mtk_rescan_context.rescan_done) {
		dev_err(&pdev->dev, "rescan failed because last rescan undone\n");
		spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
		return;
	}

	g_mtk_rescan_context.dev = pdev;
	spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
	queue_work(g_mtk_rescan_context.pcie_rescan_wq, &g_mtk_rescan_context.service_task);
}

int t7xx_rescan_init(void)
{
	spin_lock_init(&g_mtk_rescan_context.dev_lock);
	g_mtk_rescan_context.rescan_done = 1;
	g_mtk_rescan_context.dev = NULL;
	g_mtk_rescan_context.pcie_rescan_wq = create_singlethread_workqueue(MTK_RESCAN_WQ);
	if (!g_mtk_rescan_context.pcie_rescan_wq) {
		pr_err("Failed to create workqueue: %s\n", MTK_RESCAN_WQ);
		return -ENOMEM;
	}

	INIT_WORK(&g_mtk_rescan_context.service_task, t7xx_remove_rescan);

	return 0;
}

void t7xx_rescan_deinit(void)
{
	unsigned long flags;

	spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
	g_mtk_rescan_context.rescan_done = 0;
	g_mtk_rescan_context.dev = NULL;
	spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
	cancel_work_sync(&g_mtk_rescan_context.service_task);
	destroy_workqueue(g_mtk_rescan_context.pcie_rescan_wq);
}
Loading