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