mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 16:53:49 +00:00
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 <neeraj.sanjaykale@nxp.com> Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
parent
6db0cd5543
commit
c135a5bc34
@ -16,6 +16,7 @@
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -82,6 +83,7 @@
|
||||
#define WAKEUP_METHOD_BREAK 1
|
||||
#define WAKEUP_METHOD_EXT_BREAK 2
|
||||
#define WAKEUP_METHOD_RTS 3
|
||||
#define WAKEUP_METHOD_GPIO 4
|
||||
#define WAKEUP_METHOD_INVALID 0xff
|
||||
|
||||
/* power save mode status */
|
||||
@ -135,6 +137,7 @@ struct ps_data {
|
||||
bool driver_sent_cmd;
|
||||
u16 h2c_ps_interval;
|
||||
u16 c2h_ps_interval;
|
||||
struct gpio_desc *h2c_ps_gpio;
|
||||
struct hci_dev *hdev;
|
||||
struct work_struct work;
|
||||
struct timer_list ps_timer;
|
||||
@ -365,7 +368,7 @@ static void ps_control(struct hci_dev *hdev, u8 ps_state)
|
||||
{
|
||||
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
|
||||
struct ps_data *psdata = &nxpdev->psdata;
|
||||
int status;
|
||||
int status = 0;
|
||||
|
||||
if (psdata->ps_state == ps_state ||
|
||||
!test_bit(BTNXPUART_SERDEV_OPEN, &nxpdev->tx_state))
|
||||
@ -373,6 +376,14 @@ static void ps_control(struct hci_dev *hdev, u8 ps_state)
|
||||
|
||||
mutex_lock(&psdata->ps_lock);
|
||||
switch (psdata->cur_h2c_wakeupmode) {
|
||||
case WAKEUP_METHOD_GPIO:
|
||||
if (ps_state == PS_STATE_AWAKE)
|
||||
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
|
||||
else
|
||||
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 1);
|
||||
bt_dev_dbg(hdev, "Set h2c_ps_gpio: %s",
|
||||
str_high_low(ps_state == PS_STATE_SLEEP));
|
||||
break;
|
||||
case WAKEUP_METHOD_DTR:
|
||||
if (ps_state == PS_STATE_AWAKE)
|
||||
status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
|
||||
@ -422,15 +433,29 @@ static void ps_timeout_func(struct timer_list *t)
|
||||
}
|
||||
}
|
||||
|
||||
static void ps_setup(struct hci_dev *hdev)
|
||||
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;
|
||||
|
||||
psdata->h2c_ps_gpio = devm_gpiod_get_optional(&serdev->dev, "device-wakeup",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(psdata->h2c_ps_gpio)) {
|
||||
bt_dev_err(hdev, "Error fetching device-wakeup-gpios: %ld",
|
||||
PTR_ERR(psdata->h2c_ps_gpio));
|
||||
return PTR_ERR(psdata->h2c_ps_gpio);
|
||||
}
|
||||
|
||||
if (!psdata->h2c_ps_gpio)
|
||||
psdata->h2c_wakeup_gpio = 0xff;
|
||||
|
||||
psdata->hdev = hdev;
|
||||
INIT_WORK(&psdata->work, ps_work_func);
|
||||
mutex_init(&psdata->ps_lock);
|
||||
timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ps_wakeup(struct btnxpuart_dev *nxpdev)
|
||||
@ -516,6 +541,9 @@ static int send_wakeup_method_cmd(struct hci_dev *hdev, void *data)
|
||||
pcmd.c2h_wakeupmode = psdata->c2h_wakeupmode;
|
||||
pcmd.c2h_wakeup_gpio = psdata->c2h_wakeup_gpio;
|
||||
switch (psdata->h2c_wakeupmode) {
|
||||
case WAKEUP_METHOD_GPIO:
|
||||
pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_GPIO;
|
||||
break;
|
||||
case WAKEUP_METHOD_DTR:
|
||||
pcmd.h2c_wakeupmode = BT_CTRL_WAKEUP_METHOD_DSR;
|
||||
break;
|
||||
@ -550,6 +578,7 @@ static void ps_init(struct hci_dev *hdev)
|
||||
{
|
||||
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
|
||||
struct ps_data *psdata = &nxpdev->psdata;
|
||||
u8 default_h2c_wakeup_mode = DEFAULT_H2C_WAKEUP_MODE;
|
||||
|
||||
serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_RTS);
|
||||
usleep_range(5000, 10000);
|
||||
@ -561,8 +590,17 @@ static void ps_init(struct hci_dev *hdev)
|
||||
psdata->c2h_wakeup_gpio = 0xff;
|
||||
|
||||
psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
|
||||
if (psdata->h2c_ps_gpio)
|
||||
default_h2c_wakeup_mode = WAKEUP_METHOD_GPIO;
|
||||
|
||||
psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
|
||||
switch (DEFAULT_H2C_WAKEUP_MODE) {
|
||||
|
||||
switch (default_h2c_wakeup_mode) {
|
||||
case WAKEUP_METHOD_GPIO:
|
||||
psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
|
||||
gpiod_set_value_cansleep(psdata->h2c_ps_gpio, 0);
|
||||
usleep_range(5000, 10000);
|
||||
break;
|
||||
case WAKEUP_METHOD_DTR:
|
||||
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
|
||||
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)
|
||||
psdata->c2h_wakeup_gpio = wakeup_parm.c2h_wakeup_gpio;
|
||||
psdata->h2c_wakeup_gpio = wakeup_parm.h2c_wakeup_gpio;
|
||||
switch (wakeup_parm.h2c_wakeupmode) {
|
||||
case BT_CTRL_WAKEUP_METHOD_GPIO:
|
||||
psdata->h2c_wakeupmode = WAKEUP_METHOD_GPIO;
|
||||
break;
|
||||
case BT_CTRL_WAKEUP_METHOD_DSR:
|
||||
psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
|
||||
break;
|
||||
@ -1509,13 +1550,17 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
dev_err(&serdev->dev, "Can't register HCI device\n");
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
goto probe_fail;
|
||||
}
|
||||
|
||||
ps_setup(hdev);
|
||||
if (ps_setup(hdev))
|
||||
goto probe_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
probe_fail:
|
||||
hci_free_dev(hdev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void nxp_serdev_remove(struct serdev_device *serdev)
|
||||
|
Loading…
Reference in New Issue
Block a user