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
13static 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
28static 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
41static 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
60static 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
77static 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
96static 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
137static 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
237void 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}
247EXPORT_SYMBOL(brcmu_d11_attach);
248