wbrf.c 8.6 KB

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