summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/msm_serial_hs.c125
1 files changed, 84 insertions, 41 deletions
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 856deefe0a8e..6843711774b2 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -3,7 +3,7 @@
* MSM 7k High speed uart driver
*
* Copyright (c) 2008 Google Inc.
- * Copyright (c) 2007-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2016, The Linux Foundation. All rights reserved.
* Modified: Nick Pelly <npelly@google.com>
*
* All source code in this file is licensed under the following license
@@ -216,7 +216,6 @@ struct msm_hs_port {
struct msm_hs_rx rx;
atomic_t clk_count;
struct msm_hs_wakeup wakeup;
- struct wakeup_source ws;
struct dentry *loopback_dir;
struct work_struct clock_off_w; /* work for actual clock off */
@@ -292,13 +291,16 @@ static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd,
int ret = 0, state = 1;
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ if (!msm_uport)
+ return -ENODEV;
+
switch (cmd) {
case MSM_ENABLE_UART_CLOCK: {
- msm_hs_request_clock_on(&msm_uport->uport);
+ ret = msm_hs_request_clock_on(&msm_uport->uport);
break;
}
case MSM_DISABLE_UART_CLOCK: {
- msm_hs_request_clock_off(&msm_uport->uport);
+ ret = msm_hs_request_clock_off(&msm_uport->uport);
break;
}
case MSM_GET_UART_CLOCK_STATUS: {
@@ -398,8 +400,8 @@ static void msm_hs_resource_vote(struct msm_hs_port *msm_uport)
struct uart_port *uport = &(msm_uport->uport);
ret = pm_runtime_get_sync(uport->dev);
if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
- MSM_HS_WARN("%s(): %p runtime PM callback not invoked",
- __func__, uport->dev);
+ MSM_HS_WARN("%s(): %p runtime PM callback not invoked(%d)",
+ __func__, uport->dev, ret);
msm_hs_pm_resume(uport->dev);
}
@@ -719,7 +721,6 @@ static int msm_hs_remove(struct platform_device *pdev)
msm_uport->rx.buffer = NULL;
msm_uport->rx.rbuffer = 0;
- wakeup_source_trash(&msm_uport->ws);
destroy_workqueue(msm_uport->hsuart_wq);
mutex_destroy(&msm_uport->mtx);
@@ -2262,14 +2263,30 @@ void msm_hs_resource_on(struct msm_hs_port *msm_uport)
}
/* Request to turn off uart clock once pending TX is flushed */
-void msm_hs_request_clock_off(struct uart_port *uport)
+int msm_hs_request_clock_off(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+ int ret = 0;
+
+ mutex_lock(&msm_uport->mtx);
+ /*
+ * If we're in the middle of a system suspend, don't process these
+ * userspace/kernel API commands.
+ */
+ if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
+ MSM_HS_WARN("%s:Can't process clk request during suspend",
+ __func__);
+ ret = -EIO;
+ }
+ mutex_unlock(&msm_uport->mtx);
+ if (ret)
+ goto exit_request_clock_off;
if (atomic_read(&msm_uport->client_count) <= 0) {
MSM_HS_WARN("%s(): ioctl count -ve, client check voting",
__func__);
- return;
+ ret = -EPERM;
+ goto exit_request_clock_off;
}
/* Set the flag to disable flow control and wakeup irq */
if (msm_uport->obs)
@@ -2278,23 +2295,42 @@ void msm_hs_request_clock_off(struct uart_port *uport)
atomic_dec(&msm_uport->client_count);
MSM_HS_INFO("%s():DISABLE UART CLOCK: ioc %d\n",
__func__, atomic_read(&msm_uport->client_count));
+exit_request_clock_off:
+ return ret;
}
EXPORT_SYMBOL(msm_hs_request_clock_off);
-void msm_hs_request_clock_on(struct uart_port *uport)
+int msm_hs_request_clock_on(struct uart_port *uport)
{
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
int client_count;
+ int ret = 0;
+ mutex_lock(&msm_uport->mtx);
+ /*
+ * If we're in the middle of a system suspend, don't process these
+ * userspace/kernel API commands.
+ */
+ if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
+ MSM_HS_WARN("%s:Can't process clk request during suspend",
+ __func__);
+ ret = -EIO;
+ }
+ mutex_unlock(&msm_uport->mtx);
+ if (ret)
+ goto exit_request_clock_on;
+
+ msm_hs_resource_vote(UARTDM_TO_MSM(uport));
atomic_inc(&msm_uport->client_count);
client_count = atomic_read(&msm_uport->client_count);
MSM_HS_INFO("%s():ENABLE UART CLOCK: ioc %d\n",
__func__, client_count);
- msm_hs_resource_vote(UARTDM_TO_MSM(uport));
/* Clear the flag */
if (msm_uport->obs)
atomic_set(&msm_uport->client_req_state, 0);
+exit_request_clock_on:
+ return ret;
}
EXPORT_SYMBOL(msm_hs_request_clock_on);
@@ -2548,7 +2584,7 @@ static int msm_hs_startup(struct uart_port *uport)
ret = msm_hs_config_uart_gpios(uport);
if (ret) {
MSM_HS_ERR("Uart GPIO request failed\n");
- goto deinit_ws;
+ goto free_uart_irq;
}
msm_hs_write(uport, UART_DM_DMEN, 0);
@@ -2651,8 +2687,6 @@ sps_disconnect_tx:
sps_disconnect(sps_pipe_handle_tx);
unconfig_uart_gpios:
msm_hs_unconfig_uart_gpios(uport);
-deinit_ws:
- wakeup_source_trash(&msm_uport->ws);
free_uart_irq:
free_irq(uport->irq, msm_uport);
unvote_exit:
@@ -3061,6 +3095,7 @@ static void msm_hs_pm_suspend(struct device *dev)
if (!msm_uport)
goto err_suspend;
+ mutex_lock(&msm_uport->mtx);
/* For OBS, don't use wakeup interrupt, set gpio to suspended state */
if (msm_uport->obs) {
@@ -3077,8 +3112,8 @@ static void msm_hs_pm_suspend(struct device *dev)
msm_hs_clk_bus_unvote(msm_uport);
if (!atomic_read(&msm_uport->client_req_state))
toggle_wakeup_interrupt(msm_uport);
- __pm_relax(&msm_uport->ws);
MSM_HS_DBG("%s(): return suspend\n", __func__);
+ mutex_unlock(&msm_uport->mtx);
return;
err_suspend:
pr_err("%s(): invalid uport", __func__);
@@ -3093,12 +3128,13 @@ static int msm_hs_pm_resume(struct device *dev)
if (!msm_uport)
goto err_resume;
+
+ mutex_lock(&msm_uport->mtx);
if (msm_uport->pm_state == MSM_HS_PM_ACTIVE)
- return 0;
+ goto exit_pm_resume;
if (!atomic_read(&msm_uport->client_req_state))
toggle_wakeup_interrupt(msm_uport);
msm_hs_clk_bus_vote(msm_uport);
- __pm_stay_awake(&msm_uport->ws);
obs_manage_irq(msm_uport, true);
msm_uport->pm_state = MSM_HS_PM_ACTIVE;
msm_hs_resource_on(msm_uport);
@@ -3113,6 +3149,8 @@ static int msm_hs_pm_resume(struct device *dev)
}
MSM_HS_DBG("%s(): return resume\n", __func__);
+exit_pm_resume:
+ mutex_unlock(&msm_uport->mtx);
return 0;
err_resume:
pr_err("%s(): invalid uport", __func__);
@@ -3125,36 +3163,34 @@ static int msm_hs_pm_sys_suspend_noirq(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
enum msm_hs_pm_state prev_pwr_state;
- struct uart_port *uport;
+ int clk_cnt, client_count, ret = 0;
if (IS_ERR_OR_NULL(msm_uport))
return -ENODEV;
- /* client vote is active, fail sys suspend */
- if (atomic_read(&msm_uport->client_count))
- return -EBUSY;
+ mutex_lock(&msm_uport->mtx);
+
+ /*
+ * If there is an active clk request or an impending userspace request
+ * fail the suspend callback.
+ */
+ clk_cnt = atomic_read(&msm_uport->clk_count);
+ client_count = atomic_read(&msm_uport->client_count);
+ if (clk_cnt || (pm_runtime_enabled(dev) &&
+ !pm_runtime_suspended(dev))) {
+ MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d,RPM:%d\n",
+ __func__, clk_cnt, client_count,
+ dev->power.runtime_status);
+ ret = -EBUSY;
+ goto exit_suspend_noirq;
+ }
- MSM_HS_DBG("%s(): suspending", __func__);
prev_pwr_state = msm_uport->pm_state;
- uport = &(msm_uport->uport);
- mutex_lock(&msm_uport->mtx);
msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED;
+ MSM_HS_DBG("%s(): suspending", __func__);
+exit_suspend_noirq:
mutex_unlock(&msm_uport->mtx);
-
- if (prev_pwr_state == MSM_HS_PM_ACTIVE) {
- msm_hs_pm_suspend(dev);
- /*
- * Synchronize runtime pm and system pm states:
- * at this point we are already suspended. However
- * the RT PM framework still thinks that we are active.
- * The calls below let RT PM know that we are suspended
- * without re-invoking the suspend callback
- */
- pm_runtime_disable(uport->dev);
- pm_runtime_set_suspended(uport->dev);
- pm_runtime_enable(uport->dev);
- }
- return 0;
+ return ret;
};
static int msm_hs_pm_sys_resume_noirq(struct device *dev)
@@ -3169,8 +3205,12 @@ static int msm_hs_pm_sys_resume_noirq(struct device *dev)
* variable. Resource activation will be done
* when transfer is requested.
*/
+
+ mutex_lock(&msm_uport->mtx);
MSM_HS_DBG("%s(): system resume", __func__);
- msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+ if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED)
+ msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+ mutex_unlock(&msm_uport->mtx);
return 0;
}
#endif
@@ -3440,7 +3480,6 @@ static int msm_hs_probe(struct platform_device *pdev)
uport->line = pdata->userid;
ret = uart_add_one_port(&msm_hs_driver, uport);
if (!ret) {
- wakeup_source_init(&msm_uport->ws, dev_name(&pdev->dev));
msm_hs_clk_bus_unvote(msm_uport);
msm_serial_hs_rt_init(uport);
return ret;
@@ -3593,6 +3632,10 @@ static void msm_hs_shutdown(struct uart_port *uport)
MSM_HS_WARN("%s: Client clock vote imbalance\n", __func__);
atomic_set(&msm_uport->client_req_state, 0);
}
+ if (atomic_read(&msm_uport->client_count)) {
+ MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__);
+ atomic_set(&msm_uport->client_count, 0);
+ }
msm_hs_unconfig_uart_gpios(uport);
MSM_HS_INFO("%s:UART port closed successfully\n", __func__);
}