Skip to content

Commit c135a5b

Browse files
NeerajSanjayKaleVudentz
authored andcommitted
Bluetooth: btnxpuart: Add GPIO support to power save feature
This adds support for driving the chip into sleep or wakeup with a GPIO. If the device tree property device-wakeup-gpios is defined, the driver utilizes this GPIO for controlling the chip's power save state, else it uses the default UART-break method. Signed-off-by: Neeraj Sanjay Kale <[email protected]> Signed-off-by: Luiz Augusto von Dentz <[email protected]>
1 parent 6db0cd5 commit c135a5b

File tree

1 file changed

+51
-6
lines changed

1 file changed

+51
-6
lines changed

drivers/bluetooth/btnxpuart.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/crc8.h>
1717
#include <linux/crc32.h>
1818
#include <linux/string_helpers.h>
19+
#include <linux/gpio/consumer.h>
1920

2021
#include <net/bluetooth/bluetooth.h>
2122
#include <net/bluetooth/hci_core.h>
@@ -82,6 +83,7 @@
8283
#define WAKEUP_METHOD_BREAK 1
8384
#define WAKEUP_METHOD_EXT_BREAK 2
8485
#define WAKEUP_METHOD_RTS 3
86+
#define WAKEUP_METHOD_GPIO 4
8587
#define WAKEUP_METHOD_INVALID 0xff
8688

8789
/* power save mode status */
@@ -135,6 +137,7 @@ struct ps_data {
135137
bool driver_sent_cmd;
136138
u16 h2c_ps_interval;
137139
u16 c2h_ps_interval;
140+
struct gpio_desc *h2c_ps_gpio;
138141
struct hci_dev *hdev;
139142
struct work_struct work;
140143
struct timer_list ps_timer;
@@ -365,14 +368,22 @@ static void ps_control(struct hci_dev *hdev, u8 ps_state)
365368
{
366369
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
367370
struct ps_data *psdata = &nxpdev->psdata;
368-
int status;
371+
int status = 0;
369372

370373
if (psdata->ps_state == ps_state ||
371374
!test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state))
372375
return;
373376

374377
mutex_lock(&psdata->ps_lock);
375378
switch (psdata->cur_h2c_wakeupmode) {
379+
case WAKEUP_METHOD_GPIO:
380+
if (ps_state == PS_STATE_AWAKE)
381+
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
382+
else
383+
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 1);
384+
bt_dev_dbg(hdev, "Set h2c_ps_gpio: %s",
385+
str_high_low(ps_state == PS_STATE_SLEEP));
386+
break;
376387
case WAKEUP_METHOD_DTR:
377388
if (ps_state == PS_STATE_AWAKE)
378389
status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
@@ -422,15 +433,29 @@ static void ps_timeout_func(struct timer_list *t)
422433
}
423434
}
424435

425-
static void ps_setup(struct hci_dev *hdev)
436+
static int ps_setup(struct hci_dev *hdev)
426437
{
427438
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
439+
struct serdev_device *serdev = nxpdev->serdev;
428440
struct ps_data *psdata = &nxpdev->psdata;
429441

442+
psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
443+
GPIOD_OUT_LOW);
444+
if (IS_ERR(psdata->h2c_ps_gpio)) {
445+
bt_dev_err(hdev, "Error fetching device-wakeup-gpios: %ld",
446+
PTR_ERR(psdata->h2c_ps_gpio));
447+
return PTR_ERR(psdata->h2c_ps_gpio);
448+
}
449+
450+
if (!psdata->h2c_ps_gpio)
451+
psdata->h2c_wakeup_gpio = 0xff;
452+
430453
psdata->hdev = hdev;
431454
INIT_WORK(&psdata->work, ps_work_func);
432455
mutex_init(&psdata->ps_lock);
433456
timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
457+
458+
return 0;
434459
}
435460

436461
static bool ps_wakeup(struct btnxpuart_dev *nxpdev)
@@ -516,6 +541,9 @@ static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data)
516541
pcmd.c2h_wakeupmode = psdata->c2h_wakeupmode;
517542
pcmd.c2h_wakeup_gpio = psdata->c2h_wakeup_gpio;
518543
switch (psdata->h2c_wakeupmode) {
544+
case WAKEUP_METHOD_GPIO:
545+
pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_GPIO;
546+
break;
519547
case WAKEUP_METHOD_DTR:
520548
pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_DSR;
521549
break;
@@ -550,6 +578,7 @@ static void ps_init(struct hci_dev *hdev)
550578
{
551579
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
552580
struct ps_data *psdata = &nxpdev->psdata;
581+
u8 default_h2c_wakeup_mode = DEFAULT_H2C_WAKEUP_MODE;
553582

554583
serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_RTS);
555584
usleep_range(5000, 10000);
@@ -561,8 +590,17 @@ static void ps_init(struct hci_dev *hdev)
561590
psdata->c2h_wakeup_gpio = 0xff;
562591

563592
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
593+
if (psdata->h2c_ps_gpio)
594+
default_h2c_wakeup_mode = WAKEUP_METHOD_GPIO;
595+
564596
psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
565-
switch (DEFAULT_H2C_WAKEUP_MODE) {
597+
598+
switch (default_h2c_wakeup_mode) {
599+
case WAKEUP_METHOD_GPIO:
600+
psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
601+
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
602+
usleep_range(5000, 10000);
603+
break;
566604
case WAKEUP_METHOD_DTR:
567605
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
568606
serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR);
@@ -1279,6 +1317,9 @@ static int nxp_enqueue(struct hci_dev *hdev, struct sk_buff *skb)
12791317
psdata->c2h_wakeup_gpio = wakeup_parm.c2h_wakeup_gpio;
12801318
psdata->h2c_wakeup_gpio = wakeup_parm.h2c_wakeup_gpio;
12811319
switch (wakeup_parm.h2c_wakeupmode) {
1320+
case BT_CTRL_WAKEUP_METHOD_GPIO:
1321+
psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
1322+
break;
12821323
case BT_CTRL_WAKEUP_METHOD_DSR:
12831324
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
12841325
break;
@@ -1509,13 +1550,17 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
15091550

15101551
if (hci_register_dev(hdev) < 0) {
15111552
dev_err(&serdev->dev, "Can't register HCI device\n");
1512-
hci_free_dev(hdev);
1513-
return -ENODEV;
1553+
goto probe_fail;
15141554
}
15151555

1516-
ps_setup(hdev);
1556+
if (ps_setup(hdev))
1557+
goto probe_fail;
15171558

15181559
return 0;
1560+
1561+
probe_fail:
1562+
hci_free_dev(hdev);
1563+
return -ENODEV;
15191564
}
15201565

15211566
static void nxp_serdev_remove(struct serdev_device *serdev)

0 commit comments

Comments
 (0)