summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorMartin Kaiser <martin@kaiser.cx>2018-01-05 11:46:43 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-01-22 08:20:56 -0500
commit09df0b3464e528c6a4ca2c48d9ff6d2fd7cbd775 (patch)
tree6ad96fd82010fc817f77015b6dc4d2e0cbf132ec /drivers/tty
parent44117a1d1732c513875d5a163f10d9adbe866c08 (diff)
serial: imx: fix endless loop during suspend
Before we go into suspend mode, we enable the imx uart's interrupt for the awake bit in the UART Status Register 1. If, for some reason, the awake bit is already set before we enter suspend mode, we get an interrupt immediately when we enable interrupts for awake. The uart's clk_ipg is disabled at this point (unless there's an ongoing transfer). We end up in the interrupt handler, which usually tries to clear the awake bit. This doesn't work with the clock disabled. Therefore, we keep getting interrupts forever, resulting in an endless loop. Clear the awake bit before setting the awaken bit to signal that we want an imx interrupt when the awake bit will be set. This ensures that we're not woken up by events that happened before we started going into suspend mode. Change the clock handling so that suspend prepares and enables the clock and suspend_noirq disables it. Revert these operations in resume_noirq and resume. With these preparations in place, we can now modify awake and awaken in the suspend function when the actual imx interrupt is disabled and the required clk_ipg is active. Update the thaw and freeze functions to use the new clock handling since we share the suspend_noirq function between suspend and hibernate. Signed-off-by: Martin Kaiser <martin@kaiser.cx> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/imx.c32
1 files changed, 15 insertions, 17 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 7143da39c170..1d7ca382bc12 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -2219,8 +2219,10 @@ static void serial_imx_enable_wakeup(struct imx_port *sport, bool on)
2219 unsigned int val; 2219 unsigned int val;
2220 2220
2221 val = readl(sport->port.membase + UCR3); 2221 val = readl(sport->port.membase + UCR3);
2222 if (on) 2222 if (on) {
2223 writel(USR1_AWAKE, sport->port.membase + USR1);
2223 val |= UCR3_AWAKEN; 2224 val |= UCR3_AWAKEN;
2225 }
2224 else 2226 else
2225 val &= ~UCR3_AWAKEN; 2227 val &= ~UCR3_AWAKEN;
2226 writel(val, sport->port.membase + UCR3); 2228 writel(val, sport->port.membase + UCR3);
@@ -2239,11 +2241,6 @@ static int imx_serial_port_suspend_noirq(struct device *dev)
2239{ 2241{
2240 struct platform_device *pdev = to_platform_device(dev); 2242 struct platform_device *pdev = to_platform_device(dev);
2241 struct imx_port *sport = platform_get_drvdata(pdev); 2243 struct imx_port *sport = platform_get_drvdata(pdev);
2242 int ret;
2243
2244 ret = clk_enable(sport->clk_ipg);
2245 if (ret)
2246 return ret;
2247 2244
2248 serial_imx_save_context(sport); 2245 serial_imx_save_context(sport);
2249 2246
@@ -2264,8 +2261,6 @@ static int imx_serial_port_resume_noirq(struct device *dev)
2264 2261
2265 serial_imx_restore_context(sport); 2262 serial_imx_restore_context(sport);
2266 2263
2267 clk_disable(sport->clk_ipg);
2268
2269 return 0; 2264 return 0;
2270} 2265}
2271 2266
@@ -2273,15 +2268,19 @@ static int imx_serial_port_suspend(struct device *dev)
2273{ 2268{
2274 struct platform_device *pdev = to_platform_device(dev); 2269 struct platform_device *pdev = to_platform_device(dev);
2275 struct imx_port *sport = platform_get_drvdata(pdev); 2270 struct imx_port *sport = platform_get_drvdata(pdev);
2276 2271 int ret;
2277 /* enable wakeup from i.MX UART */
2278 serial_imx_enable_wakeup(sport, true);
2279 2272
2280 uart_suspend_port(&imx_reg, &sport->port); 2273 uart_suspend_port(&imx_reg, &sport->port);
2281 disable_irq(sport->port.irq); 2274 disable_irq(sport->port.irq);
2282 2275
2283 /* Needed to enable clock in suspend_noirq */ 2276 ret = clk_prepare_enable(sport->clk_ipg);
2284 return clk_prepare(sport->clk_ipg); 2277 if (ret)
2278 return ret;
2279
2280 /* enable wakeup from i.MX UART */
2281 serial_imx_enable_wakeup(sport, true);
2282
2283 return 0;
2285} 2284}
2286 2285
2287static int imx_serial_port_resume(struct device *dev) 2286static int imx_serial_port_resume(struct device *dev)
@@ -2295,7 +2294,7 @@ static int imx_serial_port_resume(struct device *dev)
2295 uart_resume_port(&imx_reg, &sport->port); 2294 uart_resume_port(&imx_reg, &sport->port);
2296 enable_irq(sport->port.irq); 2295 enable_irq(sport->port.irq);
2297 2296
2298 clk_unprepare(sport->clk_ipg); 2297 clk_disable_unprepare(sport->clk_ipg);
2299 2298
2300 return 0; 2299 return 0;
2301} 2300}
@@ -2307,8 +2306,7 @@ static int imx_serial_port_freeze(struct device *dev)
2307 2306
2308 uart_suspend_port(&imx_reg, &sport->port); 2307 uart_suspend_port(&imx_reg, &sport->port);
2309 2308
2310 /* Needed to enable clock in suspend_noirq */ 2309 return clk_prepare_enable(sport->clk_ipg);
2311 return clk_prepare(sport->clk_ipg);
2312} 2310}
2313 2311
2314static int imx_serial_port_thaw(struct device *dev) 2312static int imx_serial_port_thaw(struct device *dev)
@@ -2318,7 +2316,7 @@ static int imx_serial_port_thaw(struct device *dev)
2318 2316
2319 uart_resume_port(&imx_reg, &sport->port); 2317 uart_resume_port(&imx_reg, &sport->port);
2320 2318
2321 clk_unprepare(sport->clk_ipg); 2319 clk_disable_unprepare(sport->clk_ipg);
2322 2320
2323 return 0; 2321 return 0;
2324} 2322}