1 // SPDX-License-Identifier: ISC
2 /*
3 * Copyright (c) 2013 Broadcom Corporation
4 */
5 /*********************channel spec common functions*********************/
6
7 #include <linux/module.h>
8
9 #include <brcmu_utils.h>
10 #include <brcmu_wifi.h>
11 #include <brcmu_d11.h>
12
d11n_sb(enum brcmu_chan_sb sb)13 static u16 d11n_sb(enum brcmu_chan_sb sb)
14 {
15 switch (sb) {
16 case BRCMU_CHAN_SB_NONE:
17 return BRCMU_CHSPEC_D11N_SB_N;
18 case BRCMU_CHAN_SB_L:
19 return BRCMU_CHSPEC_D11N_SB_L;
20 case BRCMU_CHAN_SB_U:
21 return BRCMU_CHSPEC_D11N_SB_U;
22 default:
23 WARN_ON(1);
24 }
25 return 0;
26 }
27
d11n_bw(enum brcmu_chan_bw bw)28 static u16 d11n_bw(enum brcmu_chan_bw bw)
29 {
30 switch (bw) {
31 case BRCMU_CHAN_BW_20:
32 return BRCMU_CHSPEC_D11N_BW_20;
33 case BRCMU_CHAN_BW_40:
34 return BRCMU_CHSPEC_D11N_BW_40;
35 default:
36 WARN_ON(1);
37 }
38 return 0;
39 }
40
brcmu_d11n_encchspec(struct brcmu_chan * ch)41 static void brcmu_d11n_encchspec(struct brcmu_chan *ch)
42 {
43 if (ch->bw == BRCMU_CHAN_BW_20)
44 ch->sb = BRCMU_CHAN_SB_NONE;
45
46 ch->chspec = 0;
47 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
48 BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
49 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_SB_MASK,
50 0, d11n_sb(ch->sb));
51 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11N_BW_MASK,
52 0, d11n_bw(ch->bw));
53
54 if (ch->chnum <= CH_MAX_2G_CHANNEL)
55 ch->chspec |= BRCMU_CHSPEC_D11N_BND_2G;
56 else
57 ch->chspec |= BRCMU_CHSPEC_D11N_BND_5G;
58 }
59
d11ac_bw(enum brcmu_chan_bw bw)60 static u16 d11ac_bw(enum brcmu_chan_bw bw)
61 {
62 switch (bw) {
63 case BRCMU_CHAN_BW_20:
64 return BRCMU_CHSPEC_D11AC_BW_20;
65 case BRCMU_CHAN_BW_40:
66 return BRCMU_CHSPEC_D11AC_BW_40;
67 case BRCMU_CHAN_BW_80:
68 return BRCMU_CHSPEC_D11AC_BW_80;
69 case BRCMU_CHAN_BW_160:
70 return BRCMU_CHSPEC_D11AC_BW_160;
71 default:
72 WARN_ON(1);
73 }
74 return 0;
75 }
76
brcmu_d11ac_encchspec(struct brcmu_chan * ch)77 static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
78 {
79 if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE)
80 ch->sb = BRCMU_CHAN_SB_L;
81
82 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
83 BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
84 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
85 BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb);
86 brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK,
87 0, d11ac_bw(ch->bw));
88
89 ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
90 if (ch->chnum <= CH_MAX_2G_CHANNEL)
91 ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
92 else
93 ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G;
94 }
95
brcmu_d11n_decchspec(struct brcmu_chan * ch)96 static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
97 {
98 u16 val;
99
100 ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
101 ch->control_ch_num = ch->chnum;
102
103 switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) {
104 case BRCMU_CHSPEC_D11N_BW_20:
105 ch->bw = BRCMU_CHAN_BW_20;
106 ch->sb = BRCMU_CHAN_SB_NONE;
107 break;
108 case BRCMU_CHSPEC_D11N_BW_40:
109 ch->bw = BRCMU_CHAN_BW_40;
110 val = ch->chspec & BRCMU_CHSPEC_D11N_SB_MASK;
111 if (val == BRCMU_CHSPEC_D11N_SB_L) {
112 ch->sb = BRCMU_CHAN_SB_L;
113 ch->control_ch_num -= CH_10MHZ_APART;
114 } else {
115 ch->sb = BRCMU_CHAN_SB_U;
116 ch->control_ch_num += CH_10MHZ_APART;
117 }
118 break;
119 default:
120 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
121 break;
122 }
123
124 switch (ch->chspec & BRCMU_CHSPEC_D11N_BND_MASK) {
125 case BRCMU_CHSPEC_D11N_BND_5G:
126 ch->band = BRCMU_CHAN_BAND_5G;
127 break;
128 case BRCMU_CHSPEC_D11N_BND_2G:
129 ch->band = BRCMU_CHAN_BAND_2G;
130 break;
131 default:
132 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
133 break;
134 }
135 }
136
brcmu_d11ac_decchspec(struct brcmu_chan * ch)137 static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
138 {
139 u16 val;
140
141 ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
142 ch->control_ch_num = ch->chnum;
143
144 switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) {
145 case BRCMU_CHSPEC_D11AC_BW_20:
146 ch->bw = BRCMU_CHAN_BW_20;
147 ch->sb = BRCMU_CHAN_SB_NONE;
148 break;
149 case BRCMU_CHSPEC_D11AC_BW_40:
150 ch->bw = BRCMU_CHAN_BW_40;
151 val = ch->chspec & BRCMU_CHSPEC_D11AC_SB_MASK;
152 if (val == BRCMU_CHSPEC_D11AC_SB_L) {
153 ch->sb = BRCMU_CHAN_SB_L;
154 ch->control_ch_num -= CH_10MHZ_APART;
155 } else if (val == BRCMU_CHSPEC_D11AC_SB_U) {
156 ch->sb = BRCMU_CHAN_SB_U;
157 ch->control_ch_num += CH_10MHZ_APART;
158 } else {
159 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
160 }
161 break;
162 case BRCMU_CHSPEC_D11AC_BW_80:
163 ch->bw = BRCMU_CHAN_BW_80;
164 ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
165 BRCMU_CHSPEC_D11AC_SB_SHIFT);
166 switch (ch->sb) {
167 case BRCMU_CHAN_SB_LL:
168 ch->control_ch_num -= CH_30MHZ_APART;
169 break;
170 case BRCMU_CHAN_SB_LU:
171 ch->control_ch_num -= CH_10MHZ_APART;
172 break;
173 case BRCMU_CHAN_SB_UL:
174 ch->control_ch_num += CH_10MHZ_APART;
175 break;
176 case BRCMU_CHAN_SB_UU:
177 ch->control_ch_num += CH_30MHZ_APART;
178 break;
179 default:
180 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
181 break;
182 }
183 break;
184 case BRCMU_CHSPEC_D11AC_BW_160:
185 ch->bw = BRCMU_CHAN_BW_160;
186 ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
187 BRCMU_CHSPEC_D11AC_SB_SHIFT);
188 switch (ch->sb) {
189 case BRCMU_CHAN_SB_LLL:
190 ch->control_ch_num -= CH_70MHZ_APART;
191 break;
192 case BRCMU_CHAN_SB_LLU:
193 ch->control_ch_num -= CH_50MHZ_APART;
194 break;
195 case BRCMU_CHAN_SB_LUL:
196 ch->control_ch_num -= CH_30MHZ_APART;
197 break;
198 case BRCMU_CHAN_SB_LUU:
199 ch->control_ch_num -= CH_10MHZ_APART;
200 break;
201 case BRCMU_CHAN_SB_ULL:
202 ch->control_ch_num += CH_10MHZ_APART;
203 break;
204 case BRCMU_CHAN_SB_ULU:
205 ch->control_ch_num += CH_30MHZ_APART;
206 break;
207 case BRCMU_CHAN_SB_UUL:
208 ch->control_ch_num += CH_50MHZ_APART;
209 break;
210 case BRCMU_CHAN_SB_UUU:
211 ch->control_ch_num += CH_70MHZ_APART;
212 break;
213 default:
214 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
215 break;
216 }
217 break;
218 case BRCMU_CHSPEC_D11AC_BW_8080:
219 default:
220 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
221 break;
222 }
223
224 switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) {
225 case BRCMU_CHSPEC_D11AC_BND_5G:
226 ch->band = BRCMU_CHAN_BAND_5G;
227 break;
228 case BRCMU_CHSPEC_D11AC_BND_2G:
229 ch->band = BRCMU_CHAN_BAND_2G;
230 break;
231 default:
232 WARN_ONCE(1, "Invalid chanspec 0x%04x\n", ch->chspec);
233 break;
234 }
235 }
236
brcmu_d11_attach(struct brcmu_d11inf * d11inf)237 void brcmu_d11_attach(struct brcmu_d11inf *d11inf)
238 {
239 if (d11inf->io_type == BRCMU_D11N_IOTYPE) {
240 d11inf->encchspec = brcmu_d11n_encchspec;
241 d11inf->decchspec = brcmu_d11n_decchspec;
242 } else {
243 d11inf->encchspec = brcmu_d11ac_encchspec;
244 d11inf->decchspec = brcmu_d11ac_decchspec;
245 }
246 }
247 EXPORT_SYMBOL(brcmu_d11_attach);
248