The hci_h5 already supports Realtek controllers discovered via ACPI. This commit adds support for discovering via device tree for ACPI-less platforms. Signed-off-by: Hugo Grostabussiat <bonstra@xxxxxxxxxxxxxxxxx> --- drivers/bluetooth/Kconfig | 2 +- drivers/bluetooth/btrtl.c | 75 +++++++++++++++++++++----------------- drivers/bluetooth/btrtl.h | 3 +- drivers/bluetooth/hci_h5.c | 65 ++++++++++++++++++++++++++------- 4 files changed, 95 insertions(+), 50 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index aae665a3a254..48c9a004b033 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -211,7 +211,7 @@ config BT_HCIUART_RTL depends on BT_HCIUART depends on BT_HCIUART_SERDEV depends on GPIOLIB - depends on ACPI + depends on (ACPI || OF) select BT_HCIUART_3WIRE select BT_RTL help diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index 4f75a9b61d09..01b0eb4b57d7 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -17,6 +17,8 @@ #define VERSION "0.1" +#define FW_DIR "rtl_bt" + #define RTL_EPATCH_SIGNATURE "Realtech" #define RTL_ROM_LMP_3499 0x3499 #define RTL_ROM_LMP_8723A 0x1200 @@ -45,7 +47,7 @@ struct id_table { bool config_needed; bool has_rom_version; char *fw_name; - char *cfg_name; + char *cfg_basename; }; struct btrtl_device_info { @@ -61,14 +63,14 @@ static const struct id_table ic_id_table[] = { { IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8723A, 0x0, .config_needed = false, .has_rom_version = false, - .fw_name = "rtl_bt/rtl8723a_fw.bin", - .cfg_name = NULL }, + .fw_name = FW_DIR "/rtl8723a_fw.bin", + .cfg_basename = NULL }, { IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_3499, 0x0, .config_needed = false, .has_rom_version = false, - .fw_name = "rtl_bt/rtl8723a_fw.bin", - .cfg_name = NULL }, + .fw_name = FW_DIR "/rtl8723a_fw.bin", + .cfg_basename = NULL }, /* 8723BS */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | @@ -79,22 +81,22 @@ static const struct id_table ic_id_table[] = { .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8723bs_fw.bin", - .cfg_name = "rtl_bt/rtl8723bs_config" }, + .fw_name = FW_DIR "/rtl8723bs_fw.bin", + .cfg_basename = "rtl8723bs_config" }, /* 8723B */ { IC_INFO(RTL_ROM_LMP_8723B, 0xb), .config_needed = false, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8723b_fw.bin", - .cfg_name = "rtl_bt/rtl8723b_config" }, + .fw_name = FW_DIR "/rtl8723b_fw.bin", + .cfg_basename = "rtl8723b_config" }, /* 8723D */ { IC_INFO(RTL_ROM_LMP_8723B, 0xd), .config_needed = true, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8723d_fw.bin", - .cfg_name = "rtl_bt/rtl8723d_config" }, + .fw_name = FW_DIR "/rtl8723d_fw.bin", + .cfg_basename = "rtl8723d_config" }, /* 8723DS */ { .match_flags = IC_MATCH_FL_LMPSUBV | IC_MATCH_FL_HCIREV | @@ -105,50 +107,50 @@ static const struct id_table ic_id_table[] = { .hci_bus = HCI_UART, .config_needed = true, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8723ds_fw.bin", - .cfg_name = "rtl_bt/rtl8723ds_config" }, + .fw_name = FW_DIR "/rtl8723ds_fw.bin", + .cfg_basename = "rtl8723ds_config" }, /* 8723DU */ { IC_INFO(RTL_ROM_LMP_8723D, 0x826C), .config_needed = true, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8723d_fw.bin", - .cfg_name = "rtl_bt/rtl8723d_config" }, + .fw_name = FW_DIR "/rtl8723d_fw.bin", + .cfg_basename = "rtl8723d_config" }, /* 8821A */ { IC_INFO(RTL_ROM_LMP_8821A, 0xa), .config_needed = false, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8821a_fw.bin", - .cfg_name = "rtl_bt/rtl8821a_config" }, + .fw_name = FW_DIR "/rtl8821a_fw.bin", + .cfg_basename = "rtl8821a_config" }, /* 8821C */ { IC_INFO(RTL_ROM_LMP_8821A, 0xc), .config_needed = false, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8821c_fw.bin", - .cfg_name = "rtl_bt/rtl8821c_config" }, + .fw_name = FW_DIR "/rtl8821c_fw.bin", + .cfg_basename = "rtl8821c_config" }, /* 8761A */ { IC_MATCH_FL_LMPSUBV, RTL_ROM_LMP_8761A, 0x0, .config_needed = false, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8761a_fw.bin", - .cfg_name = "rtl_bt/rtl8761a_config" }, + .fw_name = FW_DIR "/rtl8761a_fw.bin", + .cfg_basename = "rtl8761a_config" }, /* 8822C with USB interface */ { IC_INFO(RTL_ROM_LMP_8822B, 0xc), .config_needed = false, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8822cu_fw.bin", - .cfg_name = "rtl_bt/rtl8822cu_config" }, + .fw_name = FW_DIR "/rtl8822cu_fw.bin", + .cfg_basename = "rtl8822cu_config" }, /* 8822B */ { IC_INFO(RTL_ROM_LMP_8822B, 0xb), .config_needed = true, .has_rom_version = true, - .fw_name = "rtl_bt/rtl8822b_fw.bin", - .cfg_name = "rtl_bt/rtl8822b_config" }, + .fw_name = FW_DIR "/rtl8822b_fw.bin", + .cfg_basename = "rtl8822b_config" }, }; static const struct id_table *btrtl_match_ic(u16 lmp_subver, u16 hci_rev, @@ -514,7 +516,8 @@ void btrtl_free(struct btrtl_device_info *btrtl_dev) EXPORT_SYMBOL_GPL(btrtl_free); struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, - const char *postfix) + const char *cfg, + bool cfg_is_postfix) { struct btrtl_device_info *btrtl_dev; struct sk_buff *skb; @@ -570,20 +573,24 @@ struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, goto err_free; } - if (btrtl_dev->ic_info->cfg_name) { - if (postfix) { - snprintf(cfg_name, sizeof(cfg_name), "%s-%s.bin", - btrtl_dev->ic_info->cfg_name, postfix); + if (btrtl_dev->ic_info->cfg_basename) { + if (cfg && cfg_is_postfix) { + snprintf(cfg_name, sizeof(cfg_name), "%s/%s-%s.bin", + FW_DIR, btrtl_dev->ic_info->cfg_basename, + cfg); + } else if (cfg && !cfg_is_postfix) { + snprintf(cfg_name, sizeof(cfg_name), "%s/%s", + FW_DIR, cfg); } else { - snprintf(cfg_name, sizeof(cfg_name), "%s.bin", - btrtl_dev->ic_info->cfg_name); + snprintf(cfg_name, sizeof(cfg_name), "%s/%s.bin", + FW_DIR, btrtl_dev->ic_info->cfg_basename); } btrtl_dev->cfg_len = rtl_load_file(hdev, cfg_name, &btrtl_dev->cfg_data); if (btrtl_dev->ic_info->config_needed && btrtl_dev->cfg_len <= 0) { rtl_dev_err(hdev, "mandatory config file %s not found\n", - btrtl_dev->ic_info->cfg_name); + cfg_name); ret = btrtl_dev->cfg_len; goto err_free; } @@ -633,7 +640,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev) struct btrtl_device_info *btrtl_dev; int ret; - btrtl_dev = btrtl_initialize(hdev, NULL); + btrtl_dev = btrtl_initialize(hdev, NULL, false); if (IS_ERR(btrtl_dev)) return PTR_ERR(btrtl_dev); diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index 10ad40c3e42c..b83c0a3f2a49 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -50,7 +50,8 @@ struct rtl_vendor_config { #if IS_ENABLED(CONFIG_BT_RTL) struct btrtl_device_info *btrtl_initialize(struct hci_dev *hdev, - const char *postfix); + const char *cfg, + bool cfg_is_postfix); void btrtl_free(struct btrtl_device_info *btrtl_dev); int btrtl_download_firmware(struct hci_dev *hdev, struct btrtl_device_info *btrtl_dev); diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index dacf297baf59..a0d8d4be9b0e 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -11,6 +11,7 @@ #include <linux/gpio/consumer.h> #include <linux/kernel.h> #include <linux/mod_devicetable.h> +#include <linux/of_device.h> #include <linux/serdev.h> #include <linux/skbuff.h> @@ -90,9 +91,12 @@ struct h5 { } sleep; const struct h5_vnd *vnd; - const char *id; + const char *cfg; /* Board config file name or postfix */ + bool cfg_is_postfix; /* Whether cfg must be appended to the base name + * to the full config file name + */ - struct gpio_desc *enable_gpio; + struct gpio_desc *powerdown_gpio; struct gpio_desc *device_wake_gpio; }; @@ -782,7 +786,6 @@ static const struct hci_uart_proto h5p = { static int h5_serdev_probe(struct serdev_device *serdev) { - const struct acpi_device_id *match; struct device *dev = &serdev->dev; struct h5 *h5; @@ -797,21 +800,40 @@ static int h5_serdev_probe(struct serdev_device *serdev) serdev_device_set_drvdata(serdev, h5); if (has_acpi_companion(dev)) { - match = acpi_match_device(dev->driver->acpi_match_table, dev); + const struct acpi_device_id *match; + + match = acpi_match_device( + dev->driver->acpi_match_table, dev); if (!match) return -ENODEV; h5->vnd = (const struct h5_vnd *)match->driver_data; - h5->id = (char *)match->id; + h5->cfg = (char *)match->id; + h5->cfg_is_postfix = true; if (h5->vnd->acpi_gpio_map) devm_acpi_dev_add_driver_gpios(dev, h5->vnd->acpi_gpio_map); + } else if (dev->of_node) { + const struct of_device_id *match; + const char *cfgname = NULL; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match) + return -ENODEV; + + of_property_read_string(dev->of_node, + "firmware-name", &cfgname); + + h5->vnd = (const struct h5_vnd *)match->data; + h5->cfg = cfgname; + h5->cfg_is_postfix = false; } - h5->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); - if (IS_ERR(h5->enable_gpio)) - return PTR_ERR(h5->enable_gpio); + h5->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(h5->powerdown_gpio)) + return PTR_ERR(h5->powerdown_gpio); h5->device_wake_gpio = devm_gpiod_get_optional(dev, "device-wake", GPIOD_OUT_LOW); @@ -861,7 +883,8 @@ static int h5_btrtl_setup(struct h5 *h5) bool flow_control; int err; - btrtl_dev = btrtl_initialize(h5->hu->hdev, h5->id); + btrtl_dev = btrtl_initialize(h5->hu->hdev, h5->cfg, + h5->cfg_is_postfix); if (IS_ERR(btrtl_dev)) return PTR_ERR(btrtl_dev); @@ -905,7 +928,7 @@ static void h5_btrtl_open(struct h5 *h5) serdev_device_set_baudrate(h5->hu->serdev, 115200); /* The controller needs up to 500ms to wakeup */ - gpiod_set_value_cansleep(h5->enable_gpio, 1); + gpiod_set_value_cansleep(h5->powerdown_gpio, 0); gpiod_set_value_cansleep(h5->device_wake_gpio, 1); msleep(500); } @@ -913,7 +936,7 @@ static void h5_btrtl_open(struct h5 *h5) static void h5_btrtl_close(struct h5 *h5) { gpiod_set_value_cansleep(h5->device_wake_gpio, 0); - gpiod_set_value_cansleep(h5->enable_gpio, 0); + gpiod_set_value_cansleep(h5->powerdown_gpio, 1); } /* Suspend/resume support. On many devices the RTL BT device loses power during @@ -926,7 +949,7 @@ static int h5_btrtl_suspend(struct h5 *h5) { serdev_device_set_flow_control(h5->hu->serdev, false); gpiod_set_value_cansleep(h5->device_wake_gpio, 0); - gpiod_set_value_cansleep(h5->enable_gpio, 0); + gpiod_set_value_cansleep(h5->powerdown_gpio, 1); return 0; } @@ -967,11 +990,11 @@ static int h5_btrtl_resume(struct h5 *h5) } static const struct acpi_gpio_params btrtl_device_wake_gpios = { 0, 0, false }; -static const struct acpi_gpio_params btrtl_enable_gpios = { 1, 0, false }; +static const struct acpi_gpio_params btrtl_powerdown_gpios = { 1, 0, true }; static const struct acpi_gpio_params btrtl_host_wake_gpios = { 2, 0, false }; static const struct acpi_gpio_mapping acpi_btrtl_gpios[] = { { "device-wake-gpios", &btrtl_device_wake_gpios, 1 }, - { "enable-gpios", &btrtl_enable_gpios, 1 }, + { "powerdown-gpios", &btrtl_powerdown_gpios, 1 }, { "host-wake-gpios", &btrtl_host_wake_gpios, 1 }, {}, }; @@ -996,6 +1019,19 @@ static const struct acpi_device_id h5_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, h5_acpi_match); #endif +#ifdef CONFIG_OF +static const struct of_device_id h5_of_match[] = { +#ifdef CONFIG_BT_HCIUART_RTL + { + .compatible = "realtek,rtl8723bs-bt", + .data = &rtl_vnd + }, +#endif + { }, +}; +MODULE_DEVICE_TABLE(of, h5_of_match); +#endif + static const struct dev_pm_ops h5_serdev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(h5_serdev_suspend, h5_serdev_resume) }; @@ -1006,6 +1042,7 @@ static struct serdev_device_driver h5_serdev_driver = { .driver = { .name = "hci_uart_h5", .acpi_match_table = ACPI_PTR(h5_acpi_match), + .of_match_table = of_match_ptr(h5_of_match), .pm = &h5_serdev_pm_ops, }, }; -- 2.24.0