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