mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
Bluetooth: btnxpuart: Implement host-wakeup feature
This implements host wakeup feature by reading the device tree property wakeup-source and 'wakeup' interrupt, and nxp,wakeout-pin, and configuring it as a FALLING EDGE triggered interrupt. 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@nxp.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
a12a5f5ff0
commit
c50b56664e
1 changed files with 52 additions and 6 deletions
|
@ -17,6 +17,7 @@
|
|||
#include <linux/crc32.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
@ -143,7 +144,9 @@ struct ps_data {
|
|||
bool driver_sent_cmd;
|
||||
u16 h2c_ps_interval;
|
||||
u16 c2h_ps_interval;
|
||||
bool wakeup_source;
|
||||
struct gpio_desc *h2c_ps_gpio;
|
||||
s32 irq_handler;
|
||||
struct hci_dev *hdev;
|
||||
struct work_struct work;
|
||||
struct timer_list ps_timer;
|
||||
|
@ -476,12 +479,21 @@ static void ps_timeout_func(struct timer_list *t)
|
|||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ps_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 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;
|
||||
|
||||
/* Out-Of-Band Device Wakeup */
|
||||
psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(psdata->h2c_ps_gpio)) {
|
||||
|
@ -493,11 +505,37 @@ 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);
|
||||
/* Out-Of-Band Host Wakeup */
|
||||
if (of_property_read_bool(serdev->dev.of_node, "wakeup-source")) {
|
||||
psdata->irq_handler = of_irq_get_byname(serdev->dev.of_node, "wakeup");
|
||||
bt_dev_info(nxpdev->hdev, "irq_handler: %d", psdata->irq_handler);
|
||||
if (psdata->irq_handler > 0)
|
||||
psdata->wakeup_source = true;
|
||||
}
|
||||
|
||||
if (device_property_read_u8(&serdev->dev, "nxp,wakeout-pin", &psdata->c2h_wakeup_gpio)) {
|
||||
psdata->c2h_wakeup_gpio = 0xff;
|
||||
if (psdata->wakeup_source) {
|
||||
bt_dev_warn(hdev, "host wakeup interrupt without nxp,wakeout-pin");
|
||||
psdata->wakeup_source = false;
|
||||
}
|
||||
} else if (!psdata->wakeup_source) {
|
||||
bt_dev_warn(hdev, "nxp,wakeout-pin property without host wakeup interrupt");
|
||||
psdata->c2h_wakeup_gpio = 0xff;
|
||||
}
|
||||
|
||||
if (psdata->wakeup_source) {
|
||||
ret = devm_request_irq(&serdev->dev, psdata->irq_handler,
|
||||
ps_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 +675,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)
|
||||
|
@ -1821,6 +1857,11 @@ static int nxp_serdev_suspend(struct device *dev)
|
|||
struct ps_data *psdata = &nxpdev->psdata;
|
||||
|
||||
ps_control(psdata->hdev, PS_STATE_SLEEP);
|
||||
|
||||
if (psdata->wakeup_source) {
|
||||
enable_irq_wake(psdata->irq_handler);
|
||||
enable_irq(psdata->irq_handler);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1829,6 +1870,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->wakeup_source) {
|
||||
disable_irq(psdata->irq_handler);
|
||||
disable_irq_wake(psdata->irq_handler);
|
||||
}
|
||||
|
||||
ps_control(psdata->hdev, PS_STATE_AWAKE);
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue