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
24static 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
39static 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
52static 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
71static 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	default:
81		WARN_ON(1);
82	}
83	return 0;
84}
85
86static void brcmu_d11ac_encchspec(struct brcmu_chan *ch)
87{
88	if (ch->bw == BRCMU_CHAN_BW_20 || ch->sb == BRCMU_CHAN_SB_NONE)
89		ch->sb = BRCMU_CHAN_SB_L;
90
91	brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_CH_MASK,
92			BRCMU_CHSPEC_CH_SHIFT, ch->chnum);
93	brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
94			BRCMU_CHSPEC_D11AC_SB_SHIFT, ch->sb);
95	brcmu_maskset16(&ch->chspec, BRCMU_CHSPEC_D11AC_BW_MASK,
96			0, d11ac_bw(ch->bw));
97
98	ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK;
99	if (ch->chnum <= CH_MAX_2G_CHANNEL)
100		ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G;
101	else
102		ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G;
103}
104
105static void brcmu_d11n_decchspec(struct brcmu_chan *ch)
106{
107	u16 val;
108
109	ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
110
111	switch (ch->chspec & BRCMU_CHSPEC_D11N_BW_MASK) {
112	case BRCMU_CHSPEC_D11N_BW_20:
113		ch->bw = BRCMU_CHAN_BW_20;
114		ch->sb = BRCMU_CHAN_SB_NONE;
115		break;
116	case BRCMU_CHSPEC_D11N_BW_40:
117		ch->bw = BRCMU_CHAN_BW_40;
118		val = ch->chspec & BRCMU_CHSPEC_D11N_SB_MASK;
119		if (val == BRCMU_CHSPEC_D11N_SB_L) {
120			ch->sb = BRCMU_CHAN_SB_L;
121			ch->chnum -= CH_10MHZ_APART;
122		} else {
123			ch->sb = BRCMU_CHAN_SB_U;
124			ch->chnum += CH_10MHZ_APART;
125		}
126		break;
127	default:
128		WARN_ON_ONCE(1);
129		break;
130	}
131
132	switch (ch->chspec & BRCMU_CHSPEC_D11N_BND_MASK) {
133	case BRCMU_CHSPEC_D11N_BND_5G:
134		ch->band = BRCMU_CHAN_BAND_5G;
135		break;
136	case BRCMU_CHSPEC_D11N_BND_2G:
137		ch->band = BRCMU_CHAN_BAND_2G;
138		break;
139	default:
140		WARN_ON_ONCE(1);
141		break;
142	}
143}
144
145static void brcmu_d11ac_decchspec(struct brcmu_chan *ch)
146{
147	u16 val;
148
149	ch->chnum = (u8)(ch->chspec & BRCMU_CHSPEC_CH_MASK);
150
151	switch (ch->chspec & BRCMU_CHSPEC_D11AC_BW_MASK) {
152	case BRCMU_CHSPEC_D11AC_BW_20:
153		ch->bw = BRCMU_CHAN_BW_20;
154		ch->sb = BRCMU_CHAN_SB_NONE;
155		break;
156	case BRCMU_CHSPEC_D11AC_BW_40:
157		ch->bw = BRCMU_CHAN_BW_40;
158		val = ch->chspec & BRCMU_CHSPEC_D11AC_SB_MASK;
159		if (val == BRCMU_CHSPEC_D11AC_SB_L) {
160			ch->sb = BRCMU_CHAN_SB_L;
161			ch->chnum -= CH_10MHZ_APART;
162		} else if (val == BRCMU_CHSPEC_D11AC_SB_U) {
163			ch->sb = BRCMU_CHAN_SB_U;
164			ch->chnum += CH_10MHZ_APART;
165		} else {
166			WARN_ON_ONCE(1);
167		}
168		break;
169	case BRCMU_CHSPEC_D11AC_BW_80:
170		ch->bw = BRCMU_CHAN_BW_80;
171		ch->sb = brcmu_maskget16(ch->chspec, BRCMU_CHSPEC_D11AC_SB_MASK,
172					 BRCMU_CHSPEC_D11AC_SB_SHIFT);
173		switch (ch->sb) {
174		case BRCMU_CHAN_SB_LL:
175			ch->chnum -= CH_30MHZ_APART;
176			break;
177		case BRCMU_CHAN_SB_LU:
178			ch->chnum -= CH_10MHZ_APART;
179			break;
180		case BRCMU_CHAN_SB_UL:
181			ch->chnum += CH_10MHZ_APART;
182			break;
183		case BRCMU_CHAN_SB_UU:
184			ch->chnum += CH_30MHZ_APART;
185			break;
186		default:
187			WARN_ON_ONCE(1);
188			break;
189		}
190		break;
191	case BRCMU_CHSPEC_D11AC_BW_8080:
192	case BRCMU_CHSPEC_D11AC_BW_160:
193	default:
194		WARN_ON_ONCE(1);
195		break;
196	}
197
198	switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) {
199	case BRCMU_CHSPEC_D11AC_BND_5G:
200		ch->band = BRCMU_CHAN_BAND_5G;
201		break;
202	case BRCMU_CHSPEC_D11AC_BND_2G:
203		ch->band = BRCMU_CHAN_BAND_2G;
204		break;
205	default:
206		WARN_ON_ONCE(1);
207		break;
208	}
209}
210
211void brcmu_d11_attach(struct brcmu_d11inf *d11inf)
212{
213	if (d11inf->io_type == BRCMU_D11N_IOTYPE) {
214		d11inf->encchspec = brcmu_d11n_encchspec;
215		d11inf->decchspec = brcmu_d11n_decchspec;
216	} else {
217		d11inf->encchspec = brcmu_d11ac_encchspec;
218		d11inf->decchspec = brcmu_d11ac_decchspec;
219	}
220}
221EXPORT_SYMBOL(brcmu_d11_attach);
222