Commit 66f077dd authored by Archie Pusaka's avatar Archie Pusaka Committed by Marcel Holtmann

Bluetooth: hci_h5: add WAKEUP_DISABLE flag

Some RTL chips resets the FW on suspend, so wakeup is disabled on
those chips. This patch introduces this WAKEUP_DISABLE flag so that
chips that doesn't reset FW on suspend can leave the flag unset and
is allowed to wake the host.

This patch also left RTL8822 WAKEUP_DISABLE flag unset, therefore
allowing it to wake the host, and preventing reprobing on resume.
Signed-off-by: default avatarArchie Pusaka <apusaka@chromium.org>
Reviewed-by: default avatarAbhishek Pandit-Subedi <abhishekpandit@chromium.org>
Reviewed-by: default avatarHilda Wu <hildawu@realtek.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 64832df2
...@@ -51,8 +51,9 @@ ...@@ -51,8 +51,9 @@
/* H5 state flags */ /* H5 state flags */
enum { enum {
H5_RX_ESC, /* SLIP escape mode */ H5_RX_ESC, /* SLIP escape mode */
H5_TX_ACK_REQ, /* Pending ack to send */ H5_TX_ACK_REQ, /* Pending ack to send */
H5_WAKEUP_DISABLE, /* Device cannot wake host */
}; };
struct h5 { struct h5 {
...@@ -97,6 +98,10 @@ struct h5 { ...@@ -97,6 +98,10 @@ struct h5 {
struct gpio_desc *device_wake_gpio; struct gpio_desc *device_wake_gpio;
}; };
enum h5_driver_info {
H5_INFO_WAKEUP_DISABLE = BIT(0),
};
struct h5_vnd { struct h5_vnd {
int (*setup)(struct h5 *h5); int (*setup)(struct h5 *h5);
void (*open)(struct h5 *h5); void (*open)(struct h5 *h5);
...@@ -106,6 +111,11 @@ struct h5_vnd { ...@@ -106,6 +111,11 @@ struct h5_vnd {
const struct acpi_gpio_mapping *acpi_gpio_map; const struct acpi_gpio_mapping *acpi_gpio_map;
}; };
struct h5_device_data {
uint32_t driver_info;
struct h5_vnd *vnd;
};
static void h5_reset_rx(struct h5 *h5); static void h5_reset_rx(struct h5 *h5);
static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) static void h5_link_control(struct hci_uart *hu, const void *data, size_t len)
...@@ -791,6 +801,8 @@ static int h5_serdev_probe(struct serdev_device *serdev) ...@@ -791,6 +801,8 @@ static int h5_serdev_probe(struct serdev_device *serdev)
{ {
struct device *dev = &serdev->dev; struct device *dev = &serdev->dev;
struct h5 *h5; struct h5 *h5;
const struct h5_device_data *data;
int err;
h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL); h5 = devm_kzalloc(dev, sizeof(*h5), GFP_KERNEL);
if (!h5) if (!h5)
...@@ -807,20 +819,19 @@ static int h5_serdev_probe(struct serdev_device *serdev) ...@@ -807,20 +819,19 @@ static int h5_serdev_probe(struct serdev_device *serdev)
if (!match) if (!match)
return -ENODEV; return -ENODEV;
h5->vnd = (const struct h5_vnd *)match->driver_data; data = (const struct h5_device_data *)match->driver_data;
h5->vnd = data->vnd;
h5->id = (char *)match->id; h5->id = (char *)match->id;
if (h5->vnd->acpi_gpio_map) if (h5->vnd->acpi_gpio_map)
devm_acpi_dev_add_driver_gpios(dev, devm_acpi_dev_add_driver_gpios(dev,
h5->vnd->acpi_gpio_map); h5->vnd->acpi_gpio_map);
} else { } else {
const void *data;
data = of_device_get_match_data(dev); data = of_device_get_match_data(dev);
if (!data) if (!data)
return -ENODEV; return -ENODEV;
h5->vnd = (const struct h5_vnd *)data; h5->vnd = data->vnd;
} }
...@@ -833,7 +844,14 @@ static int h5_serdev_probe(struct serdev_device *serdev) ...@@ -833,7 +844,14 @@ static int h5_serdev_probe(struct serdev_device *serdev)
if (IS_ERR(h5->device_wake_gpio)) if (IS_ERR(h5->device_wake_gpio))
return PTR_ERR(h5->device_wake_gpio); return PTR_ERR(h5->device_wake_gpio);
return hci_uart_register_device(&h5->serdev_hu, &h5p); err = hci_uart_register_device(&h5->serdev_hu, &h5p);
if (err)
return err;
if (data->driver_info & H5_INFO_WAKEUP_DISABLE)
set_bit(H5_WAKEUP_DISABLE, &h5->flags);
return 0;
} }
static void h5_serdev_remove(struct serdev_device *serdev) static void h5_serdev_remove(struct serdev_device *serdev)
...@@ -921,7 +939,8 @@ static void h5_btrtl_open(struct h5 *h5) ...@@ -921,7 +939,8 @@ static void h5_btrtl_open(struct h5 *h5)
* done by the hci_suspend_notifier is not necessary; it actually causes * done by the hci_suspend_notifier is not necessary; it actually causes
* delays and a bunch of errors to get logged, so disable it. * delays and a bunch of errors to get logged, so disable it.
*/ */
set_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &h5->hu->flags); if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
set_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &h5->hu->flags);
/* Devices always start with these fixed parameters */ /* Devices always start with these fixed parameters */
serdev_device_set_flow_control(h5->hu->serdev, false); serdev_device_set_flow_control(h5->hu->serdev, false);
...@@ -942,15 +961,18 @@ static void h5_btrtl_close(struct h5 *h5) ...@@ -942,15 +961,18 @@ static void h5_btrtl_close(struct h5 *h5)
/* Suspend/resume support. On many devices the RTL BT device loses power during /* Suspend/resume support. On many devices the RTL BT device loses power during
* suspend/resume, causing it to lose its firmware and all state. So we simply * suspend/resume, causing it to lose its firmware and all state. So we simply
* turn it off on suspend and reprobe on resume. This mirrors how RTL devices * turn it off on suspend and reprobe on resume. This mirrors how RTL devices
* are handled in the USB driver, where the USB_QUIRK_RESET_RESUME is used which * are handled in the USB driver, where the BTUSB_WAKEUP_DISABLE is used which
* also causes a reprobe on resume. * also causes a reprobe on resume.
*/ */
static int h5_btrtl_suspend(struct h5 *h5) static int h5_btrtl_suspend(struct h5 *h5)
{ {
serdev_device_set_flow_control(h5->hu->serdev, false); serdev_device_set_flow_control(h5->hu->serdev, false);
gpiod_set_value_cansleep(h5->device_wake_gpio, 0); gpiod_set_value_cansleep(h5->device_wake_gpio, 0);
gpiod_set_value_cansleep(h5->enable_gpio, 0);
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
gpiod_set_value_cansleep(h5->enable_gpio, 0);
return 0; return 0;
} }
...@@ -976,17 +998,21 @@ static void h5_btrtl_reprobe_worker(struct work_struct *work) ...@@ -976,17 +998,21 @@ static void h5_btrtl_reprobe_worker(struct work_struct *work)
static int h5_btrtl_resume(struct h5 *h5) static int h5_btrtl_resume(struct h5 *h5)
{ {
struct h5_btrtl_reprobe *reprobe; if (test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
struct h5_btrtl_reprobe *reprobe;
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
if (!reprobe) if (!reprobe)
return -ENOMEM; return -ENOMEM;
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker); INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
reprobe->dev = get_device(&h5->hu->serdev->dev); reprobe->dev = get_device(&h5->hu->serdev->dev);
queue_work(system_long_wq, &reprobe->work); queue_work(system_long_wq, &reprobe->work);
} else {
gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
}
return 0; return 0;
} }
...@@ -1008,13 +1034,22 @@ static struct h5_vnd rtl_vnd = { ...@@ -1008,13 +1034,22 @@ static struct h5_vnd rtl_vnd = {
.resume = h5_btrtl_resume, .resume = h5_btrtl_resume,
.acpi_gpio_map = acpi_btrtl_gpios, .acpi_gpio_map = acpi_btrtl_gpios,
}; };
static const struct h5_device_data h5_data_rtl8822cs = {
.vnd = &rtl_vnd,
};
static const struct h5_device_data h5_data_rtl8723bs = {
.driver_info = H5_INFO_WAKEUP_DISABLE,
.vnd = &rtl_vnd,
};
#endif #endif
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
static const struct acpi_device_id h5_acpi_match[] = { static const struct acpi_device_id h5_acpi_match[] = {
#ifdef CONFIG_BT_HCIUART_RTL #ifdef CONFIG_BT_HCIUART_RTL
{ "OBDA0623", (kernel_ulong_t)&rtl_vnd }, { "OBDA0623", (kernel_ulong_t)&h5_data_rtl8723bs },
{ "OBDA8723", (kernel_ulong_t)&rtl_vnd }, { "OBDA8723", (kernel_ulong_t)&h5_data_rtl8723bs },
#endif #endif
{ }, { },
}; };
...@@ -1028,11 +1063,11 @@ static const struct dev_pm_ops h5_serdev_pm_ops = { ...@@ -1028,11 +1063,11 @@ static const struct dev_pm_ops h5_serdev_pm_ops = {
static const struct of_device_id rtl_bluetooth_of_match[] = { static const struct of_device_id rtl_bluetooth_of_match[] = {
#ifdef CONFIG_BT_HCIUART_RTL #ifdef CONFIG_BT_HCIUART_RTL
{ .compatible = "realtek,rtl8822cs-bt", { .compatible = "realtek,rtl8822cs-bt",
.data = (const void *)&rtl_vnd }, .data = (const void *)&h5_data_rtl8822cs },
{ .compatible = "realtek,rtl8723bs-bt", { .compatible = "realtek,rtl8723bs-bt",
.data = (const void *)&rtl_vnd }, .data = (const void *)&h5_data_rtl8723bs },
{ .compatible = "realtek,rtl8723ds-bt", { .compatible = "realtek,rtl8723ds-bt",
.data = (const void *)&rtl_vnd }, .data = (const void *)&h5_data_rtl8723bs },
#endif #endif
{ }, { },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment