hal.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. * Intel Wireless Multicomm 3200 WiFi driver
  3. *
  4. * Copyright (C) 2009 Intel Corporation. All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in
  14. * the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of Intel Corporation nor the names of its
  17. * contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. *
  33. * Intel Corporation <ilw@linux.intel.com>
  34. * Samuel Ortiz <samuel.ortiz@intel.com>
  35. * Zhu Yi <yi.zhu@intel.com>
  36. *
  37. */
  38. /*
  39. * Hardware Abstraction Layer for iwm.
  40. *
  41. * This file mostly defines an abstraction API for
  42. * sending various commands to the target.
  43. *
  44. * We have 2 types of commands: wifi and non-wifi ones.
  45. *
  46. * - wifi commands:
  47. * They are used for sending LMAC and UMAC commands,
  48. * and thus are the most commonly used ones.
  49. * There are 2 different wifi command types, the regular
  50. * one and the LMAC one. The former is used to send
  51. * UMAC commands (see UMAC_CMD_OPCODE_* from umac.h)
  52. * while the latter is used for sending commands to the
  53. * LMAC. If you look at LMAC commands you'll se that they
  54. * are actually regular iwlwifi target commands encapsulated
  55. * into a special UMAC command called UMAC passthrough.
  56. * This is due to the fact the the host talks exclusively
  57. * to the UMAC and so there needs to be a special UMAC
  58. * command for talking to the LMAC.
  59. * This is how a wifi command is layed out:
  60. * ------------------------
  61. * | iwm_udma_out_wifi_hdr |
  62. * ------------------------
  63. * | SW meta_data (32 bits) |
  64. * ------------------------
  65. * | iwm_dev_cmd_hdr |
  66. * ------------------------
  67. * | payload |
  68. * | .... |
  69. *
  70. * - non-wifi, or general commands:
  71. * Those commands are handled by the device's bootrom,
  72. * and are typically sent when the UMAC and the LMAC
  73. * are not yet available.
  74. * * This is how a non-wifi command is layed out:
  75. * ---------------------------
  76. * | iwm_udma_out_nonwifi_hdr |
  77. * ---------------------------
  78. * | payload |
  79. * | .... |
  80. *
  81. * All the commands start with a UDMA header, which is
  82. * basically a 32 bits field. The 4 LSB there define
  83. * an opcode that allows the target to differentiate
  84. * between wifi (opcode is 0xf) and non-wifi commands
  85. * (opcode is [0..0xe]).
  86. *
  87. * When a command (wifi or non-wifi) is supposed to receive
  88. * an answer, we queue the command buffer. When we do receive
  89. * a command response from the UMAC, we go through the list
  90. * of pending command, and pass both the command and the answer
  91. * to the rx handler. Each command is sent with a unique
  92. * sequence id, and the answer is sent with the same one. This
  93. * is how we're supposed to match an answer with its command.
  94. * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi()
  95. * for the implementation details.
  96. */
  97. #include <linux/kernel.h>
  98. #include <linux/netdevice.h>
  99. #include "iwm.h"
  100. #include "bus.h"
  101. #include "hal.h"
  102. #include "umac.h"
  103. #include "debug.h"
  104. static int iwm_nonwifi_cmd_init(struct iwm_priv *iwm,
  105. struct iwm_nonwifi_cmd *cmd,
  106. struct iwm_udma_nonwifi_cmd *udma_cmd)
  107. {
  108. INIT_LIST_HEAD(&cmd->pending);
  109. spin_lock(&iwm->cmd_lock);
  110. cmd->resp_received = 0;
  111. cmd->seq_num = iwm->nonwifi_seq_num;
  112. udma_cmd->seq_num = cpu_to_le16(cmd->seq_num);
  113. iwm->nonwifi_seq_num++;
  114. iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX;
  115. if (udma_cmd->resp)
  116. list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd);
  117. spin_unlock(&iwm->cmd_lock);
  118. cmd->buf.start = cmd->buf.payload;
  119. cmd->buf.len = 0;
  120. memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
  121. return cmd->seq_num;
  122. }
  123. u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm)
  124. {
  125. u16 seq_num = iwm->wifi_seq_num;
  126. iwm->wifi_seq_num++;
  127. iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX;
  128. return seq_num;
  129. }
  130. static void iwm_wifi_cmd_init(struct iwm_priv *iwm,
  131. struct iwm_wifi_cmd *cmd,
  132. struct iwm_udma_wifi_cmd *udma_cmd,
  133. struct iwm_umac_cmd *umac_cmd,
  134. struct iwm_lmac_cmd *lmac_cmd,
  135. u16 payload_size)
  136. {
  137. INIT_LIST_HEAD(&cmd->pending);
  138. spin_lock(&iwm->cmd_lock);
  139. cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm);
  140. umac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
  141. if (umac_cmd->resp)
  142. list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd);
  143. spin_unlock(&iwm->cmd_lock);
  144. cmd->buf.start = cmd->buf.payload;
  145. cmd->buf.len = 0;
  146. if (lmac_cmd) {
  147. cmd->buf.start -= sizeof(struct iwm_lmac_hdr);
  148. lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num);
  149. lmac_cmd->count = cpu_to_le16(payload_size);
  150. memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd));
  151. umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr));
  152. } else
  153. umac_cmd->count = 0;
  154. umac_cmd->count = cpu_to_le16(payload_size +
  155. le16_to_cpu(umac_cmd->count));
  156. udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) +
  157. le16_to_cpu(umac_cmd->count));
  158. memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd));
  159. memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd));
  160. }
  161. void iwm_cmd_flush(struct iwm_priv *iwm)
  162. {
  163. struct iwm_wifi_cmd *wcmd, *wnext;
  164. struct iwm_nonwifi_cmd *nwcmd, *nwnext;
  165. list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) {
  166. list_del(&wcmd->pending);
  167. kfree(wcmd);
  168. }
  169. list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd,
  170. pending) {
  171. list_del(&nwcmd->pending);
  172. kfree(nwcmd);
  173. }
  174. }
  175. struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num)
  176. {
  177. struct iwm_wifi_cmd *cmd, *next;
  178. list_for_each_entry_safe(cmd, next, &iwm->wifi_pending_cmd, pending)
  179. if (cmd->seq_num == seq_num) {
  180. list_del(&cmd->pending);
  181. return cmd;
  182. }
  183. return NULL;
  184. }
  185. struct iwm_nonwifi_cmd *
  186. iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, u8 seq_num, u8 cmd_opcode)
  187. {
  188. struct iwm_nonwifi_cmd *cmd, *next;
  189. list_for_each_entry_safe(cmd, next, &iwm->nonwifi_pending_cmd, pending)
  190. if ((cmd->seq_num == seq_num) &&
  191. (cmd->udma_cmd.opcode == cmd_opcode) &&
  192. (cmd->resp_received)) {
  193. list_del(&cmd->pending);
  194. return cmd;
  195. }
  196. return NULL;
  197. }
  198. static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm,
  199. struct iwm_udma_out_nonwifi_hdr *hdr,
  200. struct iwm_udma_nonwifi_cmd *cmd)
  201. {
  202. memset(hdr, 0, sizeof(*hdr));
  203. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode);
  204. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp);
  205. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1);
  206. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW,
  207. cmd->handle_by_hw);
  208. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
  209. SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM,
  210. le16_to_cpu(cmd->seq_num));
  211. hdr->addr = cmd->addr;
  212. hdr->op1_sz = cmd->op1_sz;
  213. hdr->op2 = cmd->op2;
  214. }
  215. static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm,
  216. struct iwm_nonwifi_cmd *cmd)
  217. {
  218. struct iwm_udma_out_nonwifi_hdr *udma_hdr;
  219. struct iwm_nonwifi_cmd_buff *buf;
  220. struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd;
  221. buf = &cmd->buf;
  222. buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr);
  223. buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr);
  224. udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start);
  225. iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd);
  226. IWM_DBG_CMD(iwm, DBG,
  227. "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, "
  228. "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, "
  229. "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp,
  230. udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr,
  231. udma_cmd->op1_sz, udma_cmd->op2);
  232. return iwm_bus_send_chunk(iwm, buf->start, buf->len);
  233. }
  234. void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop)
  235. {
  236. struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf;
  237. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop);
  238. }
  239. void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm,
  240. struct iwm_udma_out_wifi_hdr *hdr,
  241. struct iwm_udma_wifi_cmd *cmd)
  242. {
  243. memset(hdr, 0, sizeof(*hdr));
  244. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI);
  245. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop);
  246. SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE);
  247. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT,
  248. le16_to_cpu(cmd->count));
  249. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group);
  250. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid);
  251. SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset);
  252. }
  253. void iwm_build_umac_hdr(struct iwm_priv *iwm,
  254. struct iwm_umac_fw_cmd_hdr *hdr,
  255. struct iwm_umac_cmd *cmd)
  256. {
  257. memset(hdr, 0, sizeof(*hdr));
  258. SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT,
  259. le16_to_cpu(cmd->count));
  260. SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color);
  261. SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp);
  262. hdr->cmd.cmd = cmd->id;
  263. hdr->cmd.seq_num = cmd->seq_num;
  264. }
  265. static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm,
  266. struct iwm_wifi_cmd *cmd)
  267. {
  268. struct iwm_umac_wifi_out_hdr *umac_hdr;
  269. struct iwm_wifi_cmd_buff *buf;
  270. struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd;
  271. struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd;
  272. int ret;
  273. buf = &cmd->buf;
  274. buf->start -= sizeof(struct iwm_umac_wifi_out_hdr);
  275. buf->len += sizeof(struct iwm_umac_wifi_out_hdr);
  276. umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start);
  277. iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd);
  278. iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd);
  279. IWM_DBG_CMD(iwm, DBG,
  280. "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, "
  281. "eop = 0x%x, count = 0x%x, credit_group = 0x%x, "
  282. "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n",
  283. UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id,
  284. udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group,
  285. udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num);
  286. if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH)
  287. IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n",
  288. cmd->lmac_cmd.id);
  289. ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len);
  290. /* We keep sending UMAC reset regardless of the command credits.
  291. * The UMAC is supposed to be reset anyway and the Tx credits are
  292. * reinitialized afterwards. If we are lucky, the reset could
  293. * still be done even though we have run out of credits for the
  294. * command pool at this moment.*/
  295. if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) {
  296. IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n",
  297. umac_cmd->id);
  298. return ret;
  299. }
  300. return iwm_bus_send_chunk(iwm, buf->start, buf->len);
  301. }
  302. /* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */
  303. int iwm_hal_send_target_cmd(struct iwm_priv *iwm,
  304. struct iwm_udma_nonwifi_cmd *udma_cmd,
  305. const void *payload)
  306. {
  307. struct iwm_nonwifi_cmd *cmd;
  308. int ret, seq_num;
  309. cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL);
  310. if (!cmd) {
  311. IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n");
  312. return -ENOMEM;
  313. }
  314. seq_num = iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd);
  315. if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE ||
  316. cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) {
  317. cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz);
  318. memcpy(&cmd->buf.payload, payload, cmd->buf.len);
  319. }
  320. ret = iwm_send_udma_nonwifi_cmd(iwm, cmd);
  321. if (!udma_cmd->resp)
  322. kfree(cmd);
  323. if (ret < 0)
  324. return ret;
  325. return seq_num;
  326. }
  327. static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr,
  328. struct iwm_lmac_cmd *cmd)
  329. {
  330. memset(hdr, 0, sizeof(*hdr));
  331. hdr->id = cmd->id;
  332. hdr->flags = 0; /* Is this ever used? */
  333. hdr->seq_num = cmd->seq_num;
  334. }
  335. /*
  336. * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC.
  337. * Sending command to the LMAC is equivalent to sending a
  338. * regular UMAC command with the LMAC passthrough or the LMAC
  339. * wrapper UMAC command IDs.
  340. */
  341. int iwm_hal_send_host_cmd(struct iwm_priv *iwm,
  342. struct iwm_udma_wifi_cmd *udma_cmd,
  343. struct iwm_umac_cmd *umac_cmd,
  344. struct iwm_lmac_cmd *lmac_cmd,
  345. const void *payload, u16 payload_size)
  346. {
  347. struct iwm_wifi_cmd *cmd;
  348. struct iwm_lmac_hdr *hdr;
  349. int lmac_hdr_len = 0;
  350. int ret;
  351. cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL);
  352. if (!cmd) {
  353. IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n");
  354. return -ENOMEM;
  355. }
  356. iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size);
  357. if (lmac_cmd) {
  358. hdr = (struct iwm_lmac_hdr *)(cmd->buf.start);
  359. iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd);
  360. lmac_hdr_len = sizeof(struct iwm_lmac_hdr);
  361. }
  362. memcpy(cmd->buf.payload, payload, payload_size);
  363. cmd->buf.len = le16_to_cpu(umac_cmd->count);
  364. ret = iwm_send_udma_wifi_cmd(iwm, cmd);
  365. /* We free the cmd if we're not expecting any response */
  366. if (!umac_cmd->resp)
  367. kfree(cmd);
  368. return ret;
  369. }
  370. /*
  371. * iwm_hal_send_umac_cmd(): This is a special case for
  372. * iwm_hal_send_host_cmd() to send direct UMAC cmd (without
  373. * LMAC involved).
  374. */
  375. int iwm_hal_send_umac_cmd(struct iwm_priv *iwm,
  376. struct iwm_udma_wifi_cmd *udma_cmd,
  377. struct iwm_umac_cmd *umac_cmd,
  378. const void *payload, u16 payload_size)
  379. {
  380. return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL,
  381. payload, payload_size);
  382. }