net: wwan: t7xx: Add sysfs attribute for device state machine
authorJinjian Song <jinjian.song@fibocom.com>
Mon, 5 Feb 2024 10:22:28 +0000 (18:22 +0800)
committerDavid S. Miller <davem@davemloft.net>
Fri, 9 Feb 2024 12:07:48 +0000 (12:07 +0000)
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: Jinjian Song <jinjian.song@fibocom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Documentation/networking/device_drivers/wwan/t7xx.rst
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_state_monitor.c

index dd5b731957ca40948048c284b9c022d12b882492..8429b992734177f030da74990c2d67bcbe80f5b0 100644 (file)
@@ -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
index 24e7d491468e0a5232bce89ea6faaea2aae89cf8..ca262d2961ed7f8d65f276c9a6bdccb01b0a65f9 100644 (file)
@@ -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;
index abe633cf7adc01816cedf3a54dbe026458ccea7f..b39e945a92e01720572486c0781c42a54e6d18dc 100644 (file)
@@ -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__ */
index 91256e005b846f17f869d62ceb6ac645d698c5fc..f99eb21cb8ccb0b710521f1a32b292f6c511eaef 100644 (file)
 #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++) {
index f08f1ab7446917b0b84f053fe31a0f9902d76f90..49a11586d8d84406313d58c71b5b57eac2c2821a 100644 (file)
@@ -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__ */
index 0bc97430211bf2e79b8b48826db898c4d0c80fa3..c5d46f45fa6237f3051e2c91816cf1d217c94dce 100644 (file)
@@ -272,6 +272,7 @@ static void fsm_routine_ready(struct t7xx_fsm_ctl *ctl)
 
        ctl->curr_state = FSM_STATE_READY;
        t7xx_fsm_broadcast_ready_state(ctl);
+       t7xx_mode_update(md->t7xx_dev, T7XX_READY);
        t7xx_md_event_notify(md, FSM_READY);
 }