Commit d785ed94 authored by Jinjian Song's avatar Jinjian Song Committed by David S. Miller
Browse files

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: default avatarJinjian Song <jinjian.song@fibocom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4d36b2b1
Loading
Loading
Loading
Loading
+40 −7
Original line number Diff line number Diff line
@@ -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;
+7 −2
Original line number Diff line number Diff line
@@ -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__ */
+43 −10
Original line number Diff line number Diff line
@@ -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 ||
+3 −0
Original line number Diff line number Diff line
@@ -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__ */
+0 −1
Original line number Diff line number Diff line
@@ -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;
}
Loading