1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Wifi Band Exclusion Interface for WLAN
4 * Copyright (C) 2023 Advanced Micro Devices
5 * Copyright (C) 2025 Intel Corporation
6 *
7 */
8
9 #include <linux/acpi_amd_wbrf.h>
10 #include <linux/units.h>
11 #include <net/cfg80211.h>
12 #include "ieee80211_i.h"
13
ieee80211_check_wbrf_support(struct ieee80211_local * local)14 void ieee80211_check_wbrf_support(struct ieee80211_local *local)
15 {
16 struct wiphy *wiphy = local->hw.wiphy;
17 struct device *dev;
18
19 if (!wiphy)
20 return;
21
22 dev = wiphy->dev.parent;
23 if (!dev)
24 return;
25
26 local->wbrf_supported = acpi_amd_wbrf_supported_producer(dev);
27 }
28
get_chan_freq_boundary(u32 center_freq,u32 bandwidth,u64 * start,u64 * end)29 static void get_chan_freq_boundary(u32 center_freq, u32 bandwidth, u64 *start, u64 *end)
30 {
31 bandwidth *= KHZ_PER_MHZ;
32 center_freq *= KHZ_PER_MHZ;
33
34 *start = center_freq - bandwidth / 2;
35 *end = center_freq + bandwidth / 2;
36
37 /* Frequency in Hz is expected */
38 *start = *start * HZ_PER_KHZ;
39 *end = *end * HZ_PER_KHZ;
40 }
41
get_ranges_from_chandef(struct cfg80211_chan_def * chandef,struct wbrf_ranges_in_out * ranges_in)42 static void get_ranges_from_chandef(struct cfg80211_chan_def *chandef,
43 struct wbrf_ranges_in_out *ranges_in)
44 {
45 u64 start_freq1, end_freq1;
46 u64 start_freq2, end_freq2;
47 int bandwidth;
48
49 bandwidth = cfg80211_chandef_get_width(chandef);
50
51 get_chan_freq_boundary(chandef->center_freq1, bandwidth, &start_freq1, &end_freq1);
52
53 ranges_in->band_list[0].start = start_freq1;
54 ranges_in->band_list[0].end = end_freq1;
55 ranges_in->num_of_ranges = 1;
56
57 if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
58 get_chan_freq_boundary(chandef->center_freq2, bandwidth, &start_freq2, &end_freq2);
59
60 ranges_in->band_list[1].start = start_freq2;
61 ranges_in->band_list[1].end = end_freq2;
62 ranges_in->num_of_ranges++;
63 }
64 }
65
ieee80211_add_wbrf(struct ieee80211_local * local,struct cfg80211_chan_def * chandef)66 void ieee80211_add_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef)
67 {
68 struct wbrf_ranges_in_out ranges_in = {0};
69 struct device *dev;
70
71 if (!local->wbrf_supported)
72 return;
73
74 dev = local->hw.wiphy->dev.parent;
75
76 get_ranges_from_chandef(chandef, &ranges_in);
77
78 acpi_amd_wbrf_add_remove(dev, WBRF_RECORD_ADD, &ranges_in);
79 }
80
ieee80211_remove_wbrf(struct ieee80211_local * local,struct cfg80211_chan_def * chandef)81 void ieee80211_remove_wbrf(struct ieee80211_local *local, struct cfg80211_chan_def *chandef)
82 {
83 struct wbrf_ranges_in_out ranges_in = {0};
84 struct device *dev;
85
86 if (!local->wbrf_supported)
87 return;
88
89 dev = local->hw.wiphy->dev.parent;
90
91 get_ranges_from_chandef(chandef, &ranges_in);
92
93 acpi_amd_wbrf_add_remove(dev, WBRF_RECORD_REMOVE, &ranges_in);
94 }
95