net: wwan: t7xx: PCIe reset rescan
authorJinjian Song <jinjian.song@fibocom.com>
Sat, 17 Aug 2024 08:33:55 +0000 (16:33 +0800)
committerDavid S. Miller <davem@davemloft.net>
Wed, 21 Aug 2024 11:57:24 +0000 (12:57 +0100)
WWAN device is programmed to boot in normal mode or fastboot mode,
when triggering a device reset through ACPI call or fastboot switch
command. Maintain state machine synchronization and reprobe logic
after a device reset.

The PCIe device reset triggered by several ways.
E.g.:
 - fastboot: echo "fastboot_switching" > /sys/bus/pci/devices/${bdf}/t7xx_mode.
 - reset: echo "reset" > /sys/bus/pci/devices/${bdf}/t7xx_mode.
 - IRQ: PCIe device request driver to reset itself by an interrupt request.

Use pci_reset_function() as a generic way to reset device, save and
restore the PCIe configuration before and after reset device to ensure
the reprobe process.

Suggestion from Bjorn:
Link: https://lore.kernel.org/all/20230127133034.GA1364550@bhelgaas/
Signed-off-by: Jinjian Song <jinjian.song@fibocom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/wwan/t7xx/t7xx_modem_ops.c
drivers/net/wwan/t7xx/t7xx_modem_ops.h
drivers/net/wwan/t7xx/t7xx_pci.c
drivers/net/wwan/t7xx/t7xx_pci.h
drivers/net/wwan/t7xx/t7xx_port_proxy.c
drivers/net/wwan/t7xx/t7xx_port_trace.c
drivers/net/wwan/t7xx/t7xx_state_monitor.c

index 8d864d4..79f1710 100644 (file)
@@ -53,6 +53,7 @@
 
 #define RGU_RESET_DELAY_MS     10
 #define PORT_RESET_DELAY_MS    2000
+#define FASTBOOT_RESET_DELAY_MS        2000
 #define EX_HS_TIMEOUT_MS       5000
 #define EX_HS_POLL_DELAY_MS    10
 
@@ -167,19 +168,52 @@ static int t7xx_acpi_reset(struct t7xx_pci_dev *t7xx_dev, char *fn_name)
        }
 
        kfree(buffer.pointer);
+#else
+       struct device *dev = &t7xx_dev->pdev->dev;
+       int ret;
 
+       ret = pci_reset_function(t7xx_dev->pdev);
+       if (ret) {
+               dev_err(dev, "Failed to reset device, error:%d\n", ret);
+               return ret;
+       }
 #endif
        return 0;
 }
 
-int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev)
+static void t7xx_host_event_notify(struct t7xx_pci_dev *t7xx_dev, unsigned int event_id)
 {
-       return t7xx_acpi_reset(t7xx_dev, "_RST");
+       u32 value;
+
+       value = ioread32(IREG_BASE(t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
+       value &= ~HOST_EVENT_MASK;
+       value |= FIELD_PREP(HOST_EVENT_MASK, event_id);
+       iowrite32(value, IREG_BASE(t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
 }
 
-int t7xx_acpi_pldr_func(struct t7xx_pci_dev *t7xx_dev)
+int t7xx_reset_device(struct t7xx_pci_dev *t7xx_dev, enum reset_type type)
 {
-       return t7xx_acpi_reset(t7xx_dev, "MRST._RST");
+       int ret = 0;
+
+       pci_save_state(t7xx_dev->pdev);
+       t7xx_pci_reprobe_early(t7xx_dev);
+       t7xx_mode_update(t7xx_dev, T7XX_RESET);
+
+       if (type == FLDR) {
+               ret = t7xx_acpi_reset(t7xx_dev, "_RST");
+       } else if (type == PLDR) {
+               ret = t7xx_acpi_reset(t7xx_dev, "MRST._RST");
+       } else if (type == FASTBOOT) {
+               t7xx_host_event_notify(t7xx_dev, FASTBOOT_DL_NOTIFY);
+               t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET);
+               msleep(FASTBOOT_RESET_DELAY_MS);
+       }
+
+       pci_restore_state(t7xx_dev->pdev);
+       if (ret)
+               return ret;
+
+       return t7xx_pci_reprobe(t7xx_dev, true);
 }
 
 static void t7xx_reset_device_via_pmic(struct t7xx_pci_dev *t7xx_dev)
@@ -188,16 +222,15 @@ static void t7xx_reset_device_via_pmic(struct t7xx_pci_dev *t7xx_dev)
 
        val = ioread32(IREG_BASE(t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
        if (val & MISC_RESET_TYPE_PLDR)
-               t7xx_acpi_reset(t7xx_dev, "MRST._RST");
+               t7xx_reset_device(t7xx_dev, PLDR);
        else if (val & MISC_RESET_TYPE_FLDR)
-               t7xx_acpi_fldr_func(t7xx_dev);
+               t7xx_reset_device(t7xx_dev, FLDR);
 }
 
 static irqreturn_t t7xx_rgu_isr_thread(int irq, void *data)
 {
        struct t7xx_pci_dev *t7xx_dev = data;
 
-       t7xx_mode_update(t7xx_dev, T7XX_RESET);
        msleep(RGU_RESET_DELAY_MS);
        t7xx_reset_device_via_pmic(t7xx_dev);
        return IRQ_HANDLED;
index b39e945..39ed000 100644 (file)
@@ -78,14 +78,19 @@ struct t7xx_modem {
        spinlock_t                      exp_lock; /* Protects exception events */
 };
 
+enum reset_type {
+       FLDR,
+       PLDR,
+       FASTBOOT,
+};
+
 void t7xx_md_exception_handshake(struct t7xx_modem *md);
 void t7xx_md_event_notify(struct t7xx_modem *md, enum md_event_id evt_id);
 int t7xx_md_reset(struct t7xx_pci_dev *t7xx_dev);
 int t7xx_md_init(struct t7xx_pci_dev *t7xx_dev);
 void t7xx_md_exit(struct t7xx_pci_dev *t7xx_dev);
 void t7xx_clear_rgu_irq(struct t7xx_pci_dev *t7xx_dev);
-int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev);
-int t7xx_acpi_pldr_func(struct t7xx_pci_dev *t7xx_dev);
+int t7xx_reset_device(struct t7xx_pci_dev *t7xx_dev, enum reset_type type);
 int t7xx_pci_mhccif_isr(struct t7xx_pci_dev *t7xx_dev);
 
 #endif /* __T7XX_MODEM_OPS_H__ */
index 10a8c10..e556e5b 100644 (file)
@@ -69,6 +69,7 @@ static ssize_t t7xx_mode_store(struct device *dev,
 {
        struct t7xx_pci_dev *t7xx_dev;
        struct pci_dev *pdev;
+       enum t7xx_mode mode;
        int index = 0;
 
        pdev = to_pci_dev(dev);
@@ -76,12 +77,22 @@ static ssize_t t7xx_mode_store(struct device *dev,
        if (!t7xx_dev)
                return -ENODEV;
 
+       mode = READ_ONCE(t7xx_dev->mode);
+
        index = sysfs_match_string(t7xx_mode_names, buf);
+       if (index == mode)
+               return -EBUSY;
+
        if (index == T7XX_FASTBOOT_SWITCHING) {
+               if (mode == T7XX_FASTBOOT_DOWNLOAD)
+                       return count;
+
                WRITE_ONCE(t7xx_dev->mode, T7XX_FASTBOOT_SWITCHING);
+               pm_runtime_resume(dev);
+               t7xx_reset_device(t7xx_dev, FASTBOOT);
        } else if (index == T7XX_RESET) {
-               WRITE_ONCE(t7xx_dev->mode, T7XX_RESET);
-               t7xx_acpi_pldr_func(t7xx_dev);
+               pm_runtime_resume(dev);
+               t7xx_reset_device(t7xx_dev, PLDR);
        }
 
        return count;
@@ -446,7 +457,7 @@ static int t7xx_pcie_reinit(struct t7xx_pci_dev *t7xx_dev, bool is_d3)
 
        if (is_d3) {
                t7xx_mhccif_init(t7xx_dev);
-               return t7xx_pci_pm_reinit(t7xx_dev);
+               t7xx_pci_pm_reinit(t7xx_dev);
        }
 
        return 0;
@@ -481,6 +492,33 @@ static int t7xx_send_fsm_command(struct t7xx_pci_dev *t7xx_dev, u32 event)
        return ret;
 }
 
+int t7xx_pci_reprobe_early(struct t7xx_pci_dev *t7xx_dev)
+{
+       enum t7xx_mode mode = READ_ONCE(t7xx_dev->mode);
+       int ret;
+
+       if (mode == T7XX_FASTBOOT_DOWNLOAD)
+               pm_runtime_put_noidle(&t7xx_dev->pdev->dev);
+
+       ret = t7xx_send_fsm_command(t7xx_dev, FSM_CMD_STOP);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+int t7xx_pci_reprobe(struct t7xx_pci_dev *t7xx_dev, bool boot)
+{
+       int ret;
+
+       ret = t7xx_pcie_reinit(t7xx_dev, boot);
+       if (ret)
+               return ret;
+
+       t7xx_clear_rgu_irq(t7xx_dev);
+       return t7xx_send_fsm_command(t7xx_dev, FSM_CMD_START);
+}
+
 static int __t7xx_pci_pm_resume(struct pci_dev *pdev, bool state_check)
 {
        struct t7xx_pci_dev *t7xx_dev;
@@ -507,16 +545,11 @@ static int __t7xx_pci_pm_resume(struct pci_dev *pdev, bool state_check)
                if (prev_state == PM_RESUME_REG_STATE_L3 ||
                    (prev_state == PM_RESUME_REG_STATE_INIT &&
                     atr_reg_val == ATR_SRC_ADDR_INVALID)) {
-                       ret = t7xx_send_fsm_command(t7xx_dev, FSM_CMD_STOP);
-                       if (ret)
-                               return ret;
-
-                       ret = t7xx_pcie_reinit(t7xx_dev, true);
+                       ret = t7xx_pci_reprobe_early(t7xx_dev);
                        if (ret)
                                return ret;
 
-                       t7xx_clear_rgu_irq(t7xx_dev);
-                       return t7xx_send_fsm_command(t7xx_dev, FSM_CMD_START);
+                       return t7xx_pci_reprobe(t7xx_dev, true);
                }
 
                if (prev_state == PM_RESUME_REG_STATE_EXP ||
index 49a1158..cd8ea17 100644 (file)
@@ -133,4 +133,7 @@ int t7xx_pci_pm_entity_unregister(struct t7xx_pci_dev *t7xx_dev, struct md_pm_en
 void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev);
 void t7xx_pci_pm_exp_detected(struct t7xx_pci_dev *t7xx_dev);
 void t7xx_mode_update(struct t7xx_pci_dev *t7xx_dev, enum t7xx_mode mode);
+int t7xx_pci_reprobe(struct t7xx_pci_dev *t7xx_dev, bool boot);
+int t7xx_pci_reprobe_early(struct t7xx_pci_dev *t7xx_dev);
+
 #endif /* __T7XX_PCI_H__ */
index 7d6388b..35743e7 100644 (file)
@@ -553,7 +553,6 @@ static int t7xx_proxy_alloc(struct t7xx_modem *md)
 
        md->port_prox = port_prox;
        port_prox->dev = dev;
-       t7xx_port_proxy_set_cfg(md, PORT_CFG_ID_EARLY);
 
        return 0;
 }
index 6a3f363..4ed8b4e 100644 (file)
@@ -59,6 +59,7 @@ static void t7xx_trace_port_uninit(struct t7xx_port *port)
 
        relay_close(relaych);
        debugfs_remove_recursive(debugfs_dir);
+       port->log.relaych = NULL;
 }
 
 static int t7xx_trace_port_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
index 9889ca4..3931c7a 100644 (file)
@@ -213,16 +213,6 @@ static void fsm_routine_exception(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comm
                fsm_finish_command(ctl, cmd, 0);
 }
 
-static void t7xx_host_event_notify(struct t7xx_modem *md, unsigned int event_id)
-{
-       u32 value;
-
-       value = ioread32(IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
-       value &= ~HOST_EVENT_MASK;
-       value |= FIELD_PREP(HOST_EVENT_MASK, event_id);
-       iowrite32(value, IREG_BASE(md->t7xx_dev) + T7XX_PCIE_MISC_DEV_STATUS);
-}
-
 static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int status)
 {
        struct t7xx_modem *md = ctl->md;
@@ -264,8 +254,14 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
 
 static int fsm_stopped_handler(struct t7xx_fsm_ctl *ctl)
 {
+       enum t7xx_mode mode;
+
        ctl->curr_state = FSM_STATE_STOPPED;
 
+       mode = READ_ONCE(ctl->md->t7xx_dev->mode);
+       if (mode == T7XX_FASTBOOT_DOWNLOAD || mode == T7XX_FASTBOOT_DUMP)
+               return 0;
+
        t7xx_fsm_broadcast_state(ctl, MD_STATE_STOPPED);
        return t7xx_md_reset(ctl->md->t7xx_dev);
 }
@@ -284,8 +280,6 @@ static void fsm_routine_stopping(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comma
 {
        struct cldma_ctrl *md_ctrl = ctl->md->md_ctrl[CLDMA_ID_MD];
        struct t7xx_pci_dev *t7xx_dev = ctl->md->t7xx_dev;
-       enum t7xx_mode mode = READ_ONCE(t7xx_dev->mode);
-       int err;
 
        if (ctl->curr_state == FSM_STATE_STOPPED || ctl->curr_state == FSM_STATE_STOPPING) {
                fsm_finish_command(ctl, cmd, -EINVAL);
@@ -296,21 +290,10 @@ static void fsm_routine_stopping(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_comma
        t7xx_fsm_broadcast_state(ctl, MD_STATE_WAITING_TO_STOP);
        t7xx_cldma_stop(md_ctrl);
 
-       if (mode == T7XX_FASTBOOT_SWITCHING)
-               t7xx_host_event_notify(ctl->md, FASTBOOT_DL_NOTIFY);
-
        t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DRM_DISABLE_AP);
        /* Wait for the DRM disable to take effect */
        msleep(FSM_DRM_DISABLE_DELAY_MS);
 
-       if (mode == T7XX_FASTBOOT_SWITCHING) {
-               t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET);
-       } else {
-               err = t7xx_acpi_fldr_func(t7xx_dev);
-               if (err)
-                       t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_DEVICE_RESET);
-       }
-
        fsm_finish_command(ctl, cmd, fsm_stopped_handler(ctl));
 }
 
@@ -414,7 +397,9 @@ static void fsm_routine_start(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command
 
                case T7XX_DEV_STAGE_LK:
                        dev_dbg(dev, "LK_STAGE Entered\n");
+                       t7xx_port_proxy_set_cfg(md, PORT_CFG_ID_EARLY);
                        t7xx_lk_stage_event_handling(ctl, status);
+
                        break;
 
                case T7XX_DEV_STAGE_LINUX:
@@ -436,6 +421,9 @@ static void fsm_routine_start(struct t7xx_fsm_ctl *ctl, struct t7xx_fsm_command
        }
 
 finish_command:
+       if (ret)
+               t7xx_mode_update(md->t7xx_dev, T7XX_UNKNOWN);
+
        fsm_finish_command(ctl, cmd, ret);
 }