Skip to content

Commit

Permalink
Merge tag 'for-net-2024-08-23' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/bluetooth/bluetooth

Luiz Augusto von Dentz says:

====================
bluetooth pull request for net:

 - btintel: Allow configuring drive strength of BRI
 - hci_core: Fix not handling hibernation actions
 - btnxpuart: Fix random crash seen while removing driver

* tag 'for-net-2024-08-23' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth:
  Bluetooth: hci_core: Fix not handling hibernation actions
  Bluetooth: btnxpuart: Fix random crash seen while removing driver
  Bluetooth: btintel: Allow configuring drive strength of BRI
====================

Link: https://patch.msgid.link/20240823200008.65241-1-luiz.dentz@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
kuba-moo committed Aug 26, 2024
2 parents 4186c8d + 18b3256 commit 31a9729
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 4 deletions.
124 changes: 124 additions & 0 deletions drivers/bluetooth/btintel.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include <asm/unaligned.h>
#include <linux/efi.h>

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
Expand All @@ -26,6 +27,8 @@
#define ECDSA_OFFSET 644
#define ECDSA_HEADER_LEN 320

#define BTINTEL_EFI_DSBR L"UefiCnvCommonDSBR"

enum {
DSM_SET_WDISABLE2_DELAY = 1,
DSM_SET_RESET_METHOD = 3,
Expand Down Expand Up @@ -2616,6 +2619,120 @@ static u8 btintel_classify_pkt_type(struct hci_dev *hdev, struct sk_buff *skb)
return hci_skb_pkt_type(skb);
}

/*
* UefiCnvCommonDSBR UEFI variable provides information from the OEM platforms
* if they have replaced the BRI (Bluetooth Radio Interface) resistor to
* overcome the potential STEP errors on their designs. Based on the
* configauration, bluetooth firmware shall adjust the BRI response line drive
* strength. The below structure represents DSBR data.
* struct {
* u8 header;
* u32 dsbr;
* } __packed;
*
* header - defines revision number of the structure
* dsbr - defines drive strength BRI response
* bit0
* 0 - instructs bluetooth firmware to use default values
* 1 - instructs bluetooth firmware to override default values
* bit3:1
* Reserved
* bit7:4
* DSBR override values (only if bit0 is set. Default value is 0xF
* bit31:7
* Reserved
* Expected values for dsbr field:
* 1. 0xF1 - indicates that the resistor on board is 33 Ohm
* 2. 0x00 or 0xB1 - indicates that the resistor on board is 10 Ohm
* 3. Non existing UEFI variable or invalid (none of the above) - indicates
* that the resistor on board is 10 Ohm
* Even if uefi variable is not present, driver shall send 0xfc0a command to
* firmware to use default values.
*
*/
static int btintel_uefi_get_dsbr(u32 *dsbr_var)
{
struct btintel_dsbr {
u8 header;
u32 dsbr;
} __packed data;

efi_status_t status;
unsigned long data_size = 0;
efi_guid_t guid = EFI_GUID(0xe65d8884, 0xd4af, 0x4b20, 0x8d, 0x03,
0x77, 0x2e, 0xcc, 0x3d, 0xa5, 0x31);

if (!IS_ENABLED(CONFIG_EFI))
return -EOPNOTSUPP;

if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
return -EOPNOTSUPP;

status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size,
NULL);

if (status != EFI_BUFFER_TOO_SMALL || !data_size)
return -EIO;

status = efi.get_variable(BTINTEL_EFI_DSBR, &guid, NULL, &data_size,
&data);

if (status != EFI_SUCCESS)
return -ENXIO;

*dsbr_var = data.dsbr;
return 0;
}

static int btintel_set_dsbr(struct hci_dev *hdev, struct intel_version_tlv *ver)
{
struct btintel_dsbr_cmd {
u8 enable;
u8 dsbr;
} __packed;

struct btintel_dsbr_cmd cmd;
struct sk_buff *skb;
u8 status;
u32 dsbr;
bool apply_dsbr;
int err;

/* DSBR command needs to be sent for BlazarI + B0 step product after
* downloading IML image.
*/
apply_dsbr = (ver->img_type == BTINTEL_IMG_IML &&
((ver->cnvi_top & 0xfff) == BTINTEL_CNVI_BLAZARI) &&
INTEL_CNVX_TOP_STEP(ver->cnvi_top) == 0x01);

if (!apply_dsbr)
return 0;

dsbr = 0;
err = btintel_uefi_get_dsbr(&dsbr);
if (err < 0)
bt_dev_dbg(hdev, "Error reading efi: %ls (%d)",
BTINTEL_EFI_DSBR, err);

cmd.enable = dsbr & BIT(0);
cmd.dsbr = dsbr >> 4 & 0xF;

bt_dev_info(hdev, "dsbr: enable: 0x%2.2x value: 0x%2.2x", cmd.enable,
cmd.dsbr);

skb = __hci_cmd_sync(hdev, 0xfc0a, sizeof(cmd), &cmd, HCI_CMD_TIMEOUT);
if (IS_ERR(skb))
return -bt_to_errno(PTR_ERR(skb));

status = skb->data[0];
kfree_skb(skb);

if (status)
return -bt_to_errno(status);

return 0;
}

int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
struct intel_version_tlv *ver)
{
Expand Down Expand Up @@ -2650,6 +2767,13 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
if (err)
return err;

/* set drive strength of BRI response */
err = btintel_set_dsbr(hdev, ver);
if (err) {
bt_dev_err(hdev, "Failed to send dsbr command (%d)", err);
return err;
}

/* If image type returned is BTINTEL_IMG_IML, then controller supports
* intermediate loader image
*/
Expand Down
20 changes: 18 additions & 2 deletions drivers/bluetooth/btnxpuart.c
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,23 @@ static bool ps_wakeup(struct btnxpuart_dev *nxpdev)
return false;
}

static void ps_cleanup(struct btnxpuart_dev *nxpdev)
{
struct ps_data *psdata = &nxpdev->psdata;
u8 ps_state;

mutex_lock(&psdata->ps_lock);
ps_state = psdata->ps_state;
mutex_unlock(&psdata->ps_lock);

if (ps_state != PS_STATE_AWAKE)
ps_control(psdata->hdev, PS_STATE_AWAKE);

ps_cancel_timer(nxpdev);
cancel_work_sync(&psdata->work);
mutex_destroy(&psdata->ps_lock);
}

static int send_ps_cmd(struct hci_dev *hdev, void *data)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
Expand Down Expand Up @@ -1363,7 +1380,6 @@ static int btnxpuart_close(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);

ps_wakeup(nxpdev);
serdev_device_close(nxpdev->serdev);
skb_queue_purge(&nxpdev->txq);
if (!IS_ERR_OR_NULL(nxpdev->rx_skb)) {
Expand Down Expand Up @@ -1516,8 +1532,8 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
nxpdev->new_baudrate = nxpdev->fw_init_baudrate;
nxp_set_baudrate_cmd(hdev, NULL);
}
ps_cancel_timer(nxpdev);
}
ps_cleanup(nxpdev);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
Expand Down
10 changes: 8 additions & 2 deletions net/bluetooth/hci_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -2406,10 +2406,16 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action,
/* To avoid a potential race with hci_unregister_dev. */
hci_dev_hold(hdev);

if (action == PM_SUSPEND_PREPARE)
switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
ret = hci_suspend_dev(hdev);
else if (action == PM_POST_SUSPEND)
break;
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
ret = hci_resume_dev(hdev);
break;
}

if (ret)
bt_dev_err(hdev, "Suspend notifier action (%lu) failed: %d",
Expand Down

0 comments on commit 31a9729

Please sign in to comment.