1 // SPDX-License-Identifier: GPL-2.0
3 * Wifi Frequency Band Manage Interface
4 * Copyright (C) 2023 Advanced Micro Devices
7 #include <linux/acpi.h>
8 #include <linux/acpi_amd_wbrf.h>
11 * Functions bit vector for WBRF method
13 * Bit 0: WBRF supported.
14 * Bit 1: Function 1 (Add / Remove frequency) is supported.
15 * Bit 2: Function 2 (Get frequency list) is supported.
17 #define WBRF_ENABLED 0x0
18 #define WBRF_RECORD 0x1
19 #define WBRF_RETRIEVE 0x2
21 #define WBRF_REVISION 0x1
24 * The data structure used for WBRF_RETRIEVE is not naturally aligned.
25 * And unfortunately the design has been settled down.
27 struct amd_wbrf_ranges_out
{
29 struct freq_band_range band_list
[MAX_NUM_OF_WBRF_RANGES
];
32 static const guid_t wifi_acpi_dsm_guid
=
33 GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c,
34 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70);
37 * Used to notify consumer (amdgpu driver currently) about
38 * the wifi frequency is change.
40 static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head
);
42 static int wbrf_record(struct acpi_device
*adev
, uint8_t action
, struct wbrf_ranges_in_out
*in
)
44 union acpi_object argv4
;
45 union acpi_object
*tmp
;
46 union acpi_object
*obj
;
47 u32 num_of_ranges
= 0;
56 for (i
= 0; i
< ARRAY_SIZE(in
->band_list
); i
++) {
57 if (in
->band_list
[i
].start
&& in
->band_list
[i
].end
)
62 * The num_of_ranges value in the "in" object supplied by
63 * the caller is required to be equal to the number of
64 * entries in the band_list array in there.
66 if (num_of_ranges
!= in
->num_of_ranges
)
70 * Every input frequency band comes with two end points(start/end)
71 * and each is accounted as an element. Meanwhile the range count
72 * and action type are accounted as an element each.
73 * So, the total element count = 2 * num_of_ranges + 1 + 1.
75 num_of_elements
= 2 * num_of_ranges
+ 2;
77 tmp
= kcalloc(num_of_elements
, sizeof(*tmp
), GFP_KERNEL
);
81 argv4
.package
.type
= ACPI_TYPE_PACKAGE
;
82 argv4
.package
.count
= num_of_elements
;
83 argv4
.package
.elements
= tmp
;
85 /* save the number of ranges*/
86 tmp
[0].integer
.type
= ACPI_TYPE_INTEGER
;
87 tmp
[0].integer
.value
= num_of_ranges
;
89 /* save the action(WBRF_RECORD_ADD/REMOVE/RETRIEVE) */
90 tmp
[1].integer
.type
= ACPI_TYPE_INTEGER
;
91 tmp
[1].integer
.value
= action
;
94 for (i
= 0; i
< ARRAY_SIZE(in
->band_list
); i
++) {
95 if (!in
->band_list
[i
].start
|| !in
->band_list
[i
].end
)
98 tmp
[arg_idx
].integer
.type
= ACPI_TYPE_INTEGER
;
99 tmp
[arg_idx
++].integer
.value
= in
->band_list
[i
].start
;
100 tmp
[arg_idx
].integer
.type
= ACPI_TYPE_INTEGER
;
101 tmp
[arg_idx
++].integer
.value
= in
->band_list
[i
].end
;
104 obj
= acpi_evaluate_dsm(adev
->handle
, &wifi_acpi_dsm_guid
,
105 WBRF_REVISION
, WBRF_RECORD
, &argv4
);
110 if (obj
->type
!= ACPI_TYPE_INTEGER
) {
115 ret
= obj
->integer
.value
;
127 * acpi_amd_wbrf_add_remove - add or remove the frequency band the device is using
129 * @dev: device pointer
130 * @action: remove or add the frequency band into bios
131 * @in: input structure containing the frequency band the device is using
133 * Broadcast to other consumers the frequency band the device starts
134 * to use. Underneath the surface the information is cached into an
135 * internal buffer first. Then a notification is sent to all those
136 * registered consumers. So then they can retrieve that buffer to
137 * know the latest active frequency bands. Consumers that haven't
138 * yet been registered can retrieve the information from the cache
139 * when they register.
142 * 0 for success add/remove wifi frequency band.
143 * Returns a negative error code for failure.
145 int acpi_amd_wbrf_add_remove(struct device
*dev
, uint8_t action
, struct wbrf_ranges_in_out
*in
)
147 struct acpi_device
*adev
;
150 adev
= ACPI_COMPANION(dev
);
154 ret
= wbrf_record(adev
, action
, in
);
158 blocking_notifier_call_chain(&wbrf_chain_head
, WBRF_CHANGED
, NULL
);
162 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_remove
);
165 * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled
166 * for the device as a producer
168 * @dev: device pointer
170 * Check if the platform equipped with necessary implementations to
171 * support WBRF for the device as a producer.
174 * true if WBRF is supported, otherwise returns false
176 bool acpi_amd_wbrf_supported_producer(struct device
*dev
)
178 struct acpi_device
*adev
;
180 adev
= ACPI_COMPANION(dev
);
184 return acpi_check_dsm(adev
->handle
, &wifi_acpi_dsm_guid
,
185 WBRF_REVISION
, BIT(WBRF_RECORD
));
187 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_producer
);
190 * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled
191 * for the device as a consumer
193 * @dev: device pointer
195 * Determine if the platform equipped with necessary implementations to
196 * support WBRF for the device as a consumer.
199 * true if WBRF is supported, otherwise returns false.
201 bool acpi_amd_wbrf_supported_consumer(struct device
*dev
)
203 struct acpi_device
*adev
;
205 adev
= ACPI_COMPANION(dev
);
209 return acpi_check_dsm(adev
->handle
, &wifi_acpi_dsm_guid
,
210 WBRF_REVISION
, BIT(WBRF_RETRIEVE
));
212 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer
);
215 * amd_wbrf_retrieve_freq_band - retrieve current active frequency bands
217 * @dev: device pointer
218 * @out: output structure containing all the active frequency bands
220 * Retrieve the current active frequency bands which were broadcasted
221 * by other producers. The consumer who calls this API should take
222 * proper actions if any of the frequency band may cause RFI with its
223 * own frequency band used.
226 * 0 for getting wifi freq band successfully.
227 * Returns a negative error code for failure.
229 int amd_wbrf_retrieve_freq_band(struct device
*dev
, struct wbrf_ranges_in_out
*out
)
231 struct amd_wbrf_ranges_out acpi_out
= {0};
232 struct acpi_device
*adev
;
233 union acpi_object
*obj
;
234 union acpi_object param
;
237 adev
= ACPI_COMPANION(dev
);
241 param
.type
= ACPI_TYPE_STRING
;
242 param
.string
.length
= 0;
243 param
.string
.pointer
= NULL
;
245 obj
= acpi_evaluate_dsm(adev
->handle
, &wifi_acpi_dsm_guid
,
246 WBRF_REVISION
, WBRF_RETRIEVE
, ¶m
);
251 * The return buffer is with variable length and the format below:
252 * number_of_entries(1 DWORD): Number of entries
253 * start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry
254 * end_freq of 1st entry(1 QWORD): End frequency of the 1st entry
257 * start_freq of the last entry(1 QWORD)
258 * end_freq of the last entry(1 QWORD)
260 * Thus the buffer length is determined by the number of entries.
261 * - For zero entry scenario, the buffer length will be 4 bytes.
262 * - For one entry scenario, the buffer length will be 20 bytes.
264 if (obj
->buffer
.length
> sizeof(acpi_out
) || obj
->buffer
.length
< 4) {
265 dev_err(dev
, "Wrong sized WBRT information");
269 memcpy(&acpi_out
, obj
->buffer
.pointer
, obj
->buffer
.length
);
271 out
->num_of_ranges
= acpi_out
.num_of_ranges
;
272 memcpy(out
->band_list
, acpi_out
.band_list
, sizeof(acpi_out
.band_list
));
278 EXPORT_SYMBOL_GPL(amd_wbrf_retrieve_freq_band
);
281 * amd_wbrf_register_notifier - register for notifications of frequency
284 * @nb: driver notifier block
286 * The consumer should register itself via this API so that it can get
287 * notified on the frequency band updates from other producers.
290 * 0 for registering a consumer driver successfully.
291 * Returns a negative error code for failure.
293 int amd_wbrf_register_notifier(struct notifier_block
*nb
)
295 return blocking_notifier_chain_register(&wbrf_chain_head
, nb
);
297 EXPORT_SYMBOL_GPL(amd_wbrf_register_notifier
);
300 * amd_wbrf_unregister_notifier - unregister for notifications of
301 * frequency band update
303 * @nb: driver notifier block
305 * The consumer should call this API when it is longer interested with
306 * the frequency band updates from other producers. Usually, this should
307 * be performed during driver cleanup.
310 * 0 for unregistering a consumer driver.
311 * Returns a negative error code for failure.
313 int amd_wbrf_unregister_notifier(struct notifier_block
*nb
)
315 return blocking_notifier_chain_unregister(&wbrf_chain_head
, nb
);
317 EXPORT_SYMBOL_GPL(amd_wbrf_unregister_notifier
);