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@xxxxxxx> --- v3: Remove GPIO toggling in ps_init(). (Shenwei Wang) --- drivers/bluetooth/btnxpuart.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c index 2b8a07c745c9..4b7e8b7c522a 100644 --- a/drivers/bluetooth/btnxpuart.c +++ b/drivers/bluetooth/btnxpuart.c @@ -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,12 @@ 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); + break; case WAKEUP_METHOD_DTR: if (ps_state == PS_STATE_AWAKE) status = serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0); @@ -425,8 +434,13 @@ static void ps_timeout_func(struct timer_list *t) static void 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(&serdev->dev, "device-wakeup", GPIOD_OUT_LOW); + if (IS_ERR(psdata->h2c_ps_gpio)) + psdata->h2c_wakeup_gpio = 0xff; + psdata->hdev = hdev; INIT_WORK(&psdata->work, ps_work_func); mutex_init(&psdata->ps_lock); @@ -516,6 +530,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 +567,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 +579,17 @@ static void ps_init(struct hci_dev *hdev) psdata->c2h_wakeup_gpio = 0xff; psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID; + if (!IS_ERR(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 +1306,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; -- 2.25.1