mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
net: wwan: t7xx: PCIe reset rescan
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>
This commit is contained in:
parent
4d36b2b1de
commit
d785ed945d
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
ret = t7xx_pci_reprobe_early(t7xx_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = t7xx_pcie_reinit(t7xx_dev, true);
|
||||
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 ||
|
||||
|
@ -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__ */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user