Commit c50b5666 authored by Neeraj Sanjay Kale's avatar Neeraj Sanjay Kale Committed by Luiz Augusto von Dentz
Browse files

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: default avatarNeeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>
Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent a12a5f5f
Loading
Loading
Loading
Loading
+52 −6
Original line number Diff line number Diff line
@@ -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;
}