antsel.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. /*
  2. * Copyright (c) 2010 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. #include <linux/slab.h>
  17. #include <net/mac80211.h>
  18. #include "types.h"
  19. #include "main.h"
  20. #include "phy_shim.h"
  21. #include "antsel.h"
  22. #include "debug.h"
  23. #define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */
  24. #define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */
  25. #define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */
  26. #define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */
  27. #define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */
  28. #define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */
  29. /* useful macros */
  30. #define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
  31. #define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
  32. #define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\
  33. (BRCMS_ANTSEL_11N_1(ant)))
  34. #define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
  35. #define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
  36. /* antenna switch */
  37. /* defines for no boardlevel antenna diversity */
  38. #define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */
  39. /* 2x3 antdiv defines and tables for GPIO communication */
  40. #define ANT_SELCFG_NUM_2x3 3
  41. #define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */
  42. /* 2x4 antdiv rev4 defines and tables for GPIO communication */
  43. #define ANT_SELCFG_NUM_2x4 4
  44. #define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */
  45. static const u16 mimo_2x4_div_antselpat_tbl[] = {
  46. 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */
  47. 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */
  48. 0, 0, 0, 0, /* n.a. */
  49. 0, 0, 0, 0 /* n.a. */
  50. };
  51. static const u8 mimo_2x4_div_antselid_tbl[16] = {
  52. 0, 0, 0, 0, 0, 2, 3, 0,
  53. 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */
  54. };
  55. static const u16 mimo_2x3_div_antselpat_tbl[] = {
  56. 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */
  57. 16, 16, 16, 16, /* n.a. */
  58. 16, 2, 16, 16, /* ant0: 2 ant1: 1 */
  59. 16, 16, 16, 16 /* n.a. */
  60. };
  61. static const u8 mimo_2x3_div_antselid_tbl[16] = {
  62. 0, 1, 2, 0, 0, 0, 0, 0,
  63. 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */
  64. };
  65. /* boardlevel antenna selection: init antenna selection structure */
  66. static void
  67. brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,
  68. bool auto_sel)
  69. {
  70. if (asi->antsel_type == ANTSEL_2x3) {
  71. u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
  72. ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
  73. antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
  74. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
  75. antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
  76. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
  77. antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
  78. } else if (asi->antsel_type == ANTSEL_2x4) {
  79. antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
  80. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
  81. antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
  82. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
  83. antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
  84. } else { /* no antenna selection available */
  85. antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
  86. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
  87. antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
  88. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
  89. antsel->num_antcfg = 0;
  90. }
  91. }
  92. struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)
  93. {
  94. struct antsel_info *asi;
  95. struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
  96. asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
  97. if (!asi)
  98. return NULL;
  99. asi->wlc = wlc;
  100. asi->pub = wlc->pub;
  101. asi->antsel_type = ANTSEL_NA;
  102. asi->antsel_avail = false;
  103. asi->antsel_antswitch = sprom->antswitch;
  104. if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
  105. switch (asi->antsel_antswitch) {
  106. case ANTSWITCH_TYPE_1:
  107. case ANTSWITCH_TYPE_2:
  108. case ANTSWITCH_TYPE_3:
  109. /* 4321/2 board with 2x3 switch logic */
  110. asi->antsel_type = ANTSEL_2x3;
  111. /* Antenna selection availability */
  112. if ((sprom->ant_available_bg == 7) ||
  113. (sprom->ant_available_a == 7)) {
  114. asi->antsel_avail = true;
  115. } else if (
  116. sprom->ant_available_bg == 3 ||
  117. sprom->ant_available_a == 3) {
  118. asi->antsel_avail = false;
  119. } else {
  120. asi->antsel_avail = false;
  121. brcms_err(wlc->hw->d11core,
  122. "antsel_attach: 2o3 "
  123. "board cfg invalid\n");
  124. }
  125. break;
  126. default:
  127. break;
  128. }
  129. } else if ((asi->pub->sromrev == 4) &&
  130. (sprom->ant_available_bg == 7) &&
  131. (sprom->ant_available_a == 0)) {
  132. /* hack to match old 4321CB2 cards with 2of3 antenna switch */
  133. asi->antsel_type = ANTSEL_2x3;
  134. asi->antsel_avail = true;
  135. } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
  136. asi->antsel_type = ANTSEL_2x4;
  137. asi->antsel_avail = true;
  138. }
  139. /* Set the antenna selection type for the low driver */
  140. brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);
  141. /* Init (auto/manual) antenna selection */
  142. brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);
  143. brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);
  144. return asi;
  145. }
  146. void brcms_c_antsel_detach(struct antsel_info *asi)
  147. {
  148. kfree(asi);
  149. }
  150. /*
  151. * boardlevel antenna selection:
  152. * convert ant_cfg to mimo_antsel (ucode interface)
  153. */
  154. static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
  155. {
  156. u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));
  157. u16 mimo_antsel = 0;
  158. if (asi->antsel_type == ANTSEL_2x4) {
  159. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  160. mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
  161. return mimo_antsel;
  162. } else if (asi->antsel_type == ANTSEL_2x3) {
  163. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  164. mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
  165. return mimo_antsel;
  166. }
  167. return mimo_antsel;
  168. }
  169. /* boardlevel antenna selection: ucode interface control */
  170. static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
  171. struct brcms_antselcfg *antsel)
  172. {
  173. struct brcms_c_info *wlc = asi->wlc;
  174. u8 ant_cfg;
  175. u16 mimo_antsel;
  176. /* 1) Update TX antconfig for all frames that are not unicast data
  177. * (aka default TX)
  178. */
  179. ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
  180. mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
  181. brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
  182. /*
  183. * Update driver stats for currently selected
  184. * default tx/rx antenna config
  185. */
  186. asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
  187. /* 2) Update RX antconfig for all frames that are not unicast data
  188. * (aka default RX)
  189. */
  190. ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
  191. mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
  192. brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
  193. /*
  194. * Update driver stats for currently selected
  195. * default tx/rx antenna config
  196. */
  197. asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
  198. return 0;
  199. }
  200. void brcms_c_antsel_init(struct antsel_info *asi)
  201. {
  202. if ((asi->antsel_type == ANTSEL_2x3) ||
  203. (asi->antsel_type == ANTSEL_2x4))
  204. brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);
  205. }
  206. /* boardlevel antenna selection: convert id to ant_cfg */
  207. static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)
  208. {
  209. u8 antcfg = ANT_SELCFG_DEF_2x2;
  210. if (asi->antsel_type == ANTSEL_2x4) {
  211. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  212. antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
  213. return antcfg;
  214. } else if (asi->antsel_type == ANTSEL_2x3) {
  215. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  216. antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
  217. return antcfg;
  218. }
  219. return antcfg;
  220. }
  221. void
  222. brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
  223. u8 antselid, u8 fbantselid, u8 *antcfg,
  224. u8 *fbantcfg)
  225. {
  226. u8 ant;
  227. /* if use default, assign it and return */
  228. if (usedef) {
  229. *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
  230. *fbantcfg = *antcfg;
  231. return;
  232. }
  233. if (!sel) {
  234. *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  235. *fbantcfg = *antcfg;
  236. } else {
  237. ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  238. if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
  239. *antcfg = brcms_c_antsel_id2antcfg(asi, antselid);
  240. *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);
  241. } else {
  242. *antcfg =
  243. asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  244. *fbantcfg = *antcfg;
  245. }
  246. }
  247. return;
  248. }
  249. /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
  250. u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
  251. {
  252. u8 antselid = 0;
  253. if (asi->antsel_type == ANTSEL_2x4) {
  254. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  255. antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
  256. return antselid;
  257. } else if (asi->antsel_type == ANTSEL_2x3) {
  258. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  259. antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
  260. return antselid;
  261. }
  262. return antselid;
  263. }