Commit 31a97295 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
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: default avatarJakub Kicinski <kuba@kernel.org>
parents 4186c8d9 18b3256d
Loading
Loading
Loading
Loading
+124 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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,
@@ -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)
{
@@ -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
	 */
+18 −2
Original line number Diff line number Diff line
@@ -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);
@@ -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)) {
@@ -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);
}
+8 −2
Original line number Diff line number Diff line
@@ -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",