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: Archie Pusaka <apusaka@chromium.org> Reviewed-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org> Reviewed-by: Hilda Wu <hildawu@realtek.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
64832df2ac
commit
66f077dde7
1 changed files with 59 additions and 24 deletions
|
@ -53,6 +53,7 @@
|
||||||
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 {
|
||||||
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 {
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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)
|
||||||
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,6 +939,7 @@ 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.
|
||||||
*/
|
*/
|
||||||
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
|
||||||
set_bit(HCI_UART_NO_SUSPEND_NOTIFIER, &h5->hu->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 */
|
||||||
|
@ -943,14 +962,17 @@ 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);
|
||||||
|
|
||||||
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
|
||||||
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
gpiod_set_value_cansleep(h5->enable_gpio, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,6 +998,7 @@ 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)
|
||||||
{
|
{
|
||||||
|
if (test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
|
||||||
struct h5_btrtl_reprobe *reprobe;
|
struct h5_btrtl_reprobe *reprobe;
|
||||||
|
|
||||||
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
|
reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL);
|
||||||
|
@ -987,6 +1010,9 @@ static int h5_btrtl_resume(struct h5 *h5)
|
||||||
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 = {
|
||||||
.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 = {
|
||||||
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
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue