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