mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 06:33:07 +00:00
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: Jinjian Song <jinjian.song@fibocom.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
e3caf18410
commit
409c38d4f1
6 changed files with 145 additions and 6 deletions
|
@ -39,6 +39,34 @@ command and receive response:
|
||||||
|
|
||||||
- open the AT control channel using a UART tool or a special user tool
|
- 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
|
Management application development
|
||||||
==================================
|
==================================
|
||||||
The driver and userspace interfaces are described below. The MBIM protocol is
|
The driver and userspace interfaces are described below. The MBIM protocol is
|
||||||
|
|
|
@ -177,6 +177,11 @@ int t7xx_acpi_fldr_func(struct t7xx_pci_dev *t7xx_dev)
|
||||||
return t7xx_acpi_reset(t7xx_dev, "_RST");
|
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)
|
static void t7xx_reset_device_via_pmic(struct t7xx_pci_dev *t7xx_dev)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -192,6 +197,7 @@ static irqreturn_t t7xx_rgu_isr_thread(int irq, void *data)
|
||||||
{
|
{
|
||||||
struct t7xx_pci_dev *t7xx_dev = data;
|
struct t7xx_pci_dev *t7xx_dev = data;
|
||||||
|
|
||||||
|
t7xx_mode_update(t7xx_dev, T7XX_RESET);
|
||||||
msleep(RGU_RESET_DELAY_MS);
|
msleep(RGU_RESET_DELAY_MS);
|
||||||
t7xx_reset_device_via_pmic(t7xx_dev);
|
t7xx_reset_device_via_pmic(t7xx_dev);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
|
|
@ -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_md_exit(struct t7xx_pci_dev *t7xx_dev);
|
||||||
void t7xx_clear_rgu_irq(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_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);
|
int t7xx_pci_mhccif_isr(struct t7xx_pci_dev *t7xx_dev);
|
||||||
|
|
||||||
#endif /* __T7XX_MODEM_OPS_H__ */
|
#endif /* __T7XX_MODEM_OPS_H__ */
|
||||||
|
|
|
@ -52,6 +52,81 @@
|
||||||
#define PM_RESOURCE_POLL_TIMEOUT_US 10000
|
#define PM_RESOURCE_POLL_TIMEOUT_US 10000
|
||||||
#define PM_RESOURCE_POLL_STEP_US 100
|
#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 {
|
enum t7xx_pm_state {
|
||||||
MTK_PM_EXCEPTION,
|
MTK_PM_EXCEPTION,
|
||||||
MTK_PM_INIT, /* Device initialized, but handshake not completed */
|
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;
|
int ret;
|
||||||
|
|
||||||
t7xx_dev = pci_get_drvdata(pdev);
|
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");
|
dev_err(&pdev->dev, "[PM] Exiting suspend, modem in invalid state\n");
|
||||||
return -EFAULT;
|
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);
|
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);
|
ret = t7xx_interrupt_init(t7xx_dev);
|
||||||
if (ret) {
|
if (ret)
|
||||||
t7xx_md_exit(t7xx_dev);
|
goto err_remove_group;
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
t7xx_pcie_mac_set_int(t7xx_dev, MHCCIF_INT);
|
t7xx_pcie_mac_set_int(t7xx_dev, MHCCIF_INT);
|
||||||
t7xx_pcie_mac_interrupts_en(t7xx_dev);
|
t7xx_pcie_mac_interrupts_en(t7xx_dev);
|
||||||
|
|
||||||
return 0;
|
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)
|
static void t7xx_pci_remove(struct pci_dev *pdev)
|
||||||
|
@ -747,6 +835,9 @@ static void t7xx_pci_remove(struct pci_dev *pdev)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
t7xx_dev = pci_get_drvdata(pdev);
|
t7xx_dev = pci_get_drvdata(pdev);
|
||||||
|
|
||||||
|
sysfs_remove_group(&t7xx_dev->pdev->dev.kobj,
|
||||||
|
&t7xx_mode_attribute_group);
|
||||||
t7xx_md_exit(t7xx_dev);
|
t7xx_md_exit(t7xx_dev);
|
||||||
|
|
||||||
for (i = 0; i < EXT_INT_NUM; i++) {
|
for (i = 0; i < EXT_INT_NUM; i++) {
|
||||||
|
|
|
@ -43,6 +43,16 @@ struct t7xx_addr_base {
|
||||||
|
|
||||||
typedef irqreturn_t (*t7xx_intr_callback)(int irq, void *param);
|
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
|
/* struct t7xx_pci_dev - MTK device context structure
|
||||||
* @intr_handler: array of handler function for request_threaded_irq
|
* @intr_handler: array of handler function for request_threaded_irq
|
||||||
* @intr_thread: array of thread_fn 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
|
* @md_pm_lock: protects PCIe sleep lock
|
||||||
* @sleep_disable_count: PCIe L1.2 lock counter
|
* @sleep_disable_count: PCIe L1.2 lock counter
|
||||||
* @sleep_lock_acquire: indicates that sleep has been disabled
|
* @sleep_lock_acquire: indicates that sleep has been disabled
|
||||||
|
* @mode: indicates the device mode
|
||||||
*/
|
*/
|
||||||
struct t7xx_pci_dev {
|
struct t7xx_pci_dev {
|
||||||
t7xx_intr_callback intr_handler[EXT_INT_NUM];
|
t7xx_intr_callback intr_handler[EXT_INT_NUM];
|
||||||
|
@ -82,6 +93,7 @@ struct t7xx_pci_dev {
|
||||||
#ifdef CONFIG_WWAN_DEBUGFS
|
#ifdef CONFIG_WWAN_DEBUGFS
|
||||||
struct dentry *debugfs_dir;
|
struct dentry *debugfs_dir;
|
||||||
#endif
|
#endif
|
||||||
|
u32 mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum t7xx_pm_id {
|
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);
|
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_init_late(struct t7xx_pci_dev *t7xx_dev);
|
||||||
void t7xx_pci_pm_exp_detected(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__ */
|
#endif /* __T7XX_PCI_H__ */
|
||||||
|
|
|
@ -272,6 +272,7 @@ static void fsm_routine_ready(struct t7xx_fsm_ctl *ctl)
|
||||||
|
|
||||||
ctl->curr_state = FSM_STATE_READY;
|
ctl->curr_state = FSM_STATE_READY;
|
||||||
t7xx_fsm_broadcast_ready_state(ctl);
|
t7xx_fsm_broadcast_ready_state(ctl);
|
||||||
|
t7xx_mode_update(md->t7xx_dev, T7XX_READY);
|
||||||
t7xx_md_event_notify(md, FSM_READY);
|
t7xx_md_event_notify(md, FSM_READY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue