[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250411163719.326558-2-neeraj.sanjaykale@nxp.com>
Date: Fri, 11 Apr 2025 22:07:19 +0530
From: Neeraj Sanjay Kale <neeraj.sanjaykale@....com>
To: marcel@...tmann.org,
luiz.dentz@...il.com,
robh@...nel.org,
krzk+dt@...nel.org,
conor+dt@...nel.org
Cc: linux-bluetooth@...r.kernel.org,
linux-kernel@...r.kernel.org,
devicetree@...r.kernel.org,
amitkumar.karwar@....com,
neeraj.sanjaykale@....com,
sherry.sun@....com,
manjeet.gupta@....com
Subject: [PATCH v1 2/2] Bluetooth: btnxpuart: Implement host-wakeup feature
This implements host-wakeup feature by reading the device tree property
host-wakeup-gpios and nxp,wakeout-pin, and configuring the
host-wakeup-gpio as a FALLING EDGE triggered interrupt pin.
When host is suspended, a trigger from the WAKE_OUT pin of the
controller wakes it up.
To enable this feature, both device tree properties are needed to be
defined.
Signed-off-by: Neeraj Sanjay Kale <neeraj.sanjaykale@....com>
---
drivers/bluetooth/btnxpuart.c | 66 +++++++++++++++++++++++++++++++----
1 file changed, 60 insertions(+), 6 deletions(-)
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 604ab2bba231..265f6bb1c533 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -17,6 +17,8 @@
#include <linux/crc32.h>
#include <linux/string_helpers.h>
#include <linux/gpio/consumer.h>
+#include <linux/of_irq.h>
+#include <linux/suspend.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -144,6 +146,8 @@ struct ps_data {
u16 h2c_ps_interval;
u16 c2h_ps_interval;
struct gpio_desc *h2c_ps_gpio;
+ struct gpio_desc *c2h_ps_gpio;
+ s32 irq_handler;
struct hci_dev *hdev;
struct work_struct work;
struct timer_list ps_timer;
@@ -476,11 +480,13 @@ static void ps_timeout_func(struct timer_list *t)
}
}
+static irqreturn_t btnxpuart_host_wakeup_irq_handler(int irq, void *priv);
static int ps_setup(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
struct serdev_device *serdev = nxpdev->serdev;
struct ps_data *psdata = &nxpdev->psdata;
+ int ret;
psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
GPIOD_OUT_LOW);
@@ -493,11 +499,41 @@ static int ps_setup(struct hci_dev *hdev)
if (device_property_read_u8(&serdev->dev, "nxp,wakein-pin", &psdata->h2c_wakeup_gpio)) {
psdata->h2c_wakeup_gpio = 0xff; /* 0xff: use default pin/gpio */
} else if (!psdata->h2c_ps_gpio) {
- bt_dev_warn(hdev, "nxp,wakein-pin property without device-wakeup GPIO");
+ bt_dev_warn(hdev, "nxp,wakein-pin property without device-wakeup-gpios");
psdata->h2c_wakeup_gpio = 0xff;
}
- device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio);
+ psdata->c2h_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "host-wakeup",
+ GPIOD_IN);
+ if (IS_ERR(psdata->c2h_ps_gpio)) {
+ bt_dev_err(hdev, "Error fetching host-wakeup-gpios: %ld",
+ PTR_ERR(psdata->h2c_ps_gpio));
+ return PTR_ERR(psdata->c2h_ps_gpio);
+ }
+
+ if (device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio)) {
+ psdata->c2h_wakeup_gpio = 0xff;
+ if (psdata->c2h_ps_gpio) {
+ bt_dev_warn(hdev, "host-wakeup-gpios property without nxp,wakeout-pin");
+ gpiod_put(psdata->c2h_ps_gpio);
+ psdata->c2h_ps_gpio = NULL;
+ }
+ } else if (!psdata->c2h_ps_gpio) {
+ bt_dev_warn(hdev, "nxp,wakeout-pin property without host-wakeup-gpios");
+ psdata->c2h_wakeup_gpio = 0xff;
+ }
+
+ if (psdata->c2h_ps_gpio) {
+ psdata->irq_handler = gpiod_to_irq(psdata->c2h_ps_gpio);
+ bt_dev_dbg(nxpdev->hdev, "host-wakeup irq_handler: %d", psdata->irq_handler);
+
+ ret = devm_request_irq(&serdev->dev, psdata->irq_handler,
+ btnxpuart_host_wakeup_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ dev_name(&serdev->dev), nxpdev);
+ disable_irq(psdata->irq_handler);
+ device_init_wakeup(&serdev->dev, true);
+ }
psdata->hdev = hdev;
INIT_WORK(&psdata->work, ps_work_func);
@@ -637,12 +673,10 @@ static void ps_init(struct hci_dev *hdev)
psdata->ps_state = PS_STATE_AWAKE;
- if (psdata->c2h_wakeup_gpio) {
+ if (psdata->c2h_wakeup_gpio != 0xff)
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_GPIO;
- } else {
+ else
psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
- psdata->c2h_wakeup_gpio = 0xff;
- }
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
if (psdata->h2c_ps_gpio)
@@ -1697,6 +1731,15 @@ static size_t btnxpuart_receive_buf(struct serdev_device *serdev,
return count;
}
+static irqreturn_t btnxpuart_host_wakeup_irq_handler(int irq, void *priv)
+{
+ struct btnxpuart_dev *nxpdev = (struct btnxpuart_dev *)priv;
+
+ bt_dev_dbg(nxpdev->hdev, "Host wakeup interrupt");
+
+ return IRQ_HANDLED;
+}
+
static void btnxpuart_write_wakeup(struct serdev_device *serdev)
{
serdev_device_write_wakeup(serdev);
@@ -1821,6 +1864,12 @@ static int nxp_serdev_suspend(struct device *dev)
struct ps_data *psdata = &nxpdev->psdata;
ps_control(psdata->hdev, PS_STATE_SLEEP);
+
+ if (psdata->irq_handler > 0 &&
+ device_may_wakeup(&nxpdev->serdev->dev)) {
+ enable_irq_wake(psdata->irq_handler);
+ enable_irq(psdata->irq_handler);
+ }
return 0;
}
@@ -1829,6 +1878,11 @@ static int nxp_serdev_resume(struct device *dev)
struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
struct ps_data *psdata = &nxpdev->psdata;
+ if (psdata->irq_handler > 0) {
+ disable_irq(psdata->irq_handler);
+ disable_irq_wake(psdata->irq_handler);
+ }
+
ps_control(psdata->hdev, PS_STATE_AWAKE);
return 0;
}
--
2.25.1
Powered by blists - more mailing lists