| From 551c98690104592a4c273681a6ea819c47677b17 Mon Sep 17 00:00:00 2001 |
| From: Claire Chang <tientzu@chromium.org> |
| Date: Mon, 27 May 2019 16:31:50 +0800 |
| Subject: [PATCH] uart: mediatek: support Rx in-band wakeup |
| |
| commit 9315ad99ed5bc3865cef0da56e705549cd04fe7e upstream. |
| |
| In order to support Rx in-band wakeup, we need to enable irq wake on an |
| edge sensitive interrupt of Rx pin before suspend and disable it when |
| resuming. |
| |
| This interrupt is used only as wake source to resume the system when |
| suspended. Note that the sent character will be lost as the controller is |
| actually suspended. |
| |
| We use this to support wakeup on bluetooth. Bluetooth will repeatedly send |
| 0xFD to wakeup host. Once host detects Rx falling, an interrupt is |
| triggered, and the system leaves sleep state. Then, the bluetooth driver |
| will send 0xFC to bluetooth and bluetooth can start to send normal HCI |
| packets. |
| |
| Signed-off-by: Claire Chang <tientzu@chromium.org> |
| Reviewed-by: Nicolas Boichat <drinkcat@chromium.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c |
| index 417c7c810df9..5b94b853387d 100644 |
| --- a/drivers/tty/serial/8250/8250_mtk.c |
| +++ b/drivers/tty/serial/8250/8250_mtk.c |
| @@ -10,6 +10,7 @@ |
| #include <linux/module.h> |
| #include <linux/of_irq.h> |
| #include <linux/of_platform.h> |
| +#include <linux/pinctrl/consumer.h> |
| #include <linux/platform_device.h> |
| #include <linux/pm_runtime.h> |
| #include <linux/serial_8250.h> |
| @@ -70,6 +71,7 @@ struct mtk8250_data { |
| #ifdef CONFIG_SERIAL_8250_DMA |
| enum dma_rx_status rx_status; |
| #endif |
| + int rx_wakeup_irq; |
| }; |
| |
| /* flow control mode */ |
| @@ -551,6 +553,8 @@ static int mtk8250_probe(struct platform_device *pdev) |
| pm_runtime_set_active(&pdev->dev); |
| pm_runtime_enable(&pdev->dev); |
| |
| + data->rx_wakeup_irq = platform_get_irq(pdev, 1); |
| + |
| return 0; |
| } |
| |
| @@ -572,15 +576,35 @@ static int mtk8250_remove(struct platform_device *pdev) |
| static int __maybe_unused mtk8250_suspend(struct device *dev) |
| { |
| struct mtk8250_data *data = dev_get_drvdata(dev); |
| + int irq = data->rx_wakeup_irq; |
| + int err; |
| |
| serial8250_suspend_port(data->line); |
| |
| + pinctrl_pm_select_sleep_state(dev); |
| + if (irq >= 0) { |
| + err = enable_irq_wake(irq); |
| + if (err) { |
| + dev_err(dev, |
| + "failed to enable irq wake on IRQ %d: %d\n", |
| + irq, err); |
| + pinctrl_pm_select_default_state(dev); |
| + serial8250_resume_port(data->line); |
| + return err; |
| + } |
| + } |
| + |
| return 0; |
| } |
| |
| static int __maybe_unused mtk8250_resume(struct device *dev) |
| { |
| struct mtk8250_data *data = dev_get_drvdata(dev); |
| + int irq = data->rx_wakeup_irq; |
| + |
| + if (irq >= 0) |
| + disable_irq_wake(irq); |
| + pinctrl_pm_select_default_state(dev); |
| |
| serial8250_resume_port(data->line); |
| |
| -- |
| 2.7.4 |
| |