From 790ab2f4d7246ff64d3a3fa2c5dde9a54f1efc53 Mon Sep 17 00:00:00 2001 From: Ashish Kori Date: Thu, 28 Nov 2019 14:07:01 +0530 Subject: [PATCH] serial: msm_geni_serial: Allow clock_on/off ioctl to be optional Add support in the driver to manage power without enabling pm runtime. UART driver gives control to user space client to vote for clock on/off by exposing ioctls. These vote clock on/off APIs depend on PM runtime framework for power related operations. This change relaxes driver from depending on userspace ioctls to invoke runtime resume/suspend for basic uart functionality which will now be done by system suspend/resume. This will block PM framework API's and use system suspend/resume API for UART on/off. Change-Id: Idf0632c0e2d40907f7a226cd7e3e814d1c1f6751 Signed-off-by: Ashish Kori --- drivers/tty/serial/msm_geni_serial.c | 86 ++++++++++++++++++++++------ 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index 05af56fc5adc..3ae76c939b6d 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -206,6 +206,7 @@ struct msm_geni_serial_port { struct completion m_cmd_timeout; struct completion s_cmd_timeout; spinlock_t rx_lock; + bool pm_auto_suspend_disable; }; static const struct uart_ops msm_geni_serial_pops; @@ -531,8 +532,12 @@ static int vote_clock_off(struct uart_port *uport) static int msm_geni_serial_ioctl(struct uart_port *uport, unsigned int cmd, unsigned long arg) { + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); int ret = -ENOIOCTLCMD; + if (port->pm_auto_suspend_disable) + return ret; + switch (cmd) { case TIOCPMGET: case MSM_GENI_SERIAL_TIOCPMGET: { @@ -709,8 +714,11 @@ static void msm_geni_serial_power_off(struct uart_port *uport) __func__); return; } - pm_runtime_mark_last_busy(uport->dev); - pm_runtime_put_autosuspend(uport->dev); + + if (pm_runtime_enabled(uport->dev)) { + pm_runtime_mark_last_busy(uport->dev); + pm_runtime_put_autosuspend(uport->dev); + } } static int msm_geni_serial_poll_bit(struct uart_port *uport, @@ -1167,7 +1175,7 @@ static void msm_geni_serial_start_tx(struct uart_port *uport) goto exit_start_tx; } - if (!uart_console(uport)) { + if (!uart_console(uport) && pm_runtime_enabled(uport->dev)) { IPC_LOG_MSG(msm_port->ipc_log_misc, "%s.Power on.\n", __func__); pm_runtime_get(uport->dev); @@ -2207,6 +2215,9 @@ static void msm_geni_serial_shutdown(struct uart_port *uport) msm_geni_serial_stop_tx(uport); } + if (msm_port->pm_auto_suspend_disable) + disable_irq(uport->irq); + if (!uart_console(uport)) { if (msm_port->ioctl_count) { int i; @@ -2220,10 +2231,12 @@ static void msm_geni_serial_shutdown(struct uart_port *uport) msm_port->ioctl_count = 0; } - ret = pm_runtime_put_sync_suspend(uport->dev); - if (ret) { - IPC_LOG_MSG(msm_port->ipc_log_pwr, - "%s: Failed to suspend:%d\n", __func__, ret); + if (pm_runtime_enabled(uport->dev)) { + ret = pm_runtime_put_sync_suspend(uport->dev); + if (ret) { + IPC_LOG_MSG(msm_port->ipc_log_pwr, + "%s: Failed to suspend:%d\n", __func__, ret); + } } if (msm_port->wakeup_irq > 0) { @@ -2371,7 +2384,7 @@ static int msm_geni_serial_startup(struct uart_port *uport) * and disabled in runtime_suspend to avoid spurious interrupts * after suspend. */ - if (uart_console(uport)) + if (uart_console(uport) || msm_port->pm_auto_suspend_disable) enable_irq(uport->irq); if (msm_port->wakeup_irq > 0) { @@ -2842,9 +2855,32 @@ static void msm_geni_serial_cons_pm(struct uart_port *uport, { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - if (unlikely(!uart_console(uport))) + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + se_geni_resources_on(&msm_port->serial_rsc); + else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) + se_geni_resources_off(&msm_port->serial_rsc); +} + +static void msm_geni_serial_hs_pm(struct uart_port *uport, + unsigned int new_state, unsigned int old_state) +{ + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + + /* + * This will get call for system suspend/resume and + * Applicable for hs-uart without runtime pm framework support. + */ + if (pm_runtime_enabled(uport->dev)) return; + /* + * Default PM State is UNDEFINED Setting it to OFF State. + * This will allow add one port to do resources on and off during probe + */ + if (old_state == UART_PM_STATE_UNDEFINED) + old_state = UART_PM_STATE_OFF; + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) se_geni_resources_on(&msm_port->serial_rsc); else if (new_state == UART_PM_STATE_OFF && @@ -2886,6 +2922,8 @@ static const struct uart_ops msm_geni_serial_pops = { .break_ctl = msm_geni_serial_break_ctl, .flush_buffer = NULL, .ioctl = msm_geni_serial_ioctl, + /* For HSUART nodes without IOCTL support */ + .pm = msm_geni_serial_hs_pm, }; static const struct of_device_id msm_geni_device_tbl[] = { @@ -3217,6 +3255,14 @@ static int msm_geni_serial_probe(struct platform_device *pdev) uport->private_data = (void *)drv; platform_set_drvdata(pdev, dev_port); + /* + * To Disable PM runtime API that will make ioctl based + * vote_clock_on/off optional and rely on system PM + */ + dev_port->pm_auto_suspend_disable = + of_property_read_bool(pdev->dev.of_node, + "qcom,auto-suspend-disable"); + if (is_console) { dev_port->handle_rx = handle_rx_console; dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32), @@ -3226,10 +3272,15 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(dev_port->rx_fifo_depth * sizeof(u32)), GFP_KERNEL); - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, 150); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_enable(&pdev->dev); + if (dev_port->pm_auto_suspend_disable) { + pm_runtime_set_active(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + } else { + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 150); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } } if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) && @@ -3290,6 +3341,8 @@ static int msm_geni_serial_remove(struct platform_device *pdev) if (!uart_console(&port->uport)) wakeup_source_unregister(port->geni_wake); + if (port->pm_auto_suspend_disable) + pm_runtime_allow(&pdev->dev); uart_remove_one_port(drv, &port->uport); if (port->rx_dma) { geni_se_iommu_free_buf(port->wrapper_dev, &port->rx_dma, @@ -3416,7 +3469,7 @@ static int msm_geni_serial_sys_suspend(struct device *dev) struct msm_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_port *uport = &port->uport; - if (uart_console(uport)) { + if (uart_console(uport) || port->pm_auto_suspend_disable) { uart_suspend_port((struct uart_driver *)uport->private_data, uport); } else { @@ -3445,8 +3498,9 @@ static int msm_geni_serial_sys_resume(struct device *dev) struct msm_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_port *uport = &port->uport; - if (uart_console(uport) && - console_suspend_enabled && uport->suspended) { + if ((uart_console(uport) && + console_suspend_enabled && uport->suspended) || + port->pm_auto_suspend_disable) { uart_resume_port((struct uart_driver *)uport->private_data, uport); }