blob: 8d9a0d984befb707c04181ed3a8455b99c6bf72f [file] [log] [blame]
// 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/acpi.h>
#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;
#ifdef CONFIG_ACPI
acpi_status status;
acpi_handle handle;
bool cold_reboot;
#endif
spin_lock_irqsave(&g_mtk_rescan_context.dev_lock, flags);
g_mtk_rescan_context.rescan_done = 0;
#ifdef CONFIG_ACPI
cold_reboot = g_mtk_rescan_context.cold_reboot;
#endif
pdev = g_mtk_rescan_context.dev;
spin_unlock_irqrestore(&g_mtk_rescan_context.dev_lock, flags);
if (pdev) {
#ifdef CONFIG_ACPI
handle = ACPI_HANDLE(&pdev->dev);
#endif
pr_debug("start remove and rescan flow\n");
pci_stop_and_remove_bus_device_locked(pdev);
#ifdef CONFIG_ACPI
if (cold_reboot) {
pr_info("Performing cold modem reboot\n");
status = acpi_execute_simple_method(handle, "FHRF", 1);
if (ACPI_FAILURE(status)) {
dev_err(&pdev->dev, "Failed to call _FHRF: %d\n",
status);
}
status = acpi_evaluate_object(handle, "SHRF", NULL, NULL);
if (ACPI_FAILURE(status)) {
dev_err(&pdev->dev, "Failed to call _SHRF: %d\n",
status);
}
}
#endif
}
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, bool cold_reboot)
{
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;
g_mtk_rescan_context.cold_reboot = cold_reboot;
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);
}