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 
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 
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 
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 
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 
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 
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 
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 
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