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 u32 num_of_ranges = 0; 46 u32 num_of_elements; 47 u32 arg_idx = 0; 48 int ret; 49 u32 i; 50 51 if (!in) 52 return -EINVAL; 53 54 for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { 55 if (in->band_list[i].start && in->band_list[i].end) 56 num_of_ranges++; 57 } 58 59 /* 60 * The num_of_ranges value in the "in" object supplied by 61 * the caller is required to be equal to the number of 62 * entries in the band_list array in there. 63 */ 64 if (num_of_ranges != in->num_of_ranges) 65 return -EINVAL; 66 67 /* 68 * Every input frequency band comes with two end points(start/end) 69 * and each is accounted as an element. Meanwhile the range count 70 * and action type are accounted as an element each. 71 * So, the total element count = 2 * num_of_ranges + 1 + 1. 72 */ 73 num_of_elements = 2 * num_of_ranges + 2; 74 75 union acpi_object *tmp __free(kfree) = kcalloc(num_of_elements, sizeof(*tmp), GFP_KERNEL); 76 if (!tmp) 77 return -ENOMEM; 78 79 argv4.package.type = ACPI_TYPE_PACKAGE; 80 argv4.package.count = num_of_elements; 81 argv4.package.elements = tmp; 82 83 /* save the number of ranges*/ 84 tmp[0].integer.type = ACPI_TYPE_INTEGER; 85 tmp[0].integer.value = num_of_ranges; 86 87 /* save the action(WBRF_RECORD_ADD/REMOVE/RETRIEVE) */ 88 tmp[1].integer.type = ACPI_TYPE_INTEGER; 89 tmp[1].integer.value = action; 90 91 arg_idx = 2; 92 for (i = 0; i < ARRAY_SIZE(in->band_list); i++) { 93 if (!in->band_list[i].start || !in->band_list[i].end) 94 continue; 95 96 tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER; 97 tmp[arg_idx++].integer.value = in->band_list[i].start; 98 tmp[arg_idx].integer.type = ACPI_TYPE_INTEGER; 99 tmp[arg_idx++].integer.value = in->band_list[i].end; 100 } 101 102 union acpi_object *obj __free(kfree) = 103 acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, 104 WBRF_REVISION, WBRF_RECORD, &argv4); 105 106 if (!obj) 107 return -EINVAL; 108 109 if (obj->type != ACPI_TYPE_INTEGER) 110 return -EINVAL; 111 112 ret = obj->integer.value; 113 if (ret) 114 return -EINVAL; 115 116 return ret; 117 } 118 119 /** 120 * acpi_amd_wbrf_add_remove - add or remove the frequency band the device is using 121 * 122 * @dev: device pointer 123 * @action: remove or add the frequency band into bios 124 * @in: input structure containing the frequency band the device is using 125 * 126 * Broadcast to other consumers the frequency band the device starts 127 * to use. Underneath the surface the information is cached into an 128 * internal buffer first. Then a notification is sent to all those 129 * registered consumers. So then they can retrieve that buffer to 130 * know the latest active frequency bands. Consumers that haven't 131 * yet been registered can retrieve the information from the cache 132 * when they register. 133 * 134 * Return: 135 * 0 for success add/remove wifi frequency band. 136 * Returns a negative error code for failure. 137 */ 138 int acpi_amd_wbrf_add_remove(struct device *dev, uint8_t action, struct wbrf_ranges_in_out *in) 139 { 140 struct acpi_device *adev; 141 int ret; 142 143 adev = ACPI_COMPANION(dev); 144 if (!adev) 145 return -ENODEV; 146 147 ret = wbrf_record(adev, action, in); 148 if (ret) 149 return ret; 150 151 blocking_notifier_call_chain(&wbrf_chain_head, WBRF_CHANGED, NULL); 152 153 return 0; 154 } 155 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_add_remove); 156 157 /** 158 * acpi_amd_wbrf_supported_producer - determine if the WBRF can be enabled 159 * for the device as a producer 160 * 161 * @dev: device pointer 162 * 163 * Check if the platform equipped with necessary implementations to 164 * support WBRF for the device as a producer. 165 * 166 * Return: 167 * true if WBRF is supported, otherwise returns false 168 */ 169 bool acpi_amd_wbrf_supported_producer(struct device *dev) 170 { 171 struct acpi_device *adev; 172 173 adev = ACPI_COMPANION(dev); 174 if (!adev) 175 return false; 176 177 return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, 178 WBRF_REVISION, BIT(WBRF_RECORD)); 179 } 180 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_producer); 181 182 /** 183 * acpi_amd_wbrf_supported_consumer - determine if the WBRF can be enabled 184 * for the device as a consumer 185 * 186 * @dev: device pointer 187 * 188 * Determine if the platform equipped with necessary implementations to 189 * support WBRF for the device as a consumer. 190 * 191 * Return: 192 * true if WBRF is supported, otherwise returns false. 193 */ 194 bool acpi_amd_wbrf_supported_consumer(struct device *dev) 195 { 196 struct acpi_device *adev; 197 198 adev = ACPI_COMPANION(dev); 199 if (!adev) 200 return false; 201 202 return acpi_check_dsm(adev->handle, &wifi_acpi_dsm_guid, 203 WBRF_REVISION, BIT(WBRF_RETRIEVE)); 204 } 205 EXPORT_SYMBOL_GPL(acpi_amd_wbrf_supported_consumer); 206 207 /** 208 * amd_wbrf_retrieve_freq_band - retrieve current active frequency bands 209 * 210 * @dev: device pointer 211 * @out: output structure containing all the active frequency bands 212 * 213 * Retrieve the current active frequency bands which were broadcasted 214 * by other producers. The consumer who calls this API should take 215 * proper actions if any of the frequency band may cause RFI with its 216 * own frequency band used. 217 * 218 * Return: 219 * 0 for getting wifi freq band successfully. 220 * Returns a negative error code for failure. 221 */ 222 int amd_wbrf_retrieve_freq_band(struct device *dev, struct wbrf_ranges_in_out *out) 223 { 224 struct amd_wbrf_ranges_out acpi_out = {0}; 225 struct acpi_device *adev; 226 union acpi_object *obj; 227 union acpi_object param; 228 int ret = 0; 229 230 adev = ACPI_COMPANION(dev); 231 if (!adev) 232 return -ENODEV; 233 234 param.type = ACPI_TYPE_STRING; 235 param.string.length = 0; 236 param.string.pointer = NULL; 237 238 obj = acpi_evaluate_dsm(adev->handle, &wifi_acpi_dsm_guid, 239 WBRF_REVISION, WBRF_RETRIEVE, ¶m); 240 if (!obj) 241 return -EINVAL; 242 243 /* 244 * The return buffer is with variable length and the format below: 245 * number_of_entries(1 DWORD): Number of entries 246 * start_freq of 1st entry(1 QWORD): Start frequency of the 1st entry 247 * end_freq of 1st entry(1 QWORD): End frequency of the 1st entry 248 * ... 249 * ... 250 * start_freq of the last entry(1 QWORD) 251 * end_freq of the last entry(1 QWORD) 252 * 253 * Thus the buffer length is determined by the number of entries. 254 * - For zero entry scenario, the buffer length will be 4 bytes. 255 * - For one entry scenario, the buffer length will be 20 bytes. 256 */ 257 if (obj->buffer.length > sizeof(acpi_out) || obj->buffer.length < 4) { 258 dev_err(dev, "Wrong sized WBRT information"); 259 ret = -EINVAL; 260 goto out; 261 } 262 memcpy(&acpi_out, obj->buffer.pointer, obj->buffer.length); 263 264 out->num_of_ranges = acpi_out.num_of_ranges; 265 memcpy(out->band_list, acpi_out.band_list, sizeof(acpi_out.band_list)); 266 267 out: 268 ACPI_FREE(obj); 269 return ret; 270 } 271 EXPORT_SYMBOL_GPL(amd_wbrf_retrieve_freq_band); 272 273 /** 274 * amd_wbrf_register_notifier - register for notifications of frequency 275 * band update 276 * 277 * @nb: driver notifier block 278 * 279 * The consumer should register itself via this API so that it can get 280 * notified on the frequency band updates from other producers. 281 * 282 * Return: 283 * 0 for registering a consumer driver successfully. 284 * Returns a negative error code for failure. 285 */ 286 int amd_wbrf_register_notifier(struct notifier_block *nb) 287 { 288 return blocking_notifier_chain_register(&wbrf_chain_head, nb); 289 } 290 EXPORT_SYMBOL_GPL(amd_wbrf_register_notifier); 291 292 /** 293 * amd_wbrf_unregister_notifier - unregister for notifications of 294 * frequency band update 295 * 296 * @nb: driver notifier block 297 * 298 * The consumer should call this API when it is longer interested with 299 * the frequency band updates from other producers. Usually, this should 300 * be performed during driver cleanup. 301 * 302 * Return: 303 * 0 for unregistering a consumer driver. 304 * Returns a negative error code for failure. 305 */ 306 int amd_wbrf_unregister_notifier(struct notifier_block *nb) 307 { 308 return blocking_notifier_chain_unregister(&wbrf_chain_head, nb); 309 } 310 EXPORT_SYMBOL_GPL(amd_wbrf_unregister_notifier); 311