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