All of lore.kernel.org
 help / color / mirror / Atom feed
From: Evan Quan <evan.quan@amd.com>
To: <rafael@kernel.org>, <lenb@kernel.org>,
	<Alexander.Deucher@amd.com>, <Christian.Koenig@amd.com>,
	<Xinhui.Pan@amd.com>, <airlied@gmail.com>, <daniel@ffwll.ch>,
	<johannes@sipsolutions.net>, <davem@davemloft.net>,
	<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
	<Mario.Limonciello@amd.com>, <mdaenzer@redhat.com>,
	<maarten.lankhorst@linux.intel.com>, <tzimmermann@suse.de>,
	<hdegoede@redhat.com>, <jingyuwang_vip@163.com>,
	<Lijo.Lazar@amd.com>, <jim.cromie@gmail.com>,
	<bellosilicio@gmail.com>, <andrealmeid@igalia.com>,
	<trix@redhat.com>, <jsg@jsg.id.au>, <arnd@arndb.de>
Cc: <linux-kernel@vger.kernel.org>, <linux-acpi@vger.kernel.org>,
	<amd-gfx@lists.freedesktop.org>,
	<dri-devel@lists.freedesktop.org>,
	<linux-wireless@vger.kernel.org>, <netdev@vger.kernel.org>,
	Evan Quan <evan.quan@amd.com>,
	Mario Limonciello <mario.limonciello@amd.com>
Subject: [PATCH V5 2/9] driver core: add ACPI based WBRF mechanism introduced by AMD
Date: Fri, 30 Jun 2023 18:32:33 +0800	[thread overview]
Message-ID: <20230630103240.1557100-3-evan.quan@amd.com> (raw)
In-Reply-To: <20230630103240.1557100-1-evan.quan@amd.com>

AMD has introduced an ACPI based mechanism to support WBRF for some
platforms with AMD dGPU + WLAN. This needs support from BIOS equipped
with necessary AML implementations and dGPU firmwares.

For those systems without the ACPI mechanism and developing solutions,
user can use the generic WBRF solution for diagnosing potential
interference issues.

Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <evan.quan@amd.com>
Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v4->v5:
  - promote this to be a more generic solution with input argument taking
    `struct device` and provide better scalability to support non-ACPI
    scenarios(Andrew)
  - update the APIs naming and some other minor fixes(Rafael)
---
 drivers/acpi/Makefile         |   2 +
 drivers/acpi/amd_wbrf.c       | 236 ++++++++++++++++++++++++++++++++++
 drivers/base/Kconfig          |  29 +++++
 drivers/base/wbrf.c           |  35 ++++-
 include/linux/acpi_amd_wbrf.h |  38 ++++++
 include/linux/wbrf.h          |   2 +
 6 files changed, 336 insertions(+), 6 deletions(-)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index feb36c0b9446..94b940ddbf88 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -131,3 +131,5 @@ obj-y				+= dptf/
 obj-$(CONFIG_ARM64)		+= arm64/
 
 obj-$(CONFIG_ACPI_VIOT)		+= viot.o
+
+obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
new file mode 100644
index 000000000000..44e38c97acf0
--- /dev/null
+++ b/drivers/acpi/amd_wbrf.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/acpi_amd_wbrf.h>
+
+/* functions */
+#define WBRF_RECORD		0x1
+#define WBRF_RETRIEVE		0x2
+
+/* record actions */
+#define WBRF_RECORD_ADD		0x0
+#define WBRF_RECORD_REMOVE	0x1
+
+#define WBRF_REVISION		0x1
+
+static const guid_t wifi_acpi_dsm_guid =
+	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
+		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
+
+static int wbrf_dsm(struct acpi_device *adev, u8 fn,
+		    union acpi_object *argv4,
+		    union acpi_object **out)
+{
+	union acpi_object *obj;
+	int rc;
+
+	obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
+				WBRF_REVISION, fn, argv4);
+	if (!obj)
+		return -ENXIO;
+
+	switch (obj->type) {
+	case ACPI_TYPE_BUFFER:
+		*out = obj;
+		return 0;
+
+	case ACPI_TYPE_INTEGER:
+		rc =  obj->integer.value ? -EINVAL : 0;
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+	}
+
+	ACPI_FREE(obj);
+
+	return rc;
+}
+
+static int wbrf_record(struct acpi_device *adev, uint8_t action,
+		       struct wbrf_ranges_in *in)
+{
+	union acpi_object *argv4;
+	uint32_t num_of_ranges = 0;
+	uint32_t arg_idx = 0;
+	uint32_t loop_idx;
+	int ret;
+
+	if (!in)
+		return -EINVAL;
+
+	for (loop_idx = 0; loop_idx < ARRAY_SIZE(in->band_list);
+	     loop_idx++)
+		if (in->band_list[loop_idx].start &&
+		    in->band_list[loop_idx].end)
+			num_of_ranges++;
+
+	argv4 = kzalloc(sizeof(*argv4) * (2 * num_of_ranges + 2 + 1), GFP_KERNEL);
+	if (!argv4)
+		return -ENOMEM;
+
+	argv4[arg_idx].package.type = ACPI_TYPE_PACKAGE;
+	argv4[arg_idx].package.count = 2 + 2 * num_of_ranges;
+	argv4[arg_idx++].package.elements = &argv4[1];
+	argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	argv4[arg_idx++].integer.value = num_of_ranges;
+	argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	argv4[arg_idx++].integer.value = action;
+
+	for (loop_idx = 0; loop_idx < ARRAY_SIZE(in->band_list);
+	     loop_idx++) {
+		if (!in->band_list[loop_idx].start ||
+		    !in->band_list[loop_idx].end)
+			continue;
+
+		argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		argv4[arg_idx++].integer.value = in->band_list[loop_idx].start;
+		argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		argv4[arg_idx++].integer.value = in->band_list[loop_idx].end;
+	}
+
+	ret = wbrf_dsm(adev, WBRF_RECORD, argv4, NULL);
+
+	kfree(argv4);
+
+	return ret;
+}
+
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return -ENODEV;
+
+	return wbrf_record(adev, WBRF_RECORD_ADD, in);
+}
+
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return -ENODEV;
+
+	return wbrf_record(adev, WBRF_RECORD_REMOVE, in);
+}
+
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return false;
+
+	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
+			      WBRF_REVISION,
+			      BIT(WBRF_RECORD));
+}
+
+static union acpi_object *
+acpi_evaluate_wbrf(acpi_handle handle, u64 rev, u64 func)
+{
+	acpi_status ret;
+	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object params[4];
+	struct acpi_object_list input = {
+		.count = 4,
+		.pointer = params,
+	};
+
+	params[0].type = ACPI_TYPE_INTEGER;
+	params[0].integer.value = rev;
+	params[1].type = ACPI_TYPE_INTEGER;
+	params[1].integer.value = func;
+	params[2].type = ACPI_TYPE_PACKAGE;
+	params[2].package.count = 0;
+	params[2].package.elements = NULL;
+	params[3].type = ACPI_TYPE_STRING;
+	params[3].string.length = 0;
+	params[3].string.pointer = NULL;
+
+	ret = acpi_evaluate_object(handle, "WBRF", &input, &buf);
+	if (ACPI_SUCCESS(ret))
+		return (union acpi_object *)buf.pointer;
+
+	return NULL;
+}
+
+static bool check_acpi_wbrf(acpi_handle handle, u64 rev, u64 funcs)
+{
+	int i;
+	u64 mask = 0;
+	union acpi_object *obj;
+
+	if (funcs == 0)
+		return false;
+
+	obj = acpi_evaluate_wbrf(handle, rev, 0);
+	if (!obj)
+		return false;
+
+	if (obj->type != ACPI_TYPE_BUFFER)
+		return false;
+
+	/*
+	 * Bit vector providing supported functions information.
+	 * Each bit marks support for one specific function of the WBRF method.
+	 */
+	for (i = 0; i < obj->buffer.length && i < 8; i++)
+		mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
+
+	ACPI_FREE(obj);
+
+	/*
+	 * Bit 0 indicates whether there's support for any functions other than
+	 * function 0.
+	 */
+	if ((mask & 0x1) && (mask & funcs) == funcs)
+		return true;
+
+	return false;
+}
+
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return false;
+
+	return check_acpi_wbrf(adev->handle,
+			       WBRF_REVISION,
+			       BIT(WBRF_RETRIEVE));
+}
+
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_out *out)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	union acpi_object *obj;
+
+	if (!adev)
+		return -ENODEV;
+
+	obj = acpi_evaluate_wbrf(adev->handle,
+				 WBRF_REVISION,
+				 WBRF_RETRIEVE);
+	if (!obj)
+		return -EINVAL;
+
+	WARN(obj->buffer.length != sizeof(*out),
+		"Unexpected buffer length");
+	memcpy(out, obj->buffer.pointer, obj->buffer.length);
+
+	ACPI_FREE(obj);
+
+	return 0;
+}
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 5b441017b225..cbf0b2358c17 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -250,4 +250,33 @@ config WBRF
 	  different domains to notify the frequencies in use so that hardware
 	  can be reconfigured to avoid harmonic conflicts.
 
+config WBRF_AMD_ACPI
+	bool "Use the ACPI mechanism introduced by AMD to support WBRF"
+	default n
+	depends on ACPI
+	select WBRF
+	help
+	  AMD has introduced an ACPI based mechanism to support WBRF for some
+	  platforms with AMD dGPU and WLAN. This needs support from BIOS equipped
+	  with necessary AML implementations and dGPU firmwares.
+
+	  Say Y to enable this ACPI based mechanism. It is suggested to confirm
+	  with the hardware designer/provider first whether your platform
+	  equipped with necessary BIOS and firmwares.
+
+config WBRF_GENERIC
+	bool "Use the generic WBRF solution"
+	default n
+	depends on !WBRF_AMD_ACPI
+	select WBRF
+	help
+	  Ideally it is the hardware designer/provider who should provide a
+	  solution for the possible RF interference issue. Since they know
+	  well whether there could be RF interference issue with their
+	  platforms.
+
+	  Say Y to enable this generic WBRF solution for diagnosing potential
+	  interference issues on systems without the ACPI mechanism and
+	  developing solutions.
+
 endmenu
diff --git a/drivers/base/wbrf.c b/drivers/base/wbrf.c
index 2163a8ec8a9a..d9fab9f3045e 100644
--- a/drivers/base/wbrf.c
+++ b/drivers/base/wbrf.c
@@ -6,9 +6,12 @@
  */
 
 #include <linux/wbrf.h>
+#include <linux/acpi_amd_wbrf.h>
 
 static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
 static DEFINE_MUTEX(wbrf_mutex);
+
+#if defined(CONFIG_WBRF_GENERIC)
 static struct exclusion_range_pool wbrf_pool;
 
 static int _wbrf_add_exclusion_ranges(struct wbrf_ranges_in *in)
@@ -89,6 +92,7 @@ static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out)
 
 	return 0;
 }
+#endif
 
 /**
  * wbrf_supported_producer - Determine if the device can report frequencies
@@ -100,7 +104,12 @@ static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out)
  */
 bool wbrf_supported_producer(struct device *dev)
 {
+#ifdef CONFIG_WBRF_AMD_ACPI
+	return acpi_amd_wbrf_supported_producer(dev);
+#elif defined(CONFIG_WBRF_GENERIC)
 	return true;
+#endif
+	return false;
 }
 EXPORT_SYMBOL_GPL(wbrf_supported_producer);
 
@@ -116,12 +125,15 @@ EXPORT_SYMBOL_GPL(wbrf_supported_producer);
 int wbrf_add_exclusion(struct device *dev,
 		       struct wbrf_ranges_in *in)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r = acpi_amd_wbrf_add_exclusion(dev, in);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_add_exclusion_ranges(in);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 	if (r)
 		return r;
@@ -144,12 +156,15 @@ EXPORT_SYMBOL_GPL(wbrf_add_exclusion);
 int wbrf_remove_exclusion(struct device *dev,
 			  struct wbrf_ranges_in *in)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r  = acpi_amd_wbrf_remove_exclusion(dev, in);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_remove_exclusion_ranges(in);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 	if (r)
 		return r;
@@ -171,7 +186,12 @@ EXPORT_SYMBOL_GPL(wbrf_remove_exclusion);
  */
 bool wbrf_supported_consumer(struct device *dev)
 {
+#ifdef CONFIG_WBRF_AMD_ACPI
+	return acpi_amd_wbrf_supported_consumer(dev);
+#elif defined(CONFIG_WBRF_GENERIC)
 	return true;
+#endif
+	return false;
 }
 EXPORT_SYMBOL_GPL(wbrf_supported_consumer);
 
@@ -214,12 +234,15 @@ EXPORT_SYMBOL_GPL(wbrf_unregister_notifier);
 int wbrf_retrieve_exclusions(struct device *dev,
 			     struct wbrf_ranges_out *out)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r = acpi_amd_wbrf_retrieve_exclusions(dev, out);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_retrieve_exclusion_ranges(out);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 
 	return r;
diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..c274093972e3
--- /dev/null
+++ b/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#ifndef _ACPI_AMD_WBRF_H
+#define _ACPI_AMD_WBRF_H
+
+#include <linux/wbrf.h>
+
+#ifdef CONFIG_WBRF_AMD_ACPI
+bool acpi_amd_wbrf_supported_consumer(struct device *dev);
+bool acpi_amd_wbrf_supported_producer(struct device *dev);
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in *in);
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in *in);
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_out *out);
+#else
+static inline bool
+acpi_amd_wbrf_supported_consumer(struct device *dev) { return false; }
+static inline bool
+acpi_amd_wbrf_supported_producer(struct device *dev) {return false; }
+static inline int
+acpi_amd_wbrf_remove_exclusion(struct device *dev,
+			       struct wbrf_ranges_in *in) { return -ENODEV; }
+static inline int
+acpi_amd_wbrf_add_exclusion(struct device *dev,
+			    struct wbrf_ranges_in *in) { return -ENODEV; }
+static inline int
+acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				  struct wbrf_ranges_out *out) { return -ENODEV; }
+#endif
+
+#endif /* _ACPI_AMD_WBRF_H */
diff --git a/include/linux/wbrf.h b/include/linux/wbrf.h
index 3ca95786cef5..f71ac2343b29 100644
--- a/include/linux/wbrf.h
+++ b/include/linux/wbrf.h
@@ -18,10 +18,12 @@ struct exclusion_range {
 	uint64_t	end;
 };
 
+#if defined(CONFIG_WBRF_GENERIC)
 struct exclusion_range_pool {
 	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
 	uint64_t		ref_counter[MAX_NUM_OF_WBRF_RANGES];
 };
+#endif
 
 struct wbrf_ranges_in {
 	/* valid entry: `start` and `end` filled with non-zero values */
-- 
2.34.1


WARNING: multiple messages have this Message-ID (diff)
From: Evan Quan <evan.quan@amd.com>
To: <rafael@kernel.org>, <lenb@kernel.org>,
	<Alexander.Deucher@amd.com>, <Christian.Koenig@amd.com>,
	<Xinhui.Pan@amd.com>, <airlied@gmail.com>, <daniel@ffwll.ch>,
	<johannes@sipsolutions.net>, <davem@davemloft.net>,
	<edumazet@google.com>, <kuba@kernel.org>, <pabeni@redhat.com>,
	<Mario.Limonciello@amd.com>, <mdaenzer@redhat.com>,
	<maarten.lankhorst@linux.intel.com>, <tzimmermann@suse.de>,
	<hdegoede@redhat.com>, <jingyuwang_vip@163.com>,
	<Lijo.Lazar@amd.com>, <jim.cromie@gmail.com>,
	<bellosilicio@gmail.com>, <andrealmeid@igalia.com>,
	<trix@redhat.com>, <jsg@jsg.id.au>, <arnd@arndb.de>
Cc: netdev@vger.kernel.org, linux-wireless@vger.kernel.org,
	linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
	linux-acpi@vger.kernel.org, amd-gfx@lists.freedesktop.org,
	Evan Quan <evan.quan@amd.com>,
	Mario Limonciello <mario.limonciello@amd.com>
Subject: [PATCH V5 2/9] driver core: add ACPI based WBRF mechanism introduced by AMD
Date: Fri, 30 Jun 2023 18:32:33 +0800	[thread overview]
Message-ID: <20230630103240.1557100-3-evan.quan@amd.com> (raw)
In-Reply-To: <20230630103240.1557100-1-evan.quan@amd.com>

AMD has introduced an ACPI based mechanism to support WBRF for some
platforms with AMD dGPU + WLAN. This needs support from BIOS equipped
with necessary AML implementations and dGPU firmwares.

For those systems without the ACPI mechanism and developing solutions,
user can use the generic WBRF solution for diagnosing potential
interference issues.

Co-developed-by: Mario Limonciello <mario.limonciello@amd.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Co-developed-by: Evan Quan <evan.quan@amd.com>
Signed-off-by: Evan Quan <evan.quan@amd.com>
--
v4->v5:
  - promote this to be a more generic solution with input argument taking
    `struct device` and provide better scalability to support non-ACPI
    scenarios(Andrew)
  - update the APIs naming and some other minor fixes(Rafael)
---
 drivers/acpi/Makefile         |   2 +
 drivers/acpi/amd_wbrf.c       | 236 ++++++++++++++++++++++++++++++++++
 drivers/base/Kconfig          |  29 +++++
 drivers/base/wbrf.c           |  35 ++++-
 include/linux/acpi_amd_wbrf.h |  38 ++++++
 include/linux/wbrf.h          |   2 +
 6 files changed, 336 insertions(+), 6 deletions(-)
 create mode 100644 drivers/acpi/amd_wbrf.c
 create mode 100644 include/linux/acpi_amd_wbrf.h

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index feb36c0b9446..94b940ddbf88 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -131,3 +131,5 @@ obj-y				+= dptf/
 obj-$(CONFIG_ARM64)		+= arm64/
 
 obj-$(CONFIG_ACPI_VIOT)		+= viot.o
+
+obj-$(CONFIG_WBRF_AMD_ACPI)	+= amd_wbrf.o
diff --git a/drivers/acpi/amd_wbrf.c b/drivers/acpi/amd_wbrf.c
new file mode 100644
index 000000000000..44e38c97acf0
--- /dev/null
+++ b/drivers/acpi/amd_wbrf.c
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/acpi_amd_wbrf.h>
+
+/* functions */
+#define WBRF_RECORD		0x1
+#define WBRF_RETRIEVE		0x2
+
+/* record actions */
+#define WBRF_RECORD_ADD		0x0
+#define WBRF_RECORD_REMOVE	0x1
+
+#define WBRF_REVISION		0x1
+
+static const guid_t wifi_acpi_dsm_guid =
+	GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
+		  0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
+
+static int wbrf_dsm(struct acpi_device *adev, u8 fn,
+		    union acpi_object *argv4,
+		    union acpi_object **out)
+{
+	union acpi_object *obj;
+	int rc;
+
+	obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid,
+				WBRF_REVISION, fn, argv4);
+	if (!obj)
+		return -ENXIO;
+
+	switch (obj->type) {
+	case ACPI_TYPE_BUFFER:
+		*out = obj;
+		return 0;
+
+	case ACPI_TYPE_INTEGER:
+		rc =  obj->integer.value ? -EINVAL : 0;
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+	}
+
+	ACPI_FREE(obj);
+
+	return rc;
+}
+
+static int wbrf_record(struct acpi_device *adev, uint8_t action,
+		       struct wbrf_ranges_in *in)
+{
+	union acpi_object *argv4;
+	uint32_t num_of_ranges = 0;
+	uint32_t arg_idx = 0;
+	uint32_t loop_idx;
+	int ret;
+
+	if (!in)
+		return -EINVAL;
+
+	for (loop_idx = 0; loop_idx < ARRAY_SIZE(in->band_list);
+	     loop_idx++)
+		if (in->band_list[loop_idx].start &&
+		    in->band_list[loop_idx].end)
+			num_of_ranges++;
+
+	argv4 = kzalloc(sizeof(*argv4) * (2 * num_of_ranges + 2 + 1), GFP_KERNEL);
+	if (!argv4)
+		return -ENOMEM;
+
+	argv4[arg_idx].package.type = ACPI_TYPE_PACKAGE;
+	argv4[arg_idx].package.count = 2 + 2 * num_of_ranges;
+	argv4[arg_idx++].package.elements = &argv4[1];
+	argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	argv4[arg_idx++].integer.value = num_of_ranges;
+	argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+	argv4[arg_idx++].integer.value = action;
+
+	for (loop_idx = 0; loop_idx < ARRAY_SIZE(in->band_list);
+	     loop_idx++) {
+		if (!in->band_list[loop_idx].start ||
+		    !in->band_list[loop_idx].end)
+			continue;
+
+		argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		argv4[arg_idx++].integer.value = in->band_list[loop_idx].start;
+		argv4[arg_idx].integer.type = ACPI_TYPE_INTEGER;
+		argv4[arg_idx++].integer.value = in->band_list[loop_idx].end;
+	}
+
+	ret = wbrf_dsm(adev, WBRF_RECORD, argv4, NULL);
+
+	kfree(argv4);
+
+	return ret;
+}
+
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return -ENODEV;
+
+	return wbrf_record(adev, WBRF_RECORD_ADD, in);
+}
+
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in *in)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return -ENODEV;
+
+	return wbrf_record(adev, WBRF_RECORD_REMOVE, in);
+}
+
+bool acpi_amd_wbrf_supported_producer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return false;
+
+	return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid,
+			      WBRF_REVISION,
+			      BIT(WBRF_RECORD));
+}
+
+static union acpi_object *
+acpi_evaluate_wbrf(acpi_handle handle, u64 rev, u64 func)
+{
+	acpi_status ret;
+	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object params[4];
+	struct acpi_object_list input = {
+		.count = 4,
+		.pointer = params,
+	};
+
+	params[0].type = ACPI_TYPE_INTEGER;
+	params[0].integer.value = rev;
+	params[1].type = ACPI_TYPE_INTEGER;
+	params[1].integer.value = func;
+	params[2].type = ACPI_TYPE_PACKAGE;
+	params[2].package.count = 0;
+	params[2].package.elements = NULL;
+	params[3].type = ACPI_TYPE_STRING;
+	params[3].string.length = 0;
+	params[3].string.pointer = NULL;
+
+	ret = acpi_evaluate_object(handle, "WBRF", &input, &buf);
+	if (ACPI_SUCCESS(ret))
+		return (union acpi_object *)buf.pointer;
+
+	return NULL;
+}
+
+static bool check_acpi_wbrf(acpi_handle handle, u64 rev, u64 funcs)
+{
+	int i;
+	u64 mask = 0;
+	union acpi_object *obj;
+
+	if (funcs == 0)
+		return false;
+
+	obj = acpi_evaluate_wbrf(handle, rev, 0);
+	if (!obj)
+		return false;
+
+	if (obj->type != ACPI_TYPE_BUFFER)
+		return false;
+
+	/*
+	 * Bit vector providing supported functions information.
+	 * Each bit marks support for one specific function of the WBRF method.
+	 */
+	for (i = 0; i < obj->buffer.length && i < 8; i++)
+		mask |= (((u64)obj->buffer.pointer[i]) << (i * 8));
+
+	ACPI_FREE(obj);
+
+	/*
+	 * Bit 0 indicates whether there's support for any functions other than
+	 * function 0.
+	 */
+	if ((mask & 0x1) && (mask & funcs) == funcs)
+		return true;
+
+	return false;
+}
+
+bool acpi_amd_wbrf_supported_consumer(struct device *dev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+
+	if (!adev)
+		return false;
+
+	return check_acpi_wbrf(adev->handle,
+			       WBRF_REVISION,
+			       BIT(WBRF_RETRIEVE));
+}
+
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_out *out)
+{
+	struct acpi_device *adev = ACPI_COMPANION(dev);
+	union acpi_object *obj;
+
+	if (!adev)
+		return -ENODEV;
+
+	obj = acpi_evaluate_wbrf(adev->handle,
+				 WBRF_REVISION,
+				 WBRF_RETRIEVE);
+	if (!obj)
+		return -EINVAL;
+
+	WARN(obj->buffer.length != sizeof(*out),
+		"Unexpected buffer length");
+	memcpy(out, obj->buffer.pointer, obj->buffer.length);
+
+	ACPI_FREE(obj);
+
+	return 0;
+}
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 5b441017b225..cbf0b2358c17 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -250,4 +250,33 @@ config WBRF
 	  different domains to notify the frequencies in use so that hardware
 	  can be reconfigured to avoid harmonic conflicts.
 
+config WBRF_AMD_ACPI
+	bool "Use the ACPI mechanism introduced by AMD to support WBRF"
+	default n
+	depends on ACPI
+	select WBRF
+	help
+	  AMD has introduced an ACPI based mechanism to support WBRF for some
+	  platforms with AMD dGPU and WLAN. This needs support from BIOS equipped
+	  with necessary AML implementations and dGPU firmwares.
+
+	  Say Y to enable this ACPI based mechanism. It is suggested to confirm
+	  with the hardware designer/provider first whether your platform
+	  equipped with necessary BIOS and firmwares.
+
+config WBRF_GENERIC
+	bool "Use the generic WBRF solution"
+	default n
+	depends on !WBRF_AMD_ACPI
+	select WBRF
+	help
+	  Ideally it is the hardware designer/provider who should provide a
+	  solution for the possible RF interference issue. Since they know
+	  well whether there could be RF interference issue with their
+	  platforms.
+
+	  Say Y to enable this generic WBRF solution for diagnosing potential
+	  interference issues on systems without the ACPI mechanism and
+	  developing solutions.
+
 endmenu
diff --git a/drivers/base/wbrf.c b/drivers/base/wbrf.c
index 2163a8ec8a9a..d9fab9f3045e 100644
--- a/drivers/base/wbrf.c
+++ b/drivers/base/wbrf.c
@@ -6,9 +6,12 @@
  */
 
 #include <linux/wbrf.h>
+#include <linux/acpi_amd_wbrf.h>
 
 static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head);
 static DEFINE_MUTEX(wbrf_mutex);
+
+#if defined(CONFIG_WBRF_GENERIC)
 static struct exclusion_range_pool wbrf_pool;
 
 static int _wbrf_add_exclusion_ranges(struct wbrf_ranges_in *in)
@@ -89,6 +92,7 @@ static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out)
 
 	return 0;
 }
+#endif
 
 /**
  * wbrf_supported_producer - Determine if the device can report frequencies
@@ -100,7 +104,12 @@ static int _wbrf_retrieve_exclusion_ranges(struct wbrf_ranges_out *out)
  */
 bool wbrf_supported_producer(struct device *dev)
 {
+#ifdef CONFIG_WBRF_AMD_ACPI
+	return acpi_amd_wbrf_supported_producer(dev);
+#elif defined(CONFIG_WBRF_GENERIC)
 	return true;
+#endif
+	return false;
 }
 EXPORT_SYMBOL_GPL(wbrf_supported_producer);
 
@@ -116,12 +125,15 @@ EXPORT_SYMBOL_GPL(wbrf_supported_producer);
 int wbrf_add_exclusion(struct device *dev,
 		       struct wbrf_ranges_in *in)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r = acpi_amd_wbrf_add_exclusion(dev, in);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_add_exclusion_ranges(in);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 	if (r)
 		return r;
@@ -144,12 +156,15 @@ EXPORT_SYMBOL_GPL(wbrf_add_exclusion);
 int wbrf_remove_exclusion(struct device *dev,
 			  struct wbrf_ranges_in *in)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r  = acpi_amd_wbrf_remove_exclusion(dev, in);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_remove_exclusion_ranges(in);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 	if (r)
 		return r;
@@ -171,7 +186,12 @@ EXPORT_SYMBOL_GPL(wbrf_remove_exclusion);
  */
 bool wbrf_supported_consumer(struct device *dev)
 {
+#ifdef CONFIG_WBRF_AMD_ACPI
+	return acpi_amd_wbrf_supported_consumer(dev);
+#elif defined(CONFIG_WBRF_GENERIC)
 	return true;
+#endif
+	return false;
 }
 EXPORT_SYMBOL_GPL(wbrf_supported_consumer);
 
@@ -214,12 +234,15 @@ EXPORT_SYMBOL_GPL(wbrf_unregister_notifier);
 int wbrf_retrieve_exclusions(struct device *dev,
 			     struct wbrf_ranges_out *out)
 {
-	int r;
+	int r = -ENODEV;
 
 	mutex_lock(&wbrf_mutex);
 
+#ifdef CONFIG_WBRF_AMD_ACPI
+	r = acpi_amd_wbrf_retrieve_exclusions(dev, out);
+#elif defined(CONFIG_WBRF_GENERIC)
 	r = _wbrf_retrieve_exclusion_ranges(out);
-
+#endif
 	mutex_unlock(&wbrf_mutex);
 
 	return r;
diff --git a/include/linux/acpi_amd_wbrf.h b/include/linux/acpi_amd_wbrf.h
new file mode 100644
index 000000000000..c274093972e3
--- /dev/null
+++ b/include/linux/acpi_amd_wbrf.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Wifi Band Exclusion Interface (AMD ACPI Implementation)
+ * Copyright (C) 2023 Advanced Micro Devices
+ *
+ */
+
+#ifndef _ACPI_AMD_WBRF_H
+#define _ACPI_AMD_WBRF_H
+
+#include <linux/wbrf.h>
+
+#ifdef CONFIG_WBRF_AMD_ACPI
+bool acpi_amd_wbrf_supported_consumer(struct device *dev);
+bool acpi_amd_wbrf_supported_producer(struct device *dev);
+int acpi_amd_wbrf_remove_exclusion(struct device *dev,
+				   struct wbrf_ranges_in *in);
+int acpi_amd_wbrf_add_exclusion(struct device *dev,
+				struct wbrf_ranges_in *in);
+int acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				      struct wbrf_ranges_out *out);
+#else
+static inline bool
+acpi_amd_wbrf_supported_consumer(struct device *dev) { return false; }
+static inline bool
+acpi_amd_wbrf_supported_producer(struct device *dev) {return false; }
+static inline int
+acpi_amd_wbrf_remove_exclusion(struct device *dev,
+			       struct wbrf_ranges_in *in) { return -ENODEV; }
+static inline int
+acpi_amd_wbrf_add_exclusion(struct device *dev,
+			    struct wbrf_ranges_in *in) { return -ENODEV; }
+static inline int
+acpi_amd_wbrf_retrieve_exclusions(struct device *dev,
+				  struct wbrf_ranges_out *out) { return -ENODEV; }
+#endif
+
+#endif /* _ACPI_AMD_WBRF_H */
diff --git a/include/linux/wbrf.h b/include/linux/wbrf.h
index 3ca95786cef5..f71ac2343b29 100644
--- a/include/linux/wbrf.h
+++ b/include/linux/wbrf.h
@@ -18,10 +18,12 @@ struct exclusion_range {
 	uint64_t	end;
 };
 
+#if defined(CONFIG_WBRF_GENERIC)
 struct exclusion_range_pool {
 	struct exclusion_range	band_list[MAX_NUM_OF_WBRF_RANGES];
 	uint64_t		ref_counter[MAX_NUM_OF_WBRF_RANGES];
 };
+#endif
 
 struct wbrf_ranges_in {
 	/* valid entry: `start` and `end` filled with non-zero values */
-- 
2.34.1


  parent reply	other threads:[~2023-06-30 10:34 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-06-30 10:32 [PATCH V5 0/9] Enable Wifi RFI interference mitigation feature support Evan Quan
2023-06-30 10:32 ` Evan Quan
2023-06-30 10:32 ` [PATCH V5 1/9] drivers core: Add support for Wifi band RF mitigations Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 13:38   ` Simon Horman
2023-06-30 13:38     ` Simon Horman
2023-06-30 13:38     ` Simon Horman
2023-07-04  3:41     ` Quan, Evan
2023-07-04  3:41       ` Quan, Evan
2023-07-04  3:41       ` Quan, Evan
2023-06-30 16:40   ` Limonciello, Mario
2023-06-30 16:40     ` Limonciello, Mario
2023-07-01  0:25     ` Andrew Lunn
2023-07-01  0:25       ` Andrew Lunn
2023-07-01  0:25       ` Andrew Lunn
2023-07-04  3:25       ` Quan, Evan
2023-07-04  3:25         ` Quan, Evan
2023-07-04  3:25         ` Quan, Evan
2023-07-04  3:40     ` Quan, Evan
2023-07-04  3:40       ` Quan, Evan
2023-07-04  3:53       ` Mario Limonciello
2023-07-04  3:53         ` Mario Limonciello
2023-07-01  0:19   ` Andrew Lunn
2023-07-01  0:19     ` Andrew Lunn
2023-07-01  0:19     ` Andrew Lunn
2023-07-04  3:30     ` Quan, Evan
2023-07-04  3:30       ` Quan, Evan
2023-07-04  3:30       ` Quan, Evan
2023-07-04 13:07       ` Andrew Lunn
2023-07-04 13:07         ` Andrew Lunn
2023-07-04 13:07         ` Andrew Lunn
2023-07-06  2:58         ` Quan, Evan
2023-07-06  2:58           ` Quan, Evan
2023-07-06  2:58           ` Quan, Evan
2023-07-06  3:09           ` Mario Limonciello
2023-07-06  3:09             ` Mario Limonciello
2023-07-06  3:09             ` Mario Limonciello
2023-06-30 10:32 ` Evan Quan [this message]
2023-06-30 10:32   ` [PATCH V5 2/9] driver core: add ACPI based WBRF mechanism introduced by AMD Evan Quan
2023-07-01  0:51   ` Andrew Lunn
2023-07-01  0:51     ` Andrew Lunn
2023-07-01  0:51     ` Andrew Lunn
2023-07-04  3:24     ` Quan, Evan
2023-07-04  3:24       ` Quan, Evan
2023-07-04  3:24       ` Quan, Evan
2023-06-30 10:32 ` [PATCH V5 3/9] cfg80211: expose nl80211_chan_width_to_mhz for wide sharing Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 10:32 ` [PATCH V5 4/9] wifi: mac80211: Add support for ACPI WBRF Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 14:08   ` kernel test robot
2023-06-30 14:08     ` kernel test robot
2023-07-01  1:02   ` Andrew Lunn
2023-07-01  1:02     ` Andrew Lunn
2023-07-01  1:02     ` Andrew Lunn
2023-07-04  3:12     ` Quan, Evan
2023-07-04  3:12       ` Quan, Evan
2023-07-04  3:12       ` Quan, Evan
2023-06-30 10:32 ` [PATCH V5 5/9] drm/amd/pm: update driver_if and ppsmc headers for coming wbrf feature Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 10:32 ` [PATCH V5 6/9] drm/amd/pm: setup the framework to support Wifi RFI mitigation feature Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 10:32 ` [PATCH V5 7/9] drm/amd/pm: add flood detection for wbrf events Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 14:29   ` kernel test robot
2023-06-30 14:29     ` kernel test robot
2023-06-30 10:32 ` [PATCH V5 8/9] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.0 Evan Quan
2023-06-30 10:32   ` Evan Quan
2023-06-30 10:32 ` [PATCH V5 9/9] drm/amd/pm: enable Wifi RFI mitigation feature support for SMU13.0.7 Evan Quan
2023-06-30 10:32   ` Evan Quan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230630103240.1557100-3-evan.quan@amd.com \
    --to=evan.quan@amd.com \
    --cc=Alexander.Deucher@amd.com \
    --cc=Christian.Koenig@amd.com \
    --cc=Lijo.Lazar@amd.com \
    --cc=Mario.Limonciello@amd.com \
    --cc=Xinhui.Pan@amd.com \
    --cc=airlied@gmail.com \
    --cc=amd-gfx@lists.freedesktop.org \
    --cc=andrealmeid@igalia.com \
    --cc=arnd@arndb.de \
    --cc=bellosilicio@gmail.com \
    --cc=daniel@ffwll.ch \
    --cc=davem@davemloft.net \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=edumazet@google.com \
    --cc=hdegoede@redhat.com \
    --cc=jim.cromie@gmail.com \
    --cc=jingyuwang_vip@163.com \
    --cc=johannes@sipsolutions.net \
    --cc=jsg@jsg.id.au \
    --cc=kuba@kernel.org \
    --cc=lenb@kernel.org \
    --cc=linux-acpi@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mdaenzer@redhat.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=rafael@kernel.org \
    --cc=trix@redhat.com \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.