mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg says: ==================== pull request: bluetooth-next 2018-05-18 Here's the first bluetooth-next pull request for the 4.18 kernel: - Refactoring of the btbcm driver - New USB IDs for QCA_ROME and LiteOn controllers - Buffer overflow fix if the controller sends invalid advertising data length - Various cleanups & fixes for Qualcomm controllers Please let me know if there are any issues pulling. Thanks. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
2c71ab4bb4
|
@ -0,0 +1,30 @@
|
||||||
|
Qualcomm Bluetooth Chips
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This documents the binding structure and common properties for serial
|
||||||
|
attached Qualcomm devices.
|
||||||
|
|
||||||
|
Serial attached Qualcomm devices shall be a child node of the host UART
|
||||||
|
device the slave device is attached to.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- compatible: should contain one of the following:
|
||||||
|
* "qcom,qca6174-bt"
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- enable-gpios: gpio specifier used to enable chip
|
||||||
|
- clocks: clock provided to the controller (SUSCLK_32KHZ)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
serial@7570000 {
|
||||||
|
label = "BT-UART";
|
||||||
|
status = "okay";
|
||||||
|
|
||||||
|
bluetooth {
|
||||||
|
compatible = "qcom,qca6174-bt";
|
||||||
|
|
||||||
|
enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>;
|
||||||
|
clocks = <&divclk4>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -36,4 +36,30 @@ config {
|
||||||
drive-strength = <2>; /* 2 MA */
|
drive-strength = <2>; /* 2 MA */
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
blsp1_uart1_default: blsp1_uart1_default {
|
||||||
|
mux {
|
||||||
|
pins = "gpio41", "gpio42", "gpio43", "gpio44";
|
||||||
|
function = "blsp_uart2";
|
||||||
|
};
|
||||||
|
|
||||||
|
config {
|
||||||
|
pins = "gpio41", "gpio42", "gpio43", "gpio44";
|
||||||
|
drive-strength = <16>;
|
||||||
|
bias-disable;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
blsp1_uart1_sleep: blsp1_uart1_sleep {
|
||||||
|
mux {
|
||||||
|
pins = "gpio41", "gpio42", "gpio43", "gpio44";
|
||||||
|
function = "gpio";
|
||||||
|
};
|
||||||
|
|
||||||
|
config {
|
||||||
|
pins = "gpio41", "gpio42", "gpio43", "gpio44";
|
||||||
|
drive-strength = <2>;
|
||||||
|
bias-disable;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,28 @@ pinconf {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bt_en_gpios: bt_en_gpios {
|
||||||
|
pinconf {
|
||||||
|
pins = "gpio19";
|
||||||
|
function = PMIC_GPIO_FUNC_NORMAL;
|
||||||
|
output-low;
|
||||||
|
power-source = <PM8994_GPIO_S4>; // 1.8V
|
||||||
|
qcom,drive-strength = <PMIC_GPIO_STRENGTH_LOW>;
|
||||||
|
bias-pull-down;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
wlan_en_gpios: wlan_en_gpios {
|
||||||
|
pinconf {
|
||||||
|
pins = "gpio8";
|
||||||
|
function = PMIC_GPIO_FUNC_NORMAL;
|
||||||
|
output-low;
|
||||||
|
power-source = <PM8994_GPIO_S4>; // 1.8V
|
||||||
|
qcom,drive-strength = <PMIC_GPIO_STRENGTH_LOW>;
|
||||||
|
bias-pull-down;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
volume_up_gpio: pm8996_gpio2 {
|
volume_up_gpio: pm8996_gpio2 {
|
||||||
pinconf {
|
pinconf {
|
||||||
pins = "gpio2";
|
pins = "gpio2";
|
||||||
|
@ -26,6 +48,16 @@ pinconf {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
divclk4_pin_a: divclk4 {
|
||||||
|
pinconf {
|
||||||
|
pins = "gpio18";
|
||||||
|
function = PMIC_GPIO_FUNC_FUNC2;
|
||||||
|
|
||||||
|
bias-disable;
|
||||||
|
power-source = <PM8994_GPIO_S4>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
usb3_vbus_det_gpio: pm8996_gpio22 {
|
usb3_vbus_det_gpio: pm8996_gpio22 {
|
||||||
pinconf {
|
pinconf {
|
||||||
pins = "gpio22";
|
pins = "gpio22";
|
||||||
|
|
|
@ -23,6 +23,7 @@ / {
|
||||||
aliases {
|
aliases {
|
||||||
serial0 = &blsp2_uart1;
|
serial0 = &blsp2_uart1;
|
||||||
serial1 = &blsp2_uart2;
|
serial1 = &blsp2_uart2;
|
||||||
|
serial2 = &blsp1_uart1;
|
||||||
i2c0 = &blsp1_i2c2;
|
i2c0 = &blsp1_i2c2;
|
||||||
i2c1 = &blsp2_i2c1;
|
i2c1 = &blsp2_i2c1;
|
||||||
i2c2 = &blsp2_i2c0;
|
i2c2 = &blsp2_i2c0;
|
||||||
|
@ -34,7 +35,36 @@ chosen {
|
||||||
stdout-path = "serial0:115200n8";
|
stdout-path = "serial0:115200n8";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
clocks {
|
||||||
|
divclk4: divclk4 {
|
||||||
|
compatible = "fixed-clock";
|
||||||
|
#clock-cells = <0>;
|
||||||
|
clock-frequency = <32768>;
|
||||||
|
clock-output-names = "divclk4";
|
||||||
|
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&divclk4_pin_a>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
soc {
|
soc {
|
||||||
|
serial@7570000 {
|
||||||
|
label = "BT-UART";
|
||||||
|
status = "okay";
|
||||||
|
pinctrl-names = "default", "sleep";
|
||||||
|
pinctrl-0 = <&blsp1_uart1_default>;
|
||||||
|
pinctrl-1 = <&blsp1_uart1_sleep>;
|
||||||
|
|
||||||
|
bluetooth {
|
||||||
|
compatible = "qcom,qca6174-bt";
|
||||||
|
|
||||||
|
/* bt_disable_n gpio */
|
||||||
|
enable-gpios = <&pm8994_gpios 19 GPIO_ACTIVE_HIGH>;
|
||||||
|
|
||||||
|
clocks = <&divclk4>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
serial@75b0000 {
|
serial@75b0000 {
|
||||||
label = "LS-UART1";
|
label = "LS-UART1";
|
||||||
status = "okay";
|
status = "okay";
|
||||||
|
@ -139,9 +169,40 @@ usb2_id: usb2-id {
|
||||||
pinctrl-0 = <&usb2_vbus_det_gpio>;
|
pinctrl-0 = <&usb2_vbus_det_gpio>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bt_en: bt-en-1-8v {
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&bt_en_gpios>;
|
||||||
|
compatible = "regulator-fixed";
|
||||||
|
regulator-name = "bt-en-regulator";
|
||||||
|
regulator-min-microvolt = <1800000>;
|
||||||
|
regulator-max-microvolt = <1800000>;
|
||||||
|
|
||||||
|
/* WLAN card specific delay */
|
||||||
|
startup-delay-us = <70000>;
|
||||||
|
enable-active-high;
|
||||||
|
};
|
||||||
|
|
||||||
|
wlan_en: wlan-en-1-8v {
|
||||||
|
pinctrl-names = "default";
|
||||||
|
pinctrl-0 = <&wlan_en_gpios>;
|
||||||
|
compatible = "regulator-fixed";
|
||||||
|
regulator-name = "wlan-en-regulator";
|
||||||
|
regulator-min-microvolt = <1800000>;
|
||||||
|
regulator-max-microvolt = <1800000>;
|
||||||
|
|
||||||
|
gpio = <&pm8994_gpios 8 0>;
|
||||||
|
|
||||||
|
/* WLAN card specific delay */
|
||||||
|
startup-delay-us = <70000>;
|
||||||
|
enable-active-high;
|
||||||
|
};
|
||||||
|
|
||||||
agnoc@0 {
|
agnoc@0 {
|
||||||
qcom,pcie@600000 {
|
qcom,pcie@600000 {
|
||||||
|
status = "okay";
|
||||||
perst-gpio = <&msmgpio 35 GPIO_ACTIVE_LOW>;
|
perst-gpio = <&msmgpio 35 GPIO_ACTIVE_LOW>;
|
||||||
|
vddpe-supply = <&wlan_en>;
|
||||||
|
vddpe1-supply = <&bt_en>;
|
||||||
};
|
};
|
||||||
|
|
||||||
qcom,pcie@608000 {
|
qcom,pcie@608000 {
|
||||||
|
|
|
@ -419,6 +419,16 @@ kryocc: clock-controller@6400000 {
|
||||||
#clock-cells = <1>;
|
#clock-cells = <1>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
blsp1_uart1: serial@7570000 {
|
||||||
|
compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
|
||||||
|
reg = <0x07570000 0x1000>;
|
||||||
|
interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>,
|
||||||
|
<&gcc GCC_BLSP1_AHB_CLK>;
|
||||||
|
clock-names = "core", "iface";
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
blsp1_spi0: spi@7575000 {
|
blsp1_spi0: spi@7575000 {
|
||||||
compatible = "qcom,spi-qup-v2.2.1";
|
compatible = "qcom,spi-qup-v2.2.1";
|
||||||
reg = <0x07575000 0x600>;
|
reg = <0x07575000 0x600>;
|
||||||
|
|
|
@ -197,6 +197,7 @@ config BT_HCIUART_BCM
|
||||||
config BT_HCIUART_QCA
|
config BT_HCIUART_QCA
|
||||||
bool "Qualcomm Atheros protocol support"
|
bool "Qualcomm Atheros protocol support"
|
||||||
depends on BT_HCIUART
|
depends on BT_HCIUART
|
||||||
|
depends on BT_HCIUART_SERDEV
|
||||||
select BT_HCIUART_H4
|
select BT_HCIUART_H4
|
||||||
select BT_QCA
|
select BT_QCA
|
||||||
help
|
help
|
||||||
|
|
|
@ -315,10 +315,12 @@ static int btbcm_read_info(struct hci_dev *hdev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct {
|
struct bcm_subver_table {
|
||||||
u16 subver;
|
u16 subver;
|
||||||
const char *name;
|
const char *name;
|
||||||
} bcm_uart_subver_table[] = {
|
};
|
||||||
|
|
||||||
|
static const struct bcm_subver_table bcm_uart_subver_table[] = {
|
||||||
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
||||||
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
||||||
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
||||||
|
@ -330,98 +332,7 @@ static const struct {
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
|
static const struct bcm_subver_table bcm_usb_subver_table[] = {
|
||||||
{
|
|
||||||
u16 subver, rev;
|
|
||||||
const char *hw_name = NULL;
|
|
||||||
struct sk_buff *skb;
|
|
||||||
struct hci_rp_read_local_version *ver;
|
|
||||||
int i, err;
|
|
||||||
|
|
||||||
/* Reset */
|
|
||||||
err = btbcm_reset(hdev);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Read Local Version Info */
|
|
||||||
skb = btbcm_read_local_version(hdev);
|
|
||||||
if (IS_ERR(skb))
|
|
||||||
return PTR_ERR(skb);
|
|
||||||
|
|
||||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
|
||||||
rev = le16_to_cpu(ver->hci_rev);
|
|
||||||
subver = le16_to_cpu(ver->lmp_subver);
|
|
||||||
kfree_skb(skb);
|
|
||||||
|
|
||||||
/* Read controller information */
|
|
||||||
err = btbcm_read_info(hdev);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
switch ((rev & 0xf000) >> 12) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
for (i = 0; bcm_uart_subver_table[i].name; i++) {
|
|
||||||
if (subver == bcm_uart_subver_table[i].subver) {
|
|
||||||
hw_name = bcm_uart_subver_table[i].name;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(fw_name, len, "brcm/%s.hcd", hw_name ? : "BCM");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
|
|
||||||
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(btbcm_initialize);
|
|
||||||
|
|
||||||
int btbcm_finalize(struct hci_dev *hdev)
|
|
||||||
{
|
|
||||||
struct sk_buff *skb;
|
|
||||||
struct hci_rp_read_local_version *ver;
|
|
||||||
u16 subver, rev;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* Reset */
|
|
||||||
err = btbcm_reset(hdev);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Read Local Version Info */
|
|
||||||
skb = btbcm_read_local_version(hdev);
|
|
||||||
if (IS_ERR(skb))
|
|
||||||
return PTR_ERR(skb);
|
|
||||||
|
|
||||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
|
||||||
rev = le16_to_cpu(ver->hci_rev);
|
|
||||||
subver = le16_to_cpu(ver->lmp_subver);
|
|
||||||
kfree_skb(skb);
|
|
||||||
|
|
||||||
bt_dev_info(hdev, "BCM (%3.3u.%3.3u.%3.3u) build %4.4u",
|
|
||||||
(subver & 0xe000) >> 13, (subver & 0x1f00) >> 8,
|
|
||||||
(subver & 0x00ff), rev & 0x0fff);
|
|
||||||
|
|
||||||
btbcm_check_bdaddr(hdev);
|
|
||||||
|
|
||||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(btbcm_finalize);
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
u16 subver;
|
|
||||||
const char *name;
|
|
||||||
} bcm_usb_subver_table[] = {
|
|
||||||
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
|
{ 0x210b, "BCM43142A0" }, /* 001.001.011 */
|
||||||
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
|
{ 0x2112, "BCM4314A0" }, /* 001.001.018 */
|
||||||
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
|
{ 0x2118, "BCM20702A0" }, /* 001.001.024 */
|
||||||
|
@ -435,14 +346,14 @@ static const struct {
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
int btbcm_setup_patchram(struct hci_dev *hdev)
|
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len,
|
||||||
|
bool reinit)
|
||||||
{
|
{
|
||||||
char fw_name[64];
|
|
||||||
const struct firmware *fw;
|
|
||||||
u16 subver, rev, pid, vid;
|
u16 subver, rev, pid, vid;
|
||||||
const char *hw_name = NULL;
|
const char *hw_name = "BCM";
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct hci_rp_read_local_version *ver;
|
struct hci_rp_read_local_version *ver;
|
||||||
|
const struct bcm_subver_table *bcm_subver_table;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
/* Reset */
|
/* Reset */
|
||||||
|
@ -461,25 +372,27 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
/* Read controller information */
|
/* Read controller information */
|
||||||
err = btbcm_read_info(hdev);
|
if (!reinit) {
|
||||||
if (err)
|
err = btbcm_read_info(hdev);
|
||||||
return err;
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
switch ((rev & 0xf000) >> 12) {
|
/* Upper nibble of rev should be between 0 and 3? */
|
||||||
case 0:
|
if (((rev & 0xf000) >> 12) > 3)
|
||||||
case 3:
|
return 0;
|
||||||
for (i = 0; bcm_uart_subver_table[i].name; i++) {
|
|
||||||
if (subver == bcm_uart_subver_table[i].subver) {
|
bcm_subver_table = (hdev->bus == HCI_USB) ? bcm_usb_subver_table :
|
||||||
hw_name = bcm_uart_subver_table[i].name;
|
bcm_uart_subver_table;
|
||||||
break;
|
|
||||||
}
|
for (i = 0; bcm_subver_table[i].name; i++) {
|
||||||
|
if (subver == bcm_subver_table[i].subver) {
|
||||||
|
hw_name = bcm_subver_table[i].name;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(fw_name, sizeof(fw_name), "brcm/%s.hcd",
|
if (hdev->bus == HCI_USB) {
|
||||||
hw_name ? : "BCM");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
/* Read USB Product Info */
|
/* Read USB Product Info */
|
||||||
skb = btbcm_read_usb_product(hdev);
|
skb = btbcm_read_usb_product(hdev);
|
||||||
if (IS_ERR(skb))
|
if (IS_ERR(skb))
|
||||||
|
@ -489,24 +402,50 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||||
pid = get_unaligned_le16(skb->data + 3);
|
pid = get_unaligned_le16(skb->data + 3);
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
|
|
||||||
for (i = 0; bcm_usb_subver_table[i].name; i++) {
|
snprintf(fw_name, len, "brcm/%s-%4.4x-%4.4x.hcd",
|
||||||
if (subver == bcm_usb_subver_table[i].subver) {
|
hw_name, vid, pid);
|
||||||
hw_name = bcm_usb_subver_table[i].name;
|
} else {
|
||||||
break;
|
snprintf(fw_name, len, "brcm/%s.hcd", hw_name);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(fw_name, sizeof(fw_name), "brcm/%s-%4.4x-%4.4x.hcd",
|
|
||||||
hw_name ? : "BCM", vid, pid);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
|
bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
|
||||||
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
hw_name, (subver & 0xe000) >> 13,
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btbcm_initialize);
|
||||||
|
|
||||||
|
int btbcm_finalize(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
char fw_name[64];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Re-initialize */
|
||||||
|
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
btbcm_check_bdaddr(hdev);
|
||||||
|
|
||||||
|
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(btbcm_finalize);
|
||||||
|
|
||||||
|
int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||||
|
{
|
||||||
|
char fw_name[64];
|
||||||
|
const struct firmware *fw;
|
||||||
|
struct sk_buff *skb;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Initialize */
|
||||||
|
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), false);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = request_firmware(&fw, fw_name, &hdev->dev);
|
err = request_firmware(&fw, fw_name, &hdev->dev);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
bt_dev_info(hdev, "BCM: Patch %s not found", fw_name);
|
bt_dev_info(hdev, "BCM: Patch %s not found", fw_name);
|
||||||
|
@ -517,25 +456,11 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||||
|
|
||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
|
|
||||||
/* Reset */
|
/* Re-initialize */
|
||||||
err = btbcm_reset(hdev);
|
err = btbcm_initialize(hdev, fw_name, sizeof(fw_name), true);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* Read Local Version Info */
|
|
||||||
skb = btbcm_read_local_version(hdev);
|
|
||||||
if (IS_ERR(skb))
|
|
||||||
return PTR_ERR(skb);
|
|
||||||
|
|
||||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
|
||||||
rev = le16_to_cpu(ver->hci_rev);
|
|
||||||
subver = le16_to_cpu(ver->lmp_subver);
|
|
||||||
kfree_skb(skb);
|
|
||||||
|
|
||||||
bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u",
|
|
||||||
hw_name ? : "BCM", (subver & 0xe000) >> 13,
|
|
||||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
|
||||||
|
|
||||||
/* Read Local Name */
|
/* Read Local Name */
|
||||||
skb = btbcm_read_local_name(hdev);
|
skb = btbcm_read_local_name(hdev);
|
||||||
if (IS_ERR(skb))
|
if (IS_ERR(skb))
|
||||||
|
|
|
@ -73,7 +73,8 @@ int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw);
|
||||||
int btbcm_setup_patchram(struct hci_dev *hdev);
|
int btbcm_setup_patchram(struct hci_dev *hdev);
|
||||||
int btbcm_setup_apple(struct hci_dev *hdev);
|
int btbcm_setup_apple(struct hci_dev *hdev);
|
||||||
|
|
||||||
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len);
|
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len,
|
||||||
|
bool reinit);
|
||||||
int btbcm_finalize(struct hci_dev *hdev);
|
int btbcm_finalize(struct hci_dev *hdev);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -104,7 +105,7 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
|
static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
|
||||||
size_t len)
|
size_t len, bool reinit)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,28 +127,41 @@ static void rome_tlv_check_data(struct rome_config *config,
|
||||||
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
|
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
|
||||||
BT_DBG("Length\t\t : %d bytes", length);
|
BT_DBG("Length\t\t : %d bytes", length);
|
||||||
|
|
||||||
|
config->dnld_mode = ROME_SKIP_EVT_NONE;
|
||||||
|
|
||||||
switch (config->type) {
|
switch (config->type) {
|
||||||
case TLV_TYPE_PATCH:
|
case TLV_TYPE_PATCH:
|
||||||
tlv_patch = (struct tlv_type_patch *)tlv->data;
|
tlv_patch = (struct tlv_type_patch *)tlv->data;
|
||||||
BT_DBG("Total Length\t\t : %d bytes",
|
|
||||||
|
/* For Rome version 1.1 to 3.1, all segment commands
|
||||||
|
* are acked by a vendor specific event (VSE).
|
||||||
|
* For Rome >= 3.2, the download mode field indicates
|
||||||
|
* if VSE is skipped by the controller.
|
||||||
|
* In case VSE is skipped, only the last segment is acked.
|
||||||
|
*/
|
||||||
|
config->dnld_mode = tlv_patch->download_mode;
|
||||||
|
|
||||||
|
BT_DBG("Total Length : %d bytes",
|
||||||
le32_to_cpu(tlv_patch->total_size));
|
le32_to_cpu(tlv_patch->total_size));
|
||||||
BT_DBG("Patch Data Length\t : %d bytes",
|
BT_DBG("Patch Data Length : %d bytes",
|
||||||
le32_to_cpu(tlv_patch->data_length));
|
le32_to_cpu(tlv_patch->data_length));
|
||||||
BT_DBG("Signing Format Version : 0x%x",
|
BT_DBG("Signing Format Version : 0x%x",
|
||||||
tlv_patch->format_version);
|
tlv_patch->format_version);
|
||||||
BT_DBG("Signature Algorithm\t : 0x%x",
|
BT_DBG("Signature Algorithm : 0x%x",
|
||||||
tlv_patch->signature);
|
tlv_patch->signature);
|
||||||
BT_DBG("Reserved\t\t : 0x%x",
|
BT_DBG("Download mode : 0x%x",
|
||||||
le16_to_cpu(tlv_patch->reserved1));
|
tlv_patch->download_mode);
|
||||||
BT_DBG("Product ID\t\t : 0x%04x",
|
BT_DBG("Reserved : 0x%x",
|
||||||
|
tlv_patch->reserved1);
|
||||||
|
BT_DBG("Product ID : 0x%04x",
|
||||||
le16_to_cpu(tlv_patch->product_id));
|
le16_to_cpu(tlv_patch->product_id));
|
||||||
BT_DBG("Rom Build Version\t : 0x%04x",
|
BT_DBG("Rom Build Version : 0x%04x",
|
||||||
le16_to_cpu(tlv_patch->rom_build));
|
le16_to_cpu(tlv_patch->rom_build));
|
||||||
BT_DBG("Patch Version\t\t : 0x%04x",
|
BT_DBG("Patch Version : 0x%04x",
|
||||||
le16_to_cpu(tlv_patch->patch_version));
|
le16_to_cpu(tlv_patch->patch_version));
|
||||||
BT_DBG("Reserved\t\t : 0x%x",
|
BT_DBG("Reserved : 0x%x",
|
||||||
le16_to_cpu(tlv_patch->reserved2));
|
le16_to_cpu(tlv_patch->reserved2));
|
||||||
BT_DBG("Patch Entry Address\t : 0x%x",
|
BT_DBG("Patch Entry Address : 0x%x",
|
||||||
le32_to_cpu(tlv_patch->entry));
|
le32_to_cpu(tlv_patch->entry));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -194,8 +207,8 @@ static void rome_tlv_check_data(struct rome_config *config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
|
static int rome_tlv_send_segment(struct hci_dev *hdev, int seg_size,
|
||||||
const u8 *data)
|
const u8 *data, enum rome_tlv_dnld_mode mode)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct edl_event_hdr *edl;
|
struct edl_event_hdr *edl;
|
||||||
|
@ -203,12 +216,14 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
|
||||||
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
|
u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size);
|
|
||||||
|
|
||||||
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
|
cmd[0] = EDL_PATCH_TLV_REQ_CMD;
|
||||||
cmd[1] = seg_size;
|
cmd[1] = seg_size;
|
||||||
memcpy(cmd + 2, data, seg_size);
|
memcpy(cmd + 2, data, seg_size);
|
||||||
|
|
||||||
|
if (mode == ROME_SKIP_EVT_VSE_CC || mode == ROME_SKIP_EVT_VSE)
|
||||||
|
return __hci_cmd_send(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
|
||||||
|
cmd);
|
||||||
|
|
||||||
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
|
skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2, cmd,
|
||||||
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
|
HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
|
||||||
if (IS_ERR(skb)) {
|
if (IS_ERR(skb)) {
|
||||||
|
@ -245,47 +260,12 @@ static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rome_tlv_download_request(struct hci_dev *hdev,
|
|
||||||
const struct firmware *fw)
|
|
||||||
{
|
|
||||||
const u8 *buffer, *data;
|
|
||||||
int total_segment, remain_size;
|
|
||||||
int ret, i;
|
|
||||||
|
|
||||||
if (!fw || !fw->data)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT;
|
|
||||||
remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT;
|
|
||||||
|
|
||||||
BT_DBG("%s: Total segment num %d remain size %d total size %zu",
|
|
||||||
hdev->name, total_segment, remain_size, fw->size);
|
|
||||||
|
|
||||||
data = fw->data;
|
|
||||||
for (i = 0; i < total_segment; i++) {
|
|
||||||
buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT;
|
|
||||||
ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT,
|
|
||||||
buffer);
|
|
||||||
if (ret < 0)
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remain_size) {
|
|
||||||
buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT;
|
|
||||||
ret = rome_tlv_send_segment(hdev, total_segment, remain_size,
|
|
||||||
buffer);
|
|
||||||
if (ret < 0)
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rome_download_firmware(struct hci_dev *hdev,
|
static int rome_download_firmware(struct hci_dev *hdev,
|
||||||
struct rome_config *config)
|
struct rome_config *config)
|
||||||
{
|
{
|
||||||
const struct firmware *fw;
|
const struct firmware *fw;
|
||||||
int ret;
|
const u8 *segment;
|
||||||
|
int ret, remain, i = 0;
|
||||||
|
|
||||||
bt_dev_info(hdev, "ROME Downloading %s", config->fwname);
|
bt_dev_info(hdev, "ROME Downloading %s", config->fwname);
|
||||||
|
|
||||||
|
@ -298,10 +278,24 @@ static int rome_download_firmware(struct hci_dev *hdev,
|
||||||
|
|
||||||
rome_tlv_check_data(config, fw);
|
rome_tlv_check_data(config, fw);
|
||||||
|
|
||||||
ret = rome_tlv_download_request(hdev, fw);
|
segment = fw->data;
|
||||||
if (ret) {
|
remain = fw->size;
|
||||||
BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
|
while (remain > 0) {
|
||||||
config->fwname, ret);
|
int segsize = min(MAX_SIZE_PER_TLV_SEGMENT, remain);
|
||||||
|
|
||||||
|
bt_dev_dbg(hdev, "Send segment %d, size %d", i++, segsize);
|
||||||
|
|
||||||
|
remain -= segsize;
|
||||||
|
/* The last segment is always acked regardless download mode */
|
||||||
|
if (!remain || segsize < MAX_SIZE_PER_TLV_SEGMENT)
|
||||||
|
config->dnld_mode = ROME_SKIP_EVT_NONE;
|
||||||
|
|
||||||
|
ret = rome_tlv_send_segment(hdev, segsize, segment,
|
||||||
|
config->dnld_mode);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
|
||||||
|
segment += segsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_firmware(fw);
|
release_firmware(fw);
|
||||||
|
|
|
@ -61,6 +61,13 @@ enum qca_bardrate {
|
||||||
QCA_BAUDRATE_RESERVED
|
QCA_BAUDRATE_RESERVED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum rome_tlv_dnld_mode {
|
||||||
|
ROME_SKIP_EVT_NONE,
|
||||||
|
ROME_SKIP_EVT_VSE,
|
||||||
|
ROME_SKIP_EVT_CC,
|
||||||
|
ROME_SKIP_EVT_VSE_CC
|
||||||
|
};
|
||||||
|
|
||||||
enum rome_tlv_type {
|
enum rome_tlv_type {
|
||||||
TLV_TYPE_PATCH = 1,
|
TLV_TYPE_PATCH = 1,
|
||||||
TLV_TYPE_NVM
|
TLV_TYPE_NVM
|
||||||
|
@ -70,6 +77,7 @@ struct rome_config {
|
||||||
u8 type;
|
u8 type;
|
||||||
char fwname[64];
|
char fwname[64];
|
||||||
uint8_t user_baud_rate;
|
uint8_t user_baud_rate;
|
||||||
|
enum rome_tlv_dnld_mode dnld_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct edl_event_hdr {
|
struct edl_event_hdr {
|
||||||
|
@ -94,7 +102,8 @@ struct tlv_type_patch {
|
||||||
__le32 data_length;
|
__le32 data_length;
|
||||||
__u8 format_version;
|
__u8 format_version;
|
||||||
__u8 signature;
|
__u8 signature;
|
||||||
__le16 reserved1;
|
__u8 download_mode;
|
||||||
|
__u8 reserved1;
|
||||||
__le16 product_id;
|
__le16 product_id;
|
||||||
__le16 rom_build;
|
__le16 rom_build;
|
||||||
__le16 patch_version;
|
__le16 patch_version;
|
||||||
|
|
|
@ -65,6 +65,7 @@ static int btqcomsmd_cmd_callback(struct rpmsg_device *rpdev, void *data,
|
||||||
{
|
{
|
||||||
struct btqcomsmd *btq = priv;
|
struct btqcomsmd *btq = priv;
|
||||||
|
|
||||||
|
btq->hdev->stat.byte_rx += count;
|
||||||
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
|
return btqcomsmd_recv(btq->hdev, HCI_EVENT_PKT, data, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,12 +77,21 @@ static int btqcomsmd_send(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
switch (hci_skb_pkt_type(skb)) {
|
switch (hci_skb_pkt_type(skb)) {
|
||||||
case HCI_ACLDATA_PKT:
|
case HCI_ACLDATA_PKT:
|
||||||
ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
|
ret = rpmsg_send(btq->acl_channel, skb->data, skb->len);
|
||||||
|
if (ret) {
|
||||||
|
hdev->stat.err_tx++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
hdev->stat.acl_tx++;
|
hdev->stat.acl_tx++;
|
||||||
hdev->stat.byte_tx += skb->len;
|
hdev->stat.byte_tx += skb->len;
|
||||||
break;
|
break;
|
||||||
case HCI_COMMAND_PKT:
|
case HCI_COMMAND_PKT:
|
||||||
ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
|
ret = rpmsg_send(btq->cmd_channel, skb->data, skb->len);
|
||||||
|
if (ret) {
|
||||||
|
hdev->stat.err_tx++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
hdev->stat.cmd_tx++;
|
hdev->stat.cmd_tx++;
|
||||||
|
hdev->stat.byte_tx += skb->len;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EILSEQ;
|
ret = -EILSEQ;
|
||||||
|
|
|
@ -276,6 +276,8 @@ static const struct usb_device_id blacklist_table[] = {
|
||||||
{ USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME },
|
{ USB_DEVICE(0x04ca, 0x3011), .driver_info = BTUSB_QCA_ROME },
|
||||||
{ USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME },
|
{ USB_DEVICE(0x04ca, 0x3015), .driver_info = BTUSB_QCA_ROME },
|
||||||
{ USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME },
|
{ USB_DEVICE(0x04ca, 0x3016), .driver_info = BTUSB_QCA_ROME },
|
||||||
|
{ USB_DEVICE(0x04ca, 0x301a), .driver_info = BTUSB_QCA_ROME },
|
||||||
|
{ USB_DEVICE(0x13d3, 0x3496), .driver_info = BTUSB_QCA_ROME },
|
||||||
|
|
||||||
/* Broadcom BCM2035 */
|
/* Broadcom BCM2035 */
|
||||||
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
{ USB_DEVICE(0x0a5c, 0x2009), .driver_info = BTUSB_BCM92035 },
|
||||||
|
|
|
@ -501,7 +501,7 @@ static int bcm_setup(struct hci_uart *hu)
|
||||||
hu->hdev->set_diag = bcm_set_diag;
|
hu->hdev->set_diag = bcm_set_diag;
|
||||||
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||||
|
|
||||||
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
|
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name), false);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -794,19 +794,21 @@ static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios[] = {
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
/* Some firmware reports an IRQ which does not work (wrong pin in fw table?) */
|
||||||
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
|
static const struct dmi_system_id bcm_broken_irq_dmi_table[] = {
|
||||||
static const struct dmi_system_id bcm_active_low_irq_dmi_table[] = {
|
{
|
||||||
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
|
.ident = "Meegopad T08",
|
||||||
.ident = "Lenovo ThinkPad 8",
|
|
||||||
.matches = {
|
.matches = {
|
||||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
DMI_EXACT_MATCH(DMI_BOARD_VENDOR,
|
||||||
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"),
|
"To be filled by OEM."),
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_NAME, "T3 MRD"),
|
||||||
|
DMI_EXACT_MATCH(DMI_BOARD_VERSION, "V1.1"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
static int bcm_resource(struct acpi_resource *ares, void *data)
|
static int bcm_resource(struct acpi_resource *ares, void *data)
|
||||||
{
|
{
|
||||||
struct bcm_device *dev = data;
|
struct bcm_device *dev = data;
|
||||||
|
@ -904,6 +906,8 @@ static int bcm_gpio_set_shutdown(struct bcm_device *dev, bool powered)
|
||||||
|
|
||||||
static int bcm_get_resources(struct bcm_device *dev)
|
static int bcm_get_resources(struct bcm_device *dev)
|
||||||
{
|
{
|
||||||
|
const struct dmi_system_id *dmi_id;
|
||||||
|
|
||||||
dev->name = dev_name(dev->dev);
|
dev->name = dev_name(dev->dev);
|
||||||
|
|
||||||
if (x86_apple_machine && !bcm_apple_get_resources(dev))
|
if (x86_apple_machine && !bcm_apple_get_resources(dev))
|
||||||
|
@ -936,6 +940,13 @@ static int bcm_get_resources(struct bcm_device *dev)
|
||||||
dev->irq = gpiod_to_irq(gpio);
|
dev->irq = gpiod_to_irq(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dmi_id = dmi_first_match(bcm_broken_irq_dmi_table);
|
||||||
|
if (dmi_id) {
|
||||||
|
dev_info(dev->dev, "%s: Has a broken IRQ config, disabling IRQ support / runtime-pm\n",
|
||||||
|
dmi_id->ident);
|
||||||
|
dev->irq = 0;
|
||||||
|
}
|
||||||
|
|
||||||
dev_dbg(dev->dev, "BCM irq: %d\n", dev->irq);
|
dev_dbg(dev->dev, "BCM irq: %d\n", dev->irq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -944,7 +955,6 @@ static int bcm_get_resources(struct bcm_device *dev)
|
||||||
static int bcm_acpi_probe(struct bcm_device *dev)
|
static int bcm_acpi_probe(struct bcm_device *dev)
|
||||||
{
|
{
|
||||||
LIST_HEAD(resources);
|
LIST_HEAD(resources);
|
||||||
const struct dmi_system_id *dmi_id;
|
|
||||||
const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
|
const struct acpi_gpio_mapping *gpio_mapping = acpi_bcm_int_last_gpios;
|
||||||
struct resource_entry *entry;
|
struct resource_entry *entry;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -991,13 +1001,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
|
||||||
dev->irq_active_low = irq_polarity;
|
dev->irq_active_low = irq_polarity;
|
||||||
dev_warn(dev->dev, "Overwriting IRQ polarity to active %s by module-param\n",
|
dev_warn(dev->dev, "Overwriting IRQ polarity to active %s by module-param\n",
|
||||||
dev->irq_active_low ? "low" : "high");
|
dev->irq_active_low ? "low" : "high");
|
||||||
} else {
|
|
||||||
dmi_id = dmi_first_match(bcm_active_low_irq_dmi_table);
|
|
||||||
if (dmi_id) {
|
|
||||||
dev_warn(dev->dev, "%s: Overwriting IRQ polarity to active low",
|
|
||||||
dmi_id->ident);
|
|
||||||
dev->irq_active_low = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -447,6 +447,8 @@ static int hci_uart_setup(struct hci_dev *hdev)
|
||||||
btbcm_check_bdaddr(hdev);
|
btbcm_check_bdaddr(hdev);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
|
@ -29,7 +29,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/mod_devicetable.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/serdev.h>
|
||||||
|
|
||||||
#include <net/bluetooth/bluetooth.h>
|
#include <net/bluetooth/bluetooth.h>
|
||||||
#include <net/bluetooth/hci_core.h>
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
@ -50,6 +55,9 @@
|
||||||
#define IBS_TX_IDLE_TIMEOUT_MS 2000
|
#define IBS_TX_IDLE_TIMEOUT_MS 2000
|
||||||
#define BAUDRATE_SETTLE_TIMEOUT_MS 300
|
#define BAUDRATE_SETTLE_TIMEOUT_MS 300
|
||||||
|
|
||||||
|
/* susclk rate */
|
||||||
|
#define SUSCLK_RATE_32KHZ 32768
|
||||||
|
|
||||||
/* HCI_IBS transmit side sleep protocol states */
|
/* HCI_IBS transmit side sleep protocol states */
|
||||||
enum tx_ibs_states {
|
enum tx_ibs_states {
|
||||||
HCI_IBS_TX_ASLEEP,
|
HCI_IBS_TX_ASLEEP,
|
||||||
|
@ -111,6 +119,12 @@ struct qca_data {
|
||||||
u64 votes_off;
|
u64 votes_off;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct qca_serdev {
|
||||||
|
struct hci_uart serdev_hu;
|
||||||
|
struct gpio_desc *bt_en;
|
||||||
|
struct clk *susclk;
|
||||||
|
};
|
||||||
|
|
||||||
static void __serial_clock_on(struct tty_struct *tty)
|
static void __serial_clock_on(struct tty_struct *tty)
|
||||||
{
|
{
|
||||||
/* TODO: Some chipset requires to enable UART clock on client
|
/* TODO: Some chipset requires to enable UART clock on client
|
||||||
|
@ -386,6 +400,7 @@ static void hci_ibs_wake_retrans_timeout(struct timer_list *t)
|
||||||
/* Initialize protocol */
|
/* Initialize protocol */
|
||||||
static int qca_open(struct hci_uart *hu)
|
static int qca_open(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
|
struct qca_serdev *qcadev;
|
||||||
struct qca_data *qca;
|
struct qca_data *qca;
|
||||||
|
|
||||||
BT_DBG("hu %p qca_open", hu);
|
BT_DBG("hu %p qca_open", hu);
|
||||||
|
@ -444,6 +459,13 @@ static int qca_open(struct hci_uart *hu)
|
||||||
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
timer_setup(&qca->tx_idle_timer, hci_ibs_tx_idle_timeout, 0);
|
||||||
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
|
qca->tx_idle_delay = IBS_TX_IDLE_TIMEOUT_MS;
|
||||||
|
|
||||||
|
if (hu->serdev) {
|
||||||
|
serdev_device_open(hu->serdev);
|
||||||
|
|
||||||
|
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||||
|
gpiod_set_value_cansleep(qcadev->bt_en, 1);
|
||||||
|
}
|
||||||
|
|
||||||
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
BT_DBG("HCI_UART_QCA open, tx_idle_delay=%u, wake_retrans=%u",
|
||||||
qca->tx_idle_delay, qca->wake_retrans);
|
qca->tx_idle_delay, qca->wake_retrans);
|
||||||
|
|
||||||
|
@ -512,6 +534,7 @@ static int qca_flush(struct hci_uart *hu)
|
||||||
/* Close protocol */
|
/* Close protocol */
|
||||||
static int qca_close(struct hci_uart *hu)
|
static int qca_close(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
|
struct qca_serdev *qcadev;
|
||||||
struct qca_data *qca = hu->priv;
|
struct qca_data *qca = hu->priv;
|
||||||
|
|
||||||
BT_DBG("hu %p qca close", hu);
|
BT_DBG("hu %p qca close", hu);
|
||||||
|
@ -525,6 +548,13 @@ static int qca_close(struct hci_uart *hu)
|
||||||
destroy_workqueue(qca->workqueue);
|
destroy_workqueue(qca->workqueue);
|
||||||
qca->hu = NULL;
|
qca->hu = NULL;
|
||||||
|
|
||||||
|
if (hu->serdev) {
|
||||||
|
serdev_device_close(hu->serdev);
|
||||||
|
|
||||||
|
qcadev = serdev_device_get_drvdata(hu->serdev);
|
||||||
|
gpiod_set_value_cansleep(qcadev->bt_en, 0);
|
||||||
|
}
|
||||||
|
|
||||||
kfree_skb(qca->rx_skb);
|
kfree_skb(qca->rx_skb);
|
||||||
|
|
||||||
hu->priv = NULL;
|
hu->priv = NULL;
|
||||||
|
@ -885,6 +915,14 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||||
|
{
|
||||||
|
if (hu->serdev)
|
||||||
|
serdev_device_set_baudrate(hu->serdev, speed);
|
||||||
|
else
|
||||||
|
hci_uart_set_baudrate(hu, speed);
|
||||||
|
}
|
||||||
|
|
||||||
static int qca_setup(struct hci_uart *hu)
|
static int qca_setup(struct hci_uart *hu)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = hu->hdev;
|
struct hci_dev *hdev = hu->hdev;
|
||||||
|
@ -905,7 +943,7 @@ static int qca_setup(struct hci_uart *hu)
|
||||||
speed = hu->proto->init_speed;
|
speed = hu->proto->init_speed;
|
||||||
|
|
||||||
if (speed)
|
if (speed)
|
||||||
hci_uart_set_baudrate(hu, speed);
|
host_set_baudrate(hu, speed);
|
||||||
|
|
||||||
/* Setup user speed if needed */
|
/* Setup user speed if needed */
|
||||||
speed = 0;
|
speed = 0;
|
||||||
|
@ -924,7 +962,7 @@ static int qca_setup(struct hci_uart *hu)
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
hci_uart_set_baudrate(hu, speed);
|
host_set_baudrate(hu, speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup patch / NVM configurations */
|
/* Setup patch / NVM configurations */
|
||||||
|
@ -935,6 +973,12 @@ static int qca_setup(struct hci_uart *hu)
|
||||||
} else if (ret == -ENOENT) {
|
} else if (ret == -ENOENT) {
|
||||||
/* No patch/nvm-config found, run with original fw/config */
|
/* No patch/nvm-config found, run with original fw/config */
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
} else if (ret == -EAGAIN) {
|
||||||
|
/*
|
||||||
|
* Userspace firmware loader will return -EAGAIN in case no
|
||||||
|
* patch/nvm-config is found, so run with original fw/config.
|
||||||
|
*/
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup bdaddr */
|
/* Setup bdaddr */
|
||||||
|
@ -958,12 +1002,80 @@ static struct hci_uart_proto qca_proto = {
|
||||||
.dequeue = qca_dequeue,
|
.dequeue = qca_dequeue,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int qca_serdev_probe(struct serdev_device *serdev)
|
||||||
|
{
|
||||||
|
struct qca_serdev *qcadev;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL);
|
||||||
|
if (!qcadev)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
qcadev->serdev_hu.serdev = serdev;
|
||||||
|
serdev_device_set_drvdata(serdev, qcadev);
|
||||||
|
|
||||||
|
qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
|
||||||
|
GPIOD_OUT_LOW);
|
||||||
|
if (IS_ERR(qcadev->bt_en)) {
|
||||||
|
dev_err(&serdev->dev, "failed to acquire enable gpio\n");
|
||||||
|
return PTR_ERR(qcadev->bt_en);
|
||||||
|
}
|
||||||
|
|
||||||
|
qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
|
||||||
|
if (IS_ERR(qcadev->susclk)) {
|
||||||
|
dev_err(&serdev->dev, "failed to acquire clk\n");
|
||||||
|
return PTR_ERR(qcadev->susclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = clk_prepare_enable(qcadev->susclk);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
|
||||||
|
if (err)
|
||||||
|
clk_disable_unprepare(qcadev->susclk);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qca_serdev_remove(struct serdev_device *serdev)
|
||||||
|
{
|
||||||
|
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
|
||||||
|
|
||||||
|
hci_uart_unregister_device(&qcadev->serdev_hu);
|
||||||
|
|
||||||
|
clk_disable_unprepare(qcadev->susclk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id qca_bluetooth_of_match[] = {
|
||||||
|
{ .compatible = "qcom,qca6174-bt" },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match);
|
||||||
|
|
||||||
|
static struct serdev_device_driver qca_serdev_driver = {
|
||||||
|
.probe = qca_serdev_probe,
|
||||||
|
.remove = qca_serdev_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "hci_uart_qca",
|
||||||
|
.of_match_table = qca_bluetooth_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
int __init qca_init(void)
|
int __init qca_init(void)
|
||||||
{
|
{
|
||||||
|
serdev_device_driver_register(&qca_serdev_driver);
|
||||||
|
|
||||||
return hci_uart_register_proto(&qca_proto);
|
return hci_uart_register_proto(&qca_proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
int __exit qca_deinit(void)
|
int __exit qca_deinit(void)
|
||||||
{
|
{
|
||||||
|
serdev_device_driver_unregister(&qca_serdev_driver);
|
||||||
|
|
||||||
return hci_uart_unregister_proto(&qca_proto);
|
return hci_uart_unregister_proto(&qca_proto);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1393,6 +1393,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param, u32 timeout);
|
const void *param, u32 timeout);
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param, u8 event, u32 timeout);
|
const void *param, u8 event, u32 timeout);
|
||||||
|
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
|
const void *param);
|
||||||
|
|
||||||
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
const void *param);
|
const void *param);
|
||||||
|
|
|
@ -3422,6 +3422,37 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int __hci_cmd_send(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
|
const void *param)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
|
||||||
|
if (hci_opcode_ogf(opcode) != 0x3f) {
|
||||||
|
/* A controller receiving a command shall respond with either
|
||||||
|
* a Command Status Event or a Command Complete Event.
|
||||||
|
* Therefore, all standard HCI commands must be sent via the
|
||||||
|
* standard API, using hci_send_cmd or hci_cmd_sync helpers.
|
||||||
|
* Some vendors do not comply with this rule for vendor-specific
|
||||||
|
* commands and do not return any event. We want to support
|
||||||
|
* unresponded commands for such cases only.
|
||||||
|
*/
|
||||||
|
bt_dev_err(hdev, "unresponded command not supported");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb = hci_prepare_cmd(hdev, opcode, plen, param);
|
||||||
|
if (!skb) {
|
||||||
|
bt_dev_err(hdev, "no memory for command (opcode 0x%4.4x)",
|
||||||
|
opcode);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_send_frame(hdev, skb);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(__hci_cmd_send);
|
||||||
|
|
||||||
/* Get data from the previously sent command */
|
/* Get data from the previously sent command */
|
||||||
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -4942,10 +4942,14 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||||
struct hci_ev_le_advertising_info *ev = ptr;
|
struct hci_ev_le_advertising_info *ev = ptr;
|
||||||
s8 rssi;
|
s8 rssi;
|
||||||
|
|
||||||
rssi = ev->data[ev->length];
|
if (ev->length <= HCI_MAX_AD_LENGTH) {
|
||||||
process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
|
rssi = ev->data[ev->length];
|
||||||
ev->bdaddr_type, NULL, 0, rssi,
|
process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
|
||||||
ev->data, ev->length);
|
ev->bdaddr_type, NULL, 0, rssi,
|
||||||
|
ev->data, ev->length);
|
||||||
|
} else {
|
||||||
|
bt_dev_err(hdev, "Dropping invalid advertising data");
|
||||||
|
}
|
||||||
|
|
||||||
ptr += sizeof(*ev) + ev->length + 1;
|
ptr += sizeof(*ev) + ev->length + 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,6 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err)
|
||||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
const void *param, u8 event, u32 timeout)
|
const void *param, u8 event, u32 timeout)
|
||||||
{
|
{
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
@ -135,21 +134,14 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||||
|
|
||||||
hdev->req_status = HCI_REQ_PEND;
|
hdev->req_status = HCI_REQ_PEND;
|
||||||
|
|
||||||
add_wait_queue(&hdev->req_wait_q, &wait);
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
|
|
||||||
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
||||||
if (err < 0) {
|
if (err < 0)
|
||||||
remove_wait_queue(&hdev->req_wait_q, &wait);
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
|
||||||
|
|
||||||
schedule_timeout(timeout);
|
err = wait_event_interruptible_timeout(hdev->req_wait_q,
|
||||||
|
hdev->req_status != HCI_REQ_PEND, timeout);
|
||||||
|
|
||||||
remove_wait_queue(&hdev->req_wait_q, &wait);
|
if (err == -ERESTARTSYS)
|
||||||
|
|
||||||
if (signal_pending(current))
|
|
||||||
return ERR_PTR(-EINTR);
|
return ERR_PTR(-EINTR);
|
||||||
|
|
||||||
switch (hdev->req_status) {
|
switch (hdev->req_status) {
|
||||||
|
@ -197,7 +189,6 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
||||||
unsigned long opt, u32 timeout, u8 *hci_status)
|
unsigned long opt, u32 timeout, u8 *hci_status)
|
||||||
{
|
{
|
||||||
struct hci_request req;
|
struct hci_request req;
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
BT_DBG("%s start", hdev->name);
|
BT_DBG("%s start", hdev->name);
|
||||||
|
@ -213,16 +204,10 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_wait_queue(&hdev->req_wait_q, &wait);
|
|
||||||
set_current_state(TASK_INTERRUPTIBLE);
|
|
||||||
|
|
||||||
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
err = hci_req_run_skb(&req, hci_req_sync_complete);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
hdev->req_status = 0;
|
hdev->req_status = 0;
|
||||||
|
|
||||||
remove_wait_queue(&hdev->req_wait_q, &wait);
|
|
||||||
set_current_state(TASK_RUNNING);
|
|
||||||
|
|
||||||
/* ENODATA means the HCI request command queue is empty.
|
/* ENODATA means the HCI request command queue is empty.
|
||||||
* This can happen when a request with conditionals doesn't
|
* This can happen when a request with conditionals doesn't
|
||||||
* trigger any commands to be sent. This is normal behavior
|
* trigger any commands to be sent. This is normal behavior
|
||||||
|
@ -240,11 +225,10 @@ int __hci_req_sync(struct hci_dev *hdev, int (*func)(struct hci_request *req,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
schedule_timeout(timeout);
|
err = wait_event_interruptible_timeout(hdev->req_wait_q,
|
||||||
|
hdev->req_status != HCI_REQ_PEND, timeout);
|
||||||
|
|
||||||
remove_wait_queue(&hdev->req_wait_q, &wait);
|
if (err == -ERESTARTSYS)
|
||||||
|
|
||||||
if (signal_pending(current))
|
|
||||||
return -EINTR;
|
return -EINTR;
|
||||||
|
|
||||||
switch (hdev->req_status) {
|
switch (hdev->req_status) {
|
||||||
|
|
Loading…
Reference in New Issue