PageRenderTime 121ms CodeModel.GetById 20ms RepoModel.GetById 15ms app.codeStats 0ms

/drivers/staging/brcm80211/brcmsmac/wlc_antsel.c

https://bitbucket.org/wisechild/galaxy-nexus
C | 320 lines | 227 code | 47 blank | 46 comment | 51 complexity | 4764de48882bed90a4ca2965c01dc943 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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 <wlc_cfg.h>
  17. #include <linux/kernel.h>
  18. #include <linux/module.h>
  19. #include <linux/pci.h>
  20. #include <bcmdefs.h>
  21. #include <bcmutils.h>
  22. #include <bcmnvram.h>
  23. #include <aiutils.h>
  24. #include <bcmdevs.h>
  25. #include <sbhnddma.h>
  26. #include <wlioctl.h>
  27. #include "d11.h"
  28. #include "wlc_rate.h"
  29. #include "wlc_key.h"
  30. #include "wlc_scb.h"
  31. #include "wlc_pub.h"
  32. #include "wl_dbg.h"
  33. #include "phy/wlc_phy_hal.h"
  34. #include "wlc_bmac.h"
  35. #include "wlc_channel.h"
  36. #include "wlc_main.h"
  37. #include "wl_export.h"
  38. #include "wlc_phy_shim.h"
  39. #include "wlc_antsel.h"
  40. /* useful macros */
  41. #define WLC_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
  42. #define WLC_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
  43. #define WLC_ANTIDX_11N(ant) (((WLC_ANTSEL_11N_0(ant)) << 2) + (WLC_ANTSEL_11N_1(ant)))
  44. #define WLC_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
  45. #define WLC_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
  46. /* antenna switch */
  47. /* defines for no boardlevel antenna diversity */
  48. #define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */
  49. /* 2x3 antdiv defines and tables for GPIO communication */
  50. #define ANT_SELCFG_NUM_2x3 3
  51. #define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */
  52. /* 2x4 antdiv rev4 defines and tables for GPIO communication */
  53. #define ANT_SELCFG_NUM_2x4 4
  54. #define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */
  55. /* static functions */
  56. static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel);
  57. static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
  58. static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
  59. static void wlc_antsel_init_cfg(struct antsel_info *asi,
  60. wlc_antselcfg_t *antsel,
  61. bool auto_sel);
  62. const u16 mimo_2x4_div_antselpat_tbl[] = {
  63. 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */
  64. 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */
  65. 0, 0, 0, 0, /* n.a. */
  66. 0, 0, 0, 0 /* n.a. */
  67. };
  68. const u8 mimo_2x4_div_antselid_tbl[16] = {
  69. 0, 0, 0, 0, 0, 2, 3, 0,
  70. 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */
  71. };
  72. const u16 mimo_2x3_div_antselpat_tbl[] = {
  73. 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */
  74. 16, 16, 16, 16, /* n.a. */
  75. 16, 2, 16, 16, /* ant0: 2 ant1: 1 */
  76. 16, 16, 16, 16 /* n.a. */
  77. };
  78. const u8 mimo_2x3_div_antselid_tbl[16] = {
  79. 0, 1, 2, 0, 0, 0, 0, 0,
  80. 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */
  81. };
  82. struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc)
  83. {
  84. struct antsel_info *asi;
  85. asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
  86. if (!asi) {
  87. wiphy_err(wlc->wiphy, "wl%d: wlc_antsel_attach: out of mem\n",
  88. wlc->pub->unit);
  89. return NULL;
  90. }
  91. asi->wlc = wlc;
  92. asi->pub = wlc->pub;
  93. asi->antsel_type = ANTSEL_NA;
  94. asi->antsel_avail = false;
  95. asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
  96. if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
  97. switch (asi->antsel_antswitch) {
  98. case ANTSWITCH_TYPE_1:
  99. case ANTSWITCH_TYPE_2:
  100. case ANTSWITCH_TYPE_3:
  101. /* 4321/2 board with 2x3 switch logic */
  102. asi->antsel_type = ANTSEL_2x3;
  103. /* Antenna selection availability */
  104. if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
  105. ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
  106. asi->antsel_avail = true;
  107. } else
  108. if (((u16) getintvar(asi->pub->vars, "aa2g") ==
  109. 3)
  110. || ((u16) getintvar(asi->pub->vars, "aa5g")
  111. == 3)) {
  112. asi->antsel_avail = false;
  113. } else {
  114. asi->antsel_avail = false;
  115. wiphy_err(wlc->wiphy, "wlc_antsel_attach: 2o3 "
  116. "board cfg invalid\n");
  117. }
  118. break;
  119. default:
  120. break;
  121. }
  122. } else if ((asi->pub->sromrev == 4) &&
  123. ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
  124. ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
  125. /* hack to match old 4321CB2 cards with 2of3 antenna switch */
  126. asi->antsel_type = ANTSEL_2x3;
  127. asi->antsel_avail = true;
  128. } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
  129. asi->antsel_type = ANTSEL_2x4;
  130. asi->antsel_avail = true;
  131. }
  132. /* Set the antenna selection type for the low driver */
  133. wlc_bmac_antsel_type_set(wlc->hw, asi->antsel_type);
  134. /* Init (auto/manual) antenna selection */
  135. wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true);
  136. wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true);
  137. return asi;
  138. }
  139. void wlc_antsel_detach(struct antsel_info *asi)
  140. {
  141. kfree(asi);
  142. }
  143. void wlc_antsel_init(struct antsel_info *asi)
  144. {
  145. if ((asi->antsel_type == ANTSEL_2x3) ||
  146. (asi->antsel_type == ANTSEL_2x4))
  147. wlc_antsel_cfgupd(asi, &asi->antcfg_11n);
  148. }
  149. /* boardlevel antenna selection: init antenna selection structure */
  150. static void
  151. wlc_antsel_init_cfg(struct antsel_info *asi, wlc_antselcfg_t *antsel,
  152. bool auto_sel)
  153. {
  154. if (asi->antsel_type == ANTSEL_2x3) {
  155. u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
  156. ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
  157. antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
  158. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
  159. antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
  160. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
  161. antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
  162. } else if (asi->antsel_type == ANTSEL_2x4) {
  163. antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
  164. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
  165. antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
  166. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
  167. antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
  168. } else { /* no antenna selection available */
  169. antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
  170. antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
  171. antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
  172. antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
  173. antsel->num_antcfg = 0;
  174. }
  175. }
  176. void
  177. wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
  178. u8 antselid, u8 fbantselid, u8 *antcfg,
  179. u8 *fbantcfg)
  180. {
  181. u8 ant;
  182. /* if use default, assign it and return */
  183. if (usedef) {
  184. *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
  185. *fbantcfg = *antcfg;
  186. return;
  187. }
  188. if (!sel) {
  189. *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  190. *fbantcfg = *antcfg;
  191. } else {
  192. ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  193. if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
  194. *antcfg = wlc_antsel_id2antcfg(asi, antselid);
  195. *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid);
  196. } else {
  197. *antcfg =
  198. asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
  199. *fbantcfg = *antcfg;
  200. }
  201. }
  202. return;
  203. }
  204. /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
  205. u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
  206. {
  207. u8 antselid = 0;
  208. if (asi->antsel_type == ANTSEL_2x4) {
  209. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  210. antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
  211. return antselid;
  212. } else if (asi->antsel_type == ANTSEL_2x3) {
  213. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  214. antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
  215. return antselid;
  216. }
  217. return antselid;
  218. }
  219. /* boardlevel antenna selection: convert id to ant_cfg */
  220. static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id)
  221. {
  222. u8 antcfg = ANT_SELCFG_DEF_2x2;
  223. if (asi->antsel_type == ANTSEL_2x4) {
  224. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  225. antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
  226. return antcfg;
  227. } else if (asi->antsel_type == ANTSEL_2x3) {
  228. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  229. antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
  230. return antcfg;
  231. }
  232. return antcfg;
  233. }
  234. /* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */
  235. static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
  236. {
  237. u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg));
  238. u16 mimo_antsel = 0;
  239. if (asi->antsel_type == ANTSEL_2x4) {
  240. /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
  241. mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
  242. return mimo_antsel;
  243. } else if (asi->antsel_type == ANTSEL_2x3) {
  244. /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
  245. mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
  246. return mimo_antsel;
  247. }
  248. return mimo_antsel;
  249. }
  250. /* boardlevel antenna selection: ucode interface control */
  251. static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel)
  252. {
  253. struct wlc_info *wlc = asi->wlc;
  254. u8 ant_cfg;
  255. u16 mimo_antsel;
  256. /* 1) Update TX antconfig for all frames that are not unicast data
  257. * (aka default TX)
  258. */
  259. ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
  260. mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
  261. wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
  262. /* Update driver stats for currently selected default tx/rx antenna config */
  263. asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
  264. /* 2) Update RX antconfig for all frames that are not unicast data
  265. * (aka default RX)
  266. */
  267. ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
  268. mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
  269. wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
  270. /* Update driver stats for currently selected default tx/rx antenna config */
  271. asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
  272. return 0;
  273. }