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

net: wwan: t7xx: Add sysfs attribute for device state machine



Add support for userspace to get/set the device mode, device's state
machine changes between (unknown/ready/reset/fastboot).

Get the device state mode:
 - 'cat /sys/bus/pci/devices/${bdf}/t7xx_mode'

Set the device state mode:
 - reset(cold reset): 'echo reset > /sys/bus/pci/devices/${bdf}/t7xx_mode'
 - fastboot: 'echo fastboot_switching > /sys/bus/pci/devices/${bdf}/t7xx_mode'
Reload driver to get the new device state after setting operation.

Signed-off-by: default avatarJinjian Song <jinjian.song@fibocom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e3caf184
Loading
Loading
Loading
Loading
+28 −0
Original line number Diff line number Diff line
@@ -39,6 +39,34 @@ command and receive response:

- open the AT control channel using a UART tool or a special user tool

Sysfs
=====
The driver provides sysfs interfaces to userspace.

t7xx_mode
---------
The sysfs interface provides userspace with access to the device mode, this interface
supports read and write operations.

Device mode:

- ``unknown`` represents that device in unknown status
- ``ready`` represents that device in ready status
- ``reset`` represents that device in reset status
- ``fastboot_switching`` represents that device in fastboot switching status
- ``fastboot_download`` represents that device in fastboot download status
- ``fastboot_dump`` represents that device in fastboot dump status

Read from userspace to get the current device mode.

::
  $ cat /sys/bus/pci/devices/${bdf}/t7xx_mode

Write from userspace to set the device mode.

::
  $ echo fastboot_switching > /sys/bus/pci/devices/${bdf}/t7xx_mode

Management application development
==================================
The driver and userspace interfaces are described below. The MBIM protocol is
+6 −0
Original line number Diff line number Diff line
@@ -177,6 +177,11 @@ int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev)
	return t7xx_acpi_reset(t7xx_dev, "_RST");
}

int t7xx_acpi_pldr_func(struct t7xx_pci_dev *t7xx_dev)
{
	return t7xx_acpi_reset(t7xx_dev, "MRST._RST");
}

static void t7xx_reset_device_via_pmic(struct t7xx_pci_dev *t7xx_dev)
{
	u32 val;
@@ -192,6 +197,7 @@ 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;
+1 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@ 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_pci_mhccif_isr(struct t7xx_pci_dev *t7xx_dev);

#endif	/* __T7XX_MODEM_OPS_H__ */
+96 −5
Original line number Diff line number Diff line
@@ -52,6 +52,81 @@
#define PM_RESOURCE_POLL_TIMEOUT_US	10000
#define PM_RESOURCE_POLL_STEP_US	100

static const char * const t7xx_mode_names[] = {
	[T7XX_UNKNOWN] = "unknown",
	[T7XX_READY] = "ready",
	[T7XX_RESET] = "reset",
	[T7XX_FASTBOOT_SWITCHING] = "fastboot_switching",
	[T7XX_FASTBOOT_DOWNLOAD] = "fastboot_download",
	[T7XX_FASTBOOT_DUMP] = "fastboot_dump",
};

static_assert(ARRAY_SIZE(t7xx_mode_names) == T7XX_MODE_LAST);

static ssize_t t7xx_mode_store(struct device *dev,
			       struct device_attribute *attr,
			       const char *buf, size_t count)
{
	struct t7xx_pci_dev *t7xx_dev;
	struct pci_dev *pdev;
	int index = 0;

	pdev = to_pci_dev(dev);
	t7xx_dev = pci_get_drvdata(pdev);
	if (!t7xx_dev)
		return -ENODEV;

	index = sysfs_match_string(t7xx_mode_names, buf);
	if (index == T7XX_FASTBOOT_SWITCHING) {
		WRITE_ONCE(t7xx_dev->mode, T7XX_FASTBOOT_SWITCHING);
	} else if (index == T7XX_RESET) {
		WRITE_ONCE(t7xx_dev->mode, T7XX_RESET);
		t7xx_acpi_pldr_func(t7xx_dev);
	}

	return count;
};

static ssize_t t7xx_mode_show(struct device *dev,
			      struct device_attribute *attr,
			      char *buf)
{
	enum t7xx_mode mode = T7XX_UNKNOWN;
	struct t7xx_pci_dev *t7xx_dev;
	struct pci_dev *pdev;

	pdev = to_pci_dev(dev);
	t7xx_dev = pci_get_drvdata(pdev);
	if (!t7xx_dev)
		return -ENODEV;

	mode = READ_ONCE(t7xx_dev->mode);
	if (mode < T7XX_MODE_LAST)
		return sysfs_emit(buf, "%s\n", t7xx_mode_names[mode]);

	return sysfs_emit(buf, "%s\n", t7xx_mode_names[T7XX_UNKNOWN]);
}

static DEVICE_ATTR_RW(t7xx_mode);

static struct attribute *t7xx_mode_attr[] = {
	&dev_attr_t7xx_mode.attr,
	NULL
};

static const struct attribute_group t7xx_mode_attribute_group = {
	.attrs = t7xx_mode_attr,
};

void t7xx_mode_update(struct t7xx_pci_dev *t7xx_dev, enum t7xx_mode mode)
{
	if (!t7xx_dev)
		return;

	WRITE_ONCE(t7xx_dev->mode, mode);
	sysfs_notify(&t7xx_dev->pdev->dev.kobj, NULL, "t7xx_mode");
}

enum t7xx_pm_state {
	MTK_PM_EXCEPTION,
	MTK_PM_INIT,		/* Device initialized, but handshake not completed */
@@ -279,7 +354,8 @@ static int __t7xx_pci_pm_suspend(struct pci_dev *pdev)
	int ret;

	t7xx_dev = pci_get_drvdata(pdev);
	if (atomic_read(&t7xx_dev->md_pm_state) <= MTK_PM_INIT) {
	if (atomic_read(&t7xx_dev->md_pm_state) <= MTK_PM_INIT ||
	    READ_ONCE(t7xx_dev->mode) != T7XX_READY) {
		dev_err(&pdev->dev, "[PM] Exiting suspend, modem in invalid state\n");
		return -EFAULT;
	}
@@ -729,16 +805,28 @@ static int t7xx_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)

	t7xx_pcie_mac_interrupts_dis(t7xx_dev);

	ret = sysfs_create_group(&t7xx_dev->pdev->dev.kobj,
				 &t7xx_mode_attribute_group);
	if (ret)
		goto err_md_exit;

	ret = t7xx_interrupt_init(t7xx_dev);
	if (ret) {
		t7xx_md_exit(t7xx_dev);
		return ret;
	}
	if (ret)
		goto err_remove_group;


	t7xx_pcie_mac_set_int(t7xx_dev, MHCCIF_INT);
	t7xx_pcie_mac_interrupts_en(t7xx_dev);

	return 0;

err_remove_group:
	sysfs_remove_group(&t7xx_dev->pdev->dev.kobj,
			   &t7xx_mode_attribute_group);

err_md_exit:
	t7xx_md_exit(t7xx_dev);
	return ret;
}

static void t7xx_pci_remove(struct pci_dev *pdev)
@@ -747,6 +835,9 @@ static void t7xx_pci_remove(struct pci_dev *pdev)
	int i;

	t7xx_dev = pci_get_drvdata(pdev);

	sysfs_remove_group(&t7xx_dev->pdev->dev.kobj,
			   &t7xx_mode_attribute_group);
	t7xx_md_exit(t7xx_dev);

	for (i = 0; i < EXT_INT_NUM; i++) {
+13 −1
Original line number Diff line number Diff line
@@ -43,6 +43,16 @@ struct t7xx_addr_base {

typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param);

enum t7xx_mode {
	T7XX_UNKNOWN,
	T7XX_READY,
	T7XX_RESET,
	T7XX_FASTBOOT_SWITCHING,
	T7XX_FASTBOOT_DOWNLOAD,
	T7XX_FASTBOOT_DUMP,
	T7XX_MODE_LAST, /* must always be last */
};

/* struct t7xx_pci_dev - MTK device context structure
 * @intr_handler: array of handler function for request_threaded_irq
 * @intr_thread: array of thread_fn for request_threaded_irq
@@ -59,6 +69,7 @@ typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param);
 * @md_pm_lock: protects PCIe sleep lock
 * @sleep_disable_count: PCIe L1.2 lock counter
 * @sleep_lock_acquire: indicates that sleep has been disabled
 * @mode: indicates the device mode
 */
struct t7xx_pci_dev {
	t7xx_intr_callback	intr_handler[EXT_INT_NUM];
@@ -82,6 +93,7 @@ struct t7xx_pci_dev {
#ifdef CONFIG_WWAN_DEBUGFS
	struct dentry		*debugfs_dir;
#endif
	u32			mode;
};

enum t7xx_pm_id {
@@ -120,5 +132,5 @@ int t7xx_pci_pm_entity_register(struct t7xx_pci_dev *t7xx_dev, struct md_pm_enti
int t7xx_pci_pm_entity_unregister(struct t7xx_pci_dev *t7xx_dev, struct md_pm_entity *pm_entity);
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);
#endif /* __T7XX_PCI_H__ */
Loading