1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Wifi Frequency Band Manage Interface 4 * Copyright (C) 2023 Advanced Micro Devices 5 */ 6 7 #include <linux/acpi.h> 8 #include <linux/acpi_amd_wbrf.h> 9 10 /* 11 * Functions bit vector for WBRF method 12 * 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. 16 */ 17 #define WBRF_ENABLED 0x0 18 #define WBRF_RECORD 0x1 19 #define WBRF_RETRIEVE 0x2 20 21 #define WBRF_REVISION 0x1 22 23 /* 24 * The data structure used for WBRF_RETRIEVE is not naturally aligned. 25 * And unfortunately the design has been settled down. 26 */ 27 struct amd_wbrf_ranges_out { 28 u32 num_of_ranges; 29 struct freq_band_range band_list[MAX_NUM_OF_WBRF_RANGES]; 30 } __packed; 31 32 static const guid_t wifi_acpi_dsm_guid = 33 GUID_INIT(0x7b7656cf, 0xdc3d, 0x4c1c, 34 0x83, 0xe9, 0x66, 0xe7, 0x21, 0xde, 0x30, 0x70); 35 36 /* 37 * Used to notify consumer (amdgpu driver currently) about 38 * the wifi frequency is change. 39 */ 40 static BLOCKING_NOTIFIER_HEAD(wbrf_chain_head); 41 42 static int wbrf_record(struct acpi_device *adev, uint8_t action, struct wbrf_ranges_in_out *in) 43 { 44 union acpi_object argv4; 45 union acpi_object *tmp; 46 union acpi_object *obj; 47 u32 num_of_ranges = 0; 48 u32 num_of_elements; 49 u32 arg_idx = 0; 50 int ret; 51 u32 i; 52 53 if (!in) 54 return -EINVAL; 55 56 for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { 57 if (in->band_list[i].start && in->band_list[i].end) 58 num_of_ranges++; 59 } 60 61 /* 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. 65 */ 66 if (num_of_ranges != in->num_of_ranges) 67 return -EINVAL; 68 69 /* 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. 74 */ 75 num_of_elements = 2 * num_of_ranges + 2; 76 77 tmp = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL); 78 if (!tmp) 79 return -ENOMEM; 80 81 argv4.package.type = ACPI_TYPE_PACKAGE; 82 argv4.package.count = num_of_elements; 83 argv4.package.elements = tmp; 84 85 /* save the number of ranges*/ 86 tmp[0].integer.type = ACPI_TYPE_INTEGER; 87 tmp[0].integer.value = num_of_ranges; 88 89 /* save the action(WBRF_RECORD_ADD/REMOVE/RETRIEVE) */ 90 tmp[1].integer.type = ACPI_TYPE_INTEGER; 91 tmp[1].integer.value = action; 92 93 arg_idx = 2; 94 for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { 95 if (!in->band_list[i].start || !in->band_list[i].end) 96 continue; 97 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; 102 } 103 104 obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, 105 WBRF_REVISION, WBRF_RECORD, &argv4); 106 107 if (!obj) { 108 kfree(tmp); 109 return -EINVAL; 110 } 111 112 if (obj->type != ACPI_TYPE_INTEGER) { 113 ret = -EINVAL; 114 goto out; 115 } 116 117 ret = obj->integer.value; 118 if (ret) 119 ret = -EINVAL; 120 121 out: 122 ACPI_FREE(obj); 123 kfree(tmp); 124 125 return ret; 126 } 127 128 /** 129 * acpi_amd_wbrf_add_remove - add or remove the frequency band the device is using 130 * 131 * @dev: device pointer 132 * @action: remove or add the frequency band into bios 133 * @in: input structure containing the frequency band the device is using 134 * 135 * Broadcast to other consumers the frequency band the device starts 136 * to use. Underneath the surface the information is cached into an 137 * internal buffer first. Then a notification is sent to all those 138 * registered consumers. So then they can retrieve that buffer to 139 * know the latest active frequency bands. Consumers that haven't 140 * yet been registered can retrieve the information from the cache 141 * when they register. 142 * 143 * Return: 144 * 0 for success add/remove wifi frequency band. 145 * Returns a negative error code for failure. 146 */ 147 int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in) 148 { 149 struct acpi_device *adev; 150 int ret; 151 152 adev = ACPI_COMPANION(dev); 153 if (!adev) 154 return -ENODEV; 155 156 ret = wbrf_record(adev, action, in); 157 if (ret) 158 return ret; 159 160 blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL); 161 162 return 0; 163 } 164 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_remove); 165 166 /** 167 * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled 168 * for the device as a producer 169 * 170 * @dev: device pointer 171 * 172 * Check if the platform equipped with necessary implementations to 173 * support WBRF for the device as a producer. 174 * 175 * Return: 176 * true if WBRF is supported, otherwise returns false 177 */ 178 bool acpi_amd_wbrf_supported_producer(struct device *dev) 179 { 180 struct acpi_device *adev; 181 182 adev = ACPI_COMPANION(dev); 183 if (!adev) 184 return false; 185 186 return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, 187 WBRF_REVISION, BIT(WBRF_RECORD)); 188 } 189 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_producer); 190 191 /** 192 * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled 193 * for the device as a consumer 194 * 195 * @dev: device pointer 196 * 197 * Determine if the platform equipped with necessary implementations to 198 * support WBRF for the device as a consumer. 199 * 200 * Return: 201 * true if WBRF is supported, otherwise returns false. 202 */ 203 bool acpi_amd_wbrf_supported_consumer(struct device *dev) 204 { 205 struct acpi_device *adev; 206 207 adev = ACPI_COMPANION(dev); 208 if (!adev) 209 return false; 210 211 return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, 212 WBRF_REVISION, BIT(WBRF_RETRIEVE)); 213 } 214 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer); 215 216 /** 217 * amd_wbrf_retrieve_freq_band - retrieve current active frequency bands 218 * 219 * @dev: device pointer 220 * @out: output structure containing all the active frequency bands 221 * 222 * Retrieve the current active frequency bands which were broadcasted 223 * by other producers. The consumer who calls this API should take 224 * proper actions if any of the frequency band may cause RFI with its 225 * own frequency band used. 226 * 227 * Return: 228 * 0 for getting wifi freq band successfully. 229 * Returns a negative error code for failure. 230 */ 231 int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out) 232 { 233 struct amd_wbrf_ranges_out acpi_out = {0}; 234 struct acpi_device *adev; 235 union acpi_object *obj; 236 union acpi_object param; 237 int ret = 0; 238 239 adev = ACPI_COMPANION(dev); 240 if (!adev) 241 return -ENODEV; 242 243 param.type = ACPI_TYPE_STRING; 244 param.string.length = 0; 245 param.string.pointer = NULL; 246 247 obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, 248 WBRF_REVISION, WBRF_RETRIEVE, ¶m); 249 if (!obj) 250 return -EINVAL; 251 252 /* 253 * The return buffer is with variable length and the format below: 254 * number_of_entries(1 DWORD): Number of entries 255 * start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry 256 * end_freq of 1st entry(1 QWORD): End frequency of the 1st entry 257 * ... 258 * ... 259 * start_freq of the last entry(1 QWORD) 260 * end_freq of the last entry(1 QWORD) 261 * 262 * Thus the buffer length is determined by the number of entries. 263 * - For zero entry scenario, the buffer length will be 4 bytes. 264 * - For one entry scenario, the buffer length will be 20 bytes. 265 */ 266 if (obj->buffer.length > sizeof(acpi_out) || obj->buffer.length < 4) { 267 dev_err(dev, "Wrong sized WBRT information"); 268 ret = -EINVAL; 269 goto out; 270 } 271 memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length); 272 273 out->num_of_ranges = acpi_out.num_of_ranges; 274 memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list)); 275 276 out: 277 ACPI_FREE(obj); 278 return ret; 279 } 280 EXPORT_SYMBOL_GPL(amd_wbrf_retrieve_freq_band); 281 282 /** 283 * amd_wbrf_register_notifier - register for notifications of frequency 284 * band update 285 * 286 * @nb: driver notifier block 287 * 288 * The consumer should register itself via this API so that it can get 289 * notified on the frequency band updates from other producers. 290 * 291 * Return: 292 * 0 for registering a consumer driver successfully. 293 * Returns a negative error code for failure. 294 */ 295 int amd_wbrf_register_notifier(struct notifier_block *nb) 296 { 297 return blocking_notifier_chain_register(&wbrf_chain_head, nb); 298 } 299 EXPORT_SYMBOL_GPL(amd_wbrf_register_notifier); 300 301 /** 302 * amd_wbrf_unregister_notifier - unregister for notifications of 303 * frequency band update 304 * 305 * @nb: driver notifier block 306 * 307 * The consumer should call this API when it is longer interested with 308 * the frequency band updates from other producers. Usually, this should 309 * be performed during driver cleanup. 310 * 311 * Return: 312 * 0 for unregistering a consumer driver. 313 * Returns a negative error code for failure. 314 */ 315 int amd_wbrf_unregister_notifier(struct notifier_block *nb) 316 { 317 return blocking_notifier_chain_unregister(&wbrf_chain_head, nb); 318 } 319 EXPORT_SYMBOL_GPL(amd_wbrf_unregister_notifier); 320